diff --git a/.gitignore b/.gitignore index 1d7f134b09f71b9320e2868281c4c862a51bd78b..e8c1b89542e88dec12df439815950e165df4197e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ChangeLog \#* .\#* +.php_cs_cache TAGS *~ /downloads @@ -16,4 +17,5 @@ import/solrmarc.log* /solr /auth.json /core -/.env \ No newline at end of file +/.env +/lessphp_*.list \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1baa7a4fd129f325615fc66f791dbd9306b7171d..9284368abf20b63885d4b7a4dc9e3bf60b6020bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ sudo: false language: php php: - - 5.6 - 7.0 - 7.1 + - 7.2 env: - VUFIND_HOME=$PWD VUFIND_LOCAL_DIR=$PWD/local @@ -12,12 +12,13 @@ before_script: - phpenv config-rm xdebug.ini - phpenv rehash - composer install - - npm install -g eslint@"<3.0.0" + - npm install -g eslint@"<5.0.0" cache: directories: - downloads - - $HOME/.composer/cache + - .php_cs_cache + - $HOME/.composer/cache/files script: - - vendor/bin/phing phpunitfast phpcs-console php-cs-fixer-dryrun eslint + - vendor/bin/phing eslint phpunitfast phpcs-console php-cs-fixer-dryrun diff --git a/Gruntfile.js b/Gruntfile.js index 1df1ff8ba33e475aa5b6c7842d65714e344dbca6..f5769783342cf57c0676d9475edd6a181d6dba39 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -150,7 +150,13 @@ module.exports = function(grunt) { pattern: '$brand-primary: #619144 !default;', replacement: '$brand-primary: #619144;', order: 4 - } + }, + // Wrap calcs in {} + { + pattern: /calc\((\$[^ ]+)/g, + replacement: 'calc(#{$1}', + order: 5 + }, ] } } diff --git a/build.xml b/build.xml index 000a90a820d05a544c64677e4641372a248a5643..d389674afa4107c3c5c3fd6f1eac30380e7b44ae 100644 --- a/build.xml +++ b/build.xml @@ -25,19 +25,16 @@ <property name="selenium_browser" value="firefox" /> <property name="snooze_multiplier" value="1" /><!-- can be used to slow down tests (selenium only) --> <property name="solr_startup_sleep" value="0" /> - <property name="solr_version" value="6.4.2" /> - <property name="php-cs-fixers" value="no_blank_lines_before_namespaces,function_call_space,trailing_spaces,unused_use,lowercase_keywords,encoding,parenthesis,php_closing_tag,visibility,duplicate_semicolon,extra_empty_lines,no_blank_lines_after_class_opening,no_empty_lines_after_phpdocs,operators_spaces,spaces_before_semicolon,ternary_spaces,concat_with_spaces,short_array_syntax,phpdoc_no_access,remove_leading_slash_use,eof_ending" /> + <property name="solr_version" value="7.3.1" /> + <property name="phpdoc_version" value="2.9.0" /> - - <property name="version" value="4.1.3" /> + <property name="version" value="5.0.1" /> <!-- We only need the -p switch if the password is non-blank --> <if><not><equals arg1="${mysqlrootpass}" arg2="" /></not><then> <property name="mysqlpwswitch" value="-p" /> </then></if> - <includepath classpath="${srcdir}/vendor/phpdocumentor" /> - <!-- Main Target --> <target name="main" description="main target"> <phingcall target="startup" /> @@ -93,19 +90,24 @@ </target> <!-- PHP CodeSniffer --> + <target name="phpcbf"> + <exec command="${srcdir}/vendor/bin/phpcbf --standard=${srcdir}/tests/phpcs.xml" escape="false" passthru="true" checkreturn="true" /> + </target> <target name="phpcs"> - <exec command="${srcdir}/vendor/bin/phpcs --standard=PEAR --ignore=*/config/*,*/tests/* --extensions=php --report=checkstyle ${srcdir}/module > ${builddir}/reports/checkstyle.xml" escape="false" /> + <exec command="${srcdir}/vendor/bin/phpcs --standard=${srcdir}/tests/phpcs.xml --report=checkstyle > ${builddir}/reports/checkstyle.xml" escape="false" /> </target> <target name="phpcs-console"> - <exec command="${srcdir}/vendor/bin/phpcs --standard=PEAR --ignore=*/config/*,*/tests/* --extensions=php ${srcdir}/module" escape="false" passthru="true" checkreturn="true" /> + <exec command="${srcdir}/vendor/bin/phpcs --standard=${srcdir}/tests/phpcs.xml" escape="false" passthru="true" checkreturn="true" /> </target> <!-- php-cs-fixer (first task applies fixes, second task simply checks if they are needed) --> <target name="php-cs-fixer"> - <exec command="${srcdir}/vendor/bin/php-cs-fixer fix ${srcdir}/module --fixers=${php-cs-fixers} --verbose" passthru="true" escape="false" /> + <exec command="${srcdir}/vendor/bin/php-cs-fixer fix --config=${srcdir}/tests/vufind.php_cs -vvv" passthru="true" escape="false" /> + <exec command="${srcdir}/vendor/bin/php-cs-fixer fix --config=${srcdir}/tests/vufind_templates.php_cs -vvv" passthru="true" escape="false" /> </target> <target name="php-cs-fixer-dryrun"> - <exec command="${srcdir}/vendor/bin/php-cs-fixer fix ${srcdir}/module --fixers=${php-cs-fixers} --dry-run --verbose --diff" passthru="true" escape="false" checkreturn="true" /> + <exec command="${srcdir}/vendor/bin/php-cs-fixer fix --config=${srcdir}/tests/vufind.php_cs --dry-run -vvv --diff" passthru="true" escape="false" checkreturn="true" /> + <exec command="${srcdir}/vendor/bin/php-cs-fixer fix --config=${srcdir}/tests/vufind_templates.php_cs --dry-run -vvv --diff" passthru="true" escape="false" checkreturn="true" /> </target> <!-- ESLint --> @@ -121,13 +123,30 @@ <!-- PHP API Documentation --> <target name="phpdoc"> - <mkdir dir="${builddir}/apidocs"/> + <!-- GET phpDocumentor.phar --> + <if> + <not><available file="${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar" /></not> + <then> + <httpget followRedirects="true" url="https://github.com/phpDocumentor/phpDocumentor2/releases/download/v${phpdoc_version}/phpDocumentor.phar" dir="${srcdir}/vendor/bin" filename="phpDocumentor-${phpdoc_version}.phar" /> + <chmod mode="0755"> + <fileset dir="${srcdir}/vendor/bin"> + <include name="phpDocumentor-${phpdoc_version}.phar" /> + </fileset> + </chmod> + </then> + </if> + <!-- Run phpdoc --> + <mkdir dir="${builddir}/apidocs" /> + <!-- Old embedded version; no longer works correctly... <phpdoc2 title="VuFind API Documentation" + pharlocation="${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar" destdir="${builddir}/apidocs"> <fileset dir="."> <include name="module/*/src/**/*.php" /> </fileset> </phpdoc2> + --> + <exec command="php ${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar --title="VuFind API Documentation" -t ${builddir}/apidocs -d ${srcdir}/module" passthru="true" /> </target> <!-- PHPUnit --> @@ -164,9 +183,10 @@ </then> </if> <!-- unpack the archive into solr/vendor --> - <untar file="${srcdir}/downloads/solr-${solr_version}.tgz" todir="${tmp}" /> + <mkdir dir="${builddir}/solr" /> + <untar file="${srcdir}/downloads/solr-${solr_version}.tgz" todir="${builddir}/solr" /> <delete dir="${srcdir}/solr/vendor" includeemptydirs="true" failonerror="false" /> - <move file="${tmp}/solr-${solr_version}" tofile="${srcdir}/solr/vendor" /> + <move file="${builddir}/solr/solr-${solr_version}" tofile="${srcdir}/solr/vendor" /> <!-- make scripts executable --> <chmod mode="0755"> <fileset dir="${srcdir}/solr/vendor/bin"> diff --git a/composer.json b/composer.json index bd4392f7131408791d0c23cdab089c88dc6af883..3b6692f2dad1a47e079cd4900120104aee681d8c 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "license": "GPL-2.0", "config": { "platform": { - "php": "5.6.0" + "php": "7.0.8" } }, "repositories": [ @@ -20,72 +20,75 @@ } ], "require": { - "php": ">=5.6", - "aferrandini/phpqrcode": "1.0.1", - "jasig/phpcas": "1.3.5", - "cap60552/php-sip2": "1.0.0", + "php": ">=7.0.8", "ahand/mobileesp": "dev-master", - "matthiasmullie/minify": "1.3.45", - "ocramius/proxy-manager": "1.0.2", + "cap60552/php-sip2": "1.0.0", + "endroid/qr-code": "2.5.0", + "ghislainf/zf2-whoops": "dev-master#2649cf7caf400409942ddc3f8fe15b89381fc74e", + "jasig/phpcas": "1.3.5", + "matthiasmullie/minify": "1.3.60", + "ocramius/proxy-manager": "2.0.4", "oyejorge/less.php": "1.7.0.14", - "pear/file_marc": "1.1.5", + "pear/archive_tar": "^1.4", + "pear/file_marc": "1.2.0", "pear/http_request2": "2.3.0", "pear/validate_ispn": "dev-master", - "phing/phing": "2.16.0", + "phing/phing": "2.16.1", "serialssolutions/summon": "1.1.0", - "symfony/yaml": "3.3.2", + "symfony/yaml": "3.4.10", "swagger-api/swagger-ui": "2.2.10", - "vufind-org/vufindcode": "1.0.3", - "vufind-org/vufindharvest": "2.3.0", - "vufind-org/vufindhttp": "2.1.1", - "yajra/laravel-pdo-via-oci8": "1.3.1", - "zendframework/zend-cache": "2.7.2", - "zendframework/zend-captcha": "2.7.1", - "zendframework/zend-code": "2.6.3", - "zendframework/zend-config": "2.6.0", + "vufind-org/vufindcode": "1.1.0", + "vufind-org/vufinddate": "1.0.0", + "vufind-org/vufindharvest": "2.4.0", + "vufind-org/vufindhttp": "2.2.0", + "yajra/laravel-pdo-via-oci8": "1.3.6", + "zendframework/zend-cache": "2.8.2", + "zendframework/zend-captcha": "2.8.0", + "zendframework/zend-code": "3.1.0", + "zendframework/zend-config": "3.2.0", "zendframework/zend-console": "2.7.0", - "zendframework/zend-crypt": "3.2.0", - "zendframework/zend-db": "2.8.2", - "zendframework/zend-dom": "2.6.0", - "zendframework/zend-escaper": "2.5.2", - "zendframework/zend-eventmanager": "3.2.0", - "zendframework/zend-feed": "2.8.0", - "zendframework/zend-http": "2.6.0", - "zendframework/zend-i18n": "2.7.4", - "zendframework/zend-loader": "2.5.1", - "zendframework/zend-log": "2.9.2", + "zendframework/zend-crypt": "3.3.0", + "zendframework/zend-db": "2.9.3", + "zendframework/zend-dom": "2.7.1", + "zendframework/zend-escaper": "2.6.0", + "zendframework/zend-eventmanager": "3.2.1", + "zendframework/zend-feed": "2.10.3", + "zendframework/zend-filter": "2.8.0", + "zendframework/zend-http": "2.8.2", + "zendframework/zend-i18n": "2.9.0", + "zendframework/zend-loader": "2.6.0", + "zendframework/zend-log": "2.10.0", "zendframework/zend-mail": "2.8.0", - "zendframework/zend-modulemanager": "2.8.0", - "zendframework/zend-mvc": "2.7.15", - "zendframework/zend-paginator": "2.7.0", - "zendframework/zend-serializer": "2.8.0", - "zendframework/zend-servicemanager": "dev-master#ae73b1b3affd9c4e3031fb88c84a7082368ca6c3", - "zendframework/zend-session": "2.8.0", - "zendframework/zend-soap": "2.6.0", - "zendframework/zend-stdlib": "2.7.7", - "zendframework/zend-text": "2.6.0", - "zendframework/zend-validator": "2.10.1", - "zendframework/zend-view": "2.9.0", + "zendframework/zend-modulemanager": "2.8.2", + "zendframework/zend-mvc": "3.1.1", + "zendframework/zend-mvc-console": "1.2.0", + "zendframework/zend-mvc-i18n": "1.1.0", + "zendframework/zend-mvc-plugin-flashmessenger": "1.1.0", + "zendframework/zend-paginator": "2.8.1", + "zendframework/zend-serializer": "2.9.0", + "zendframework/zend-servicemanager": "3.3.2", + "zendframework/zend-session": "2.8.5", + "zendframework/zend-soap": "2.7.0", + "zendframework/zend-stdlib": "3.2.0", + "zendframework/zend-text": "2.7.0", + "zendframework/zend-validator": "2.10.2", + "zendframework/zend-view": "2.10.0", "zendframework/zendrest": "2.0.2", "zendframework/zendservice-amazon": "2.3.0", - "zendframework/zendservice-recaptcha": "3.0.0", + "zendframework/zendservice-recaptcha": "3.1.0", "zf-commons/zfc-rbac": "2.6.3", - "ghislainf/zf2-whoops": "dev-master#2649cf7caf400409942ddc3f8fe15b89381fc74e", - "pear/archive_tar": "^1.4", "sabre/vobject": "3.5.3", "finc/rules-evaluator": "v0.0.3" }, "require-dev": { "behat/mink": "1.7.1", "behat/mink-selenium2-driver": "1.3.1", - "friendsofphp/php-cs-fixer": "1.11.6", - "phpdocumentor/phpdocumentor": "2.9.0", - "phploc/phploc": "3.0.1", + "friendsofphp/php-cs-fixer": "2.11.1", + "phploc/phploc": "4.0.1", "phpmd/phpmd": "2.6.0", - "phpunit/phpunit": "5.7.15", - "sebastian/phpcpd": "2.0.4", - "squizlabs/php_codesniffer": "2.8.1", - "twig/twig": "1.27" + "phpunit/phpunit": "6.5.8", + "sebastian/phpcpd": "3.0.1", + "squizlabs/php_codesniffer": "3.2.3" }, "scripts": { "phing-install-dependencies": "phing installsolr installswaggerui", diff --git a/composer.lock b/composer.lock index 458fb391faf10e0213d69d14532f620d37eac496..a8c7b8b9809b06ad75480e841b393f853049a8f5 100644 --- a/composer.lock +++ b/composer.lock @@ -1,56 +1,11 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "54282513949fcbb541d9346bcd7dcc0a", + "content-hash": "2de76da807eebd174adf10ac0713ffa5", "packages": [ - { - "name": "aferrandini/phpqrcode", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/aferrandini/PHPQRCode.git", - "reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/aferrandini/PHPQRCode/zipball/3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46", - "reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "PHPQRCode": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ariel Ferrandini", - "email": "arielferrandini@gmail.com", - "homepage": "http://www.ferrandini.com/", - "role": "Developer" - } - ], - "description": "PHPQRCode porting and changed for PHP 5.3 compatibility", - "homepage": "https://github.com/aferrandini/PHPQRCode", - "keywords": [ - "barcode", - "php", - "qrcode" - ], - "abandoned": "endroid/qr-code", - "time": "2013-07-08T09:39:08+00:00" - }, { "name": "ahand/mobileesp", "version": "dev-master", @@ -105,6 +60,52 @@ ], "time": "2017-06-06T22:20:56+00:00" }, + { + "name": "bacon/bacon-qr-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "5a91b62b9d37cee635bbf8d553f4546057250bee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/5a91b62b9d37cee635bbf8d553f4546057250bee", + "reference": "5a91b62b9d37cee635bbf8d553f4546057250bee", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^5.4|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8" + }, + "suggest": { + "ext-gd": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-0": { + "BaconQrCode": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "http://www.dasprids.de", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "time": "2017-10-17T09:59:25+00:00" + }, { "name": "cap60552/php-sip2", "version": "v1.0.0", @@ -171,6 +172,75 @@ "homepage": "https://github.com/container-interop/container-interop", "time": "2017-02-14T19:40:03+00:00" }, + { + "name": "endroid/qr-code", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/endroid/qr-code.git", + "reference": "a9a57ab57ac75928fcdcfb2a71179963ff6fe573" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/endroid/qr-code/zipball/a9a57ab57ac75928fcdcfb2a71179963ff6fe573", + "reference": "a9a57ab57ac75928fcdcfb2a71179963ff6fe573", + "shasum": "" + }, + "require": { + "bacon/bacon-qr-code": "^1.0.3", + "ext-gd": "*", + "khanamiryan/qrcode-detector-decoder": "^1.0", + "myclabs/php-enum": "^1.5", + "php": ">=5.6", + "symfony/options-resolver": ">=2.7", + "symfony/property-access": ">=2.7" + }, + "require-dev": { + "phpunit/phpunit": ">=5.7", + "symfony/asset": ">=2.7", + "symfony/browser-kit": ">=2.7", + "symfony/finder": ">=2.7", + "symfony/framework-bundle": ">=2.7", + "symfony/http-kernel": ">=2.7", + "symfony/templating": ">=2.7", + "symfony/twig-bundle": ">=2.7", + "symfony/yaml": ">=2.7" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Endroid\\QrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeroen van den Enden", + "email": "info@endroid.nl", + "homepage": "http://endroid.nl/" + } + ], + "description": "Endroid QR Code", + "homepage": "https://github.com/endroid/QrCode", + "keywords": [ + "bundle", + "code", + "endroid", + "flex", + "qr", + "qrcode", + "symfony" + ], + "time": "2017-10-22T18:56:00+00:00" + }, { "name": "filp/whoops", "version": "2.2.0", @@ -232,40 +302,6 @@ ], "time": "2018-03-03T17:56:25+00:00" }, - { - "name": "finc/rules-evaluator", - "version": "v0.0.3", - "source": { - "type": "git", - "url": "https://git.sc.uni-leipzig.de/ubl/finc/rules-evaluator.git", - "reference": "10561b959e3793c36372eba792cb1709e868a089" - }, - "require": { - "php": ">=5.6", - "symfony/expression-language": "^3.4" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "finc\\Rules\\Evaluator\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0" - ], - "authors": [ - { - "name": "Sebastian Kehr", - "email": "kehr@ub.uni-leipzig.de" - } - ], - "description": "Rules evaluator based on Symfony Expression Language Component.", - "time": "2018-08-09T09:54:19+00:00" - }, { "name": "ghislainf/zf2-whoops", "version": "dev-master", @@ -378,18 +414,68 @@ ], "time": "2017-04-10T19:12:45+00:00" }, + { + "name": "khanamiryan/qrcode-detector-decoder", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git", + "reference": "a75482d3bc804e3f6702332bfda6cccbb0dfaa76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/a75482d3bc804e3f6702332bfda6cccbb0dfaa76", + "reference": "a75482d3bc804e3f6702332bfda6cccbb0dfaa76", + "shasum": "" + }, + "require": { + "php": "^5.6|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Zxing\\": "lib/" + }, + "files": [ + "lib/Common/customFunctions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ashot Khanamiryan", + "email": "a.khanamiryan@gmail.com", + "homepage": "https://github.com/khanamiryan", + "role": "Developer" + } + ], + "description": "QR code decoder / reader", + "homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder/", + "keywords": [ + "barcode", + "qr", + "zxing" + ], + "time": "2018-04-26T11:41:33+00:00" + }, { "name": "matthiasmullie/minify", - "version": "1.3.45", + "version": "1.3.60", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "09b83e9dbdc50cf6734c6a9652a54891c0651998" + "reference": "ab7fea80ce5ce6549baaf272bc8bd926a7e08f90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/09b83e9dbdc50cf6734c6a9652a54891c0651998", - "reference": "09b83e9dbdc50cf6734c6a9652a54891c0651998", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ab7fea80ce5ce6549baaf272bc8bd926a7e08f90", + "reference": "ab7fea80ce5ce6549baaf272bc8bd926a7e08f90", "shasum": "" }, "require": { @@ -398,7 +484,7 @@ "php": ">=5.3.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~1.0", + "friendsofphp/php-cs-fixer": "~2.0", "matthiasmullie/scrapbook": "~1.0", "phpunit/phpunit": "~4.8" }, @@ -427,7 +513,7 @@ "role": "Developer" } ], - "description": "CSS & JS minifier", + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", "homepage": "http://www.minifier.org", "keywords": [ "JS", @@ -436,7 +522,7 @@ "minifier", "minify" ], - "time": "2017-06-13T15:54:31+00:00" + "time": "2018-04-18T08:50:35+00:00" }, { "name": "matthiasmullie/path-converter", @@ -487,40 +573,135 @@ ], "time": "2018-02-02T11:30:10+00:00" }, + { + "name": "myclabs/php-enum", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "ca2f4090a7ecae6f0c67fc9bd07cfb51cdf04219" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/ca2f4090a7ecae6f0c67fc9bd07cfb51cdf04219", + "reference": "ca2f4090a7ecae6f0c67fc9bd07cfb51cdf04219", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ], + "time": "2018-08-01T21:05:54+00:00" + }, + { + "name": "ocramius/package-versions", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "ad8a245decad4897cc6b432743913dad0d69753c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/ad8a245decad4897cc6b432743913dad0d69753c", + "reference": "ad8a245decad4897cc6b432743913dad0d69753c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "php": "~7.0" + }, + "require-dev": { + "composer/composer": "^1.3", + "ext-zip": "*", + "humbug/humbug": "dev-master", + "phpunit/phpunit": "^6.4" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2017-11-24T11:07:03+00:00" + }, { "name": "ocramius/proxy-manager", - "version": "1.0.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/Ocramius/ProxyManager.git", - "reference": "57e9272ec0e8deccf09421596e0e2252df440e11" + "reference": "a55d08229f4f614bf335759ed0cf63378feeb2e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/57e9272ec0e8deccf09421596e0e2252df440e11", - "reference": "57e9272ec0e8deccf09421596e0e2252df440e11", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/a55d08229f4f614bf335759ed0cf63378feeb2e6", + "reference": "a55d08229f4f614bf335759ed0cf63378feeb2e6", "shasum": "" }, "require": { - "php": ">=5.3.3", - "zendframework/zend-code": ">2.2.5,<3.0" + "ocramius/package-versions": "^1.0", + "php": "7.0.0 - 7.0.5 || ^7.0.7", + "zendframework/zend-code": "3.0.0 - 3.0.2 || ^3.0.4" }, "require-dev": { + "couscous/couscous": "^1.4.0", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "1.5.*" + "phpbench/phpbench": "^0.11.2", + "phpunit/phpunit": "^5.4.6", + "squizlabs/php_codesniffer": "^2.6.0" }, "suggest": { "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects", "zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)", "zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)", - "zendframework/zend-stdlib": "To use the hydrator proxy", "zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -536,7 +717,7 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "http://ocramius.github.io/" } ], "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", @@ -548,7 +729,7 @@ "proxy pattern", "service proxies" ], - "time": "2015-08-09T04:28:19+00:00" + "time": "2016-11-04T15:53:15+00:00" }, { "name": "oyejorge/less.php", @@ -776,16 +957,16 @@ }, { "name": "pear/file_marc", - "version": "1.1.5", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/pear/File_MARC.git", - "reference": "ecf5a79661595e3804ad4c0ede984757b4fe8d8f" + "reference": "84b7f633c9261245bf6b16d7fbe87fe503551c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/File_MARC/zipball/ecf5a79661595e3804ad4c0ede984757b4fe8d8f", - "reference": "ecf5a79661595e3804ad4c0ede984757b4fe8d8f", + "url": "https://api.github.com/repos/pear/File_MARC/zipball/84b7f633c9261245bf6b16d7fbe87fe503551c9a", + "reference": "84b7f633c9261245bf6b16d7fbe87fe503551c9a", "shasum": "" }, "require": { @@ -819,7 +1000,7 @@ } ], "description": "Supports the MAchine Readable Cataloging (MARC) file format documented at http://loc.gov/marc/", - "time": "2017-01-23T16:49:13+00:00" + "time": "2017-12-04T10:30:19+00:00" }, { "name": "pear/http_request2", @@ -1164,21 +1345,21 @@ }, { "name": "phing/phing", - "version": "2.16.0", + "version": "2.16.1", "source": { "type": "git", "url": "https://github.com/phingofficial/phing.git", - "reference": "151a0f4d8cebf7711eccc62dde3f09bc36a00d7b" + "reference": "cbe0f969e434e269af91b4160b86fe899c6e07c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phingofficial/phing/zipball/151a0f4d8cebf7711eccc62dde3f09bc36a00d7b", - "reference": "151a0f4d8cebf7711eccc62dde3f09bc36a00d7b", + "url": "https://api.github.com/repos/phingofficial/phing/zipball/cbe0f969e434e269af91b4160b86fe899c6e07c7", + "reference": "cbe0f969e434e269af91b4160b86fe899c6e07c7", "shasum": "" }, "require": { "php": ">=5.2.0", - "symfony/yaml": "^3.1" + "symfony/yaml": "^3.1 || ^4.0" }, "require-dev": { "ext-pdo_sqlite": "*", @@ -1253,7 +1434,7 @@ "task", "tool" ], - "time": "2016-12-22T20:16:33+00:00" + "time": "2018-01-25T13:18:09+00:00" }, { "name": "psr/cache", @@ -1351,17 +1532,17 @@ "time": "2017-02-14T16:28:37+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "psr/log", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "shasum": "" }, "require": { @@ -1375,7 +1556,7 @@ }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1388,30 +1569,27 @@ "homepage": "http://www.php-fig.org/" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "http", - "http-message", + "log", "psr", - "psr-7", - "request", - "response" + "psr-3" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2016-10-10T12:19:37+00:00" }, { - "name": "psr/log", - "version": "1.0.2", + "name": "psr/simple-cache", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", "shasum": "" }, "require": { @@ -1425,7 +1603,7 @@ }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\SimpleCache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1438,129 +1616,16 @@ "homepage": "http://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Common interfaces for simple caching", "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2016-10-10T12:19:37+00:00" - }, - { - "name": "psr/simple-cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", + "cache", + "caching", "psr", "psr-16", "simple-cache" ], "time": "2017-10-23T01:57:42+00:00" }, - { - "name": "sabre/vobject", - "version": "3.5.3", - "source": { - "type": "git", - "url": "https://github.com/sabre-io/vobject.git", - "reference": "129d80533a9ec0d9cacfb50b51180c34edb6874c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sabre-io/vobject/zipball/129d80533a9ec0d9cacfb50b51180c34edb6874c", - "reference": "129d80533a9ec0d9cacfb50b51180c34edb6874c", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "*", - "squizlabs/php_codesniffer": "*" - }, - "bin": [ - "bin/vobject", - "bin/generate_vcards" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Sabre\\VObject\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Evert Pot", - "email": "me@evertpot.com", - "homepage": "http://evertpot.com/", - "role": "Developer" - }, - { - "name": "Dominik Tobschall", - "email": "dominik@fruux.com", - "homepage": "http://tobschall.de/", - "role": "Developer" - } - ], - "description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects", - "homepage": "http://sabre.io/vobject/", - "keywords": [ - "VObject", - "iCalendar", - "jCal", - "jCard", - "vCard" - ], - "time": "2016-10-07T03:20:40+00:00" - }, { "name": "serialssolutions/summon", "version": "v1.1.0", @@ -1636,38 +1701,22 @@ "time": "2017-01-05T08:57:09+00:00" }, { - "name": "symfony/cache", + "name": "symfony/inflector", "version": "v3.4.14", "source": { "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "337408485de75c884e6ab1b2e8f055d31a7aa7b6" + "url": "https://github.com/symfony/inflector.git", + "reference": "129ac0c59e516f5fe210efe51a44f79ab9925c16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/337408485de75c884e6ab1b2e8f055d31a7aa7b6", - "reference": "337408485de75c884e6ab1b2e8f055d31a7aa7b6", + "url": "https://api.github.com/repos/symfony/inflector/zipball/129ac0c59e516f5fe210efe51a44f79ab9925c16", + "reference": "129ac0c59e516f5fe210efe51a44f79ab9925c16", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", - "psr/cache": "~1.0", - "psr/log": "~1.0", - "psr/simple-cache": "^1.0", - "symfony/polyfill-apcu": "~1.1" - }, - "conflict": { - "symfony/var-dumper": "<3.3" - }, - "provide": { - "psr/cache-implementation": "1.0", - "psr/simple-cache-implementation": "1.0" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/cache": "~1.6", - "doctrine/dbal": "~2.4", - "predis/predis": "~1.0" + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { @@ -1677,7 +1726,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Component\\Cache\\": "" + "Symfony\\Component\\Inflector\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1689,39 +1738,42 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "description": "Symfony Inflector Component", "homepage": "https://symfony.com", "keywords": [ - "caching", - "psr6" + "inflection", + "pluralize", + "singularize", + "string", + "symfony", + "words" ], - "time": "2018-07-26T11:19:56+00:00" + "time": "2018-07-26T08:45:46+00:00" }, { - "name": "symfony/expression-language", + "name": "symfony/options-resolver", "version": "v3.4.14", "source": { "type": "git", - "url": "https://github.com/symfony/expression-language.git", - "reference": "d60e37ab9838338d959f2cc177a66cf498293788" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "6debc476953a45969ab39afe8dee0b825f356dc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/d60e37ab9838338d959f2cc177a66cf498293788", - "reference": "d60e37ab9838338d959f2cc177a66cf498293788", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6debc476953a45969ab39afe8dee0b825f356dc7", + "reference": "6debc476953a45969ab39afe8dee0b825f356dc7", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/cache": "~3.1|~4.0" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { @@ -1731,7 +1783,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Component\\ExpressionLanguage\\": "" + "Symfony\\Component\\OptionsResolver\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1751,25 +1803,89 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony ExpressionLanguage Component", + "description": "Symfony OptionsResolver Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2018-07-26T08:45:46+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-08-06T14:22:27+00:00" }, { - "name": "symfony/polyfill-apcu", + "name": "symfony/polyfill-php70", "version": "v1.9.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-apcu.git", - "reference": "19e1b73bf255265ad0b568f81766ae2a3266d8d2" + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/19e1b73bf255265ad0b568f81766ae2a3266d8d2", - "reference": "19e1b73bf255265ad0b568f81766ae2a3266d8d2", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", "shasum": "" }, "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", "php": ">=5.3.3" }, "type": "library", @@ -1780,10 +1896,13 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Apcu\\": "" + "Symfony\\Polyfill\\Php70\\": "" }, "files": [ "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1800,10 +1919,9 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "apcu", "compatibility", "polyfill", "portable", @@ -1811,25 +1929,97 @@ ], "time": "2018-08-06T14:22:27+00:00" }, + { + "name": "symfony/property-access", + "version": "v3.4.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "28d3129c330f73d47fafa8239a2fa6ccd44136a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/28d3129c330f73d47fafa8239a2fa6ccd44136a6", + "reference": "28d3129c330f73d47fafa8239a2fa6ccd44136a6", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/inflector": "~3.1|~4.0", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "symfony/cache": "~3.1|~4.0" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony PropertyAccess Component", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "time": "2018-07-26T09:06:28+00:00" + }, { "name": "symfony/yaml", - "version": "v3.3.2", + "version": "v3.4.10", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063" + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/9752a30000a8ca9f4b34b5227d15d0101b96b063", - "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" }, "require-dev": { - "symfony/console": "~2.8|~3.0" + "symfony/console": "~3.4|~4.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -1837,7 +2027,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1864,32 +2054,34 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-06-02T22:05:06+00:00" + "time": "2018-05-03T23:18:14+00:00" }, { "name": "vufind-org/vufindcode", - "version": "v1.0.3", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/vufind-org/vufindcode.git", - "reference": "f50091c35f50865b926bac549e671347c4d4389d" + "reference": "3a0a7628b7422e32a1313108f7aa972488c95e01" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindcode/zipball/f50091c35f50865b926bac549e671347c4d4389d", - "reference": "f50091c35f50865b926bac549e671347c4d4389d", + "url": "https://api.github.com/repos/vufind-org/vufindcode/zipball/3a0a7628b7422e32a1313108f7aa972488c95e01", + "reference": "3a0a7628b7422e32a1313108f7aa972488c95e01", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.0.8" }, "require-dev": { - "friendsofphp/php-cs-fixer": "1.11.3", - "phploc/phploc": "2.0.6", - "phpmd/phpmd": "1.5.0", - "phpunit/phpunit": "4.8.4", - "sebastian/phpcpd": "1.4.3", - "squizlabs/php_codesniffer": "2.6.0" + "friendsofphp/php-cs-fixer": "2.11.1", + "pear/http_request2": "2.3.0", + "phing/phing": "2.16.1", + "phploc/phploc": "4.0.1", + "phpmd/phpmd": "2.6.0", + "phpunit/phpunit": "6.5.8", + "sebastian/phpcpd": "3.0.1", + "squizlabs/php_codesniffer": "3.2.3" }, "type": "library", "autoload": { @@ -1909,41 +2101,39 @@ ], "description": "Class for representing ISBNs (a VuFind support library)", "homepage": "https://vufind.org/", - "time": "2016-06-06T19:12:16+00:00" + "time": "2018-05-23T18:48:59+00:00" }, { - "name": "vufind-org/vufindharvest", - "version": "v2.3.0", + "name": "vufind-org/vufinddate", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/vufind-org/vufindharvest.git", - "reference": "c59e0a64b59873120c5cda32cbb8654629a8320e" + "url": "https://github.com/vufind-org/vufinddate.git", + "reference": "1bec5458b48d96fa8ff87123584042780f4c3c24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindharvest/zipball/c59e0a64b59873120c5cda32cbb8654629a8320e", - "reference": "c59e0a64b59873120c5cda32cbb8654629a8320e", + "url": "https://api.github.com/repos/vufind-org/vufinddate/zipball/1bec5458b48d96fa8ff87123584042780f4c3c24", + "reference": "1bec5458b48d96fa8ff87123584042780f4c3c24", "shasum": "" }, "require": { - "php": ">=5.6", - "zendframework/zend-console": ">=2.2", - "zendframework/zend-http": ">=2.2" + "php": ">=7.0.8" }, "require-dev": { - "friendsofphp/php-cs-fixer": "1.11.6", - "phing/phing": "2.16.0", - "phpdocumentor/phpdocumentor": "2.9.0", - "phploc/phploc": "3.0.1", + "friendsofphp/php-cs-fixer": "2.11.1", + "pear/http_request2": "2.3.0", + "phing/phing": "2.16.1", + "phploc/phploc": "4.0.1", "phpmd/phpmd": "2.6.0", - "phpunit/phpunit": "5.7.15", - "sebastian/phpcpd": "2.0.4", - "squizlabs/php_codesniffer": "2.8.1" + "phpunit/phpunit": "6.5.8", + "sebastian/phpcpd": "3.0.1", + "squizlabs/php_codesniffer": "3.2.3" }, "type": "library", "autoload": { "psr-4": { - "VuFindHarvest\\": "src/" + "VuFind\\Date\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1953,45 +2143,46 @@ "authors": [ { "name": "Demian Katz", - "email": "demian.katz@villanova.edu", - "role": "Maintainer" + "email": "demian.katz@villanova.edu" } ], - "description": "VuFind Harvest Tools", + "description": "Date formatting tools for the VuFind project", "homepage": "https://vufind.org/", - "time": "2017-04-06T18:31:39+00:00" + "time": "2018-05-23T19:59:10+00:00" }, { - "name": "vufind-org/vufindhttp", - "version": "v2.1.1", + "name": "vufind-org/vufindharvest", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/vufind-org/vufindhttp.git", - "reference": "b1b7dcd5f2c87a199fd3d6d439898cc7ddd0d7ca" + "url": "https://github.com/vufind-org/vufindharvest.git", + "reference": "a7391a2e3b9efc031c4e223debf7a56678e420a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindhttp/zipball/b1b7dcd5f2c87a199fd3d6d439898cc7ddd0d7ca", - "reference": "b1b7dcd5f2c87a199fd3d6d439898cc7ddd0d7ca", + "url": "https://api.github.com/repos/vufind-org/vufindharvest/zipball/a7391a2e3b9efc031c4e223debf7a56678e420a8", + "reference": "a7391a2e3b9efc031c4e223debf7a56678e420a8", "shasum": "" }, "require": { - "php": ">=5.4", + "php": ">=7.0.8", + "zendframework/zend-console": ">=2.2", "zendframework/zend-http": ">=2.2" }, "require-dev": { - "friendsofphp/php-cs-fixer": "1.11.3", - "phploc/phploc": "2.0.6", - "phpmd/phpmd": "1.5.0", - "phpunit/phpunit": "4.8.4", - "sebastian/phpcpd": "2.0.0", - "squizlabs/php_codesniffer": "2.6.0", - "zendframework/zend-uri": ">=2.2" + "friendsofphp/php-cs-fixer": "2.11.1", + "pear/http_request2": "2.3.0", + "phing/phing": "2.16.1", + "phploc/phploc": "4.0.1", + "phpmd/phpmd": "2.6.0", + "phpunit/phpunit": "6.5.8", + "sebastian/phpcpd": "3.0.1", + "squizlabs/php_codesniffer": "3.2.3" }, "type": "library", "autoload": { - "psr-0": { - "VuFindHttp\\": "src/" + "psr-4": { + "VuFindHarvest\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1999,37 +2190,87 @@ "GPL-2.0" ], "authors": [ - { - "name": "David Maus", - "email": "maus@hab.de", - "role": "Developer" - }, { "name": "Demian Katz", "email": "demian.katz@villanova.edu", "role": "Maintainer" } ], - "description": "VuFind HTTP service library", + "description": "VuFind Harvest Tools", "homepage": "https://vufind.org/", - "time": "2016-09-23T12:51:36+00:00" + "time": "2018-05-23T19:14:41+00:00" + }, + { + "name": "vufind-org/vufindhttp", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/vufind-org/vufindhttp.git", + "reference": "2415b70424156ef9ebcbcff7900500c5fa62789b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vufind-org/vufindhttp/zipball/2415b70424156ef9ebcbcff7900500c5fa62789b", + "reference": "2415b70424156ef9ebcbcff7900500c5fa62789b", + "shasum": "" + }, + "require": { + "php": ">=7.0.8", + "zendframework/zend-http": ">=2.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "2.11.1", + "pear/http_request2": "2.3.0", + "phing/phing": "2.16.1", + "phploc/phploc": "4.0.1", + "phpmd/phpmd": "2.6.0", + "phpunit/phpunit": "6.5.8", + "sebastian/phpcpd": "3.0.1", + "squizlabs/php_codesniffer": "3.2.3", + "zendframework/zend-uri": ">=2.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "VuFindHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0" + ], + "authors": [ + { + "name": "David Maus", + "email": "maus@hab.de", + "role": "Developer" + }, + { + "name": "Demian Katz", + "email": "demian.katz@villanova.edu", + "role": "Maintainer" + } + ], + "description": "VuFind HTTP service library", + "homepage": "https://vufind.org/", + "time": "2018-05-23T17:51:55+00:00" }, { "name": "yajra/laravel-pdo-via-oci8", - "version": "v1.3.1", + "version": "v1.3.6", "source": { "type": "git", "url": "https://github.com/yajra/pdo-via-oci8.git", - "reference": "b3e52482af67770a0b3ea620f11ba0a1b38b4afd" + "reference": "b3a86f9f5ecb5b05e497bbbfef13e33ae201a417" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/pdo-via-oci8/zipball/b3e52482af67770a0b3ea620f11ba0a1b38b4afd", - "reference": "b3e52482af67770a0b3ea620f11ba0a1b38b4afd", + "url": "https://api.github.com/repos/yajra/pdo-via-oci8/zipball/b3a86f9f5ecb5b05e497bbbfef13e33ae201a417", + "reference": "b3a86f9f5ecb5b05e497bbbfef13e33ae201a417", "shasum": "" }, "require-dev": { - "phpunit/phpunit": "4.0.*" + "phpunit/phpunit": "^6.4" }, "type": "library", "autoload": { @@ -2048,34 +2289,41 @@ } ], "description": "PDO userspace driver proxying calls to PHP OCI8 driver", - "time": "2017-02-02T02:51:53+00:00" + "time": "2018-01-04T07:20:53+00:00" }, { "name": "zendframework/zend-cache", - "version": "2.7.2", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-cache.git", - "reference": "c98331b96d3b9d9b24cf32d02660602edb34d039" + "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/c98331b96d3b9d9b24cf32d02660602edb34d039", - "reference": "c98331b96d3b9d9b24cf32d02660602edb34d039", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/4983dff629956490c78b88adcc8ece4711d7d8a3", + "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "php": "^5.6 || ^7.0", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "zendframework/zend-eventmanager": "^2.6.3 || ^3.2", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" }, "require-dev": { - "phpbench/phpbench": "^0.10.0", - "phpunit/phpunit": "^4.8", + "cache/integration-tests": "^0.16", + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-serializer": "^2.6", - "zendframework/zend-session": "^2.6.2" + "zendframework/zend-session": "^2.7.4" }, "suggest": { "ext-apc": "APC or compatible extension, to use the APC storage adapter", @@ -2084,9 +2332,11 @@ "ext-memcache": "Memcache >= 2.0.0 to use the Memcache storage adapter", "ext-memcached": "Memcached >= 1.0.0 to use the Memcached storage adapter", "ext-mongo": "Mongo, to use MongoDb storage adapter", + "ext-mongodb": "MongoDB, to use the ExtMongoDb storage adapter", "ext-redis": "Redis, to use Redis storage adapter", "ext-wincache": "WinCache, to use the WinCache storage adapter", "ext-xcache": "XCache, to use the XCache storage adapter", + "mongodb/mongodb": "Required for use with the ext-mongodb adapter", "mongofill/mongofill": "Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement", "zendframework/zend-serializer": "Zend\\Serializer component", "zendframework/zend-session": "Zend\\Session component" @@ -2094,8 +2344,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" }, "zf": { "component": "Zend\\Cache", @@ -2103,6 +2353,9 @@ } }, "autoload": { + "files": [ + "autoload/patternPluginManagerPolyfill.php" + ], "psr-4": { "Zend\\Cache\\": "src/" } @@ -2111,39 +2364,41 @@ "license": [ "BSD-3-Clause" ], - "description": "provides a generic way to cache any data", - "homepage": "https://github.com/zendframework/zend-cache", + "description": "Caching implementation with a variety of storage options, as well as codified caching strategies for callbacks, classes, and output", "keywords": [ + "ZendFramework", "cache", - "zf2" + "psr-16", + "psr-6", + "zf" ], - "time": "2016-12-16T11:35:47+00:00" + "time": "2018-05-01T21:58:00+00:00" }, { "name": "zendframework/zend-captcha", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-captcha.git", - "reference": "2d56293a5ae3e45e7c8ee7030aa8b305768d8014" + "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/2d56293a5ae3e45e7c8ee7030aa8b305768d8014", - "reference": "2d56293a5ae3e45e7c8ee7030aa8b305768d8014", + "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "zendframework/zend-math": "^2.7 || ^3.0", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { - "phpunit/phpunit": "~4.8", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-session": "^2.6", + "zendframework/zend-session": "^2.8", "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", + "zendframework/zend-validator": "^2.10.1", "zendframework/zendservice-recaptcha": "^3.0" }, "suggest": { @@ -2156,8 +2411,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { @@ -2169,35 +2424,37 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-captcha", + "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", "keywords": [ + "ZendFramework", "captcha", - "zf2" + "zf" ], - "time": "2017-02-23T08:09:44+00:00" + "time": "2018-04-24T17:24:10+00:00" }, { "name": "zendframework/zend-code", - "version": "2.6.3", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-code.git", - "reference": "95033f061b083e16cdee60530ec260d7d628b887" + "reference": "2899c17f83a7207f2d7f53ec2f421204d3beea27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/95033f061b083e16cdee60530ec260d7d628b887", - "reference": "95033f061b083e16cdee60530ec260d7d628b887", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/2899c17f83a7207f2d7f53ec2f421204d3beea27", + "reference": "2899c17f83a7207f2d7f53ec2f421204d3beea27", "shasum": "" }, "require": { - "php": "^5.5 || 7.0.0 - 7.0.4 || ^7.0.6", + "php": "^5.6 || 7.0.0 - 7.0.4 || ^7.0.6", "zendframework/zend-eventmanager": "^2.6 || ^3.0" }, "require-dev": { "doctrine/annotations": "~1.0", - "fabpot/php-cs-fixer": "1.7.*", + "ext-phar": "*", "phpunit/phpunit": "^4.8.21", + "squizlabs/php_codesniffer": "^2.5", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "suggest": { @@ -2207,8 +2464,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "3.1-dev", + "dev-develop": "3.2-dev" } }, "autoload": { @@ -2226,45 +2483,49 @@ "code", "zf2" ], - "time": "2016-04-20T17:26:42+00:00" + "time": "2016-10-24T13:23:32+00:00" }, { "name": "zendframework/zend-config", - "version": "2.6.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-config.git", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/6796f5dcba52c84ef2501d7313618989b5ef3023", + "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "ext-json": "*", + "php": "^5.6 || ^7.0", + "psr/container": "^1.0", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "malukenho/docheader": "^0.1.6", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-filter": "^2.7.2", + "zendframework/zend-i18n": "^2.7.4", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "zendframework/zend-filter": "^2.7.2; install if you want to use the Filter processor", + "zendframework/zend-i18n": "^2.7.4; install if you want to use the Translator processor", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3; if you need an extensible plugin manager for use with the Config Factory" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" } }, "autoload": { @@ -2277,12 +2538,12 @@ "BSD-3-Clause" ], "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", "keywords": [ + "ZendFramework", "config", - "zf2" + "zf" ], - "time": "2016-02-04T23:01:10+00:00" + "time": "2018-04-24T19:26:44+00:00" }, { "name": "zendframework/zend-console", @@ -2339,28 +2600,28 @@ }, { "name": "zendframework/zend-crypt", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-crypt.git", - "reference": "274cdcb77a2165d6aff36b606e9d1c687ba9386c" + "reference": "9c2916faa9b2132a0f91cdca8e95b025c352f065" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/274cdcb77a2165d6aff36b606e9d1c687ba9386c", - "reference": "274cdcb77a2165d6aff36b606e9d1c687ba9386c", + "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/9c2916faa9b2132a0f91cdca8e95b025c352f065", + "reference": "9c2916faa9b2132a0f91cdca8e95b025c352f065", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", + "container-interop/container-interop": "^1.2", "ext-mbstring": "*", "php": "^5.6 || ^7.0", "zendframework/zend-math": "^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { - "phpunit/phpunit": "^5.6.7", - "squizlabs/php_codesniffer": "^2.3.1" + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" }, "suggest": { "ext-openssl": "Required for most features of Zend\\Crypt" @@ -2368,8 +2629,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev", - "dev-develop": "3.2-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { @@ -2381,34 +2642,35 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-crypt", + "description": "Strong cryptography tools and password hashing", "keywords": [ + "ZendFramework", "crypt", - "zf2" + "zf" ], - "time": "2016-12-06T15:33:37+00:00" + "time": "2018-04-24T22:01:58+00:00" }, { "name": "zendframework/zend-db", - "version": "2.8.2", + "version": "2.9.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-db.git", - "reference": "5926a1a2e7e035546b690cb7d4c11a3c47db2c98" + "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/5926a1a2e7e035546b690cb7d4c11a3c47db2c98", - "reference": "5926a1a2e7e035546b690cb7d4c11a3c47db2c98", + "url": "https://api.github.com/repos/zendframework/zend-db/zipball/5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", + "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", + "php": "^5.6 || ^7.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", + "phpunit/phpunit": "^5.7.25 || ^6.4.4", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", "zendframework/zend-hydrator": "^1.1 || ^2.1", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" @@ -2421,8 +2683,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev", - "dev-develop": "2.9-dev" + "dev-master": "2.9-dev", + "dev-develop": "2.10-dev" }, "zf": { "component": "Zend\\Db", @@ -2438,102 +2700,40 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-db", + "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations", "keywords": [ + "ZendFramework", "db", - "zf2" - ], - "time": "2016-08-09T19:28:55+00:00" - }, - { - "name": "zendframework/zend-diactoros", - "version": "1.8.4", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", - "reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-dom": "*", - "ext-libxml": "*", - "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev", - "dev-develop": "1.9.x-dev", - "dev-release-2.0": "2.0.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions/create_uploaded_file.php", - "src/functions/marshal_headers_from_sapi.php", - "src/functions/marshal_method_from_sapi.php", - "src/functions/marshal_protocol_version_from_sapi.php", - "src/functions/marshal_uri_from_sapi.php", - "src/functions/normalize_server.php", - "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php" - ], - "psr-4": { - "Zend\\Diactoros\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", - "keywords": [ - "http", - "psr", - "psr-7" + "zf" ], - "time": "2018-08-01T13:47:49+00:00" + "time": "2018-04-09T13:21:36+00:00" }, { "name": "zendframework/zend-dom", - "version": "2.6.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-dom.git", - "reference": "a9e145b2b52fe6de5a7a6b0ddb5c773c2c72d59e" + "reference": "ec2c66c2bb0046e895651b24f2ebb83058b9bbca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-dom/zipball/a9e145b2b52fe6de5a7a6b0ddb5c773c2c72d59e", - "reference": "a9e145b2b52fe6de5a7a6b0ddb5c773c2c72d59e", + "url": "https://api.github.com/repos/zendframework/zend-dom/zipball/ec2c66c2bb0046e895651b24f2ebb83058b9bbca", + "reference": "ec2c66c2bb0046e895651b24f2ebb83058b9bbca", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^5.6 || ^7.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "zendframework/zend-coding-standard": "~1.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -2546,39 +2746,39 @@ "BSD-3-Clause" ], "description": "provides tools for working with DOM documents and structures", - "homepage": "https://github.com/zendframework/zend-dom", "keywords": [ + "ZendFramework", "dom", - "zf2" + "zf" ], - "time": "2015-10-14T03:37:48+00:00" + "time": "2018-04-09T20:18:00+00:00" }, { "name": "zendframework/zend-escaper", - "version": "2.5.2", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "2dcd14b61a72d8b8e27d579c6344e12c26141d4e" + "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/2dcd14b61a72d8b8e27d579c6344e12c26141d4e", - "reference": "2dcd14b61a72d8b8e27d579c6344e12c26141d4e", + "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", + "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^5.6 || ^7.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" } }, "autoload": { @@ -2590,25 +2790,26 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-escaper", + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", "keywords": [ + "ZendFramework", "escaper", - "zf2" + "zf" ], - "time": "2016-06-30T19:48:38+00:00" + "time": "2018-04-25T15:48:53+00:00" }, { "name": "zendframework/zend-eventmanager", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "9d72db10ceb6e42fb92350c0cb54460da61bd79c" + "reference": "a5e2583a211f73604691586b8406ff7296a946dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/9d72db10ceb6e42fb92350c0cb54460da61bd79c", - "reference": "9d72db10ceb6e42fb92350c0cb54460da61bd79c", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd", + "reference": "a5e2583a211f73604691586b8406ff7296a946dd", "shasum": "" }, "require": { @@ -2617,7 +2818,7 @@ "require-dev": { "athletic/athletic": "^0.1", "container-interop/container-interop": "^1.1.0", - "phpunit/phpunit": "^6.0.7 || ^5.7.14", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-stdlib": "^2.7.3 || ^3.0" }, @@ -2649,50 +2850,50 @@ "events", "zf2" ], - "time": "2017-07-11T19:17:22+00:00" + "time": "2018-04-25T15:33:34+00:00" }, { "name": "zendframework/zend-feed", - "version": "2.8.0", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-feed.git", - "reference": "94579e805dd108683209fe14b3b5d4276de3de6e" + "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/94579e805dd108683209fe14b3b5d4276de3de6e", - "reference": "94579e805dd108683209fe14b3b5d4276de3de6e", + "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", + "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-stdlib": "^2.7 || ^3.1" + "zendframework/zend-escaper": "^2.5.2", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "psr/http-message": "^1.0", - "zendframework/zend-cache": "^2.6", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-message": "^1.0.1", + "zendframework/zend-cache": "^2.7.2", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6" + "zendframework/zend-db": "^2.8.2", + "zendframework/zend-http": "^2.7", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", + "zendframework/zend-validator": "^2.10.1" }, "suggest": { - "psr/http-message": "PSR-7 ^1.0, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Zend\\Feed\\Reader\\Http\\Psr7ResponseDecorator", "zendframework/zend-cache": "Zend\\Cache component, for optionally caching feeds between requests", "zendframework/zend-db": "Zend\\Db component, for use with PubSubHubbub", "zendframework/zend-http": "Zend\\Http for PubSubHubbub, and optionally for use with Zend\\Feed\\Reader", "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for easily extending ExtensionManager implementations", - "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries ehen using the Writer subcomponent" + "zendframework/zend-validator": "Zend\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev", - "dev-develop": "2.9-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" } }, "autoload": { @@ -2705,12 +2906,12 @@ "BSD-3-Clause" ], "description": "provides functionality for consuming RSS and Atom feeds", - "homepage": "https://github.com/zendframework/zend-feed", "keywords": [ + "ZendFramework", "feed", - "zf2" + "zf" ], - "time": "2017-04-01T15:03:14+00:00" + "time": "2018-08-01T13:53:20+00:00" }, { "name": "zendframework/zend-filter", @@ -2776,219 +2977,88 @@ "time": "2018-04-11T16:20:04+00:00" }, { - "name": "zendframework/zend-form", - "version": "2.12.0", + "name": "zendframework/zend-http", + "version": "2.8.2", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-form.git", - "reference": "565fb4f4bb3e0dbeea0173c923c4a8be77de9441" + "url": "https://github.com/zendframework/zend-http.git", + "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/565fb4f4bb3e0dbeea0173c923c4a8be77de9441", - "reference": "565fb4f4bb3e0dbeea0173c923c4a8be77de9441", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/2c8aed3d25522618573194e7cc51351f8cd4a45b", + "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "zendframework/zend-loader": "^2.5.1", + "zendframework/zend-stdlib": "^3.1 || ^2.7.7", + "zendframework/zend-uri": "^2.5.2", + "zendframework/zend-validator": "^2.10.1" }, "require-dev": { - "doctrine/annotations": "~1.0", - "phpunit/phpunit": "^5.7.23 || ^6.5.3", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.7.1", - "zendframework/zend-code": "^2.6 || ^3.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.6", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "^3.0.0" + "zendframework/zend-config": "^3.1 || ^2.6" }, "suggest": { - "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", - "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", - "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, required to use the form factories or provide services", - "zendframework/zend-view": "^2.6.2, required for using the zend-form view helpers", - "zendframework/zendservice-recaptcha": "in order to use the ReCaptcha form element" + "paragonie/certainty": "For automated management of cacert.pem" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" - }, - "zf": { - "component": "Zend\\Form", - "config-provider": "Zend\\Form\\ConfigProvider" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { "psr-4": { - "Zend\\Form\\": "src/" - }, - "files": [ - "autoload/formElementManagerPolyfill.php" - ] + "Zend\\Http\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", "keywords": [ "ZendFramework", - "form", + "http", + "http client", + "zend", "zf" ], - "time": "2018-05-16T18:49:44+00:00" + "time": "2018-08-13T18:47:03+00:00" }, { - "name": "zendframework/zend-http", - "version": "2.6.0", + "name": "zendframework/zend-i18n", + "version": "2.9.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-http.git", - "reference": "09f4d279f46d86be63171ff62ee0f79eca878678" + "url": "https://github.com/zendframework/zend-i18n.git", + "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/09f4d279f46d86be63171ff62ee0f79eca878678", - "reference": "09f4d279f46d86be63171ff62ee0f79eca878678", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-loader": "^2.5", - "zendframework/zend-stdlib": "^2.5 || ^3.0", - "zendframework/zend-uri": "^2.5", - "zendframework/zend-validator": "^2.5" + "php": "^5.6 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^4.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Http\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", - "homepage": "https://github.com/zendframework/zend-http", - "keywords": [ - "http", - "zf2" - ], - "time": "2017-01-31T14:41:02+00:00" - }, - { - "name": "zendframework/zend-hydrator", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", - "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", - "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-release-1.0": "1.0-dev", - "dev-release-1.1": "1.1-dev", - "dev-master": "2.0-dev", - "dev-develop": "2.1-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Hydrator\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-hydrator", - "keywords": [ - "hydrator", - "zf2" - ], - "time": "2016-02-18T22:38:26+00:00" - }, - { - "name": "zendframework/zend-i18n", - "version": "2.7.4", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "d3431e29cc00c2a1c6704e601d4371dbf24f6a31" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/d3431e29cc00c2a1c6704e601d4371dbf24f6a31", - "reference": "d3431e29cc00c2a1c6704e601d4371dbf24f6a31", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^5.6", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" + "zendframework/zend-config": "^2.6", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-filter": "^2.6.1", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-validator": "^2.6", + "zendframework/zend-view": "^2.6.3" }, "suggest": { "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", @@ -3004,8 +3074,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\I18n", @@ -3021,65 +3091,13 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-i18n", - "keywords": [ - "i18n", - "zf2" - ], - "time": "2017-05-17T17:00:12+00:00" - }, - { - "name": "zendframework/zend-inputfilter", - "version": "2.8.2", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "3f02179e014d9ef0faccda2ad6c65d38adc338d8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/3f02179e014d9ef0faccda2ad6c65d38adc338d8", - "reference": "3f02179e014d9ef0faccda2ad6c65d38adc338d8", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", - "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" - }, - "zf": { - "component": "Zend\\InputFilter", - "config-provider": "Zend\\InputFilter\\ConfigProvider" - } - }, - "autoload": { - "psr-4": { - "Zend\\InputFilter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "description": "Provide translations for your application, and filter and validate internationalized values", "keywords": [ "ZendFramework", - "inputfilter", + "i18n", "zf" ], - "time": "2018-05-14T17:38:03+00:00" + "time": "2018-05-16T16:39:13+00:00" }, { "name": "zendframework/zend-json", @@ -3133,30 +3151,30 @@ }, { "name": "zendframework/zend-loader", - "version": "2.5.1", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-loader.git", - "reference": "c5fd2f071bde071f4363def7dea8dec7393e135c" + "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/c5fd2f071bde071f4363def7dea8dec7393e135c", - "reference": "c5fd2f071bde071f4363def7dea8dec7393e135c", + "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", + "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", "shasum": "" }, "require": { - "php": ">=5.3.23" + "php": "^5.6 || ^7.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", + "zendframework/zend-coding-standard": "~1.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.6.x-dev", + "dev-develop": "2.7.x-dev" } }, "autoload": { @@ -3168,25 +3186,26 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-loader", + "description": "Autoloading and plugin loading strategies", "keywords": [ + "ZendFramework", "loader", - "zf2" + "zf" ], - "time": "2015-06-03T14:05:47+00:00" + "time": "2018-04-30T15:20:54+00:00" }, { "name": "zendframework/zend-log", - "version": "2.9.2", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-log.git", - "reference": "bf7489578d092d6ff7508117d1d920a4764fbd6a" + "reference": "9cec3b092acb39963659c2f32441cccc56b3f430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-log/zipball/bf7489578d092d6ff7508117d1d920a4764fbd6a", - "reference": "bf7489578d092d6ff7508117d1d920a4764fbd6a", + "url": "https://api.github.com/repos/zendframework/zend-log/zipball/9cec3b092acb39963659c2f32441cccc56b3f430", + "reference": "9cec3b092acb39963659c2f32441cccc56b3f430", "shasum": "" }, "require": { @@ -3206,7 +3225,7 @@ "zendframework/zend-escaper": "^2.5", "zendframework/zend-filter": "^2.5", "zendframework/zend-mail": "^2.6.1", - "zendframework/zend-validator": "^2.6" + "zendframework/zend-validator": "^2.10.1" }, "suggest": { "ext-mongo": "mongo extension to use Mongo writer", @@ -3220,8 +3239,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9-dev", - "dev-develop": "2.10-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\Log", @@ -3244,7 +3263,7 @@ "logging", "zf2" ], - "time": "2017-05-17T16:03:26+00:00" + "time": "2018-04-09T21:59:51+00:00" }, { "name": "zendframework/zend-mail", @@ -3412,23 +3431,23 @@ }, { "name": "zendframework/zend-modulemanager", - "version": "2.8.0", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "c2c5b52ad9741e0b9a9c01a0ee72ab63e5b494b9" + "reference": "394df6e12248ac430a312d4693f793ee7120baa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/c2c5b52ad9741e0b9a9c01a0ee72ab63e5b494b9", - "reference": "c2c5b52ad9741e0b9a9c01a0ee72ab63e5b494b9", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", + "reference": "394df6e12248ac430a312d4693f793ee7120baa6", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-config": "^3.1 || ^2.6", "zendframework/zend-eventmanager": "^3.2 || ^2.6.3", - "zendframework/zend-stdlib": "^3.0 || ^2.7" + "zendframework/zend-stdlib": "^3.1 || ^2.7" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", @@ -3436,7 +3455,7 @@ "zendframework/zend-console": "^2.6", "zendframework/zend-di": "^2.6", "zendframework/zend-loader": "^2.5", - "zendframework/zend-mvc": "^2.7", + "zendframework/zend-mvc": "^3.0 || ^2.7", "zendframework/zend-servicemanager": "^3.0.3 || ^2.7.5" }, "suggest": { @@ -3448,8 +3467,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev", - "dev-develop": "2.9-dev" + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" } }, "autoload": { @@ -3461,93 +3480,71 @@ "license": [ "BSD-3-Clause" ], + "description": "Modular application system for zend-mvc applications", "homepage": "https://github.com/zendframework/zend-modulemanager", "keywords": [ + "ZendFramework", "modulemanager", - "zf2" + "zf" ], - "time": "2017-07-11T19:39:57+00:00" + "time": "2017-12-02T06:11:18+00:00" }, { "name": "zendframework/zend-mvc", - "version": "2.7.15", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-mvc.git", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089" + "reference": "236e7e1e3757e988fa06530c0a3f96a148858ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", - "reference": "a8d45689d37a9e4ff4b75ea0b7478fa3d4f9c089", + "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/236e7e1e3757e988fa06530c0a3f96a148858ae8", + "reference": "236e7e1e3757e988fa06530c0a3f96a148858ae8", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.5 || ^7.0", - "zendframework/zend-console": "^2.7", - "zendframework/zend-eventmanager": "^2.6.4 || ^3.0", - "zendframework/zend-form": "^2.11", - "zendframework/zend-hydrator": "^1.1 || ^2.4", - "zendframework/zend-psr7bridge": "^0.2", - "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", - "zendframework/zend-stdlib": "^2.7.5 || ^3.0" - }, - "replace": { - "zendframework/zend-router": "^2.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "^4.8.36", - "sebastian/comparator": "^1.2.4", - "sebastian/version": "^1.0.4", - "zendframework/zend-authentication": "^2.6", - "zendframework/zend-cache": "^2.8", - "zendframework/zend-di": "^2.6", - "zendframework/zend-filter": "^2.8", - "zendframework/zend-http": "^2.8", - "zendframework/zend-i18n": "^2.8", - "zendframework/zend-inputfilter": "^2.8", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-log": "^2.9.3", + "container-interop/container-interop": "^1.2", + "php": "^5.6 || ^7.0", + "zendframework/zend-eventmanager": "^3.2", + "zendframework/zend-http": "^2.7", "zendframework/zend-modulemanager": "^2.8", - "zendframework/zend-serializer": "^2.8", - "zendframework/zend-session": "^2.8.1", - "zendframework/zend-text": "^2.7", - "zendframework/zend-uri": "^2.6", - "zendframework/zend-validator": "^2.10", + "zendframework/zend-router": "^3.0.2", + "zendframework/zend-servicemanager": "^3.3", + "zendframework/zend-stdlib": "^3.1", "zendframework/zend-view": "^2.9" }, + "require-dev": { + "http-interop/http-middleware": "^0.4.1", + "phpunit/phpunit": "^6.4.4 || ^5.7.14", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-json": "^2.6.1 || ^3.0", + "zendframework/zend-psr7bridge": "^1.0", + "zendframework/zend-stratigility": "^2.0.1" + }, "suggest": { - "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-di": "Zend\\Di component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-http": "Zend\\Http component", - "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", - "zendframework/zend-inputfilter": "Zend\\Inputfilter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-log": "Zend\\Log component", - "zendframework/zend-modulemanager": "Zend\\ModuleManager component", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager-di": "^1.0.1, if using zend-servicemanager v3 and requiring the zend-di integration", - "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", - "zendframework/zend-text": "Zend\\Text component", - "zendframework/zend-uri": "Zend\\Uri component", - "zendframework/zend-validator": "Zend\\Validator component", - "zendframework/zend-view": "Zend\\View component" + "http-interop/http-middleware": "^0.4.1 to be used together with zend-stratigility", + "zendframework/zend-json": "(^2.6.1 || ^3.0) To auto-deserialize JSON body content in AbstractRestfulController extensions, when json_decode is unavailable", + "zendframework/zend-log": "^2.9.1 To provide log functionality via LogFilterManager, LogFormatterManager, and LogProcessorManager", + "zendframework/zend-mvc-console": "zend-mvc-console provides the ability to expose zend-mvc as a console application", + "zendframework/zend-mvc-i18n": "zend-mvc-i18n provides integration with zend-i18n, including a translation bridge and translatable route segments", + "zendframework/zend-mvc-plugin-fileprg": "To provide Post/Redirect/Get functionality around forms that container file uploads", + "zendframework/zend-mvc-plugin-flashmessenger": "To provide flash messaging capabilities between requests", + "zendframework/zend-mvc-plugin-identity": "To access the authenticated identity (per zend-authentication) in controllers", + "zendframework/zend-mvc-plugin-prg": "To provide Post/Redirect/Get functionality within controllers", + "zendframework/zend-paginator": "^2.7 To provide pagination functionality via PaginatorPluginManager", + "zendframework/zend-psr7bridge": "(^0.2) To consume PSR-7 middleware within the MVC workflow", + "zendframework/zend-servicemanager-di": "zend-servicemanager-di provides utilities for integrating zend-di and zend-servicemanager in your zend-mvc application", + "zendframework/zend-stratigility": "zend-stratigility is required to use middleware pipes in the MiddlewareListener" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "3.1-dev", + "dev-develop": "3.2-dev" } }, "autoload": { - "files": [ - "src/autoload.php" - ], "psr-4": { "Zend\\Mvc\\": "src/" } @@ -3556,154 +3553,235 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-mvc", + "description": "Zend Framework's event-driven MVC layer, including MVC Applications, Controllers, and Plugins", "keywords": [ + "ZendFramework", "mvc", - "zf2" + "zf" ], - "time": "2018-05-03T13:13:41+00:00" + "time": "2017-11-24T06:32:07+00:00" }, { - "name": "zendframework/zend-paginator", - "version": "2.7.0", + "name": "zendframework/zend-mvc-console", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-paginator.git", - "reference": "42211f3e1e8230953c641e91fec5aa9fe964eb95" + "url": "https://github.com/zendframework/zend-mvc-console.git", + "reference": "821c18e0d57e71b370166bd2f35623befddaf2ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-paginator/zipball/42211f3e1e8230953c641e91fec5aa9fe964eb95", - "reference": "42211f3e1e8230953c641e91fec5aa9fe964eb95", + "url": "https://api.github.com/repos/zendframework/zend-mvc-console/zipball/821c18e0d57e71b370166bd2f35623befddaf2ee", + "reference": "821c18e0d57e71b370166bd2f35623befddaf2ee", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-config": "^2.6.0", - "zendframework/zend-db": "^2.7", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-json": "^2.6.1", + "container-interop/container-interop": "^1.1", + "php": "^5.6 || ^7.0", + "zendframework/zend-console": "^2.6", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-modulemanager": "^2.7.1", + "zendframework/zend-mvc": "^3.0.3", + "zendframework/zend-router": "^3.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-stdlib": "^2.7.5 || ^3.0", + "zendframework/zend-text": "^2.6", "zendframework/zend-view": "^2.6.3" }, + "conflict": { + "zendframework/zend-mvc": "<3.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-filter": "^2.6.1" + }, "suggest": { - "zendframework/zend-cache": "Zend\\Cache component to support cache features", - "zendframework/zend-db": "Zend\\Db component", - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-json": "Zend\\Json component", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-view": "Zend\\View component" + "zendframework/zend-filter": "^2.6.1, to filter rendered results" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "1.2.x-dev", + "dev-develop": "1.3.x-dev" }, "zf": { - "component": "Zend\\Paginator", - "config-provider": "Zend\\Paginator\\ConfigProvider" + "component": "Zend\\Mvc\\Console" } }, "autoload": { "psr-4": { - "Zend\\Paginator\\": "src/" + "Zend\\Mvc\\Console\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-paginator", + "description": "Integration between zend-mvc and zend-console", "keywords": [ - "paginator", - "zf2" + "ZendFramework", + "console", + "mvc", + "zf" ], - "time": "2016-04-11T21:18:13+00:00" + "time": "2018-04-30T19:10:26+00:00" }, { - "name": "zendframework/zend-psr7bridge", - "version": "0.2.2", + "name": "zendframework/zend-mvc-i18n", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-psr7bridge.git", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605" + "url": "https://github.com/zendframework/zend-mvc-i18n.git", + "reference": "90e64d1304385cfcf19447b6449514e8a720adfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/86c0b53b0c6381391c4add4a93a56e51d5c74605", - "reference": "86c0b53b0c6381391c4add4a93a56e51d5c74605", + "url": "https://api.github.com/repos/zendframework/zend-mvc-i18n/zipball/90e64d1304385cfcf19447b6449514e8a720adfc", + "reference": "90e64d1304385cfcf19447b6449514e8a720adfc", "shasum": "" }, "require": { - "php": ">=5.5", - "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.5" + "container-interop/container-interop": "^1.1", + "php": "^5.6 || ^7.0", + "zendframework/zend-i18n": "^2.7", + "zendframework/zend-router": "^3.0", + "zendframework/zend-servicemanager": "^2.7.10 || ^3.0.3", + "zendframework/zend-stdlib": "^2.7.6 || ^3.0", + "zendframework/zend-validator": "^2.6" + }, + "conflict": { + "zendframework/zend-mvc": "<3.0.0" }, "require-dev": { - "phpunit/phpunit": "^4.7", - "squizlabs/php_codesniffer": "^2.3" + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.5", + "zendframework/zend-cache": "^2.6.1", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "suggest": { + "zendframework/zend-cache": "To enable caching of translation strings" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev", - "dev-develop": "1.1-dev" + "dev-master": "1.1.x-dev", + "dev-develop": "1.2.x-dev" + }, + "zf": { + "component": "Zend\\Mvc\\I18n", + "config-provider": "Zend\\Mvc\\I18n\\ConfigProvider" } }, "autoload": { "psr-4": { - "Zend\\Psr7Bridge\\": "src/" + "Zend\\Mvc\\I18n\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "Integration between zend-mvc and zend-i18n", "keywords": [ - "http", - "psr", - "psr-7" + "ZendFramework", + "i18n", + "mvc", + "zf" ], - "time": "2016-05-10T21:44:39+00:00" + "time": "2018-05-01T15:48:40+00:00" }, { - "name": "zendframework/zend-serializer", - "version": "2.8.0", + "name": "zendframework/zend-mvc-plugin-flashmessenger", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "ff74ea020f5f90866eb28365327e9bc765a61a6e" + "url": "https://github.com/zendframework/zend-mvc-plugin-flashmessenger.git", + "reference": "1af2e2d69500da5ca31868c4817b6b7eb7e1cf47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/ff74ea020f5f90866eb28365327e9bc765a61a6e", - "reference": "ff74ea020f5f90866eb28365327e9bc765a61a6e", + "url": "https://api.github.com/repos/zendframework/zend-mvc-plugin-flashmessenger/zipball/1af2e2d69500da5ca31868c4817b6b7eb7e1cf47", + "reference": "1af2e2d69500da5ca31868c4817b6b7eb7e1cf47", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "zendframework/zend-mvc": "^3.0", + "zendframework/zend-session": "^2.8.5", + "zendframework/zend-stdlib": "^2.7 || ^3.0", + "zendframework/zend-view": "^2.10" + }, + "conflict": { + "zendframework/zend-mvc": "<3.0.0" }, "require-dev": { - "phpunit/phpunit": "^4.5", - "squizlabs/php_codesniffer": "^2.3.1", - "zendframework/zend-math": "^2.6", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-i18n": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev", + "dev-develop": "1.2.x-dev" + }, + "zf": { + "component": "Zend\\Mvc\\Plugin\\FlashMessenger" + } + }, + "autoload": { + "psr-4": { + "Zend\\Mvc\\Plugin\\FlashMessenger\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Plugin for creating and exposing flash messages via zend-mvc controllers", + "keywords": [ + "ZendFramework", + "mvc", + "zf" + ], + "time": "2018-04-30T18:47:56+00:00" + }, + { + "name": "zendframework/zend-paginator", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-paginator.git", + "reference": "fd58828c8280a90f133b9e0af2fe1a7885d47206" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-paginator/zipball/fd58828c8280a90f133b9e0af2fe1a7885d47206", + "reference": "fd58828c8280a90f133b9e0af2fe1a7885d47206", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^5.6", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2.1 || ^5.7.15", + "zendframework/zend-cache": "^2.6.1", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-config": "^2.6.0", + "zendframework/zend-db": "^2.9.2", + "zendframework/zend-filter": "^2.6.1", + "zendframework/zend-json": "^2.6.1", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-view": "^2.6.3" }, "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + "zendframework/zend-cache": "Zend\\Cache component to support cache features", + "zendframework/zend-db": "Zend\\Db component", + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-json": "Zend\\Json component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component", + "zendframework/zend-view": "Zend\\View component" }, "type": "library", "extra": { @@ -3711,6 +3789,125 @@ "dev-master": "2.8-dev", "dev-develop": "2.9-dev" }, + "zf": { + "component": "Zend\\Paginator", + "config-provider": "Zend\\Paginator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Paginator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "zend-paginator is a flexible component for paginating collections of data and presenting that data to users.", + "homepage": "https://github.com/zendframework/zend-paginator", + "keywords": [ + "paginator", + "zf2" + ], + "time": "2018-01-30T15:52:44+00:00" + }, + { + "name": "zendframework/zend-router", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-router.git", + "reference": "a80a7427afb8f736b9aeeb341a78dae855849291" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-router/zipball/a80a7427afb8f736b9aeeb341a78dae855849291", + "reference": "a80a7427afb8f736b9aeeb341a78dae855849291", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.2", + "php": "^5.6 || ^7.0", + "zendframework/zend-http": "^2.8.1", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "conflict": { + "zendframework/zend-mvc": "<3.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.22 || ^6.4.1", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-i18n": "^2.7.4" + }, + "suggest": { + "zendframework/zend-i18n": "^2.7.4, if defining translatable HTTP path segments" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + }, + "zf": { + "component": "Zend\\Router", + "config-provider": "Zend\\Router\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Router\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Flexible routing system for HTTP and console applications", + "keywords": [ + "ZendFramework", + "mvc", + "routing", + "zend", + "zf" + ], + "time": "2018-08-01T22:24:35+00:00" + }, + { + "name": "zendframework/zend-serializer", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-serializer.git", + "reference": "0172690db48d8935edaf625c4cba38b79719892c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", + "reference": "0172690db48d8935edaf625c4cba38b79719892c", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "zendframework/zend-json": "^2.5 || ^3.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.25 || ^6.4.4", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-math": "^2.6 || ^3.0", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + }, + "suggest": { + "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", + "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" + }, "zf": { "component": "Zend\\Serializer", "config-provider": "Zend\\Serializer\\ConfigProvider" @@ -3726,12 +3923,12 @@ "BSD-3-Clause" ], "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", - "homepage": "https://github.com/zendframework/zend-serializer", "keywords": [ + "ZendFramework", "serializer", - "zf2" + "zf" ], - "time": "2016-06-21T17:01:55+00:00" + "time": "2018-05-14T18:45:18+00:00" }, { "name": "zendframework/zend-server", @@ -3782,38 +3979,48 @@ }, { "name": "zendframework/zend-servicemanager", - "version": "dev-master", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/demiankatz/zend-servicemanager.git", - "reference": "ae73b1b3affd9c4e3031fb88c84a7082368ca6c3" + "url": "https://github.com/zendframework/zend-servicemanager.git", + "reference": "9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/demiankatz/zend-servicemanager/zipball/ae73b1b3affd9c4e3031fb88c84a7082368ca6c3", - "reference": "ae73b1b3affd9c4e3031fb88c84a7082368ca6c3", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42", + "reference": "9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0" + "container-interop/container-interop": "^1.2", + "php": "^5.6 || ^7.0", + "psr/container": "^1.0", + "zendframework/zend-stdlib": "^3.1" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.2", + "psr/container-implementation": "^1.0" }, "require-dev": { - "athletic/athletic": "dev-master", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "mikey179/vfsstream": "^1.6.5", + "ocramius/proxy-manager": "^1.0 || ^2.0", + "phpbench/phpbench": "^0.13.0", + "phpunit/phpunit": "^5.7.25 || ^6.4.4", + "zendframework/zend-coding-standard": "~1.0.0" }, "suggest": { - "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" + "ocramius/proxy-manager": "ProxyManager 1.* to handle lazy initialization of services", + "zendframework/zend-stdlib": "zend-stdlib ^2.5 if you wish to use the MergeReplaceKey or MergeRemoveKey features in Config instances" }, + "bin": [ + "bin/generate-deps-for-config-factory", + "bin/generate-factory-for-class" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" + "dev-master": "3.3-dev", + "dev-develop": "4.0-dev" } }, "autoload": { @@ -3821,49 +4028,47 @@ "Zend\\ServiceManager\\": "src/" } }, - "autoload-dev": { - "psr-4": { - "ZendTest\\ServiceManager\\": "test/", - "ZendBench\\ServiceManager\\": "benchmarks/" - } - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "description": " ", - "homepage": "https://github.com/zendframework/zend-servicemanager", + "description": "Factory-Driven Dependency Injection Container", "keywords": [ + "PSR-11", + "ZendFramework", + "dependency-injection", + "di", + "dic", + "service-manager", "servicemanager", - "zf2" + "zf" ], - "support": { - "source": "https://github.com/demiankatz/zend-servicemanager/tree/master" - }, - "time": "2018-06-08T21:19:09+00:00" + "time": "2018-01-29T16:48:37+00:00" }, { "name": "zendframework/zend-session", - "version": "2.8.0", + "version": "2.8.5", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "b1486c382decc241de8b1c7778eaf2f0a884f67d" + "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/b1486c382decc241de8b1c7778eaf2f0a884f67d", - "reference": "b1486c382decc241de8b1c7778eaf2f0a884f67d", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", "shasum": "" }, "require": { - "php": "^7.0 || ^5.6", + "php": "^5.6 || ^7.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", + "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-db": "^2.7", @@ -3900,36 +4105,37 @@ "BSD-3-Clause" ], "description": "manage and preserve session data, a logical complement of cookie data, across multiple page requests by the same client", - "homepage": "https://github.com/zendframework/zend-session", "keywords": [ + "ZendFramework", "session", - "zf2" + "zf" ], - "time": "2017-06-19T21:31:39+00:00" + "time": "2018-02-22T16:33:54+00:00" }, { "name": "zendframework/zend-soap", - "version": "2.6.0", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-soap.git", - "reference": "2d6012e7231cce550219eccfc80836a028d20bf1" + "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/2d6012e7231cce550219eccfc80836a028d20bf1", - "reference": "2d6012e7231cce550219eccfc80836a028d20bf1", + "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", + "ext-soap": "*", + "php": "^5.6 || ^7.0", "zendframework/zend-server": "^2.6.1", "zendframework/zend-stdlib": "^2.7 || ^3.0", "zendframework/zend-uri": "^2.5.2" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3.1", + "phpunit/phpunit": "^5.7.21 || ^6.3", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6", "zendframework/zend-http": "^2.5.4" }, @@ -3939,8 +4145,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -3957,49 +4163,35 @@ "soap", "zf2" ], - "time": "2016-04-21T16:06:27+00:00" + "time": "2018-01-29T17:51:26+00:00" }, { "name": "zendframework/zend-stdlib", - "version": "2.7.7", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" + "reference": "cd164b4a18b5d1aeb69be2c26db035b5ed6925ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", - "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/cd164b4a18b5d1aeb69be2c26db035b5ed6925ae", + "reference": "cd164b4a18b5d1aeb69be2c26db035b5ed6925ae", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" + "php": "^5.6 || ^7.0" }, "require-dev": { - "athletic/athletic": "~0.1", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" - }, - "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-release-2.7": "2.7-dev", - "dev-master": "3.0-dev", - "dev-develop": "3.1-dev" + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" } }, "autoload": { @@ -4011,42 +4203,43 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-stdlib", + "description": "SPL extensions, array utilities, error handlers, and more", "keywords": [ + "ZendFramework", "stdlib", - "zf2" + "zf" ], - "time": "2016-04-12T21:17:31+00:00" + "time": "2018-04-30T13:50:40+00:00" }, { "name": "zendframework/zend-text", - "version": "2.6.0", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-text.git", - "reference": "07ad9388e4d4f12620ad37b52a5b0e4ee7845f92" + "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/07ad9388e4d4f12620ad37b52a5b0e4ee7845f92", - "reference": "07ad9388e4d4f12620ad37b52a5b0e4ee7845f92", + "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", + "php": "^5.6 || ^7.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -4058,12 +4251,13 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-text", + "description": "Create FIGlets and text-based tables", "keywords": [ + "ZendFramework", "text", - "zf2" + "zf" ], - "time": "2016-02-08T19:03:52+00:00" + "time": "2018-04-30T14:55:10+00:00" }, { "name": "zendframework/zend-uri", @@ -4114,16 +4308,16 @@ }, { "name": "zendframework/zend-validator", - "version": "2.10.1", + "version": "2.10.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "010084ddbd33299bf51ea6f0e07f8f4e8bd832a8" + "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/010084ddbd33299bf51ea6f0e07f8f4e8bd832a8", - "reference": "010084ddbd33299bf51ea6f0e07f8f4e8bd832a8", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9", + "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9", "shasum": "" }, "require": { @@ -4158,8 +4352,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10-dev", - "dev-develop": "2.11-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\Validator", @@ -4181,20 +4375,20 @@ "validator", "zf2" ], - "time": "2017-08-22T14:19:23+00:00" + "time": "2018-02-01T17:05:33+00:00" }, { "name": "zendframework/zend-view", - "version": "2.9.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-view.git", - "reference": "3b6342c381c4437a03fc81d0064c0bb8924914d3" + "reference": "4478cc5dd960e2339d88b363ef99fa278700e80e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/3b6342c381c4437a03fc81d0064c0bb8924914d3", - "reference": "3b6342c381c4437a03fc81d0064c0bb8924914d3", + "url": "https://api.github.com/repos/zendframework/zend-view/zipball/4478cc5dd960e2339d88b363ef99fa278700e80e", + "reference": "4478cc5dd960e2339d88b363ef99fa278700e80e", "shasum": "" }, "require": { @@ -4225,7 +4419,7 @@ "zendframework/zend-router": "^3.0.1", "zendframework/zend-serializer": "^2.6.1", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.6.2", + "zendframework/zend-session": "^2.8.1", "zendframework/zend-uri": "^2.5" }, "suggest": { @@ -4249,8 +4443,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9-dev", - "dev-develop": "3.0-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" } }, "autoload": { @@ -4268,7 +4462,7 @@ "view", "zf2" ], - "time": "2017-03-21T15:05:56+00:00" + "time": "2018-01-17T22:21:50+00:00" }, { "name": "zendframework/zendrest", @@ -4368,16 +4562,16 @@ }, { "name": "zendframework/zendservice-recaptcha", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/zendframework/ZendService_ReCaptcha.git", - "reference": "6c6877c07c8ac73b187911ea5d264a640b234361" + "reference": "8caf28e3ab8c18d75534c0741ccd6949347d20e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/ZendService_ReCaptcha/zipball/6c6877c07c8ac73b187911ea5d264a640b234361", - "reference": "6c6877c07c8ac73b187911ea5d264a640b234361", + "url": "https://api.github.com/repos/zendframework/ZendService_ReCaptcha/zipball/8caf28e3ab8c18d75534c0741ccd6949347d20e8", + "reference": "8caf28e3ab8c18d75534c0741ccd6949347d20e8", "shasum": "" }, "require": { @@ -4386,7 +4580,7 @@ "zendframework/zend-json": "^2.6.1 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.5", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.0", "zendframework/zend-validator": "^2.8.2" @@ -4397,8 +4591,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev", - "dev-develop": "3.1-dev" + "dev-master": "3.1.x-dev", + "dev-develop": "3.2.x-dev" } }, "autoload": { @@ -4411,12 +4605,12 @@ "BSD-3-Clause" ], "description": "OOP wrapper for the ReCaptcha web service", - "homepage": "http://packages.zendframework.com/", "keywords": [ + "ZendFramework", "recaptcha", - "zf2" + "zf" ], - "time": "2017-02-09T21:38:25+00:00" + "time": "2018-05-08T17:34:06+00:00" }, { "name": "zendframework/zendxml", @@ -4719,44 +4913,35 @@ "time": "2016-03-05T09:10:18+00:00" }, { - "name": "cilex/cilex", - "version": "1.1.0", + "name": "composer/semver", + "version": "1.4.2", "source": { "type": "git", - "url": "https://github.com/Cilex/Cilex.git", - "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5" + "url": "https://github.com/composer/semver.git", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Cilex/Cilex/zipball/7acd965a609a56d0345e8b6071c261fbdb926cb5", - "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5", + "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", "shasum": "" }, "require": { - "cilex/console-service-provider": "1.*", - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/finder": "~2.1", - "symfony/process": "~2.1" + "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*", - "symfony/validator": "~2.1" - }, - "suggest": { - "monolog/monolog": ">=1.0.0", - "symfony/validator": ">=1.0.0", - "symfony/yaml": ">=1.0.0" + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "psr-0": { - "Cilex": "src/" + "psr-4": { + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -4765,50 +4950,61 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "The PHP micro-framework for Command line tools based on the Symfony2 Components", - "homepage": "http://cilex.github.com", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "cli", - "microframework" + "semantic", + "semver", + "validation", + "versioning" ], - "time": "2014-03-29T14:03:13+00:00" + "time": "2016-08-30T16:08:34+00:00" }, { - "name": "cilex/console-service-provider", - "version": "1.0.0", + "name": "doctrine/annotations", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/Cilex/console-service-provider.git", - "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e" + "url": "https://github.com/doctrine/annotations.git", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Cilex/console-service-provider/zipball/25ee3d1875243d38e1a3448ff94bdf944f70d24e", - "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", "shasum": "" }, "require": { - "php": ">=5.3.3", - "pimple/pimple": "1.*@dev", - "symfony/console": "~2.1" + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" }, "require-dev": { - "cilex/cilex": "1.*@dev", - "silex/silex": "1.*@dev" + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { - "psr-0": { - "Cilex\\Provider\\Console": "src" + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -4817,66 +5013,8 @@ ], "authors": [ { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "description": "Console Service Provider", - "keywords": [ - "cilex", - "console", - "pimple", - "service-provider", - "silex" - ], - "time": "2012-12-19T10:50:58+00:00" - }, - { - "name": "doctrine/annotations", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" + "name": "Roman Borschel", + "email": "roman@code-factory.org" }, { "name": "Benjamin Eberlei", @@ -5012,92 +5150,82 @@ ], "time": "2014-09-09T13:34:57+00:00" }, - { - "name": "erusev/parsedown", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/erusev/parsedown.git", - "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", - "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35" - }, - "type": "library", - "autoload": { - "psr-0": { - "Parsedown": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Emanuil Rusev", - "email": "hello@erusev.com", - "homepage": "http://erusev.com" - } - ], - "description": "Parser for Markdown.", - "homepage": "http://parsedown.org", - "keywords": [ - "markdown", - "parser" - ], - "time": "2018-03-08T01:11:30+00:00" - }, { "name": "friendsofphp/php-cs-fixer", - "version": "v1.11.6", + "version": "v2.11.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "41dc93abd2937a85a3889e28765231d574d2bac8" + "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/41dc93abd2937a85a3889e28765231d574d2bac8", - "reference": "41dc93abd2937a85a3889e28765231d574d2bac8", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ad94441c17b8ef096e517acccdbf3238af8a2da8", + "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8", "shasum": "" }, "require": { + "composer/semver": "^1.4", + "doctrine/annotations": "^1.2", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=5.3.6", - "sebastian/diff": "~1.1", - "symfony/console": "~2.3|~3.0", - "symfony/event-dispatcher": "~2.1|~3.0", - "symfony/filesystem": "~2.1|~3.0", - "symfony/finder": "~2.1|~3.0", - "symfony/process": "~2.3|~3.0", - "symfony/stopwatch": "~2.5|~3.0" + "php": "^5.6 || >=7.0 <7.3", + "php-cs-fixer/diff": "^1.3", + "symfony/console": "^3.2 || ^4.0", + "symfony/event-dispatcher": "^3.0 || ^4.0", + "symfony/filesystem": "^3.0 || ^4.0", + "symfony/finder": "^3.0 || ^4.0", + "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-php72": "^1.4", + "symfony/process": "^3.0 || ^4.0", + "symfony/stopwatch": "^3.0 || ^4.0" }, "conflict": { - "hhvm": "<3.9" + "hhvm": "*" }, "require-dev": { - "phpunit/phpunit": "^4.5|^5", - "satooshi/php-coveralls": "^0.7.1" + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", + "justinrainbow/json-schema": "^5.0", + "keradus/cli-executor": "^1.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.0", + "php-cs-fixer/accessible-object": "^1.0", + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "phpunitgoodpractices/traits": "^1.3.1", + "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." }, "bin": [ "php-cs-fixer" ], "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.11-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\CS\\": "Symfony/CS/" - } + "PhpCsFixer\\": "src/" + }, + "classmap": [ + "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationCaseFactory.php", + "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/Constraint/SameStringsConstraint.php", + "tests/Test/Constraint/SameStringsConstraintForV5.php", + "tests/Test/Constraint/SameStringsConstraintForV7.php", + "tests/Test/IntegrationCase.php", + "tests/Test/IntegrationCaseFactory.php", + "tests/Test/IntegrationCaseFactoryInterface.php", + "tests/Test/InternalIntegrationCaseFactory.php", + "tests/TestCase.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5114,126 +5242,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2016-07-22T06:46:28+00:00" - }, - { - "name": "herrera-io/json", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/kherge-php/json.git", - "reference": "60c696c9370a1e5136816ca557c17f82a6fa83f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kherge-php/json/zipball/60c696c9370a1e5136816ca557c17f82a6fa83f1", - "reference": "60c696c9370a1e5136816ca557c17f82a6fa83f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "justinrainbow/json-schema": ">=1.0,<2.0-dev", - "php": ">=5.3.3", - "seld/jsonlint": ">=1.0,<2.0-dev" - }, - "require-dev": { - "herrera-io/phpunit-test-case": "1.*", - "mikey179/vfsstream": "1.1.0", - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "files": [ - "src/lib/json_version.php" - ], - "psr-0": { - "Herrera\\Json": "src/lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kevin Herrera", - "email": "kevin@herrera.io", - "homepage": "http://kevin.herrera.io" - } - ], - "description": "A library for simplifying JSON linting and validation.", - "homepage": "http://herrera-io.github.com/php-json", - "keywords": [ - "json", - "lint", - "schema", - "validate" - ], - "abandoned": "kherge/json", - "time": "2013-10-30T16:51:34+00:00" - }, - { - "name": "herrera-io/phar-update", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/kherge-abandoned/php-phar-update.git", - "reference": "00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kherge-abandoned/php-phar-update/zipball/00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b", - "reference": "00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b", - "shasum": "" - }, - "require": { - "herrera-io/json": "1.*", - "kherge/version": "1.*", - "php": ">=5.3.3" - }, - "require-dev": { - "herrera-io/phpunit-test-case": "1.*", - "mikey179/vfsstream": "1.1.0", - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "files": [ - "src/lib/constants.php" - ], - "psr-0": { - "Herrera\\Phar\\Update": "src/lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kevin Herrera", - "email": "kevin@herrera.io", - "homepage": "http://kevin.herrera.io" - } - ], - "description": "A library for self-updating Phars.", - "homepage": "http://herrera-io.github.com/php-phar-update", - "keywords": [ - "phar", - "update" - ], - "abandoned": true, - "time": "2013-10-30T17:23:01+00:00" + "time": "2018-03-21T17:41:26+00:00" }, { "name": "instaclick/php-webdriver", @@ -5294,363 +5303,6 @@ ], "time": "2017-06-30T04:02:48+00:00" }, - { - "name": "jms/metadata", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/metadata.git", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "doctrine/cache": "~1.0", - "symfony/cache": "~3.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "Metadata\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Class/method/property metadata management in PHP", - "keywords": [ - "annotations", - "metadata", - "xml", - "yaml" - ], - "time": "2016-12-05T10:18:33+00:00" - }, - { - "name": "jms/parser-lib", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/parser-lib.git", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "shasum": "" - }, - "require": { - "phpoption/phpoption": ">=0.9,<2.0-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "description": "A library for easily creating recursive-descent parsers.", - "time": "2012-11-18T18:08:43+00:00" - }, - { - "name": "jms/serializer", - "version": "1.13.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/serializer.git", - "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/00863e1d55b411cc33ad3e1de09a4c8d3aae793c", - "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0", - "doctrine/instantiator": "^1.0.3", - "jms/metadata": "^1.3", - "jms/parser-lib": "1.*", - "php": "^5.5|^7.0", - "phpcollection/phpcollection": "~0.1", - "phpoption/phpoption": "^1.1" - }, - "conflict": { - "twig/twig": "<1.12" - }, - "require-dev": { - "doctrine/orm": "~2.1", - "doctrine/phpcr-odm": "^1.3|^2.0", - "ext-pdo_sqlite": "*", - "jackalope/jackalope-doctrine-dbal": "^1.1.5", - "phpunit/phpunit": "^4.8|^5.0", - "propel/propel1": "~1.7", - "psr/container": "^1.0", - "symfony/dependency-injection": "^2.7|^3.3|^4.0", - "symfony/expression-language": "^2.6|^3.0", - "symfony/filesystem": "^2.1", - "symfony/form": "~2.1|^3.0", - "symfony/translation": "^2.1|^3.0", - "symfony/validator": "^2.2|^3.0", - "symfony/yaml": "^2.1|^3.0", - "twig/twig": "~1.12|~2.0" - }, - "suggest": { - "doctrine/cache": "Required if you like to use cache functionality.", - "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", - "symfony/yaml": "Required if you'd like to serialize data to YAML format." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.13-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\Serializer": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", - "homepage": "http://jmsyst.com/libs/serializer", - "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" - ], - "time": "2018-07-25T13:58:54+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", - "shasum": "" - }, - "require": { - "php": ">=5.3.29" - }, - "require-dev": { - "json-schema/json-schema-test-suite": "1.1.0", - "phpdocumentor/phpdocumentor": "~2", - "phpunit/phpunit": "~3.7" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "time": "2016-01-25T15:43:01+00:00" - }, - { - "name": "kherge/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/kherge-abandoned/Version.git", - "reference": "f07cf83f8ce533be8f93d2893d96d674bbeb7e30" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kherge-abandoned/Version/zipball/f07cf83f8ce533be8f93d2893d96d674bbeb7e30", - "reference": "f07cf83f8ce533be8f93d2893d96d674bbeb7e30", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "KevinGH\\Version": "src/lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kevin Herrera", - "email": "me@kevingh.com" - } - ], - "description": "A parsing and comparison library for semantic versioning.", - "homepage": "http://github.com/kherge/Version", - "abandoned": true, - "time": "2012-08-16T17:13:03+00:00" - }, - { - "name": "monolog/monolog", - "version": "1.23.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "time": "2017-06-19T01:22:40+00:00" - }, { "name": "myclabs/deep-copy", "version": "1.7.0", @@ -5696,51 +5348,6 @@ ], "time": "2017-10-19T19:58:43+00:00" }, - { - "name": "nikic/php-parser", - "version": "v1.4.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "files": [ - "lib/bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2015-09-19T14:15:08+00:00" - }, { "name": "pdepend/pdepend", "version": "2.5.2", @@ -5782,208 +5389,188 @@ "time": "2017-12-13T13:21:38+00:00" }, { - "name": "phpcollection/phpcollection", - "version": "0.5.0", + "name": "phar-io/manifest", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/schmittjoh/php-collection.git", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" + "url": "https://github.com/phar-io/manifest.git", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", "shasum": "" }, "require": { - "phpoption/phpoption": "1.*" + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^1.0.1", + "php": "^5.6 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.4-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "PhpCollection": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "BSD-3-Clause" ], "authors": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "General-Purpose Collection Library for PHP", - "keywords": [ - "collection", - "list", - "map", - "sequence", - "set" - ], - "time": "2015-05-17T12:39:23+00:00" + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2017-03-05T18:14:27+00:00" }, { - "name": "phpdocumentor/fileset", - "version": "1.0.0", + "name": "phar-io/version", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/Fileset.git", - "reference": "bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0" + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Fileset/zipball/bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0", - "reference": "bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", "shasum": "" }, "require": { - "php": ">=5.3.3", - "symfony/finder": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "~3.7" + "php": "^5.6 || ^7.0" }, "type": "library", "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/" - ] - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "Fileset component for collecting a set of files given directories and file paths", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "files", - "fileset", - "phpdoc" + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } ], - "time": "2013-08-06T21:07:42+00:00" + "description": "Library for handling version information and constraints", + "time": "2017-03-05T17:38:23+00:00" }, { - "name": "phpdocumentor/graphviz", - "version": "1.0.4", + "name": "php-cs-fixer/diff", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/GraphViz.git", - "reference": "a906a90a9f230535f25ea31caf81b2323956283f" + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/GraphViz/zipball/a906a90a9f230535f25ea31caf81b2323956283f", - "reference": "a906a90a9f230535f25ea31caf81b2323956283f", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "symfony/process": "^3.3" }, "type": "library", "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit" - ] - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "SpacePossum" } ], - "time": "2016-02-02T13:00:08+00:00" + "description": "sebastian/diff v2 backport support for PHP5.6", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "time": "2018-02-15T16:58:55+00:00" }, { - "name": "phpdocumentor/phpdocumentor", - "version": "v2.9.0", + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/phpDocumentor2.git", - "reference": "be607da0eef9b9249c43c5b4820d25d631c73667" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/be607da0eef9b9249c43c5b4820d25d631c73667", - "reference": "be607da0eef9b9249c43c5b4820d25d631c73667", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { - "cilex/cilex": "~1.0", - "erusev/parsedown": "~1.0", - "herrera-io/phar-update": "1.0.3", - "jms/serializer": ">=0.12", - "monolog/monolog": "~1.6", - "php": ">=5.3.3", - "phpdocumentor/fileset": "~1.0", - "phpdocumentor/graphviz": "~1.0", - "phpdocumentor/reflection": "^3.0", - "phpdocumentor/reflection-docblock": "~2.0", - "symfony/config": "~2.3", - "symfony/console": "~2.3", - "symfony/event-dispatcher": "~2.1", - "symfony/process": "~2.0", - "symfony/stopwatch": "~2.3", - "symfony/validator": "~2.2", - "twig/twig": "~1.3", - "zendframework/zend-cache": "~2.1", - "zendframework/zend-config": "~2.1", - "zendframework/zend-filter": "~2.1", - "zendframework/zend-i18n": "~2.1", - "zendframework/zend-serializer": "~2.1", - "zendframework/zend-servicemanager": "~2.1", - "zendframework/zend-stdlib": "~2.1", - "zetacomponents/document": ">=1.3.1" + "php": ">=5.5" }, "require-dev": { - "behat/behat": "~3.0", - "mikey179/vfsstream": "~1.2", - "mockery/mockery": "~0.9@dev", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.4", - "symfony/expression-language": "~2.4" - }, - "suggest": { - "ext-twig": "Enabling the twig extension improves the generation of twig based templates.", - "ext-xslcache": "Enabling the XSLCache extension improves the generation of xml based templates." + "phpunit/phpunit": "^4.6" }, - "bin": [ - "bin/phpdoc.php", - "bin/phpdoc" - ], "type": "library", "extra": { "branch-alias": { - "dev-develop": "2.9-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/" - ], - "Cilex\\Provider": [ - "src/" + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" ] } }, @@ -5991,54 +5578,58 @@ "license": [ "MIT" ], - "description": "Documentation Generator for PHP", + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", "homepage": "http://www.phpdoc.org", "keywords": [ - "api", - "application", - "dga", - "documentation", - "phpdoc" + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" ], - "time": "2016-05-22T09:50:56+00:00" + "time": "2017-09-11T18:02:19+00:00" }, { - "name": "phpdocumentor/reflection", - "version": "3.0.1", + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", "shasum": "" }, "require": { - "nikic/php-parser": "^1.0", - "php": ">=5.3.3", - "phpdocumentor/reflection-docblock": "~2.0", - "psr/log": "~1.0" + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "behat/behat": "~2.4", - "mockery/mockery": "~0.8", - "phpunit/phpunit": "~4.0" + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/", - "tests/mocks/" + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" ] } }, @@ -6046,49 +5637,46 @@ "license": [ "MIT" ], - "description": "Reflection library to do Static Analysis for PHP Projects", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } ], - "time": "2016-05-21T08:42:32+00:00" + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-11-30T07:14:17+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.5", + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ + "psr-4": { + "phpDocumentor\\Reflection\\": [ "src/" ] } @@ -6100,34 +5688,30 @@ "authors": [ { "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "email": "me@mikevanriel.com" } ], - "time": "2016-01-25T08:17:30+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phploc/phploc", - "version": "3.0.1", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phploc.git", - "reference": "74f917e6f80f291856989960d31afa44a4196859" + "reference": "6a8a9416517b82d6326ac9c2d040ad53c13654eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/74f917e6f80f291856989960d31afa44a4196859", - "reference": "74f917e6f80f291856989960d31afa44a4196859", + "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/6a8a9416517b82d6326ac9c2d040ad53c13654eb", + "reference": "6a8a9416517b82d6326ac9c2d040ad53c13654eb", "shasum": "" }, "require": { - "php": ">=5.6", - "sebastian/finder-facade": "~1.1", - "sebastian/git": "~2.1", - "sebastian/version": "~1.0.3|~2.0", - "symfony/console": "~2.5|~3.0" - }, - "require-dev": { - "phpunit/phpunit": "~5" + "php": "^5.6 || ^7.0", + "sebastian/finder-facade": "^1.1", + "sebastian/version": "^2.0", + "symfony/console": "^2.7|^3.0|^4.0" }, "bin": [ "phploc" @@ -6135,7 +5719,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -6156,7 +5740,7 @@ ], "description": "A tool for quickly measuring the size of a PHP project.", "homepage": "https://github.com/sebastianbergmann/phploc", - "time": "2016-04-25T08:11:21+00:00" + "time": "2017-11-18T17:35:43+00:00" }, { "name": "phpmd/phpmd", @@ -6224,56 +5808,6 @@ ], "time": "2017-01-20T14:41:10+00:00" }, - { - "name": "phpoption/phpoption", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "4.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-0": { - "PhpOption\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "time": "2015-07-25T16:39:46+00:00" - }, { "name": "phpspec/prophecy", "version": "1.8.0", @@ -6339,40 +5873,40 @@ }, { "name": "phpunit/php-code-coverage", - "version": "4.0.8", + "version": "5.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" + "reference": "c89677919c5dd6d3b3852f230a663118762218ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", + "reference": "c89677919c5dd6d3b3852f230a663118762218ac", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" + "php": "^7.0", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^2.0.1", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-xdebug": "^2.5.1" + "ext-xdebug": "^2.5.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "5.3.x-dev" } }, "autoload": { @@ -6387,7 +5921,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -6398,7 +5932,7 @@ "testing", "xunit" ], - "time": "2017-04-02T07:44:40+00:00" + "time": "2018-04-06T15:36:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -6539,29 +6073,29 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.12", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" + "reference": "791198a2c6254db10131eecfe8c06670700904db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", - "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.2.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -6584,20 +6118,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-12-04T08:55:13+00:00" + "time": "2017-11-27T05:48:46+00:00" }, { "name": "phpunit/phpunit", - "version": "5.7.15", + "version": "6.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b99112aecc01f62acf3d81a3f59646700a1849e5" + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b99112aecc01f62acf3d81a3f59646700a1849e5", - "reference": "b99112aecc01f62acf3d81a3f59646700a1849e5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b", + "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b", "shasum": "" }, "require": { @@ -6606,33 +6140,35 @@ "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "~1.2", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0.3|~2.0", - "symfony/yaml": "~2.1|~3.0" + "myclabs/deep-copy": "^1.6.1", + "phar-io/manifest": "^1.0.1", + "phar-io/version": "^1.0", + "php": "^7.0", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^5.3", + "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^5.0.5", + "sebastian/comparator": "^2.1", + "sebastian/diff": "^2.0", + "sebastian/environment": "^3.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0.1" }, "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" + "phpunit/php-invoker": "^1.1" }, "bin": [ "phpunit" @@ -6640,7 +6176,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.7.x-dev" + "dev-master": "6.5.x-dev" } }, "autoload": { @@ -6666,33 +6202,33 @@ "testing", "xunit" ], - "time": "2017-03-02T15:22:43+00:00" + "time": "2018-04-10T11:38:34+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "3.4.4", + "version": "5.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" + "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", + "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" + "doctrine/instantiator": "^1.0.5", + "php": "^7.0", + "phpunit/php-text-template": "^1.2.1", + "sebastian/exporter": "^3.1" }, "conflict": { - "phpunit/phpunit": "<5.4.0" + "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "^5.4" + "phpunit/phpunit": "^6.5.11" }, "suggest": { "ext-soap": "*" @@ -6700,7 +6236,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { @@ -6715,7 +6251,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -6725,53 +6261,7 @@ "mock", "xunit" ], - "time": "2017-06-30T09:13:00+00:00" - }, - { - "name": "pimple/pimple", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Pimple": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ], - "time": "2013-11-22T08:30:29+00:00" + "time": "2018-08-09T05:50:03+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -6820,30 +6310,30 @@ }, { "name": "sebastian/comparator", - "version": "1.2.4", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": "^7.0", + "sebastian/diff": "^2.0 || ^3.0", + "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { @@ -6874,38 +6364,38 @@ } ], "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], - "time": "2017-01-29T09:50:25+00:00" + "time": "2018-02-01T13:46:46+00:00" }, { "name": "sebastian/diff", - "version": "1.4.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -6932,32 +6422,32 @@ "keywords": [ "diff" ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2017-08-03T08:09:46+00:00" }, { "name": "sebastian/environment", - "version": "2.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "phpunit/phpunit": "^6.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -6982,34 +6472,34 @@ "environment", "hhvm" ], - "time": "2016-11-26T07:53:53+00:00" + "time": "2017-07-01T08:51:00+00:00" }, { "name": "sebastian/exporter", - "version": "2.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" + "php": "^7.0", + "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -7049,7 +6539,7 @@ "export", "exporter" ], - "time": "2016-11-19T08:54:04+00:00" + "time": "2017-04-03T13:19:02+00:00" }, { "name": "sebastian/finder-facade", @@ -7091,26 +6581,32 @@ "time": "2017-11-18T17:31:49+00:00" }, { - "name": "sebastian/git", - "version": "2.1.4", + "name": "sebastian/global-state", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/git.git", - "reference": "815bbbc963cf35e5413df195aa29df58243ecd24" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/git/zipball/815bbbc963cf35e5413df195aa29df58243ecd24", - "reference": "815bbbc963cf35e5413df195aa29df58243ecd24", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -7128,41 +6624,39 @@ "email": "sebastian@phpunit.de" } ], - "description": "Simple wrapper for Git", - "homepage": "http://www.github.com/sebastianbergmann/git", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "git" + "global state" ], - "abandoned": true, - "time": "2017-01-23T20:57:12+00:00" + "time": "2017-04-27T15:39:26+00:00" }, { - "name": "sebastian/global-state", - "version": "1.1.1", + "name": "sebastian/object-enumerator", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -7180,38 +6674,34 @@ "email": "sebastian@phpunit.de" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2015-10-12T03:26:01+00:00" + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "2.0.1", + "name": "sebastian/object-reflector", + "version": "1.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -7229,31 +6719,30 @@ "email": "sebastian@phpunit.de" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18T15:18:39+00:00" + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" }, { "name": "sebastian/phpcpd", - "version": "2.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpcpd.git", - "reference": "24d9a880deadb0b8c9680e9cfe78e30b704225db" + "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/24d9a880deadb0b8c9680e9cfe78e30b704225db", - "reference": "24d9a880deadb0b8c9680e9cfe78e30b704225db", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/dfed51c1288790fc957c9433e2f49ab152e8a564", + "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-timer": ">=1.0.6", - "sebastian/finder-facade": "~1.1", - "sebastian/version": "~1.0|~2.0", - "symfony/console": "~2.7|^3.0", - "theseer/fdomdocument": "~1.4" + "php": "^5.6|^7.0", + "phpunit/php-timer": "^1.0.6", + "sebastian/finder-facade": "^1.1", + "sebastian/version": "^1.0|^2.0", + "symfony/console": "^2.7|^3.0|^4.0" }, "bin": [ "phpcpd" @@ -7261,7 +6750,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -7282,32 +6771,32 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2016-04-17T19:32:49+00:00" + "time": "2017-11-16T08:49:28+00:00" }, { "name": "sebastian/recursion-context", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -7335,7 +6824,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19T07:33:16+00:00" + "time": "2017-03-03T06:23:57+00:00" }, { "name": "sebastian/resource-operations", @@ -7422,115 +6911,39 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, - { - "name": "seld/jsonlint", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38", - "shasum": "" - }, - "require": { - "php": "^5.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "bin": [ - "bin/jsonlint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], - "time": "2018-01-24T12:46:19+00:00" - }, { "name": "squizlabs/php_codesniffer", - "version": "2.8.1", + "version": "3.2.3", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" + "reference": "4842476c434e375f9d3182ff7b89059583aa8b27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", - "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/4842476c434e375f9d3182ff7b89059583aa8b27", + "reference": "4842476c434e375f9d3182ff7b89059583aa8b27", "shasum": "" }, "require": { "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": ">=5.1.2" + "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "bin": [ - "scripts/phpcs", - "scripts/phpcbf" + "bin/phpcs", + "bin/phpcbf" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.x-dev" } }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -7547,29 +6960,36 @@ "phpcs", "standards" ], - "time": "2017-03-01T22:17:45+00:00" + "time": "2018-02-20T21:35:23+00:00" }, { "name": "symfony/config", - "version": "v2.8.44", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "06c0be4cdd8363f3ec8d592c9a4d1b981d5052af" + "reference": "7b08223b7f6abd859651c56bcabf900d1627d085" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/06c0be4cdd8363f3ec8d592c9a4d1b981d5052af", - "reference": "06c0be4cdd8363f3ec8d592c9a4d1b981d5052af", + "url": "https://api.github.com/repos/symfony/config/zipball/7b08223b7f6abd859651c56bcabf900d1627d085", + "reference": "7b08223b7f6abd859651c56bcabf900d1627d085", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/filesystem": "~2.3|~3.0.0", + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", "symfony/polyfill-ctype": "~1.8" }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, "require-dev": { - "symfony/yaml": "~2.7|~3.0.0" + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -7577,7 +6997,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7604,41 +7024,49 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:13:39+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/console", - "version": "v2.8.44", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0c1fcbb9afb5cff992c982ff99c0434f0146dcfc" + "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0c1fcbb9afb5cff992c982ff99c0434f0146dcfc", - "reference": "0c1fcbb9afb5cff992c982ff99c0434f0146dcfc", + "url": "https://api.github.com/repos/symfony/console/zipball/6b217594552b9323bcdcfc14f8a0ce126e84cd73", + "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/debug": "^2.7.2|~3.0.0", + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", "symfony/polyfill-mbstring": "~1.0" }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1|~3.0.0", - "symfony/process": "~2.1|~3.0.0" + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" }, "suggest": { "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", + "symfony/lock": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7665,7 +7093,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:13:39+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/css-selector", @@ -7722,33 +7150,32 @@ }, { "name": "symfony/debug", - "version": "v3.0.9", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a" + "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/697c527acd9ea1b2d3efac34d9806bf255278b0a", - "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a", + "url": "https://api.github.com/repos/symfony/debug/zipball/d5a058ff6ecad26b30c1ba452241306ea34c65cc", + "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7775,43 +7202,51 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-07-30T07:22:48+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/dependency-injection", - "version": "v3.2.14", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "d9f2e62e1a93d52ad4e4f6faaf66f6eef723d761" + "reference": "1c0e679e522591fd744fdf242fec41a43d62b2b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d9f2e62e1a93d52ad4e4f6faaf66f6eef723d761", - "reference": "d9f2e62e1a93d52ad4e4f6faaf66f6eef723d761", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1c0e679e522591fd744fdf242fec41a43d62b2b1", + "reference": "1c0e679e522591fd744fdf242fec41a43d62b2b1", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8", + "psr/container": "^1.0" }, "conflict": { - "symfony/yaml": "<3.2" + "symfony/config": "<3.3.7", + "symfony/finder": "<3.3", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" }, "require-dev": { - "symfony/config": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0", - "symfony/yaml": "~3.2" + "symfony/config": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" }, "suggest": { "symfony/config": "", "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", "symfony/yaml": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7838,31 +7273,34 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2017-07-28T15:22:55+00:00" + "time": "2018-07-29T15:19:31+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.44", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12" + "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/84ae343f39947aa084426ed1138bb96bf94d1f12", - "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", + "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "^2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -7871,7 +7309,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7898,29 +7336,30 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:03:18+00:00" + "time": "2018-07-26T09:06:28+00:00" }, { "name": "symfony/filesystem", - "version": "v3.0.9", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d" + "reference": "a59f917e3c5d82332514cb4538387638f5bde2d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b2da5009d9bacbd91d83486aa1f44c793a8c380d", - "reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/a59f917e3c5d82332514cb4538387638f5bde2d6", + "reference": "a59f917e3c5d82332514cb4538387638f5bde2d6", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7947,29 +7386,29 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2016-07-20T05:43:46+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/finder", - "version": "v2.8.44", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "f0de0b51913eb2caab7dfed6413b87e14fca780e" + "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/f0de0b51913eb2caab7dfed6413b87e14fca780e", - "reference": "f0de0b51913eb2caab7dfed6413b87e14fca780e", + "url": "https://api.github.com/repos/symfony/finder/zipball/8a84fcb207451df0013b2c74cbbf1b62d47b999a", + "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -7996,27 +7435,27 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:13:39+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { - "name": "symfony/polyfill-ctype", + "name": "symfony/polyfill-mbstring", "version": "v1.9.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { - "ext-ctype": "For best performance" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { @@ -8026,7 +7465,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" }, "files": [ "bootstrap.php" @@ -8038,44 +7477,42 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "ctype", + "mbstring", "polyfill", - "portable" + "portable", + "shim" ], "time": "2018-08-06T14:22:27+00:00" }, { - "name": "symfony/polyfill-mbstring", + "name": "symfony/polyfill-php72", "version": "v1.9.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae", + "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae", "shasum": "" }, "require": { "php": ">=5.3.3" }, - "suggest": { - "ext-mbstring": "For best performance" - }, "type": "library", "extra": { "branch-alias": { @@ -8084,7 +7521,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "Symfony\\Polyfill\\Php72\\": "" }, "files": [ "bootstrap.php" @@ -8104,11 +7541,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" @@ -8117,25 +7553,25 @@ }, { "name": "symfony/process", - "version": "v2.8.44", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "cc83afdb5ac99147806b3bb65a3ff1227664f596" + "reference": "0414db29bd770ec5a4152683e655f55efd4fa60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cc83afdb5ac99147806b3bb65a3ff1227664f596", - "reference": "cc83afdb5ac99147806b3bb65a3ff1227664f596", + "url": "https://api.github.com/repos/symfony/process/zipball/0414db29bd770ec5a4152683e655f55efd4fa60f", + "reference": "0414db29bd770ec5a4152683e655f55efd4fa60f", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8162,29 +7598,29 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:13:39+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/stopwatch", - "version": "v2.8.44", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "12a4b0c2a1788adf16a5548ab18ab9e8801d71d8" + "reference": "deda2765e8dab2fc38492e926ea690f2a681f59d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/12a4b0c2a1788adf16a5548ab18ab9e8801d71d8", - "reference": "12a4b0c2a1788adf16a5548ab18ab9e8801d71d8", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/deda2765e8dab2fc38492e926ea690f2a681f59d", + "reference": "deda2765e8dab2fc38492e926ea690f2a681f59d", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8211,145 +7647,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-07-24T10:05:38+00:00" - }, - { - "name": "symfony/translation", - "version": "v3.0.9", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "eee6c664853fd0576f21ae25725cfffeafe83f26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/eee6c664853fd0576f21ae25725cfffeafe83f26", - "reference": "eee6c664853fd0576f21ae25725cfffeafe83f26", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/config": "<2.8" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/intl": "~2.8|~3.0", - "symfony/yaml": "~2.8|~3.0" - }, - "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com", - "time": "2016-07-30T07:22:48+00:00" - }, - { - "name": "symfony/validator", - "version": "v2.8.44", - "source": { - "type": "git", - "url": "https://github.com/symfony/validator.git", - "reference": "30352cf38e35ef34cf60676ee72ff3f84c551fac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/30352cf38e35ef34cf60676ee72ff3f84c551fac", - "reference": "30352cf38e35ef34cf60676ee72ff3f84c551fac", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation": "~2.4|~3.0.0" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "egulias/email-validator": "^1.2.1", - "symfony/config": "~2.2|~3.0.0", - "symfony/expression-language": "~2.4|~3.0.0", - "symfony/http-foundation": "~2.3|~3.0.0", - "symfony/intl": "~2.7.25|^2.8.18|~3.2.5", - "symfony/property-access": "~2.3|~3.0.0", - "symfony/yaml": "^2.0.5|~3.0.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", - "doctrine/cache": "For using the default cached annotation reader and metadata cache.", - "egulias/email-validator": "Strict (RFC compliant) email validation", - "symfony/config": "", - "symfony/expression-language": "For using the 2.4 Expression validator", - "symfony/http-foundation": "", - "symfony/intl": "", - "symfony/property-access": "For using the 2.4 Validator API", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Validator\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Validator Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T11:13:39+00:00" + "time": "2018-07-26T10:03:52+00:00" }, { "name": "theseer/fdomdocument", @@ -8392,197 +7690,110 @@ "time": "2017-06-30T11:53:12+00:00" }, { - "name": "twig/twig", - "version": "v1.27.0", + "name": "theseer/tokenizer", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97", - "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", "shasum": "" }, "require": { - "php": ">=5.2.7" - }, - "require-dev": { - "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.27-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - }, - { - "name": "Twig Team", - "homepage": "http://twig.sensiolabs.org/contributors", - "role": "Contributors" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "http://twig.sensiolabs.org", - "keywords": [ - "templating" - ], - "time": "2016-10-25T19:17:17+00:00" - }, - { - "name": "zetacomponents/base", - "version": "1.9.1", - "source": { - "type": "git", - "url": "https://github.com/zetacomponents/Base.git", - "reference": "489e20235989ddc97fdd793af31ac803972454f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zetacomponents/Base/zipball/489e20235989ddc97fdd793af31ac803972454f1", - "reference": "489e20235989ddc97fdd793af31ac803972454f1", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "~5.7", - "zetacomponents/unit-test": "*" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" }, "type": "library", "autoload": { "classmap": [ - "src" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "BSD-3-Clause" ], "authors": [ { - "name": "Sergey Alexeev" - }, - { - "name": "Sebastian Bergmann" - }, - { - "name": "Jan Borsodi" - }, - { - "name": "Raymond Bosman" - }, - { - "name": "Frederik Holljen" - }, - { - "name": "Kore Nordmann" - }, - { - "name": "Derick Rethans" - }, - { - "name": "Vadym Savchuk" - }, - { - "name": "Tobias Schlitt" - }, - { - "name": "Alexandru Stanoi" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "The Base package provides the basic infrastructure that all packages rely on. Therefore every component relies on this package.", - "homepage": "https://github.com/zetacomponents", - "time": "2017-11-28T11:30:00+00:00" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" }, { - "name": "zetacomponents/document", - "version": "1.3.1", + "name": "webmozart/assert", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/zetacomponents/Document.git", - "reference": "688abfde573cf3fe0730f82538fbd7aa9fc95bc8" + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zetacomponents/Document/zipball/688abfde573cf3fe0730f82538fbd7aa9fc95bc8", - "reference": "688abfde573cf3fe0730f82538fbd7aa9fc95bc8", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { - "zetacomponents/base": "*" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "zetacomponents/unit-test": "dev-master" + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, "autoload": { - "classmap": [ - "src" - ] + "psr-4": { + "Webmozart\\Assert\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann" - }, - { - "name": "Kore Nordmann" - }, - { - "name": "Derick Rethans" - }, - { - "name": "Tobias Schlitt" - }, - { - "name": "Alexandru Stanoi" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "The Document components provides a general conversion framework for different semantic document markup languages like XHTML, Docbook, RST and similar.", - "homepage": "https://github.com/zetacomponents", - "time": "2013-12-19T11:40:00+00:00" + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { "ahand/mobileesp": 20, - "pear/validate_ispn": 20, - "zendframework/zend-servicemanager": 20, - "ghislainf/zf2-whoops": 20 + "ghislainf/zf2-whoops": 20, + "pear/validate_ispn": 20 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.6" + "php": ">=7.0.8" }, "platform-dev": [], "platform-overrides": { - "php": "5.6.0" + "php": "7.0.8" } } diff --git a/config/application.config.php b/config/application.config.php index 53de51f118e78b13a3bedb89f18f131d21f3a5be..d72316b3f27ed88885ccda1136ece11f22e4933d 100644 --- a/config/application.config.php +++ b/config/application.config.php @@ -1,10 +1,12 @@ <?php // Set up modules: -$modules = array( - 'ZfcRbac', 'VuFindTheme', 'VuFindSearch', 'VuFind', 'VuFindAdmin', 'VuFindApi' -); +$modules = [ + 'Zend\Router', 'ZfcRbac', + 'VuFindTheme', 'VuFindSearch', 'VuFind', 'VuFindAdmin', 'VuFindApi' +]; if (PHP_SAPI == 'cli' && !defined('VUFIND_PHPUNIT_RUNNING')) { + $modules[] = 'Zend\Mvc\Console'; $modules[] = 'VuFindConsole'; } if (APPLICATION_ENV == 'development') { @@ -53,24 +55,24 @@ if (!is_dir($cacheDir)) { $useCache = APPLICATION_ENV != 'development' && !defined('VUFIND_PHPUNIT_RUNNING'); // Build configuration: -return array( +return [ 'modules' => array_unique($modules), - 'module_listener_options' => array( - 'config_glob_paths' => array( + 'module_listener_options' => [ + 'config_glob_paths' => [ 'config/autoload/{,*.}{global,local}.php', - ), + ], 'config_cache_enabled' => $useCache, 'module_map_cache_enabled' => $useCache, 'check_dependencies' => (APPLICATION_ENV == 'development'), 'cache_dir' => $cacheDir, - 'module_paths' => array( + 'module_paths' => [ './module', './vendor', - ), - ), - 'service_manager' => array( + ], + ], + 'service_manager' => [ 'use_defaults' => true, - 'factories' => array( - ), - ), -); + 'factories' => [ + ], + ], +]; diff --git a/config/autoload/global.php b/config/autoload/global.php index b03c0c4f9b70336e51936a78a2d37884a62cdc6f..5ce1434a77848f6891552c00fd995660f9429a8f 100644 --- a/config/autoload/global.php +++ b/config/autoload/global.php @@ -2,15 +2,15 @@ /** * Global Configuration Override * - * You can use this file for overridding configuration values from modules, etc. - * You would place values in here that are agnostic to the environment and not - * sensitive to security. + * You can use this file for overridding configuration values from modules, etc. + * You would place values in here that are agnostic to the environment and not + * sensitive to security. * - * @NOTE: In practice, this file will typically be INCLUDED in your source - * control, so do not include passwords or other sensitive information in this + * @NOTE: In practice, this file will typically be INCLUDED in your source + * control, so do not include passwords or other sensitive information in this * file. */ -return array( +return [ // ... -); +]; diff --git a/config/vufind/Aleph.ini b/config/vufind/Aleph.ini index 363e84d95b319e159c92183a2454797a0ab78aed..5d24bb1782545529e3edef660907fd41ba063efc 100644 --- a/config/vufind/Aleph.ini +++ b/config/vufind/Aleph.ini @@ -95,3 +95,7 @@ extraHoldFields = comments:requiredByDate:pickUpLocation ; \VuFind\Cache\Manager cache you would like to use for storing data ("object" is recommended). [Cache] ;type = object + +[TransactionHistory] +; By default the loan history is disabled. Uncomment the following line to enable it. +;enabled = true diff --git a/config/vufind/Alma.ini b/config/vufind/Alma.ini new file mode 100644 index 0000000000000000000000000000000000000000..e6dcfde3a2644716aa3d9fb16e2ab631d45473eb --- /dev/null +++ b/config/vufind/Alma.ini @@ -0,0 +1,113 @@ +[Catalog] +; The base URL for your Alma instance (example is public demo): +apiBaseUrl = "https://api-eu.hosted.exlibrisgroup.com/almaws/v1" +; An API key configured to allow access to Alma: +apiKey = "your-key-here" + + +[Holds] +; HMACKeys - A list of hold form element names that will be analyzed for consistency +; during hold form processing. Most users should not need to change this setting. +; For activating title level hold request, add "description" and "level". +HMACKeys = id:item_id:holding_id + +; defaultRequiredDate - A colon-separated list used to set the default "not required +; after" date for holds in the format days:months:years +; e.g. 0:1:0 will set a "not required after" date of 1 month from the current date +defaultRequiredDate = 0:1:0 + +; extraHoldFields - A colon-separated list used to display extra visible fields in the +; place holds form. Supported values are "comments", "requiredByDate", +; "pickUpLocation" and "requestGroup" +extraHoldFields = comments:requiredByDate:pickUpLocation + +; A Pick Up Location Code used to pre-select the pick up location drop down list and +; provide a default option if others are not available. Must be one of the following: +; 1) empty string to indicate that the first location is default (default setting) +; 2) "user-selected" to indicate that the user always has to choose the location +; 3) a value within the Location IDs returned by getPickUpLocations() +defaultPickUpLocation = "" + + +; The "NewUser" section defines some default values that are used when creating an account +; in Alma via its API. This is only relevant if you use the authentication method "AlmaDatabase" +; in the "Authentication" section of the "config.ini" file. +[NewUser] +; Mandatory. The Alma user record type. Usually "PUBLIC". +recordType = PUBLIC + +; Mandatory. The Alma user account type. Usually this is "INTERNAL" if you use the AlmaDatabase +; authentication method. +accountType = INTERNAL + +; Mandatory. The status of the Alma user account. Usually "ACTIVE". +status = ACTIVE + +; Mandatory. The user group to which the new Alma account should belong. Use the code of one of +; the user groups that are defined in Alma (see "Alma Configuration -> User Management -> User Groups"). +userGroup = + +; Mandatory. The type of ID under which the username should be saved to Alma. Log in to the ExLibris developer +; network and check the Alma API documentation for possible values on this site: +; https://developers.exlibrisgroup.com/alma/apis/xsd/rest_user.xsd?tags=POST#user_identifier +idType = + +; Mandatory. The preferred language of the new Alma account. This should normally be the Alma language +; code of your local language (see "Alma Configuration -> General -> Institution Languages"). +preferredLanguage = + +; Mandatory. The type of eMail of the users eMail address. Log in to the ExLibris developer network and +; check the Alma API documentation for possible values on this site: +; https://developers.exlibrisgroup.com/alma/apis/xsd/rest_user.xsd?tags=POST#email_types +emailType = + +; Optional. Set the time period when the Alma account should expire. The given period will be added to the +; point in time of the Alma account creation. Use the DateInterval notation of PHP to express the period. See: +; https://secure.php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters +; If not set, 1 year (P1Y) will be used as default value. +expiryDate = + +; Optional. Set the time period that should be used for the Alma user account purge date. The given period +; will be added to the point in time of the Alma account creation. Use the DateInterval notation of PHP to +; express the period. See: +; https://secure.php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters +; If not set, the purge date of the Alma user account will be empty. +purgeDate = + + +[FulfillmentUnits] +; Specify the association of fulfillment units and its locations. Take the codes from: +; Alma Configuration -> Fulfillment -> Fulfillment Units -> [Choose Fulfillment Unit] -> Fulfillment Unit Locations. +; Tip: Export the list from Alma as Excel and use the CONCATENATE() formula to generate this list. +; Format: FULFILLMENT_UNIT_CODE[] = LOCATION_CODE +; Example: +; STACKS[] = stack1 +; STACKS[] = stack2 +; STACKS[] = stack3 +; LIMITED[] = periodicalroom +; LIMITED[] = musicrefs +; SHORTLOAN[] = office1 +; SHORTLOAN[] = office2 + +[Requestable] +; Specify for which combination of user group and fulfillment unit (see above) the request link +; should be displayed (who is allowed to request what). Define every combination of fulfillment unit +; and user group and assign "N" for "No, not requestable for this user group" or "Y" for "Yes, is +; requestable for this user group". You will find the user group codes here: +; Alma Configuration -> User Management -> User Groups +; Format: FULFILLMENT_UNIT_CODE[USER_GROUP_CODE] = N +; Example: +; STACKS[STAFF] = Y +; STACKS[STUDENT] = Y +; STACKS[GUEST] = Y +; LIMITED[STAFF] = Y +; LIMITED[STUDENT] = N +; LIMITED[GUEST] = N +; SHORTLOAN[STAFF] = Y +; SHORTLOAN[STUDENT] = Y +; SHORTLOAN[GUEST] = N + +[Webhook] +; The webhook secret. This must be the same value that was added to the Alma webhook configuration as a secret. +secret = YOUR_WEBHOOK_SECRET_FROM_ALMA + diff --git a/config/vufind/BrowZine.ini b/config/vufind/BrowZine.ini index 709f4e736afa9ac86d7ad64c12993ff53796f912..f0c213374e386dc87e6c1b2f2f2d79c336a8a983 100644 --- a/config/vufind/BrowZine.ini +++ b/config/vufind/BrowZine.ini @@ -10,3 +10,14 @@ library_id = "yyy" ; HTTP timeout timeout = 30 + +; This setting controls the default view for search results; the selected option +; should be one of the options present in the [Views] section below. +default_view = list + +; This section defines the view options available on standard search results. +; If only one view is required, set default_view under [General] above, and +; leave this section commented out. +;[Views] +;list = List +;grid = Grid diff --git a/config/vufind/ClaviusSQL.ini b/config/vufind/ClaviusSQL.ini deleted file mode 100644 index 7b5190ec534aa2b7eefd3db12c3af89af2da4eea..0000000000000000000000000000000000000000 --- a/config/vufind/ClaviusSQL.ini +++ /dev/null @@ -1,21 +0,0 @@ -[Catalog] -; mysql host server: -host = mysql.myuniversity.edu -; mysql port: -port = 3306 -; mysql username: -username = clavius -; mysql password -password = mypassword -; mysql database - usually clavius -database = clavius -; base URL to your catalog, without ending "l.dll", or "baze.htm" -url = http://www.myuniversity.edu/katalog/ -; If you are importing records with id with prefixes (KN3112....) the id_prefix setting needs to be true -id_prefix = true -; if your library enters barcodes to items manually, the use_barcode setting needs to be true, if barcodes are generated by Clavius, use false -use_barcodes = false -; your library's prefix, usually 5-digit number -prefix = 99000 -; how long are new items should be hidden in katalog -hide_days = 0 diff --git a/config/vufind/Collection.ini b/config/vufind/Collection.ini index 14c41850ae2558e9854344b56253ec01ec39f246..75f37a8d178c9ec9e29d97c850ae978e3472927a 100644 --- a/config/vufind/Collection.ini +++ b/config/vufind/Collection.ini @@ -24,6 +24,38 @@ publishDate = "adv_search_year" ; share year string w/advanced searc ; Any fields listed below will be treated as date ranges rather than plain facets: dateRange[] = publishDate +; These settings affect the way the facets are displayed +[Results_Settings] +; By default, how many values should we show for each facet? (-1 for no limit) +facet_limit = 30 +; Override facet_limit on a per-field basis using this array: +;facet_limit_by_field[format] = 50 + +; By default, the side facets will only show 6 facets and then the "show more" +; button. This can get configured with the showMore settings. +; You can use the * to set a new default setting. +showMore[*] = 6 +; Or you can set a facet specific value by using the facet name as index. +;showMore['format'] = 10 + +; Show more facets in a lightbox (paginated, no limit) +; If false, facets expand in side bar to show facets up to the above limit +; If "more", facets expand and offer an option at the bottom to open the lightbox +; If true, facets immediately open in the lightbox +showMoreInLightbox[*] = more +;lightboxLimit = 50 ; page size for the lightbox + +; Should we show "exclude" links for some or all of the facets? Set to * for +; all facets, use a comma-separated list to show for some of the facets, set +; to false or omit to disable "exclude" links +;exclude = * +; Should we OR together facets rather than ANDing them? Set to * for +; all facets, use a comma-separated list to apply to some of the facets, set +; to false or omit to disable ORed facets. +;orFacets = * +; Do we want any facets to be collapsed by default? +;collapsedFacets = * + ; These settings control which fields are available to sort on in the Collection ; module. [Sort] @@ -31,3 +63,10 @@ title = sort_title year = sort_year year asc = "sort_year asc" author = sort_author + +; See searches.ini for documentation on these sections. Collections will always +; use the filters applied in searches.ini; any additional filters defined here +; will be combined with those existing filters. Use these if you want to apply +; additional filtering to records displayed in the collection context. +;[HiddenFilters] +;[RawHiddenFilters] \ No newline at end of file diff --git a/config/vufind/Demo.ini b/config/vufind/Demo.ini index 6cb15e2558548cb518a005dff9e6b272c7a11ba4..87cb023f6e8e71142ce1e75338ae7459a6d02c6e 100644 --- a/config/vufind/Demo.ini +++ b/config/vufind/Demo.ini @@ -13,7 +13,7 @@ ILLRequests = true ; Configuration for retrieving sample records [Records] ; Search backend to pull records from -source = VuFind +source = Solr ; Query to use for record retrieval query = "*:*" @@ -28,6 +28,11 @@ services[] = 'custom' ; driver. ;transactions = '[{"id":"1234", ... "renewable": true}]'; +; This setting can be used to create fake historic loan items for specific records. +; The value is a JSON document representing the status information returned by the +; driver. +;historicTransactions = '[{"id":"1234", ... "dueDate": "01/01/2017"}]'; + ; This section can be used to create a set of fake users recognized by the ; Demo driver. If it is uncommented, only usernames and passwords listed here ; will be recognized for ILS login. If it is commented out, all username/password @@ -80,4 +85,8 @@ renewMyItems = 50 ;minLength = 4 ;maxLength = 20 ;pattern = "alphanumeric" -;hint = "Your optional custom hint can go here." \ No newline at end of file +;hint = "Your optional custom hint can go here." + +[TransactionHistory] +; By default the loan history is disabled. Uncomment the following line to enable it. +;enabled = true diff --git a/config/vufind/EDS.ini b/config/vufind/EDS.ini index f2a50448b8145fed98d9bbcda410f961e586e5a0..07bf4aa9ecdaf50413d67c0c41b255624e569a98 100644 --- a/config/vufind/EDS.ini +++ b/config/vufind/EDS.ini @@ -212,3 +212,24 @@ organization_id = "VuFind 2.x from MyUniversity" [List] view=full +; This section controls the behavior of the Autocomplete of EDS +; If enabled the option "autocomplete" is send for UIDAuth to get the token +; and the url. +[Autocomplete] +; Set this to false to disable all autocomplete behavior +enabled = true + +; Define a default_handler +default_handler = Eds + +; In this section, set the key equal to a search type from [Basic_Searches] and +; the value equal to an autocomplete handler in order to customize autocompletion +; behavior when that search type is selected. (default: Eds:rawqueries) +; These values are available: None, Eds:rawqueries and Eds:holdings +; Use None to disable autocomplete for a specific search type +; Use Eds:holdings for title completion in PubFinder. +; Use Eds:rawqueries for completion of basic textual queries. +[Autocomplete_Types] +;AllFields = Eds:rawqueries +;TI = Eds:holdings +AU = None diff --git a/config/vufind/Koha.ini b/config/vufind/Koha.ini index 46830be98f11d708c9f8e04067973b938c5aeadf..aff0187e84e4e529a8250a32af0a36d0b563833d 100644 --- a/config/vufind/Koha.ini +++ b/config/vufind/Koha.ini @@ -41,3 +41,7 @@ STAFF = "Staff Office" ;OVERDUES = false ;MANUAL = false ;DISCHARGE = false + +[TransactionHistory] +; By default the loan history is disabled. Uncomment the following line to enable it. +;enabled = true diff --git a/config/vufind/KohaILSDI.ini b/config/vufind/KohaILSDI.ini index bd036fdfd713a7cd7c9be13ef941acde922d85ff..be92848d7a6ce906c2b40db7a7d470632b79bbf7 100755 --- a/config/vufind/KohaILSDI.ini +++ b/config/vufind/KohaILSDI.ini @@ -22,6 +22,11 @@ url = http://library.myuniversity.edu/cgi-bin/koha/ilsdi.pl ; using SAML/Shibboleth for authentication for both VuFind and Koha) dontValidatePasswords = false +; If we need to we can set the Authorised Values Category that is used to lookup +; the location of items. The default is `LOC` but some Koha installs may prefer +; to use `CCODE` instead. +;locationAuthorisedValuesCategory = LOC + ;; In addition you can set 'renewals_enabled' and ;; 'cancel_holds_enabled' in config.ini to 'true' using this driver. ;; I would also recommend you set 'holds_mode' to '"holds"', as this @@ -54,7 +59,7 @@ extraHoldFields = pickUpLocation ; location. By setting this to a Koha location code (e.g. '"MAIN"'), ; Vufind will default to that location. ; If no defaultPickUpLocation and no pickupLocations are defined, -; the driver will try to use the actual holdingbranch(es) of the item/title +; the driver will try to use the actual holdingbranch(es) of the item/title ; as a fallback. defaultPickUpLocation = "MAIN" @@ -84,3 +89,7 @@ pickupLocations[] = MAIN ;OVERDUES = false ;MANUAL = false ;DISCHARGE = false + +[TransactionHistory] +; By default the loan history is disabled. Uncomment the following line to enable it. +;enabled = true diff --git a/config/vufind/NoILS.ini b/config/vufind/NoILS.ini index 25a793100b8da1cc295b7e26ce1833d6dcd5de07..6947c23537fdf808ed6d4fefa6975f78622aae23 100644 --- a/config/vufind/NoILS.ini +++ b/config/vufind/NoILS.ini @@ -1,7 +1,9 @@ [settings] ;mode - The No ILS Driver Mode - Options are "ils-none" or "ils-offline" mode = ils-offline -;hideLogin - Whether or not to hide the login option +;hideLogin - Whether or not to hide the login option; when true, this will override +; the hideLogin setting in the [Authentication] setting of config.ini whenever +; the NoILS driver is active. hideLogin = false ;useHoldings - The Holdings mode ; none = do not show holdings info in Holdings Tab (see Site/hideHoldingsTabWhenEmpty diff --git a/config/vufind/Search2.ini b/config/vufind/Search2.ini new file mode 100644 index 0000000000000000000000000000000000000000..e1ef3905d43bdd06d2fd6ffc57873f71c0f9da1e --- /dev/null +++ b/config/vufind/Search2.ini @@ -0,0 +1,265 @@ +; This configuration file can be used to set up a secondary Solr index that behaves +; similarly to VuFind's default search. This instance is accessible under /Search2 +; URLs within VuFind. +; +; This can be used in combination with Combined Search (see Combined.ini) to create +; a tabbed interface searching multiple indexes. It can also be useful for pulling +; in results from an external/third-party index. +; +; Most of the settings found in searches.ini and facets.ini can be dropped into +; this file and will behave the same way. Some selected settings from config.ini +; that apply only to the main Solr index are also overridden here. To ease +; maintenance of documentation, comments from those files are not duplicated here. +; See those files for more details of how all settings work. Look for section +; marker comments within this file to see where groups of settings come from. + +; ---------- config.ini settings ---------- + +[Index] +url = http://localhost:8080/solr +default_core = biblio +maxBooleanClauses = 1024 +timeout = 30 +default_dismax_handler = dismax + +[Spelling] +enabled = true +limit = 3 +phrase = false +expand = true +simple = false +skip_numeric = true + +[Record] +next_prev_navigation = false +related[] = Similar + +; ---------- searches.ini settings ---------- + +[General] +default_handler = AllFields +default_sort = relevance +default_view = list +default_limit = 20 +;limit_options = 10,20,40,60,80,100 +case_sensitive_bools = true +case_sensitive_ranges = true + +default_top_recommend[] = TopFacets:ResultsTop:Search2 +default_top_recommend[] = SpellingSuggestions +;default_top_recommend[] = VisualFacets:Visual_Settings +default_side_recommend[] = SideFacets:Results:CheckboxFacets:Search2 +;default_noresults_recommend[] = SwitchTab +default_noresults_recommend[] = SwitchType +default_noresults_recommend[] = SwitchQuery:::fuzzy +default_noresults_recommend[] = SpellingSuggestions +default_noresults_recommend[] = RemoveFilters + +highlighting = true +;highlighting_fields = * +snippets = true +retain_filters_by_default = true +;default_filters[] = "format:Book" +;default_filters[] = "institution:MyInstitution" +;default_filters[] = "(format:Book AND institution:MyInstitution)" + +[Cache] +type = File + +[Basic_Searches] +AllFields = "All Fields" +Title = Title +;JournalTitle = "Journal Title" +Author = Author +Subject = Subject +CallNumber = "Call Number" +ISN = "ISBN/ISSN" +;Coordinate = Coordinates +tag = Tag + +[Advanced_Searches] +AllFields = adv_search_all +Title = adv_search_title +;JournalTitle = adv_search_journaltitle +Author = adv_search_author +Subject = adv_search_subject +CallNumber = adv_search_callnumber +ISN = adv_search_isn +publisher = adv_search_publisher +Series = adv_search_series +year = adv_search_year +toc = adv_search_toc +;Coordinate = Coordinates + +[Sorting] +relevance = sort_relevance +year = sort_year +year asc = "sort_year asc" +callnumber-sort = sort_callnumber +;dewey-sort = sort_callnumber +author = sort_author +title = sort_title + +[DefaultSortingByType] +CallNumber = callnumber-sort + +[SideRecommendations] +;Subject[] = SideFacets +;Subject[] = OpenLibrarySubjectsDeferred:lookfor:5:true:topic,place,person,time + +[TopRecommendations] +Author[] = AuthorFacets +Author[] = SpellingSuggestions +;Author[] = WorldCatIdentities +CallNumber[] = "TopFacets:ResultsTop" + +[NoResultsRecommendations] +CallNumber[] = SwitchQuery::wildcard:truncatechar +CallNumber[] = RemoveFilters +;CallNumber[] = AlphaBrowseLink:lcc + +[RSS] +sort = "last_indexed desc" + +[Autocomplete] +enabled = true +default_handler = Search2 + +[Autocomplete_Types] +Title = "Search2:Title" +JournalTitle = "Search2:JournalTitle" +Author = "Search2:Author:author,author2" +Subject = "Search2:Subject:topic,genre,geographic,era" +CallNumber = "Search2CN" +ISN = "Search2:ISN:isbn,issn" +Coordinate = "None" +tag = "Tag" + +[Snippet_Captions] +author2 = "Other Authors" +contents = "Table of Contents" +topic = "Subjects" +container_title = "Journal Title" + +;[IndexShards] +;Library Catalog = localhost:8080/solr/biblio +;Website = localhost:8080/solr/website + +;[ShardPreferences] +;showCheckboxes = true +;defaultChecked[] = "Library Catalog" +;defaultChecked[] = "Website" + +[StripFields] + +;[Views] +;list = List +;grid = Grid +;visual = Visual + +[List] +view=full + +[HiddenFilters] +;institution = "MyInstitution" + +[RawHiddenFilters] +;0 = "format:\"Book\" OR format:\"Journal\"" +;1 = "language:\"English\" OR language:\"French\"" + +[ConditionalHiddenFilters] +;0 = "-conditionalFilter.MyUniversity|format:Book" +;1 = "conditionalFilter.MyUniversity|format:Article" + +[Records] +;deduplication = true +;sources = alli,testsrc + +[MoreLikeThis] +;useMoreLikeThisHandler = true +;params = "qf=title,title_short,callnumber-label,topic,language,author,publishDate mintf=1 mindf=1"; +;count = 5 + +[HomePage] +;content[] = IlsStatusMonitor +content[] = FacetList:Search2 + +; ---------- facets.ini settings ---------- + +[Results] +institution = Institution +building = Library +format = Format +callnumber-first = "Call Number" +;dewey-hundreds = "Call Number" +;hierarchy_top_title = Collections +author_facet = Author +language = Language +genre_facet = Genre +era_facet = Era +geographic_facet = Region +publishDate = "adv_search_year" + +[ResultsTop] +topic_facet = "Suggested Topics" + +[ExtraFacetLabels] +long_lat = "Geographic Search" + +[SpecialFacets] +dateRange[] = publishDate +;fullDateRange[] = example_field_date +;numericRange[] = example_field_str +;genericRange[] = example_field_str +;hierarchical[] = building +;hierarchicalFacetSortOptions[building] = top +;hierarchicalFacetDisplayStyles[format] = full +;hierarchicalFacetSeparators[format] = " > " + +[CheckboxFacets] +;edition:1st* = "First Edition" + +[Results_Settings] +facet_limit = 30 +showMore[*] = 6 +showMoreInLightbox[*] = more +;lightboxLimit = 50 +top_rows = 2 +top_cols = 3 +;exclude = * +;orFacets = * +;collapsedFacets = * +;sorted_by_index[] = building; +;sorted_by_index[] = institution; + +[Advanced_Facets] +callnumber-first = "Call Number" +language = Language +format = Format +;hierarchy_top_title = Collections + +[Advanced_Settings] +facet_limit = 100 +orFacets = * +delimiter = "{{{_:::_}}}" +special_facets = "illustrated,daterange" +;translated_facets[] = institution +;translated_facets[] = building +translated_facets[] = format +translated_facets[] = callnumber-first:CallNumberFirst +;delimited_facets[] = author_id_str +;delimited_facets[] = "author_id_str|:::" + +[HomePage_Facets] +callnumber-first = "Call Number" +language = Language +format = Format +;hierarchy_top_title = Collections + +[HomePage_Facet_Settings] +facet_limit = 20 + +[LegacyFields] + +[HideFacetValue] +;format[] = "Book" diff --git a/config/vufind/SierraRest.ini b/config/vufind/SierraRest.ini index 74463f14f6882ed488463447702e4ddeea8f3ae7..c702bcb5dc39e1d5554d5367b5d2e0e15e8e4c58 100644 --- a/config/vufind/SierraRest.ini +++ b/config/vufind/SierraRest.ini @@ -21,11 +21,11 @@ redirect_uri = "http://localhost/vufind/MyResearch/SierraAuth" ; This section is used to define library codes and named values which are used by the ; system to indicate the location at which a hold is to be collected. Sierra REST API -; does not currently support retrieval of pickup locations, so filling this section -; is mandatory for holds to work. -[pickUpLocations] -m01 = "Main Library" -m02 = "Branch Library" +; only supports retrieval of pickup locations starting at v4, so filling this section +; is mandatory for holds to work if only an older API version is available. +;[pickUpLocations] +;m01 = "Main Library" +;m02 = "Branch Library" ; This section controls hold behavior; note that you must also ensure that Holds are ; enabled in the [Catalog] section of config.ini in order to take advantage of these @@ -45,9 +45,10 @@ HMACKeys = item_id:holdtype:level ; e.g. 0:1:0 will set a "not required after" date of 1 month from the current date defaultRequiredDate = 0:1:0 -; extraHoldFields - A colon-separated list used to display extra visible fields in the -; place holds form. Supported values are "requiredByDate" and "pickUpLocation" -extraHoldFields = requiredByDate:pickUpLocation +; extraHoldFields - A colon-separated list used to display extra visible fields in +; the place holds form. Supported values are "requiredByDate", "pickUpLocation" and +; "comments". Comments are only supported with Sierra API v4 onwards. +extraHoldFields = requiredByDate:pickUpLocation:comments ; A Pick Up Location Code used to pre-select the pick up location drop down list and ; provide a default option if others are not available. Must be one of the following: @@ -85,3 +86,7 @@ title_hold_bib_levels = a:b:m:d ; config.ini defaults when Sierra is used for authentication. ;pattern = "numeric" ;hint = "Your optional custom hint can go here." + +[TransactionHistory] +; By default the loan history is disabled. Uncomment the following line to enable it. +;enabled = true diff --git a/config/vufind/Summon.ini b/config/vufind/Summon.ini index fe5ffd8de07da32a17392994595777b0d0d0766f..e7126909d1c93388ad029c3e1fb973cbf76124b6 100644 --- a/config/vufind/Summon.ini +++ b/config/vufind/Summon.ini @@ -225,6 +225,27 @@ orFacets = * ; below, no facets will be translated. translated_facets[] = ContentType +; This section controls the behavior of the Summon/Home screen. +[HomePage] +; Content blocks can be selected from the list in searches.ini. +content[] = FacetList:Summon +;content[] = Channels:Summon + +; These facets will be displayed on the Home Page when FacetList is turned on in +; the content setting of the [HomePage] section above. If this section is omitted, +; the [Advanced_Facets] section will be used instead. +[HomePage_Facets] +Language = "Language" +ContentType = "Format" + +; These settings affect the way the [HomePage_Facets] are displayed. +; NOTE: To make changes take effect immediately, you may need to clear VuFind's +; cache after changing this section. +[HomePage_Facet_Settings] +; how many values should we load for each facet? depending on the column layout +; of the homepage facet lists, we may not display all loaded values for every facet +facet_limit = 100 + ; This section shows which search types will display in the basic search box at ; the top of Summon pages. The name of each setting below corresponds with an ; index defined in the Summon API. The value of each setting is the text to diff --git a/config/vufind/Voyager.ini b/config/vufind/Voyager.ini index c911a165470716a7f38f1968b010f13e46d472ea..e855a5447f951a17c3e09c5e12835883b62585b9 100644 --- a/config/vufind/Voyager.ini +++ b/config/vufind/Voyager.ini @@ -91,6 +91,19 @@ login_field = LAST_NAME ; Uncomment this line to display the location where each loan was made ;display_borrowing_location = true +; Colon-separated list of loan intervals for which to display the time part of the +; due date. By default the time is always displayed. Normal interval types are +; H (hours), D (days) and T (term). +;display_due_time_only_for_intervals = H:D + ; This regular expression controls which status messages are displayed on the ; Checked Out Items list. show_statuses = "/lost|missing|claim/i" + +[Profile] +; This setting can be used to define which Voyager phone type is used as the primary +; phone number. Default is "Primary". +;primary_phone = Primary +; This setting can be used to define which Voyager phone type is used as the mobile +; phone number. Default is "Mobile". +;mobile_phone = Mobile \ No newline at end of file diff --git a/config/vufind/VoyagerRestful.ini b/config/vufind/VoyagerRestful.ini index 06beb4dc90bcbccde11f240dd47268a28a6dc913..82e2f248410c45029fb8ea30ab146f734f4ff051 100644 --- a/config/vufind/VoyagerRestful.ini +++ b/config/vufind/VoyagerRestful.ini @@ -299,6 +299,11 @@ disableAvailabilityCheckForRequestGroups = "15:19:21:32" ; Uncomment this line to display the location where each loan was made ;display_borrowing_location = true +; Colon-separated list of loan intervals for which to display the time part of the +; due date. By default the time is always displayed. Normal interval types are +; H (hours), D (days) and T (term). +;display_due_time_only_for_intervals = H:D + ; This regular expression controls which status messages are displayed on the ; Checked Out Items list. show_statuses = "/lost|missing|claim/i" @@ -312,4 +317,12 @@ show_statuses = "/lost|missing|claim/i" ; of config.ini for notes on these settings. When set here, these will override the ; config.ini defaults when Voyager is used for authentication. ;pattern = "alphanumeric" -;hint = "Your optional custom hint can go here." \ No newline at end of file +;hint = "Your optional custom hint can go here." + +[Profile] +; This setting can be used to define which Voyager phone type is used as the primary +; phone number. Default is "Primary". +;primary_phone = Primary +; This setting can be used to define which Voyager phone type is used as the mobile +; phone number. Default is "Mobile". +;mobile_phone = Mobile \ No newline at end of file diff --git a/config/vufind/WorldCat.ini b/config/vufind/WorldCat.ini index d78e36b365bc9b322dea05aeed06e5b5fda7b079..e3b303c0d35bb7a6d017834c4da7e71449a093a3 100644 --- a/config/vufind/WorldCat.ini +++ b/config/vufind/WorldCat.ini @@ -73,6 +73,10 @@ view=full ; This section contains additional settings to pass to the WorldCat connector ; code. [Connector] +; Set this to the latitude,longitude position of your library to ensure that +; custom holding locations are retrieved from your region first. +;latLon = "40.0373881,-75.3442107" + ; When looking up holdings at other libraries, should we retrieve holdings for ; any record matching the FRBR group (true) or only for exact matches (false)? ;useFrbrGroupingForHoldings = false \ No newline at end of file diff --git a/config/vufind/channels.ini b/config/vufind/channels.ini index f6af7eef6453b1291db566db49cbf1bc63a63a5f..3270e332bbf89ffc9d9c9e716673f0806d12e5e6 100644 --- a/config/vufind/channels.ini +++ b/config/vufind/channels.ini @@ -25,16 +25,28 @@ cache_home_channels = true ; ; listitems - Display items from specific public user lists. ; +; newilsitems - Display items that were recently added to the ILS; requires an +; ILS driver that supports this feature. +; ; random - Pick random results from the result set (in search results) or from ; the search backend from which the record was retrieved (in record results). ; +; recentlyreturned - Display items that were recently returned by patrons; +; requires an ILS driver that supports this feature. +; ; similaritems - Find records similar to a specific record, or to top hits in a ; set of search results. +; +; trendingilsitems - Display items that have displayed a lot of recent activity in +; the ILS. (Exact definition of "trending" may vary from system to system). [source.Solr] ; Providers to use on the home page (these will be populated with a blank search ; by default; order matters!) home[] = "facets:provider.facets.home" ;home[] = "random" +;home[] = "newilsitems" +;home[] = "recentlyreturned" +;home[] = "trendingilsitems" ;home[] = "listitems" ; Providers to use for record-based channels (order matters!) record[] = "similaritems" @@ -124,6 +136,13 @@ displayPublicLists = true ; How many lists should we display initially? (The rest can be added on demand) initialListsToDisplay = 2 +; This section contains default settings for the NewILSItems channel provider +[provider.newilsitems] +; Number of results to include in the channel. +channelSize = 100 +; Maximum age of results to return (in days) +maxAge = 60 + ; This section contains default settings for the Random channel provider [provider.random] ; Number of results to include in the channel. @@ -134,9 +153,23 @@ channelSize = 20 ; entire backend. mode = "retain" +; This section contains default settings for the RecentlyReturned channel provider +[provider.recentlyreturned] +; Number of results to include in the channel. +channelSize = 20 +; Maximum age of results to return (in days) +maxAge = 30 + ; This section contains default settings for the SimilarItems channel provider [provider.similaritems] ; Number of results to include in each channel. channelSize = 20 ; Maximum number of records to examine for similar results. -maxRecordsToExamine = 2 \ No newline at end of file +maxRecordsToExamine = 2 + +; This section contains default settings for the TrendingILSItems channel provider +[provider.trendingilsitems] +; Number of results to include in the channel. +channelSize = 20 +; Maximum age of results to return (in days) +maxAge = 90 diff --git a/config/vufind/combined.ini b/config/vufind/combined.ini index 58249f5ca4518559cb346b9fbb867a4ce353b421..ff466b4b69575ed66b2a15826f3c9a7ca77926e6 100644 --- a/config/vufind/combined.ini +++ b/config/vufind/combined.ini @@ -42,6 +42,11 @@ ; The order of sections in this file will control the display order of search ; results on screen. +; This section controls the behavior of the Combined/Home screen. +[HomePage] +; Content blocks can be selected from the list in searches.ini. +content[] = IlsStatusMonitor + ; This section controls how columns will be formatted [Layout] ; This is the maximum number of columns to use. diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 99b88405e078ac3752118685dd305fa9a147382b..ceea0595542652d7661248d18305afe69b6806c6 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -86,22 +86,28 @@ displayDateFormat = "m-d-Y" ; A string used to format user interface time strings using the PHP date() function ; default is H:i (HH:MM 23:01) displayTimeFormat = "H:i" -; The base VuFind URL will load the "Home" action of this module unless the user -; is logged in: +; The base VuFind URL will load this controller unless the user is logged in: defaultModule = Search -; The base VuFind URL will load the "Home" action of this module when the user -; is logged in: +; When defaultModule is used, this action will be triggered (default = Home) +;defaultAction = Home +; The base VuFind URL will load this controller when the user is logged in: defaultLoggedInModule = MyResearch +; When defaultLoggedInModule is used, this action will be triggered (default = Home) +;defaultLoggedInAction = Home ; The route VuFind will send users to following a log out operation. Set to false ; or omit to attempt to retain the user's current context after log out. ;logOutRoute = home ; This tab will show by default when a record is viewed: defaultRecordTab = Holdings -; Hide the holdings tab if no holdings are available from the ILS +; Hide the holdings tab if no holdings are available from the ILS; note that this +; feature requires your ILS driver to support the hasHoldings() method. hideHoldingsTabWhenEmpty = false ; Whether to load the default tab through AJAX (which brings some performance ; gain but breaks compatibility with non-Javascript-enabled browsers; off by default) ;loadInitialTabWithAjax = true +; The holdingsTemplate to use to display the ILS holdings (defaults to standard). +; See the templates/RecordTab/holdingsils subdirectory of your theme for options. +;holdingsTemplate = extended ; This page will show by default when a user accesses the MyResearch module: defaultAccountPage = Favorites ; Allow access to the Admin module? (See the access.AdminModule setting in @@ -132,8 +138,14 @@ bookbagTogglesInSearch = true showBulkOptions = false ; Should users be allowed to save searches in their accounts? allowSavedSearches = true +; Some VuFind features can be made compatible with non-Javascript browsers at +; a performance cost. By default, this compatibility is disabled, but it can +; be turned on here. Note that even with this setting turned on, some features +; still require Javascript; this simply improves compatibility for certain +; features (such as display of hierarchies). +nonJavascriptSupportEnabled = false ; Generator value to display in an HTML header <meta> tag: -generator = "VuFind 4.1.3" +generator = "VuFind 5.0.1" ; This section allows you to configure the mechanism used for storing user ; sessions. Available types: File, Memcache, Database. @@ -143,6 +155,8 @@ generator = "VuFind 4.1.3" [Session] type = File lifetime = 3600 ; Session lasts for 1 hour +; Should stored session data be encrypted? +secure = false ; Keep-alive interval in seconds. When set to a positive value, the session is kept ; alive with a JavaScript call as long as a VuFind page is open in the browser. ; Default is 0 (disabled). When keep-alive is enabled, session lifetime above can be @@ -177,8 +191,8 @@ session_name = VUFIND_SESSION ; ; Available drivers: ; - Aleph +; - Alma ; - Amicus -; - ClaviusSQL ; - DAIA (using either XML or JSON API) ; - Demo (fake ILS driver returning complex responses) ; - Evergreen @@ -214,9 +228,14 @@ session_name = VUFIND_SESSION [Catalog] driver = Sample -;loadNoILSOnFailure - Whether or not to load the NoILS driver if the main driver fails +; loadNoILSOnFailure - Whether or not to load the NoILS driver if the main driver fails loadNoILSOnFailure = false +; List of search backends that contain records from your ILS (defaults to Solr +; unless set otherwise). You can set ilsBackends = false to disable ILS status +; loading entirely. +;ilsBackends[] = Solr + ; This setting determines how and when hold / recall links are displayed. ; Legal values: ; - all (Show links for all items - Place Hold for Available Items and Place Recall @@ -300,15 +319,23 @@ title_level_holds_mode = "disabled" ; memory problems for users with huge numbers of items). Default = 50. ;checked_out_page_size = 50 +; The number of historic loans to display per page; 0 for no limit (may cause +; memory problems for users with a large number of historic loans). Default = 50 +;historic_loan_page_size = 50 + +; Whether to display the item barcode for each loan. Default is false. +;display_checked_out_item_barcode = true + ; This section allows you to determine how the users will authenticate. ; You can use an LDAP directory, the local ILS (or multiple ILSes through -; the MultiILS option), the VuFind database (Database), Shibboleth, SIP2, -; CAS, Facebook or some combination of these (via the MultiAuth or ChoiceAuth -; options). +; the MultiILS option), the VuFind database (Database), AlmaDatabase (combination +; of VuFind database and Alma account), Shibboleth, SIP2, CAS, Facebook or some +; combination of these (via the MultiAuth or ChoiceAuth options). [Authentication] ;method = LDAP ;method = ILS method = Database +;method = AlmaDatabase ;method = Shibboleth ;method = SIP2 ;method = CAS @@ -323,11 +350,13 @@ method = Database ; (the recommended setting in most situations). ;ILS_username_field = cat_username -; Whether or not to hide the Login Options +; Whether or not to hide the Login Options; not that even when this is set to +; false, ILS driver settings may be used to conditionally hide the login. See +; hideLogin in the [Settings] section of NoILS.ini for an example. hideLogin = false ; Set this to false if you would like to store local passwords in plain text -; (only applies when method = Database above). +; (only applies when method = Database or AlmaDatabase above). hash_passwords = false ; Allow users to recover passwords via email (if supported by Auth method) @@ -359,7 +388,7 @@ ils_encryption_key = false ;ils_encryption_algo = "blowfish" ; This setting may optionally be uncommented to restrict the email domain(s) from -; which users are allowed to register when using the Database method. +; which users are allowed to register when using the Database or AlmaDatabase method. ;domain_whitelist[] = "myuniversity.edu" ;domain_whitelist[] = "mail.myuniversity.edu" @@ -378,10 +407,16 @@ ils_encryption_key = false ; Uncomment this line to switch on "privacy mode" in which no user information ; will be stored in the database. Note that this is incompatible with social ; features, password resets, and many other features. It is not recommended for -; use with "Database" authentication, since the user will be forced to create a -; new account upon every login. +; use with "Database" or "AlmaDatabase" authentication, since the user will be +; forced to create a new account upon every login. ;privacy = true +; Allow a user to delete their account. Default is false. +;account_deletion = true +; Whether comments added by a user are deleted when they remove their account. +; Default is true. +;delete_comments_with_user = false + ; See the comments in library/VF/Auth/MultiAuth.php for full details ; on using multiple authentication methods. Note that MultiAuth assumes login ; with username and password, so some methods (i.e. Shibboleth) may not be @@ -409,7 +444,10 @@ ils_encryption_key = false ; until one can be reached. This is only useful for advanced fault-tolerant Solr ; installations. url = http://localhost:8080/solr +; Default bibliographic record core default_core = biblio +; Default authority record core +default_authority_core = authority ; This setting needs to match the <maxBooleanClauses> setting in your solrconfig.xml ; file; when VuFind has to look up large numbers of records using ID values, it may ; have to restrict the size of its result set based on this limitation. @@ -420,6 +458,11 @@ timeout = 30 ; You can choose dismax for standard Dismax (the default) or edismax for Extended ; Dismax, or you can configure your own custom handler in solrconfig.xml. default_dismax_handler = dismax +; This is the number of records to retrieve in a batch e.g. when building a record +; hierarchy. A higher number results in fewer round-trips but may increase Solr's +; memory usage. Default is 1000. +;cursor_batch_size = 1000 + ; Enable/Disable searching reserves using the "reserves" Solr core. When enabling ; this feature, you need to run the util/index_reserves.php script to populate the @@ -458,6 +501,10 @@ maximum_recipients = 1 ; will be sent based on user_email_in_from and default_from above, with the email ; setting from the [Site] section used as a last resort. disable_from = false +; From field override. Setting this allows keeping the "from" field in email forms +; but will only use it as a reply-to address. The address defined here is used as the +; actual "from" address. +;override_from = "no-reply@myuniversity.edu" ; Being a special case of mail message, sending record results via SMS ("Text this") ; may be "enabled" or "disabled" ("enabled" by default). @@ -721,6 +768,13 @@ noCoverAvailableImage = images/noCover2.gif ; content providers. ;hide_if_empty = reviews,excerpts +; You can select from Syndetics or SyndeticsPlus to add summary information to +; the description tab. +;summaries = Syndetics:MySyndeticsId,SyndeticsPlus:MySyndeticsId + +; You can select from Syndetics or SyndeticsPlus to load Tables of Contents +;toc = Syndetics:MySyndeticsId,SyndeticsPlus:MySyndeticsId + ; You can select from Syndetics or SyndeticsPlus ;authorNotes = Syndetics:MySyndeticsId,SyndeticsPlus:MySyndeticsId @@ -774,30 +828,8 @@ authors = Wikipedia ;europeanaAPI = INSERTKEY ; Geographic Display -; recordMap can be set to 'openlayers'; -; 'openlayers' will display point and rectangle features. Default setting -; (see import/marc_local.properties for more information). -; Map Tab Options: -; mapLabels: leave empty, file:filename, or driver -; Leave it empty – no map labels will be displayed (default) -; file:filename - specify a file name after the colon for the -; coordinate/label lookup file. Coordinates in file must -; be specified as WENS. -; driver - Use the getCoordinateLabels method of the record driver to fetch labels; -; by default this relies on the long_lat_label field in Solr, -; but you can override the behavior with custom record driver code. -; The field must be the same length as the number of coordinate sets. -; Coordinates will be matched to labels on an ordered basis such that -; label[0] will be assigned for coordinate[0] and so forth. -; displayCoords: true or false. Default is false. (Only for recordMap = openlayers) -; If displayCoords is true, then the coordinate values from -; coordinate field will be displayed before the map label in the label popup. -; graticule: true or false. Default is false. If graticule is true a lat/long grid will be -; displayed on the map. -;recordMap = openlayers -;mapLabels = file:geosearch_test_lookup.txt -;displayCoords = true -;graticule = true +; These configuration settings have been superseded by the geofeatures.ini file. +; See the [MapTab] section of the geofeatures.ini file for more information. ; This section controls the behavior of the cover generator when makeDynamicCovers ; above is non-false. @@ -939,6 +971,42 @@ pw = "Password" ; institution's holdings from the search results. ;OCLCCode = MYCODE +; This section must be filled in to use Relais (E-ZBorrow) functionality. When +; activated, this function will allow users to place ILL requests on unavailable +; items through the record holdings tab. +; +; If you set apikey below, requests may be made from within VuFind through a +; pop-up; if you omit apikey but set loginUrl and symbol, links will be provided +; to Relais. Setting loginUrl and symbol is strongly recommended in all cases, +; since links will be used as a fallback if the API fails. +;[Relais] +; Your library's holdings symbol (e.g. PVU for Villanova) +;symbol="XYZ" +; The pickup location to use for your institution (currently multiple pickup +; locations are not supported here). +;pickupLocation = "DEFAULT" +; Barcode number (or other user ID) to use for lookups when none is provided +;patronForLookup="99999999" +; API key (may vary for testing vs. production) +;apikey="your-relais-api-key-goes-here" +; Timeout for HTTP requests (in seconds; set high, as Relais can be slow) +;timeout = 500 +; Your institution's login URL for the remote Relais system (used to provide +; a link when the API fails) +;loginUrl = https://e-zborrow.relais-host.com/user/login.html + +; TEST VALUES (uncomment for testing) +;group="DEMO" +;authenticateurl="https://demo.relais-host.com/portal-service/user/authentication" +;availableurl="https://demo.relais-host.com/dws/item/available" +;addurl="https://demo.relais-host.com/dws/item/add" + +; PRODUCTION VALUES (uncomment for live use) +;group="EZB" +;authenticateurl="https://e-zborrow.relais-host.com/portal-service/user/authentication" +;availableurl="https://e-zborrow.relais-host.com/dws/item/available" +;addurl="https://e-zborrow.relais-host.com/dws/item/add" + ; DPLA key -- uncomment and fill in to use DPLATerms recommendations (see also ; searches.ini). ;[DPLA] @@ -1051,10 +1119,17 @@ replace_other_urls = true ; replace SHA512 with SHA1 also in EZproxy's configuration file. ;secret_hash_method = "SHA512" +; Uncomment the following line to disable relaying of user name to EZproxy on ticket +; authentication: +;anonymous_ticket = true +; Uncomment the following line to disable logging of successful ticket +; authentication requests in VuFind: +;disable_ticket_auth_logging = true + ; These settings affect RefWorks record exports. They rarely need to be changed. [RefWorks] vendor = VuFind -url = http://www.refworks.com +url = https://www.refworks.com ; These settings affect EndNote Web record exports. They rarely need to be changed. [EndNoteWeb] @@ -1101,8 +1176,9 @@ url = https://www.myendnoteweb.com/EndNoteWeb.html ;timeout = 30 ; default timeout if not overridden by more specific code/settings -; Using a curl Adapter instead of the the defaultAdapter (Socket) -; adapter = 'Zend\Http\Client\Adapter\Curl' +; Example: Using a CURL Adapter instead of the the defaultAdapter (Socket); note +; that you may also need to install CURL and PHP/CURL packages on your server. +;adapter = 'Zend\Http\Client\Adapter\Curl' ; Spelling Suggestions ; @@ -1171,6 +1247,10 @@ skip_numeric = true ;slack = #channel_name:alert,error ;slackurl = https://hooks.slack.com/services/your-private-details ;slackname = "VuFind Log" ; username messages are posted under +; You can also use the Slack settings to hook into Discord: +; - Get your url from Server Settings > Webhooks +; - Add /slack to the end of your url for Slack-compatible messages +; https://discordapp.com/developers/docs/resources/webhook#execute-slackcompatible-webhook ; This section can be used to specify a "parent configuration" from which ; the current configuration file will inherit. You can chain multiple @@ -1216,6 +1296,7 @@ fr = "French" it = "Italian" ja = "Japanese" nl = "Dutch" +;nl-be = "Flemish Dutch" pt = "Portuguese" pt-br = "Brazilian Portugese" zh-cn = "Simplified Chinese" @@ -1443,7 +1524,7 @@ HMACkey = mySuperSecretValue ; A custom directory for caching can be defined by the environment variable ; VUFIND_CACHE_DIR (see httpd-vufind.conf). The default location is inside the ; local settings directory. -;[Cache] +[Cache] ; Set time to live value for Zend caches (in seconds), 0 means maximum possible. ;ttl = 0 ; Override umask for cache directories and files. @@ -1456,7 +1537,7 @@ HMACkey = mySuperSecretValue ; This section controls the "Collections" module -- the special view for records ; that represent collections, and the mechanism for browsing these records. -;[Collections] +[Collections] ; Control whether or not the collections module is enabled in search results. ; If set to true any search results which are collection level items will ; link to the respective collections page rather than the record page @@ -1509,6 +1590,10 @@ treeSearchLimit = 100 ;sender_email = "noreply@vufind.org" ;sender_name = "VuFind Feedback" +; Note: for additional details about stats (including additional notes on Google +; Analytics and Piwik), look at the wiki page: +; https://vufind.org/wiki/configuration:usage_stats + ; Uncomment this section and provide your API key to enable Google Analytics. Be ; sure to set the "universal" setting to true once your account is upgraded to ; Universal Analytics; see: diff --git a/config/vufind/export.ini b/config/vufind/export.ini index 04a4702aac52a7f5d4901914b67b4ef32d79b420..7db9633991b7efc15e47e6be7c5bbd57598cf80b 100644 --- a/config/vufind/export.ini +++ b/config/vufind/export.ini @@ -25,16 +25,25 @@ ; {config...} setting above, except result will be URL-encoded. ; requiredMethods[] - a repeatable field indicating methods which must be available ; on the record driver object in order to allow export in this format. -; bulkExportType - [ "link" | "download" ] - link renders a download link in the UI, -; download offers to save the export-file directly; overrides the defaultType -; setting found in the [BulkExport] section of config.ini. This distinction -; currently only affects users with Javascript enabled; the 'download' option -; cannot be implemented in a user-friendly way when Javascript is disabled. +; bulkExportType - [ "link" | "download" | "post" ] - link renders a download link in +; the UI, download offers to save the export-file directly, post sends the +; metadata using the POST method in a form field named by the postField +; parameter; overrides the defaultType setting found in the [BulkExport] section +; of config.ini. This distinction currently only affects users with Javascript +; enabled; the 'download' option cannot be implemented in a user-friendly way +; when Javascript is disabled. +; postField +; Name of the POST field to use when sending records with the POST method. +; targetWindow +; Name of the window where the export is opened in the browser. Default is +; "{format}Main" (e.g. "RefWorksMain"), but could be set to e.g. "_blank" to +; make the export always open a new window. [RefWorks] requiredMethods[] = getTitle -redirectUrl = "{config|RefWorks|url|http://www.refworks.com}/express/expressimport.asp?vendor={encodedConfig|RefWorks|vendor|VuFind}&filter=RefWorks%20Tagged%20Format&url={encodedCallback}" -headers[] = "Content-type: text/plain; charset=utf-8" +redirectUrl = "{config|RefWorks|url|https://www.refworks.com}/express/expressimport.asp?vendor={encodedConfig|RefWorks|vendor|VuFind}&filter=RefWorks%20Tagged%20Format&encoding=65001" +bulkExportType = post +postField = ImportData [EndNoteWeb] requiredMethods[] = getTitle diff --git a/config/vufind/facets.ini b/config/vufind/facets.ini index 958c144e6e9fae5d9a090ca534110cdca8ab44dd..cfa85dd9bacef3447f945487c3454d45adf31e46 100644 --- a/config/vufind/facets.ini +++ b/config/vufind/facets.ini @@ -189,15 +189,16 @@ translated_facets[] = callnumber-first:CallNumberFirst ;delimited_facets[] = author_id_str ;delimited_facets[] = "author_id_str|:::" -; These facets will be displayed on the Home Page. If this section is omitted, -; the [Advanced] section will be used instead. +; These facets will be displayed on the Home Page when FacetList is turned on in +; the content setting of the [HomePage] section of searches.ini. If this section +; is omitted, the [Advanced] section will be used instead. [HomePage] callnumber-first = "Call Number" language = Language format = Format ;hierarchy_top_title = Collections -; These settings affect the way the [HomePage] facets are displayed +; These settings affect the way the [HomePage] facets are displayed. ; NOTE: To make changes take effect immediately, you may need to clear VuFind's ; cache after changing this section. [HomePage_Settings] diff --git a/config/vufind/geofeatures.ini b/config/vufind/geofeatures.ini new file mode 100644 index 0000000000000000000000000000000000000000..81fbe81f3ac6609a0fd156061d60ccd911182e37 --- /dev/null +++ b/config/vufind/geofeatures.ini @@ -0,0 +1,88 @@ +; Geographic feature configuration settings +; +; Legacy geographic feature configurations are located in +; config.ini (for Map Tab display) and in searches.ini (for Map Selection). +; Configuration settings in this file overwrite settings in the +; config.ini and searches.ini files. + +[Basemap] +; Configures the basemap that is used to display geographic featuers. +; Default basemap configuration is the osm-intl option below, and other +; open source basemap options are provided. +; The basemap can be set separately for the MapSelection and MapTab +; geographic features by adding the basemap_url and basemap_attribution +; to those sections of this file. +; Backward compatibility also allows for basemap_url and basemap_attribution +; to be set in the config.ini [Content] section where the Geographic Display +; settings are located and in the searches.ini [MapSelection] section. +; +; basemap_url : the tileserver URL for the basemap +; basemap_attribution: the attribution text for the basemap. + +; osm-intl +; osm-intl +basemap_url = https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png +basemap_attribution = "<a href=""https://wikimediafoundation.org/wiki/Maps_Terms_of_Use"">Wikimedia</a> | © <a href=""https://www.openstreetmap.org/copyright"">OpenStreetMap</a>" + +; stamen-toner +;basemap_url = http://tile.stamen.com/toner/{z}/{x}/{y}.png +;basemap_attribution = 'Map tiles: <a href="http://stamen.com" title="Map tiles by Stamen Design, under CC BY 3.0">Stamen Design</a> | Data: <a href="http://openstreetmap.org" title="Data by OpenStreetMap, under ODbL.">OpenStreetMap</a>' + +; stamen-terrain +;basemap_url = http://tile.stamen.com/terrain/{z}/{x}/{y}.png +;basemap_attribution = 'Map tiles: <a href="http://stamen.com" title="Map tiles by Stamen Design, under CC BY 3.0">Stamen Design</a> | Data: <a href="http://openstreetmap.org" title="Data by OpenStreetMap, under ODbL.">OpenStreetMap</a>' + +; cartocdn-light +;basemap_url = http://basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png +;basemap_attribution = "© <a href=""http://www.openstreetmap.org/copyright"">OpenStreetMap</a> contributors, © <a href=""https://carto.com/attribution"">CARTO</a>" + +; cartocdn-dark +;basemap_url = http://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png +;basemap_attribution = "© <a href=""http://www.openstreetmap.org/copyright"">OpenStreetMap</a> contributors, © <a href=""https://carto.com/attribution"">CARTO</a>" + +[MapSelection] +; To enable this feature, uncomment the default_top_recommend[] = MapSelection +; in the default recommendations section of searches.ini +; +; This defines the coordinates of a search region that will be highlighted when +; the user clicks the "Geographic Search" link next to the VuFind search box. +; This should ideally cover a large area of the map where most/all of your +; geographic points are located. If your dataset is not concentrated in one +; geographic area, it is advised that you pick a default area, and do not use +; the entire extent of the map for searching (otherwise the search may be slow). +; +; default_coordinates: The default coordinates specified below are in decimal +; degrees, and are ordered as WENS (west, east, north, south). Ranges of +; valid values are:; -180 to 180 (longitude) and -85 to 85 (latitude). +; Note, to search from and to the international date line, use +; west = -179 and east = -180. +; +; height: Height in pixels of the map selection interface. +;default_coordinates = "-95, 30, 72, 15" +;height = 320 + +[MapTab] +; Configures the map tab display +; recordMap: false (default) or true to turn on/off Map Tab display +; +; mapLabels: leave empty, file:filename, or driver +; Leave it empty – no map labels will be displayed (default) +; file:filename - specify a file name after the colon for the +; coordinate/label lookup file. Coordinates in file must +; be specified as WENS. +; driver - Use the getCoordinateLabels method of the record driver to fetch labels; +; by default this relies on the long_lat_label field in Solr, +; but you can override the behavior with custom record driver code. +; The field must be the same length as the number of coordinate sets. +; Coordinates will be matched to labels on an ordered basis such that +; label[0] will be assigned for coordinate[0] and so forth. +; displayCoords: true or false. Default is false. +; If displayCoords is true, then the coordinate values from +; coordinate field will be displayed before the map label in the label popup. +; graticule: true or false. Default is false. If graticule is true a lat/long grid will be +; displayed on the map. +;recordMap = false +;mapLabels = +;displayCoords = false +;graticule = false + diff --git a/config/vufind/httpd-vufind.conf b/config/vufind/httpd-vufind.conf index 31b94863c4e9493bc2aa2b632388bf8adf5702ac..ae94f69f35170a3451a16ab4a397aa6314406e15 100644 --- a/config/vufind/httpd-vufind.conf +++ b/config/vufind/httpd-vufind.conf @@ -38,8 +38,6 @@ Alias /vufind /usr/local/vufind/public </IfModule> AllowOverride All - php_value short_open_tag On - # If you wish to use the Shibboleth authentication, uncomment the following lines # AND comment out the "Require all granted" line above. There must not be any other # "Require" lines in this configuration section for the "Require shibboleth" diff --git a/config/vufind/permissions.ini b/config/vufind/permissions.ini index e36cf4e944faa9e1d9f69be38e35dc7b4658d97f..e85dab8330f0cfe51670a2c92b52dd5f26133140 100644 --- a/config/vufind/permissions.ini +++ b/config/vufind/permissions.ini @@ -166,3 +166,10 @@ role = loggedin ;require = ANY ;ipRange[] = '127.0.0.1' ;ipRange[] = '::1' + +; Example permission for Alma webbooks +;[alma.Webhooks] +;permission[] = "access.alma.webhook.user" +;permission[] = "access.alma.webhook.challenge" +;require = ALL +;ipRange[] = "127.0.0.1" diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini index 5008b5855267bd1350727ff5d4a85f8f2d1e01bd..447696c215c76b37612d55384f54bc7525b00e90 100644 --- a/config/vufind/searches.ini +++ b/config/vufind/searches.ini @@ -51,7 +51,7 @@ case_sensitive_ranges = true ; [NoResultsRecommendations] sections below. ; See the comments above those sections for details on legal settings. You may ; repeat these lines to load multiple recommendations. -;default_top_recommend[] = MapSelection ; see [MapSelection] below for details +;default_top_recommend[] = MapSelection ; see [MapSelection] in geofeatures.ini. default_top_recommend[] = TopFacets:ResultsTop default_top_recommend[] = SpellingSuggestions ;default_top_recommend[] = VisualFacets:Visual_Settings @@ -319,22 +319,28 @@ CallNumber = callnumber-sort ; set of authority records is used for recommendations; for example: ; AuthorityRecommend:record_type:Heading* OR Topical*:source:FAST ; limits record_type to strings starting with "Heading" or "Topical" and -; limits source to FAST. A special field name of "__resultlimit__" may be +; limits source to FAST. A special field name of "__resultlimit__" may be ; used to suppress authority results when the result set contains more items ; than the number specified as the corresponding value (e.g. if you configure ; AuthorityRecommend:__resultlimit__:50 then authority recommendations will ; only display on result screens displaying fewer than 50 hits; by default, -; recommendations will always display). Filtering is optional. +; recommendations will always display). A special field name of "__mode__" +; may be used to control what type of suggestions are presented (default is +; both seealso and usefor results, but you can turn on just one of these +; options if desired; for example "__mode__:seealso" would only show "see +; also" results from records whose main headings match the current search +; terms while "__mode__:usefor" would show only main headings from records +; whose "use for" headings match the current search. You can use "__mode__:*" +; to turn on all options (but this is default behavior if no __mode__ is +; set). Filtering is optional. ; Channels ; Display a link to the Channeled Browse functionality leading to channels of ; records related to the current search. -; MapSelection:[ini section]:[ini name] -; Enable geographic searching capability via OpenLayers3 API by activating -; this module. Records must be indexed using the geographic search and display -; fields. See the marc_local.properties file for more information on indexing. -; Default settings and more comments may be found in the [MapSelection] -; section in this file. The section name and ini file name loaded by the -; module may be overridden through the [ini section]/[ini name] parameters. +; MapSelection +; Enable geographic searching capability by activating this module. +; Records must be indexed using the geographic search and display fields. +; See the marc_local.properties file for more information on indexing. +; See the [MapSelection] section of the geofeatures.ini file for more information. ; PubDateVisAjax:[zooming]:[facet field 1]:[facet field 2]:...:[facet field n] ; Display a visualization of publication dates for each of the specified facet ; fields. This is designed for a field containing four-digit years. Zooming @@ -626,21 +632,10 @@ view=full ; functionality found in the MapSelection recommendation module. ; To enable this feature, uncomment the default_top_recommend[] = MapSelection ; in the default recommendations section. To set the configuration settings -; for this feature, adjust the parameters below. +; for this feature, adjust the parameters in the geofeatures.ini file. [MapSelection] -; This defines the coordinates of a search region that will be highlighted when -; the user clicks the "Geographic Search" link next to the VuFind search box. -; This should ideally cover a large area of the map where most/all of your -; geographic points are located. If your dataset is not concentrated in one -; geographic area, it is advised that you pick a default area, and do not use -; the entire extent of the map for searching (otherwise the search may be slow). -; The default coordinates specified below are in decimal degrees, and are -; ordered as WENS (west, east, north, south). Ranges of valid values are: -; -180 to 180 (longitude) and -85 to 85 (latitude). Note, to search from and to -; the international date line, use west = -179 and east = -180. -default_coordinates = "-95, 30, 72, 15" -; height: Height in pixels of the map selection interface. -height = 320 +; These configuration settings have been superseded by the geofeatures.ini file. +; See the [MapSelection] section of the geofeatures.ini file for more information. ; This section defines settings used to fetch similar records. [MoreLikeThis] @@ -656,3 +651,20 @@ height = 320 ;params = "qf=title,title_short,callnumber-label,topic,language,author,publishDate mintf=1 mindf=1"; ; This setting can be used to limit the maximum number of suggestions. Default is 5. ;count = 5 + +; This section controls the behavior of the Search/Home screen. +[HomePage] +; Content blocks can be selected from the list below: +; +; Channels:[source] - Display the homepage channels for the specified [source]. +; [source] defaults to Solr. +; +; FacetList:[source]:[column size] - Display a list of facet values +; drawn from the [source] backend, with a maximum of [column size] values per +; column. [source] defaults to Solr and [column size] defaults to 10. +; +; IlsStatusMonitor:[target] - Performs an AJAX health check of the ILS and +; prepends a warning message to the HTML element identified by the jQuery selector +; provided in [target] (which defaults to .searchHomeContent. +content[] = IlsStatusMonitor +content[] = FacetList diff --git a/config/vufind/searchspecs.yaml b/config/vufind/searchspecs.yaml index 13448bc5ab039304b1beb242a874dc66db441ce1..eaf01482b286f0cd8b8c883b67619218186a303b 100644 --- a/config/vufind/searchspecs.yaml +++ b/config/vufind/searchspecs.yaml @@ -161,6 +161,9 @@ # between multiple configured VuFind instances. You can create a chain of parent # files if necessary. # +# If @parent_yaml cannot be accessed as an absolute path, it will also be tried +# relative to the path of the file defining it. +# #----------------------------------------------------------------------------------- # These searches use Dismax when possible: diff --git a/config/vufind/searchspecs2.yaml b/config/vufind/searchspecs2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c024b5c73a33de46d2d222d6b6c59dcc0327b338 --- /dev/null +++ b/config/vufind/searchspecs2.yaml @@ -0,0 +1,3 @@ +# This is the search configuration for the secondary search handler. +# By default, it simply inherits rules from the parent handler. +"@parent_yaml": "searchspecs.yaml" \ No newline at end of file diff --git a/harvest/harvest_oai.php b/harvest/harvest_oai.php index a364a9a31b989d37560545aa9cd99ff7c924689b..52c111b9c54150ca6c309f2d350e53249e52bcfb 100644 --- a/harvest/harvest_oai.php +++ b/harvest/harvest_oai.php @@ -2,7 +2,7 @@ /** * OAI-PMH Harvest Tool * - * PHP version 5 + * PHP version 7 * * Copyright (c) Demian Katz 2010. * diff --git a/harvest/merge-marc.php b/harvest/merge-marc.php index 779f18766feac06518c83214c03f655c20b88a80..2c2a192978661925ad0f2238773a23a418634f46 100644 --- a/harvest/merge-marc.php +++ b/harvest/merge-marc.php @@ -2,7 +2,7 @@ /** * Merge harvested MARC records into a single <collection> * - * PHP version 5 + * PHP version 7 * * Copyright (c) Demian Katz 2010. * diff --git a/import-marc.bat b/import-marc.bat index 5b2257f1686b30921ffdc62b3126143961b840fb..c4322107e00484ff397fce441752e910a5fe71a6 100644 --- a/import-marc.bat +++ b/import-marc.bat @@ -60,17 +60,20 @@ set EXTRA_SOLRMARC_SETTINGS=%EXTRA_SOLRMARC_SETTINGS% -Dsolr.core.name=%SOLRCOR rem ################################################## rem # Set VUFIND_HOME rem ################################################## -if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound +if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat rem If env.bat doesn't exist, the user hasn't run the installer yet. -echo ERROR: env.bat does not exist -- could not set up environment. +echo WARNING: env.bat does not exist -- trying default environment settings. echo Please run "php install.php" to correct this problem. -goto end +rem Extract path from current batch file and trim trailing slash: +set VUFIND_HOME=%~dp0% +set VUFIND_HOME=%VUFIND_HOME:~0,-1% +goto vufindhomefound :useenvbat call env > nul -if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound +if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound echo You need to set the VUFIND_HOME environmental variable before running this script. goto end :vufindhomefound @@ -78,11 +81,11 @@ goto end rem ##################################################### rem # Build java command rem ##################################################### -if not "!%JAVA_HOME%!"=="!!" goto javahomefound +if not (!%JAVA_HOME%!)==(!!) goto javahomefound set JAVA=java goto javaset :javahomefound -set JAVA="%JAVA_HOME%\bin\java" +set JAVA=%JAVA_HOME%\bin\java :javaset rem ################################################## @@ -93,6 +96,7 @@ if not exist %VUFIND_LOCAL_DIR%\import\import.properties goto nolocalproperties set PROPERTIES_FILE=%VUFIND_LOCAL_DIR%\import\import.properties goto propertiesfound :nolocalproperties +echo WARNING: VUFIND_LOCAL_DIR environment variable is not set. Is this intentional? set PROPERTIES_FILE=%VUFIND_HOME%\import\import.properties :propertiesfound @@ -112,4 +116,4 @@ echo %RUN_CMD% :end rem We're all done -- close down the local environment. -endlocal \ No newline at end of file +endlocal diff --git a/import-marc.sh b/import-marc.sh index 9569b084c896203e1057176bb355421a879bfcc3..82e8d1b26cc388e91b1a1a5da4a394fb0b0ca34f 100755 --- a/import-marc.sh +++ b/import-marc.sh @@ -52,7 +52,6 @@ then INDEX_OPTIONS='-Xms512m -Xmx512m -DentityExpansionLimit=0' fi - ################################################## # Set SOLRCORE ################################################## @@ -61,15 +60,20 @@ then EXTRA_SOLRMARC_SETTINGS="$EXTRA_SOLRMARC_SETTINGS -Dsolr.core.name=$SOLRCORE" fi - ################################################## # Set VUFIND_HOME ################################################## if [ -z "$VUFIND_HOME" ] then - VUFIND_HOME="/usr/local/vufind" + # set VUFIND_HOME to the absolute path of the directory containing this script + # https://stackoverflow.com/questions/4774054/reliable-way-for-a-bash-script-to-get-the-full-path-to-itself + export VUFIND_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" fi +if [ -z "$VUFIND_LOCAL_DIR" ] +then + echo "WARNING: VUFIND_LOCAL_DIR environment variable is not set. Is this intentional?" +fi ##################################################### # Build java command diff --git a/import/browse-indexing.jar b/import/browse-indexing.jar index 05d3d6842ff36cfe89f974f4c8254248aa094960..1d403fa2b75d3c07781b5eac4eec46e9c2c6070f 100644 Binary files a/import/browse-indexing.jar and b/import/browse-indexing.jar differ diff --git a/import/import-xsl.php b/import/import-xsl.php index 0aec5d90e4ad9f0e3864dd27e11745b9da9e8c61..30a4b1ca9bb2e4a1b722b9a885702dc575dddbf9 100644 --- a/import/import-xsl.php +++ b/import/import-xsl.php @@ -2,7 +2,7 @@ /** * XSLT importer support methods. * - * PHP version 5 + * PHP version 7 * * Copyright (c) Demian Katz 2010. * diff --git a/import/index_java/src/org/vufind/index/ConfigManager.java b/import/index_java/src/org/vufind/index/ConfigManager.java index d9f25087780f1f1cb74304638973b56ba62e1a99..454aace2374ad201cfaac91e5022869626680d69 100644 --- a/import/index_java/src/org/vufind/index/ConfigManager.java +++ b/import/index_java/src/org/vufind/index/ConfigManager.java @@ -66,14 +66,15 @@ public class ConfigManager * Given the base name of a configuration file, locate the full path. * @param filename base name of a configuration file */ - private File findConfigFile(String filename) + private File findConfigFile(String filename) throws IllegalStateException { // Find VuFind's home directory in the environment; if it's not available, // try using a relative path on the assumption that we are currently in // VuFind's import subdirectory: String vufindHome = System.getenv("VUFIND_HOME"); if (vufindHome == null) { - vufindHome = ".."; + // this shouldn't happen since import-marc.sh and .bat always set VUFIND_HOME + throw new IllegalStateException("VUFIND_HOME must be set"); } // Check for VuFind 2.0's local directory environment variable: @@ -133,11 +134,21 @@ public class ConfigManager // Retrieve the file if it is not already cached. if (!configCache.containsKey(filename)) { Ini ini = new Ini(); + File configFile = null; try { - ini.load(new FileReader(findConfigFile(filename))); - configCache.putIfAbsent(filename, ini); + configFile = findConfigFile(filename); + } catch (IllegalStateException e) { + dieWithError("Illegal State: " + e.getMessage()); } catch (Throwable e) { - dieWithError("Unable to access " + filename); + dieWithError("Unable to locate " + filename); + } + try { + if (configFile != null) { + ini.load(new FileReader(configFile)); + configCache.putIfAbsent(filename, ini); + } + } catch (Throwable e) { + dieWithError("Unable to access " + configFile.getAbsolutePath()); } } return configCache.get(filename); @@ -231,4 +242,4 @@ public class ConfigManager logger.error(msg); throw new SolrMarcIndexerException(SolrMarcIndexerException.EXIT, msg); } -} \ No newline at end of file +} diff --git a/import/index_java/src/org/vufind/index/GeoTools.java b/import/index_java/src/org/vufind/index/GeoTools.java index 342cad8c1660b39e46626b495cb673bf0fe57e95..db00d3975830ecf50339de207ea75d72d90763f7 100644 --- a/import/index_java/src/org/vufind/index/GeoTools.java +++ b/import/index_java/src/org/vufind/index/GeoTools.java @@ -25,18 +25,32 @@ package org.vufind.index; * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +import java.io.*; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.Set; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import org.apache.log4j.Logger; -import org.marc4j.marc.Record; -import org.marc4j.marc.VariableField; import org.marc4j.marc.ControlField; import org.marc4j.marc.DataField; +import org.marc4j.marc.Record; import org.marc4j.marc.Subfield; -import java.util.HashMap; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import org.marc4j.marc.VariableField; +import org.solrmarc.index.indexer.ValueIndexerFactory; +import org.solrmarc.index.SolrIndexer; +import org.solrmarc.tools.PropertyUtils; +import org.solrmarc.tools.SolrMarcIndexerException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * Geographic indexing routines. @@ -46,10 +60,26 @@ public class GeoTools private static final Pattern COORDINATES_PATTERN = Pattern.compile("^([eEwWnNsS])(\\d{3})(\\d{2})(\\d{2})"); private static final Pattern HDMSHDD_PATTERN = Pattern.compile("^([eEwWnNsS])(\\d+(\\.\\d+)?)"); private static final Pattern PMDD_PATTERN = Pattern.compile("^([-+]?\\d+(\\.\\d+)?)"); + static String datePrefix = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + static String vufindLocal = System.getenv("VUFIND_LOCAL_DIR"); + static String vufindHome = System.getenv("VUFIND_HOME"); + private static Properties vufindConfigs = null; // Initialize logging category static Logger logger = Logger.getLogger(GeoTools.class.getName()); + /** + * Constructor + */ + public GeoTools() + { + try { + vufindConfigs = PropertyUtils.loadProperties(ValueIndexerFactory.instance().getHomeDirs(), "vufind.properties"); + } catch (IllegalArgumentException e) { + // If the properties load failed, don't worry about it -- we'll use defaults. + } + } + /** * Convert MARC coordinates into long_lat format. * @@ -62,10 +92,6 @@ public class GeoTools if (list034 != null) { for (VariableField vf : list034) { HashMap<Character, String> coords = getCoordinateValues(vf); - //DEBUG output - //ControlField recID = (ControlField) record.getVariableField("001"); - //String recNum = recID.getData(); - //logger.info("Record ID: " + recNum.trim() + " ...Coordinates: [ {" + coords.get('d') + "} {" + coords.get('e') + "} {" + coords.get('f') + "} {" + coords.get('g') + "} ]"); // Check for null coordinates if (validateCoordinateValues(record, coords)) { @@ -79,11 +105,7 @@ public class GeoTools // Note - storage in Solr follows the WENS order, but display is WSEN order String result = String.format("ENVELOPE(%s,%s,%s,%s)", new Object[] { west, east, north, south }); geo_coordinates.add(result); - } else { - logger.error(".......... Not indexing INVALID coordinates: [ {" + coords.get('d') + "} {" + coords.get('e') + "} {" + coords.get('f') + "} {" + coords.get('g') + "} ]"); - } - } else { - logger.error(".......... Not indexing INVALID coordinates: [ {" + coords.get('d') + "} {" + coords.get('e') + "} {" + coords.get('f') + "} {" + coords.get('g') + "} ]"); + } } } } @@ -106,14 +128,83 @@ public class GeoTools if (validateCoordinateValues(record, coords)) { String result = String.format("%s %s %s %s", new Object[] { coords.get('d'), coords.get('e'), coords.get('f'), coords.get('g') }); geo_coordinates.add(result); - } else { - logger.error(".......... Not indexing INVALID Display coordinates: [ {" + coords.get('d') + "} {" + coords.get('e') + "} {" + coords.get('f') + "} {" + coords.get('g') + "} ]"); } } } return geo_coordinates; } + /** + * Log coordinate indexing errors to external log file. + * + * @param Record record + * @param HashMap coords + * @param String error message + */ + public static void logErrorMessage(Record record, HashMap coords, String message) { + // Initialize error logging variables + String msgError = message; + String recNum = "Not available"; + ControlField recID = (ControlField) record.getVariableField("001"); + if (recID != null) { + recNum = recID.getData().trim(); + } + String coordinates = "Coordinates: {" + coords.get('d') + "} {" + coords.get('e') + "} {" + coords.get('f') + "} {" + coords.get('g') + "}"; + + String logPath = getLogPath(); + String logFilename = datePrefix + "_CoordinateErrors.txt"; + String outPath = logPath + "/" + logFilename; + + // Output Error message + logger.error("Not indexing INVALID coordinates for Record ID: " + recNum); + logger.error("... " + msgError); + + if (logPath != null) { + logger.error("... Check coordinate error log: " + outPath); + // Log ID and error message and coordinates in error file + try { + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outPath, true))); + out.println(logFilename + "\t" + recNum + "\t" + msgError + "\t" + coordinates); + out.close(); + } catch (IOException e) { + System.out.println("io exception occured"); + e.printStackTrace(); + } + } else { + // output error that log file cannot be created + logger.error("..... No coordinate error log. Check vufind.properties settings..."); + } + + } + + /** + * Get path for coordinate error log file. + * + * @return String logPath + */ + public static String getLogPath() { + // Get coordinate error log path setting + String coordLogPath = PropertyUtils.getProperty(vufindConfigs, "coordinate.log.path"); + + //If coordinate.log.path doesn't exist or is not set, try some other places + if (coordLogPath == null) { + if (vufindLocal != null) { + File dir = new File(vufindLocal + "/import"); + if (dir.exists()) { + coordLogPath = vufindLocal + "/import"; + } + } else if (vufindLocal == null && vufindHome != null) { + File dir = new File(vufindHome + "/import"); + if (dir.exists()) { + coordLogPath = vufindHome + "/import"; + } + } else { + coordLogPath = null; + } + } + return coordLogPath; + } + /** * Get all coordinate values from list034 * @@ -164,9 +255,8 @@ public class GeoTools if (coords.containsKey('d') && coords.containsKey('e') && coords.containsKey('f') && coords.containsKey('g')) { return true; } - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Coordinate values contain null values."); + String msgError = "Coordinate values contain null values."; + logErrorMessage(record, coords, msgError); return false; } @@ -211,8 +301,7 @@ public class GeoTools coordinate = Double.parseDouble(PMDmatcher.group(1)); return coordinate; } else { - logger.error("Decimal Degree Coordinate Conversion Error: Poorly formed coordinate: [" + coordinateStr + "] ... Returning null value ... "); - return null; + return Double.NaN; } } @@ -235,7 +324,7 @@ public class GeoTools } return coordinate; } - return null; + return Double.NaN; } /** @@ -280,9 +369,9 @@ public class GeoTools */ public boolean validateLines(Record record, Double west, Double east, Double north, Double south) { if ((!west.equals(east) && north.equals(south)) && (north == 90 || south == -90)) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Coordinates form a line at the pole"); + String msgError = "Coordinates form a line at the pole"; + HashMap<Character, String> coords = buildCoordinateHashMap(west, east, north, south); + logErrorMessage(record, coords, msgError); return false; } return true; @@ -296,10 +385,10 @@ public class GeoTools * @return boolean */ public boolean validateValues(Record record, Double west, Double east, Double north, Double south) { - if (west == null || east == null || north == null || south == null) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Decimal Degree coordinates contain null values: [ {" + west + "} {" + east + "} {" + north + "} {" + south + "} ]"); + if (west.isNaN() || east.isNaN() || north.isNaN() || south.isNaN()) { + String msgError = "Decimal Degree coordinates contain invalid values"; + HashMap<Character, String> coords = buildCoordinateHashMap(west, east, north, south); + logErrorMessage(record, coords, msgError); return false; } return true; @@ -313,16 +402,12 @@ public class GeoTools * @return boolean */ public boolean validateExtent(Record record, Double west, Double east, Double north, Double south) { - if (west > 180.0 || west < -180.0 || east > 180.0 || east < -180.0) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Coordinates exceed map extent."); - return false; - } - if (north > 90.0 || north < -90.0 || south > 90.0 || south < -90.0) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Coordinates exceed map extent."); + if (west > 180.0 || west < -180.0 || east > 180.0 || east < -180.0 + || north > 90.0 || north < -90.0 || south > 90.0 || south < -90.0 + ) { + String msgError = "Coordinates exceed map extent."; + HashMap<Character, String> coords = buildCoordinateHashMap(west, east, north, south); + logErrorMessage(record, coords, msgError); return false; } return true; @@ -337,9 +422,9 @@ public class GeoTools */ public boolean validateNorthSouth(Record record, Double north, Double south) { if (north < south) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - North < South."); + String msgError = "North < South."; + HashMap<Character, String> coords = buildCoordinateHashMap(Double.NaN, Double.NaN, north, south); + logErrorMessage(record, coords, msgError); return false; } return true; @@ -363,9 +448,9 @@ public class GeoTools } // Check again if (east < west) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - East < West."); + String msgError = "East < West."; + HashMap<Character, String> coords = buildCoordinateHashMap(west, east, Double.NaN, Double.NaN); + logErrorMessage(record, coords, msgError); return false; } } @@ -386,19 +471,34 @@ public class GeoTools //Check for South Pole coordinate distance if ((north == -90 || south == -90) && (distNS > 0 && distNS < 0.167)) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Coordinates < 0.167 degrees from South Pole. Coordinate Distance: "+distNS); + String msgError = "Coordinates < 0.167 degrees from South Pole. Coordinate Distance: "+distNS; + HashMap<Character, String> coords = buildCoordinateHashMap(west, east, north, south); + logErrorMessage(record, coords, msgError); return false; } //Check for East-West coordinate distance if ((west == 0 || east == 0) && (distEW > -2 && distEW <0)) { - ControlField recID = (ControlField) record.getVariableField("001"); - String recNum = recID.getData(); - logger.error("Record ID: " + recNum.trim() + " - Coordinates within 2 degrees of Prime Meridian. Coordinate Distance: "+distEW); + String msgError = "Coordinates within 2 degrees of Prime Meridian. Coordinate Distance: "+distEW; + HashMap<Character, String> coords = buildCoordinateHashMap(west, east, north, south); + logErrorMessage(record, coords, msgError); return false; } return true; } + + /** + * Build coordinate hash map for logging. + * + * @param Double west, east, north, south + * @return HashMap coords + */ + public HashMap buildCoordinateHashMap (Double west, Double east, Double north, Double south) { + HashMap<Character, String> coords = new HashMap(); + coords.put('d', Double.toString(west)); + coords.put('e', Double.toString(east)); + coords.put('f', Double.toString(north)); + coords.put('g', Double.toString(south)); + return coords; + } } diff --git a/import/lib/marc4j-2.8.0.jar b/import/lib/marc4j-2.8.3.jar similarity index 75% rename from import/lib/marc4j-2.8.0.jar rename to import/lib/marc4j-2.8.3.jar index ed784eedf5e45571fdc74073acaaa68d525c1ede..370ee8e3c874474711da77264f15f7002983138b 100644 Binary files a/import/lib/marc4j-2.8.0.jar and b/import/lib/marc4j-2.8.3.jar differ diff --git a/import/marc_auth.properties b/import/marc_auth.properties index 5ef540dee66792ca334980924be27811c04929c6..e4aadb68f039f1a09f8aa29ca0a5c2411e46f3d6 100644 --- a/import/marc_auth.properties +++ b/import/marc_auth.properties @@ -4,6 +4,7 @@ # bibliographic settings. # ############################################################################### id = custom, getFirstNormalizedLCCN("010a") +record_format = "marc" lccn = custom, getNormalizedLCCNs("010a") # These values should be overridden in a second properties file (for example, @@ -13,7 +14,7 @@ source = "Unknown" record_type = "Unknown" fullrecord = FullRecordAsMarc -allfields = custom, getAllSearchableFields(100, 900) +allfields = custom, getAllSearchableFieldsAsSet(100, 900) # These are just artificial examples -- as with source and record_type, they # should be overridden in a second properties file: diff --git a/import/solrmarc_core_3.0.6.jar b/import/solrmarc_core_3.0.6.jar deleted file mode 100644 index 1c2bbf20c8f6f980b6fbb25750e65c57386515da..0000000000000000000000000000000000000000 Binary files a/import/solrmarc_core_3.0.6.jar and /dev/null differ diff --git a/import/solrmarc_core_3.1.jar b/import/solrmarc_core_3.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..c161a970f447a1c607db1a943bf793ff71acfed7 Binary files /dev/null and b/import/solrmarc_core_3.1.jar differ diff --git a/import/translation_maps/format_map.properties b/import/translation_maps/format_map.properties index fcad78c6b24963a9695c5fd474055323d43a306a..48cd864c98e60e1b19b02c3bc6cfc286b9e02572 100644 --- a/import/translation_maps/format_map.properties +++ b/import/translation_maps/format_map.properties @@ -2,52 +2,52 @@ # DATA FROM INDEXER = Label # Atlas = Map -Map = Map -TapeCartridge = Software +Book = Book +BookComponentPart = Book Chapter +Braille = Braille +BRDisc = Blu-ray Disc +CDROM = Software +Chart = Photo ChipCartridge = Software +Collage = Photo DiscCartridge = Software -TapeCassette = Software -TapeReel = Software +Drawing = Photo +eBook = eBook +Electronic = Electronic +Filmstrip = Video +FlashCard = Photo FloppyDisk = Software -CDROM = Software -Software = Software Globe = Globe -Braille = Braille -Filmstrip = Video -Transparency = Slide -Slide = Slide +Journal = Journal +Kit = Kit +Manuscript = Manuscript +Map = Map Microfilm = Microfilm -Collage = Photo -Drawing = Photo +MotionPicture = Video +MusicalScore = Musical Score +MusicalScore = Musical Score +MusicRecording = Audio +Newspaper = Newspaper Painting = Photo -Print = Photo Photonegative = Photo -FlashCard = Photo -Chart = Photo Photo = Photo -MotionPicture = Video -Kit = Kit -MusicalScore = Musical Score +PhysicalObject = Physical Object +Print = Photo SensorImage = Sensor Image -SoundDisc = CD +SerialComponentPart = Article +Serial = Serial +Slide = Slide +Software = Software SoundCassette = Cassette +SoundDisc = CD SoundRecording = Audio +TapeCartridge = Software +TapeCassette = Software +TapeReel = Software +Transparency = Slide +Unknown = Unknown VideoCartridge = Video -VideoDisc = DVD -BRDisc = Blu-ray Disc VideoCassette = VHS +VideoDisc = DVD VideoReel = Video Video = Video -MusicalScore = Musical Score -MusicRecording = Audio -Electronic = Electronic -PhysicalObject = Physical Object -Manuscript = Manuscript -eBook = eBook -Book = Book -Newspaper = Newspaper -Journal = Journal -Serial = Serial -BookComponentPart = Book Chapter -SerialComponentPart = Article -Unknown = Unknown \ No newline at end of file diff --git a/import/vufind.properties b/import/vufind.properties index f7010139988b8e6a90806222890aa87bf12905d6..52985f62f92e878c5242027856c2d95f553097a2 100644 --- a/import/vufind.properties +++ b/import/vufind.properties @@ -6,3 +6,8 @@ # look in the VuFind 2.x-style directory path, but you can uncomment the line # below for compatibility with VuFind 1.x. #vufind.config.relative_path = web/conf + +# coordinate error log location - where to put coordinate indexing error logs +# if not set, the default path will be VUFIND_LOCAL_DIR/import, and failing that, +# VUFIND_HOME/import. +#coordinate.log.path = /usr/local/vufind/import diff --git a/import/webcrawl.php b/import/webcrawl.php index f7e4e53f0da3d3de2975696580f057b421605f50..95cae11aa9339520fcd45081db508a25de31d6c7 100644 --- a/import/webcrawl.php +++ b/import/webcrawl.php @@ -2,7 +2,7 @@ /** * Command-line tool to crawl website for special index. * - * PHP version 5 + * PHP version 7 * * Copyright (c) Demian Katz 2010. * diff --git a/index-alphabetic-browse.bat b/index-alphabetic-browse.bat index 315f0406982f339ea1970b52992041c2282a507c..ceccbb467fafb7b957ec515f1201899d24284eae 100644 --- a/index-alphabetic-browse.bat +++ b/index-alphabetic-browse.bat @@ -12,32 +12,35 @@ goto end rem ################################################## rem # Set SOLR_HOME rem ################################################## -if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound +if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat rem If env.bat doesn't exist, the user hasn't run the installer yet. -echo ERROR: env.bat does not exist -- could not set up environment. -echo Please run install.php to correct this problem. -goto end +echo WARNING: env.bat does not exist -- trying default environment settings. +echo Please run "php install.php" to correct this problem. +rem Extract path from current batch file and trim trailing slash: +set VUFIND_HOME=%~dp0% +set VUFIND_HOME=%VUFIND_HOME:~0,-1% +goto vufindhomefound :useenvbat call env > nul -if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound +if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound echo You need to set the VUFIND_HOME environmental variable before running this script. goto end :vufindhomefound -if not "!%SOLR_HOME%!"=="!!" goto solrhomefound +if not (!%SOLR_HOME%!)==(!!) goto solrhomefound set SOLR_HOME=%VUFIND_HOME%\solr\vufind :solrhomefound rem ##################################################### rem # Build java command rem ##################################################### -if not "!%JAVA_HOME%!"=="!!" goto javahomefound +if not (!%JAVA_HOME%!)==(!!) goto javahomefound set JAVA=java goto javaset :javahomefound -set JAVA="%JAVA_HOME%\bin\java" +set JAVA=%JAVA_HOME%\bin\java :javaset cd %VUFIND_HOME%\import diff --git a/install.php b/install.php index d6aa2e344a2f206b7fbb4f30509c6decde416f00..d2462409d80f79f6710fd6549d7d3761f3eec52c 100644 --- a/install.php +++ b/install.php @@ -2,7 +2,7 @@ /** * Command-line tool to begin VuFind installation process * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2012. * diff --git a/languages/CallNumberFirst/ar.ini b/languages/CallNumberFirst/ar.ini new file mode 100644 index 0000000000000000000000000000000000000000..4188264517b0a23936bc39b883d34ebeaad90e91 --- /dev/null +++ b/languages/CallNumberFirst/ar.ini @@ -0,0 +1,21 @@ +A - General Works = "A - أعمال عامة" +B - Philosophy, Psychology, Religion = "B - ÙلسÙØ©ØŒ علم Ù†Ùس، دين" +C - Historical Sciences = "C - علوم تاريخية" +D - World History = "D - تاريخ العالم" +E - United States History = "E - تاريخ الولايات المتØدة" +F - General American History = "F - التاريخ الأمريكي العام" +G - Geography, Anthropology, Recreation = "G - جغراÙيا، أنثروبولوجيا، ترÙيه" +H - Social Science = "H - علوم اجتماعية" +J - Political Science = "J - علوم سياسية" +K - Law = "K - قانون" +L - Education = "L - تربية وتعليم" +M - Music = "M - موسيقى" +N - Fine Arts = "N - Ùنون جميلة" +P - Language and Literature = "P - لغات وآداب" +Q - Science = "Q - علوم" +R - Medicine = "R - طب" +S - Agriculture = "S - زراعة" +T - Technology = "T - تكنولوجيا" +U - Military Science = "U - علوم عسكرية" +V - Naval Science = "V - علوم بØرية" +Z - Library Science = "Z - علم المكتبات" diff --git a/languages/CallNumberFirst/cs.ini b/languages/CallNumberFirst/cs.ini new file mode 100644 index 0000000000000000000000000000000000000000..89fb285dd033ff2bbf9db56cfc45c3845675eef5 --- /dev/null +++ b/languages/CallNumberFirst/cs.ini @@ -0,0 +1,21 @@ +A - General Works = "A - Obecná dÃla" +B - Philosophy, Psychology, Religion = "B - Filozofie, Psychologie, NáboženstvÃ" +C - Historical Sciences = "C - Pomocné vÄ›dy historické" +D - World History = "D - DÄ›jiny" +E - United States History = "E - DÄ›jiny Spojených států" +F - General American History = "F - MÃstnà dÄ›jiny Spojených států, DÄ›jiny Kanady a Jižnà Ameriky" +G - Geography, Anthropology, Recreation = "G - Geografie, Antropologie, Volný Äas" +H - Social Science = "H - Sociálnà vÄ›dy" +J - Political Science = "J - Politologie" +K - Law = "K - Právo" +L - Education = "L - VzdÄ›lánÃ" +M - Music = "M - Hudba" +N - Fine Arts = "N - Výtvarné umÄ›nÃ" +P - Language and Literature = "P - Jazyk a literatura" +Q - Science = "Q - VÄ›da" +R - Medicine = "R - LékaÅ™stvÃ" +S - Agriculture = "S - ZemÄ›dÄ›lstvÃ" +T - Technology = "T - Technologie" +U - Military Science = "U - Vojenská vÄ›da" +V - Naval Science = "V - NámoÅ™nà vÄ›da" +Z - Library Science = "Z - Bibliografie, KnihovnictvÃ, InformaÄnà zdroje" diff --git a/languages/CallNumberFirst/fr.ini b/languages/CallNumberFirst/fr.ini new file mode 100644 index 0000000000000000000000000000000000000000..fbf7c771448b3f33c7380b3976e73b26b232d36e --- /dev/null +++ b/languages/CallNumberFirst/fr.ini @@ -0,0 +1,21 @@ +A - General Works = "A - Généralités" +B - Philosophy, Psychology, Religion = "B - Philosophie, Psychologie, Religion" +C - Historical Sciences = "C - Sciences auxiliaires de l'histoire" +D - World History = "D - Histoire du monde (sauf Amériques)" +E - United States History = "E - Histoire des Amériques (États-Unis)" +F - General American History = "F - Histoire des Amériques (autres pays)" +G - Geography, Anthropology, Recreation = "G - Géographie, Anthropologie, Loisirs" +H - Social Science = "H - Sciences sociales" +J - Political Science = "J - Sciences politiques" +K - Law = "K - Droit" +L - Education = "L - Éducation" +M - Music = "M - Musique" +N - Fine Arts = "N - Beaux-Arts" +P - Language and Literature = "P - Linguistique et Littérature" +Q - Science = "Q - Sciences" +R - Medicine = "R - Médecine" +S - Agriculture = "S - Agriculture" +T - Technology = "T - Technologie" +U - Military Science = "U - Sciences militaires" +V - Naval Science = "V - Sciences navales" +Z - Library Science = "Z - Bibliographie, Bibliothéconomie, Sources d'information" diff --git a/languages/CallNumberFirst/nl-be.ini b/languages/CallNumberFirst/nl-be.ini new file mode 100644 index 0000000000000000000000000000000000000000..b90155373b3888abaa4a6d68a4800f6b55b804a8 --- /dev/null +++ b/languages/CallNumberFirst/nl-be.ini @@ -0,0 +1 @@ +@parent_ini = "CallNumberFirst/nl.ini" diff --git a/languages/CreatorRoles/en.ini b/languages/CreatorRoles/en.ini index 8f4379776facc595f2cd60d1b4ff8f30871d3bc1..c4a2917f489142194eed689495da454876d12f29 100644 --- a/languages/CreatorRoles/en.ini +++ b/languages/CreatorRoles/en.ini @@ -153,7 +153,7 @@ fmo = "Former owner" fmp = "Film producer" fnd = "Funder" Forts = "Continuation" -fotogr = "Fotographer" +fotogr = "Photographer" fpy = "First party" frg = "Forger" gis = "Geographic information specialist" diff --git a/languages/CreatorRoles/ga.ini b/languages/CreatorRoles/ga.ini new file mode 100644 index 0000000000000000000000000000000000000000..1f431a9085a13bbe3b467e2dc75bd50d74786e80 --- /dev/null +++ b/languages/CreatorRoles/ga.ini @@ -0,0 +1,311 @@ +abr = "Giorraitheoir" +acp = "Cóipeálaà ealaÃne" +act = "Aisteoir" +adi = "Stiúrthóir ealaÃne" +adp = "Oiriúnóir" +Adressat = "SeolaÃ" +adressat = "SeolaÃ" +aft = "Údar iarfhocail, colafain, srl." +anl = "AnailÃsÃ" +anm = "Beochantóir" +ann = "Anótáileoir" +ant = "Réamhtheachtaà bibleagrafach" +ape = "AchomharcaÃ" +apl = "Achomharcóir" +app = "Iarrthóir" +aqt = "Údar i sleachta nó in achoimrÃ" +arc = "Ailtire" +ard = "Feidhmeannach ealaÃne" +arr = "Cóiritheoir" +art = "EalaÃontóir" +asg = "IonadaÃ" +asn = "Ainm comhcheangailte" +ato = "SÃnitheoir" +att = "Ainm luaite" +auc = "CeantálaÃ" +aud = "Údar an chomhrá" +aui = "Údar an réamhrá" +aus = "ScrÃbhneoir scáileáin" +aut = "Údar" +bdd = "Dearthóir ceangail" +Bearb = "Eagarthóir" +bearb = "Eagarthóir" +Begr = "Maoinitheoir" +begr = "Maoinitheoir" +Beiträger = "RannpháirtÃ" +bjd = "Dearthóir chlúdach an leabhair" +bkd = "Dearthóir leabhair" +bkp = "Léiritheoir leabhair" +blw = "ScrÃbhneoir achoimre" +bnd = "Ceanglóir" +bpd = "Dearthóir leabharshuaitheantais" +brd = "Craoltóir" +brl = "Cabhróir braille" +bsl = "DÃoltóir leabhar" +bühnenbild = "Dearthóir seit" +cas = "Múnlóir" +ccp = "Coincheapadóir" +choreogr = "Córagrafóir" +chr = "Córagrafóir" +clb = "ComhoibrÃ" +cli = "Cliant" +cll = "CallagrafaÃ" +clr = "Dathadóir" +clt = "Collchlódóir" +cmm = "Tráchtaire" +cmp = "Cumadóir" +cmt = "Cló-eagraÃ" +cnd = "Stiúrthóir" +cng = "Cineamatagrafóir" +cns = "Cinsire" +coe = "Conspóideach-achomharcaÃ" +col = "Bailitheoir" +com = "Tiomsaitheoir" +con = "CoimeádaÃ" +cor = "Cláraitheoir bailiúcháin" +cos = "Conspóideach" +cot = "Conspóideach-achomharcóir" +cou = "Rialaithe ag an gcúirt" +cov = "Dearthóir an chlúdaigh" +cpc = "Éilitheoir an chóipchirt" +cpe = "Gearánach-achomharcaÃ" +cph = "Sealbhóir an chóipchirt" +cpl = "Gearánach" +cpt = "Gearánach-achomharcóir" +cre = "Cruthaitheoir" +crp = "ComhfhreagraÃ" +crr = "Ceartaitheoir" +crt = "Tuairisceoir cúirte" +csl = "Comhairleoir" +csp = "Comhairleoir le tionscnamh" +cst = "Dearthóir feistis" +ctb = "RannpháirtÃ" +cte = "ConspóidÃ-achomharcaÃ" +ctg = "CairtghrafaÃ" +ctr = "Conraitheoir" +cts = "ConspóidÃ" +ctt = "ConspóidÃ-achomharcóir" +cur = "CoimeádaÃ" +cwt = "Tráchtaire do théacs scrÃofa" +darst = "Taibheoir" +dbp = "Láthair an dáileacháin" +dfd = "Cosantóir" +dfe = "Cosantóir-achomharcaÃ" +dft = "Cosantóir-achomharcóir" +dgg = "Institiúid bhronnta na céime" +dir = "Stiúrthóir" +dis = "Mac léinn a chuireann tráchtas i láthair" +dln = "Dreachadóir" +dnc = "Damhsóir" +dnr = "Bronntóir" +dpc = "Léirithe" +dpt = "Taisceoir" +drehbuch = "ScrÃbhneoir scáileáin" +drm = "Dréachtóir" +drt = "Stiúrthóir" +dsr = "Dearthóir" +dst = "Dáileoir" +dtc = "RannÃocóir sonraÃ" +dte = "TiomnaÃ" +dtm = "Bainisteoir sonraÃ" +dto = "Tiomnóir" +dub = "Údar éiginnte" +edc = "Eagarthóir an tiomsacháin" +edm = "Eagarthóir shaothar na n-Ãomhánna gluaisteacha" +edt = "Eagarthóir" +egr = "Greanadóir" +elg = "Leictreoir" +elt = "Leictreachlódóir" +eng = "Innealtóir" +enj = "DlÃnse an achtaithe" +etr = "EitseálaÃ" +evp = "Láthair na hócáide" +exp = "SaineolaÃ" +fac = "Déantóir facsamhla" +fds = "Dáileoir scannáin" +fld = "Stiúrthóir allamuigh" +flm = "Eagarthóir scannáin" +fmd = "Stiúrthóir scannáin" +fmk = "Scannánóir" +fmo = "Iar-úinéir" +fmp = "Léiritheoir scannáin" +fnd = "Maoinitheoir" +fotogr = "Grianghrafadóir" +fpy = "An chéad pháirtÃ" +frg = "Brionnóir" +gis = "Speisialtóir le faisnéis thÃreolaÃoch" +grt = "Teicneoir grafach" +hg = "Foilsitheoir" +his = "Óstinstitiúid" +hnr = "GradamaÃ" +Hrsg = "Foilsitheoir" +hrsg = "Foilsitheoir" +hst = "Óstach" +Ill = "Maisitheoir" +ill = "Maisitheoir" +ilu = "Maisitheoir" +ins = "InscrÃobhaÃ" +inszenierung = "Bainisteoir stáitse" +interviewer = "AgallaÃ" +interviewter = "Agallóir" +inv = "Tionscnóir" +isb = "Comhlucht an eisithe" +itr = "IonstramaÃ" +ive = "AgallaÃ" +ivr = "Agallóir" +jud = "Breitheamh" +jug = "DlÃnse atá rialaithe" +kartograph = "CairtghrafaÃ" +komm = "Tráchtaire" +Komment = "Tráchtaire" +Komp = "Cumadóir" +komp = "Cumadóir" +Korresp = "ComhfhreagraÃ" +kostüm = "Dearthóir feistis" +lbr = "Saotharlann" +lbt = "Údar leabhróige" +ldr = "Stiúrthóir saotharlainne" +led = "CeannasaÃ" +lee = "LeabhalaÃ-achomharcaÃ" +lel = "LeabhalaÃ" +len = "Iasachtóir" +let = "LeabhalaÃ-achomharcóir" +lgd = "Dearthóir soilsithe" +lie = "Leabhalach-achomharcaÃ" +lil = "Leabhalach" +lit = "Leabhalach-achomharcóir" +lsa = "Ailtire tÃrdhreacha" +lse = "CeadúnaÃ" +lso = "Ceadúnóir" +ltg = "LiteagrafaÃ" +lyr = "Liriceoir" +mcp = "Tras-scrÃobhaà ceoil" +mdc = "Teagmhálaà meiteashonraÃ" +mfp = "Láthair na déantúsaÃochta" +mfr = "Déantúsóir" +mitarb = "ComhoibrÃ" +mod = "Ceansaitheoir" +mon = "Monatóir" +mrb = "Marmaróir" +mrk = "Eagarthóir marcála" +msd = "Stiúrthóir ceoil" +mte = "Greanadóir miotail" +mus = "Ceoltóir" +Nachr = "Údar iarfhocail, colafain, srl." +nrt = "Tráchtaire" +opn = "Freasúra" +org = "Cruthaitheoir" +orm = "Eagraà cruinnithe" +osp = "Láithreoir ar scáileán" +oth = "Eile" +own = "Úinéir" +pan = "Ball painéil" +pat = "Pátrún" +pbd = "Stiúrthóir foilseacháin" +pbl = "Foilsitheoir" +pdr = "Stiúrthóir tionscnaimh" +pfr = "Léitheoir profaÃ" +pht = "Grianghrafadóir" +plt = "Déantóir plátaÃ" +pma = "GnÃomhaireacht an cheadaithe" +pmn = "Bainisteoir léirithe" +pop = "Printéir plátaÃ" +ppm = "Déantóir páipéir" +ppt = "Puipeadóir" +pra = "Moltóir tráchtais" +prc = "Teagmhálaà próisis" +prd = "Pearsanra léirithe" +pre = "Láithreoir" +prf = "Taibheoir" +prg = "Cláraitheoir" +prm = "Déantóir priontaÃ" +prn = "Comhlucht léirithe" +pro = "Léiritheoir" +prod = "Léiritheoir" +prp = "Láthair an léirithe" +prs = "Dearthóir léirithe" +prt = "Printéir" +prv = "SoláthraÃ" +präses = "Moltóir tráchtais" +pta = "Iarratasóir paitinne" +pte = "GearánaÃ-achomharcaÃ" +ptf = "GearánaÃ" +pth = "Sealbhóir na paitinne" +ptt = "GearánaÃ-achomharcóir" +pup = "Láthair an fhoilsithe" +rbr = "Déantóir ceannteideal" +rcd = "Taifeadóir" +rce = "Innealtóir taifeadta" +rcp = "SeolaÃ" +rdd = "Stiúrthóir raidió" +Red = "Forbróir chreatlach an tsaothair" +red = "Forbróir chreatlach an tsaothair" +regie = "Stiúrthóir" +ren = "Déantóir scáthaithe" +reporter = "Tuairisceoir" +res = "Taighdeoir" +resp = "Freagróir" +rev = "Léirmheastóir" +rpc = "Léiritheoir raidió" +rps = "Taisclann" +rpt = "Tuairisceoir" +rpy = "An pháirtà atá freagrach" +rse = "Freagróir-achomharcaÃ" +rsg = "Ath-léiritheoir" +rsp = "Freagróir" +rsr = "Deisitheoir" +rst = "Freagróir-achomharcóir" +rth = "Ceann na foirne taighde" +rtm = "Ball na foirne taighde" +sad = "Comhairleoir eolaÃochta" +Sammler = "Bailitheoir" +sammler = "Bailitheoir" +sce = "ScrÃbhneoir radhairc" +scl = "Dealbhóir" +scr = "ScrÃobhaÃ" +sds = "Dearthóir fuaime" +sec = "RúnaÃ" +sgd = "Stiúrthóir stáitse" +sgn = "SÃnitheoir" +sht = "Óstach cúnta" +sll = "DÃoltóir" +sng = "AmhránaÃ" +spk = "Cainteoir" +spn = "Urraitheoir" +sprecher = "Cainteoir" +spy = "An dara páirtÃ" +srv = "Suirbhéir" +std = "Dearthóir seit" +stecher = "Greanadóir" +stg = "SuÃomh" +stl = "ScéalaÃ" +stm = "Bainisteoir stáitse" +stn = "Comhlacht caighdeán" +str = "Steiréaclódóir" +tcd = "Stiúrthóir teicniúil" +tch = "Múinteoir" +textverf = "Údar" +ths = "Comhairleoir tráchtais" +tld = "Stiúrthóir teilifÃse" +tlp = "Léiritheoir teilifÃse" +trc = "Tras-scrÃobhaÃ" +trl = "Aistritheoir" +tyd = "Dearthóir cló-aghaidhe" +tyg = "Bainisteoir cló-aghaidhe" +tänzer = "Damhsóir" +uvp = "Láthair na hOllscoile" +vdg = "FÃsghrafadóir" +voc = "AmhránaÃ" +Vorr = "Údar an réamhrá" +wac = "ScrÃbhneoir tráchtaireachta breise" +wal = "ScrÃbhneoir liricà breise" +wam = "ScrÃbhneoir ábhair a ghabhann leis an mÃr" +wat = "ScrÃbhneoir téacs bhreise" +wdc = "Gearrthóir adhmaid" +wde = "Greanadóir adhmaid" +widmungsempfänger = "TiomnaÃ" +wit = "Finné" +zeichner = "Dréachtóir" +zensor = "Cinsire" +Ãœbers = "Aistritheoir" +übers = "Aistritheoir" diff --git a/languages/CreatorRoles/nl-be.ini b/languages/CreatorRoles/nl-be.ini new file mode 100644 index 0000000000000000000000000000000000000000..b5e67ed194caacb141348fe185a2ba7e89cedbde --- /dev/null +++ b/languages/CreatorRoles/nl-be.ini @@ -0,0 +1 @@ +@parent_ini = "CreatorRoles/nl.ini" diff --git a/languages/HoldingStatus/nl-be.ini b/languages/HoldingStatus/nl-be.ini new file mode 100644 index 0000000000000000000000000000000000000000..508cf0a909b1ff3a16136c24e9bf83e79abfa148 --- /dev/null +++ b/languages/HoldingStatus/nl-be.ini @@ -0,0 +1 @@ +@parent_ini = "HoldingStatus/nl.ini" diff --git a/languages/ar.ini b/languages/ar.ini index 1ea759e77890025bf4aa123f016a6d334829a28b..0d234aab9388f59655369ca8c2867a5e9cacba5d 100644 --- a/languages/ar.ini +++ b/languages/ar.ini @@ -94,6 +94,7 @@ Back to Search Results = "العودة إلى نتائج البØØ«" Backtrace = "تتبع خلÙÙŠ" Bag = "سلة" Balance = "رصيد" +Barcode = "باركود" basic_search_keep_filters = "اØتÙظ بمرشØاتي الØالية" Be the first to leave a comment = "كن أول من يترك تعليقا" Be the first to tag this record = "كن أول من يضع وسما على هذه التسجيلة" @@ -173,6 +174,7 @@ catalog_login_desc = "أدخل اعتمادات Ùهرس مكتبتك." CD = "قرص مضغوط" Change Password = "تغيير كلمة المرور" channel_add_more = "أض٠المزيد من القنوات هكذا" +channel_browse = "استعراض المزيد من التسجيلات" channel_expand = "اكتش٠القنوات ذات الصلة" channel_explore = "اكتش٠القنوات" channel_search = "عرض المواد كنتائج بØØ«" @@ -182,6 +184,7 @@ Check Recall = "تØديد الاستدعاء" Checked Out = "معار" Checked Out Items = "مواد معارة" Checkedout = "معار" +Checkout Date = "تاريخ الإعارة" Chicago Citation = "استشهاد بنمط شيكاغو" child_record_count = "%%count%% تسجيلات" child_records = "المØتويات/الأجزاء" @@ -206,6 +209,7 @@ collection_disambiguation = "المجموعات المتعددة المتطاب collection_empty = "لا توجد مواد للعرض." collection_view_record = "عرض التسجيلة" Collections = "المجموعات" +comment_anonymous_user = "بدون تØديد هوية" comment_error_load = "خطأ: لا يمكن إعادة رسم قائمة التعليقات" comment_error_save = "خطأ: لا يمكن ØÙظ التعليق" Comments = "التعليقات" @@ -256,6 +260,11 @@ date_year_placeholder = "السنة" Debug Information = "معلومات التنقيØ" del_search = "Øذ٠مجموعة البØØ«" Delete = "ØØ°Ù" +delete_account_confirm = "هل أنت متأكد من أنك تريد ØØ°Ù Øسابك؟" +delete_account_description_html = "سيتم Øذ٠مدخلات البØØ« المØÙوظة والقوائم المÙضلة. لايزال بإمكانك إنشاء Øساب جديد لاØقًا إذا أردت." +delete_account_failure = "Ùشل Øذ٠الØساب." +delete_account_success_message = "تم ØØ°Ù Øسابك. جاري تسجيل الخروج..." +delete_account_title = "Øذ٠الØساب" delete_all = "Øذ٠الكل" delete_comment_failure = "لا يمكن Øذ٠التعليق." delete_comment_success = "تم Øذ٠التعليق." @@ -277,6 +286,8 @@ Document Type = "نوع الوثيقة" DOI = "DOI" doi_detected_html = "يبدو أن بØثك ÙŠØتوي على معر٠كيان رقمي. انقر هنا للتØقق من تواÙر المواد: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "مربع بØØ« الرسم" +draw_searchbox_end = "Ø£Ùلت الماوس لإنهاء الرسم." +draw_searchbox_start = "انقر واسØب لرسم مستطيل." Due = "مستØÙ‚" Due Date = "تاريخ الاستØقاق" DVD = "قرص Ùيديو رقمي" @@ -343,11 +354,10 @@ export_fail = "لم يتم تصدير موادك" export_invalid_format = "تنسيق التصدير المØدد غير مدعوم بواسطة هذه التسجيلة." export_missing = "بعض البيانات Ù…Ùقودة. لم يتم تصدير موادك." export_no_formats = "هذه التسجيلة لا تدعم التصدير." -export_redirect = "بدء التصدير إلى %%service%%" -export_refworks = "بدء التصدير إلى RefWorks" export_save = "ØÙظ الملÙ" export_selected = "تصدير المØدد" export_selected_favorites = "تصدير المÙضلة المØددة" +export_send = "بدء التصدير إلى %%service%%" export_success = "اكتمل التصدير" export_unsupported_format = "تنسيق تصدير غير مدعوم" external_auth_heading = "الوصول إلى المادة المرخصة" @@ -500,12 +510,14 @@ ill_request_processed = "معالج" ill_request_profile_html = "لمعلومات عن طلب الاستعارة بين المكتبات، يرجى إنشاء <a href="%%url%%">مل٠Ùهرسة مكتبتك</a>." ill_request_submit_text = "وضع طلب" Illustrated = "Ù…ÙˆØ¶Ø Ø¨Ø±Ø³ÙˆÙ…Ø§Øª توضيØية" +ils_action_unavailable = "الخاصية المطلوبة غير متاØØ© ببطاقة المكتبة النشطة." ils_connection_failed = "نظام إدارة مكتبتنا قيد الصيانة Øاليا." ils_offline_holdings_message = "معلومات إتاØØ© المواد والمقتنيات غير متاØØ© Øاليا. الرجاء قبول اعتذارنا عن أي إزعاج قد يسببه ذلك والاتصال بنا للمزيد من المساعدة:" ils_offline_home_message = "تÙاصيل Øسابك ومعلومات المادة المباشرة ستكون غير متاØØ© أثناء هذا الوقت. الرجاء قبول اعتذارنا عن أي إزعاج قد يسببه ذلك والاتصال بنا للمزيد من المساعدة:" ils_offline_login_message = "تÙاصيل Øسابك ستكون غير متاØØ© أثناء هذا الوقت. الرجاء قبول اعتذارنا عن أي إزعاج قد يسببه ذلك والاتصال بنا للمزيد من المساعدة :" ils_offline_status = "نظام إدارة مكتبتنا قيد الصيانة Øاليا." ils_offline_title = "النظام قيد الصيانة" +ils_transaction_history_disabled = "سجل الإعارة غير Ù…Ùعّل لبطاقة المكتبة النشطة." Import Record = "استيراد تسجيلة" in = "ÙÙŠ" In This Collection = "ÙÙŠ هذه المجموعة" @@ -580,6 +592,8 @@ list_access_denied = "ليس لديك صلاØية عرض هذه القائمة. list_edit_name_required = "اسم القائمة مطلوب." load_tag_error = "خطأ: لا يمكن تØميل الوسوم" Loading = "تØميل" +Loan History = "سجل الإعارة" +loan_history_empty = "لا يوجد لديك أي إعارات ÙÙŠ سجل الإعارة." Local Login = "تسجيل الدخول المØلي" local_login_desc = "قم بإدخال اسم المستخدم وكلمة المرور التي قمت بإنشائها لهذا الموقع." Located = "المكان" @@ -607,6 +621,7 @@ Message From Sender = "رسالة من المرسل" Metadata Prefix = "بادئة واصÙات البيانات" Microfilm = "مكروÙيلم" MLA Citation = "MLA استشهاد" +Mobile Number = "رقم الجوال" mobile_link = "يبدو أنك تستخدم جهاز Ù…Øمول Ø› هل تريد التØويل إلى عرض الهات٠المØمول؟" Monograph Title = "عنوان موØد" more = "المزيد" @@ -627,6 +642,7 @@ My Holds = "Øجوزاتي" My Profile = "صÙØتي الشخصية" Narrow Search = "تضييق البØØ«" navigate_back = "رجوع" +nearby_items = "مواد قريبة من "%%title%%"" Need Help? = "تØتاج مساعدة ØŸ" New Item Feed = "رد المادة الجديدة" New Item Search = "بØØ« مادة جديدة" @@ -635,6 +651,8 @@ New Items = "مواد جديدة" New Title = "عنوان جديد" new_password = "كلمة مرور جديدة" new_password_success = "تم تغيير كلمة مرورك بنجاØ" +new_user_welcome_subject = "Øسابك الجديد ÙÙŠ %%library%%" +new_user_welcome_text = "مرØبًا بك ÙÙŠ %%library%%. تم ÙØªØ Øساب جديد لـ %%firstname%% %%lastname%%. اسم المستخدم الخاص بك هو %%username%%. يرجى تعيين كلمة مرور ÙÙŠ هذه الصÙØØ©: %%url%%" Newspaper = "صØÙŠÙØ©" Next = "التالي" No citations are available for this record = "لا توجد استشهادات متاØØ© لهذه التسجيلة" @@ -732,6 +750,8 @@ past_days = "الـ%%range%% أيام السابقة" PDF Full Text = "PDF النص الكامل" peer_reviewed = "مراجعة النظير" peer_reviewed_limit = "تØديد إلى المقالات من دوريات تمت مراجعتها بالنظير" +permission_denied = "لقد طلبت صÙØØ© أو إجراء، لكنك لا تملك الصلاØية اللازمة." +permission_denied_title = "تم رÙض الصلاØية" Phone Number = "رقم الهاتÙ" Photo = "صورة" Physical Description = "وص٠مادي" @@ -782,6 +802,7 @@ Range slider = "تمرير النطاق" Read the full review online... = "قراءة المراجعة الكاملة أونلاين..." Recall This = "إستدعي هذه النسخة" recaptcha_not_passed = "CAPTCHA لم تمر" +recently_returned_channel_title = "تمت إعادته مؤخرًا" Record Citations = "استشهادات التسجيلة" Record Count = "عد التسجيلة" Record Type = "نوع التسجيلة" @@ -799,8 +820,17 @@ recovery_new_disabled = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بتغيير كلمة مرورك recovery_title = "استعادة كلمة المرور" recovery_too_soon = "تم إجراء طلبات استعادة أكثر من اللازم، يرجى إعادة المØاولة لاØقا" recovery_user_not_found = "لم نستطع العثور على Øسابك" +rectangle_center_message = "هذه النقطة المركزية للمستطيل المظلل" Refine Results = "ØªÙ†Ù‚ÙŠØ Ø§Ù„Ù†ØªØ§Ø¦Ø¬" Region = "المنطقة" +relais_available = "هذه المادة متاØØ© عبر الإعارة بين المكتبات. هل ترغب ÙÙŠ طلبها؟ " +relais_checking = "جاري التØقق من الإتاØØ©..." +relais_error_html = "لقد Øدثت مشكلة بخصوص هذا الطلب. انقر <a href="%%url%%" target='new'>هنا</a> لطلب هذه المادة باستخدام الموقع الالكتروني للإعارة بين المكتبات.</a>" +relais_request = "طلب إعارة بين المكتبات" +relais_requesting = "جاري الطلب..." +relais_search = "بØØ« الإعارة بين المكتبات" +relais_success_label = "تأكيد:" +relais_success_message = "تم إنشاء معر٠الطلب #%%id%%. سو٠تتلقى رسالة تأكيد عبر البريد الإلكتروني." Related Author = "مؤل٠ذو صلة" Related Items = "مواد ذات صلة" Related Subjects = "موضوعات ذات صلة" @@ -829,12 +859,14 @@ Requests = "طلبات" Reserves = "الØجز الأكاديمي" Reserves Search = "Ù„ØØ« الØجز الأكاديمي" Reserves Search Results = "نتائج البØØ« ÙÙŠ الØجز الأكاديمي" +result_checkbox_label = "تØديد النتيجة رقم %%number%%" result_count = "نتيجة %%count%%" Results = "نتائج" results = "نتائج" Results for = "نتائج لـ" Results per page = "النتائج لكل صÙØØ©" Resumption Token = "رمز الاستئناÙ" +Return Date = "تاريخ الإعادة" Review by = "مراجعة بواسطة" Reviews = "مراجعات" Save = "ØÙظ" @@ -906,8 +938,14 @@ sort_author = "المؤلÙ" sort_author_author = "أبجديا" sort_author_relevance = "الشعبية" sort_callnumber = "رقم الطلب" +sort_checkout_date_asc = "تاريخ الإعارة (الأقدم أولًا)" +sort_checkout_date_desc = "تاريخ الإعارة (الأØدث أولًا)" sort_count = "عدد النتائج" +sort_due_date_asc = "تاريخ الاستØقاق (الأقدم أولًا)" +sort_due_date_desc = "تاريخ الاستØقاق (الأØدث أولًا)" sort_relevance = "الصلة" +sort_return_date_asc = "تاريخ الإعادة (الأقدم أولًا)" +sort_return_date_desc = "تاريخ الإعادة (الأØدث أولًا)" sort_title = "العنوان" sort_year = "التاريخ تنازليا" sort_year asc = "التاريخ تصاعديا" @@ -1021,6 +1059,7 @@ total_tags = "إجمالي الوسوم" total_users = "إجمالي المستخدمين" Transliterated Title = "عنوان ترانسليتريتد" tree_search_limit_reached_html = "لقد أعاد بØثك نتائج أكثر من اللازم لعرضها ÙÙŠ الشجرة. عرض المواد الأولى <b>%%limit%%</b> Ùقط. للبØØ« الكامل قم بالنقر على <a id="fullSearchLink" href="%%url%%" target="_blank">هنا.</a>" +trending_items_channel_title = "مواد رائجة" unique_tags = "وسوم متÙردة" University Library = "مكتبة الجامعة" Unknown = "غير معروÙ" diff --git a/languages/bn.ini b/languages/bn.ini index 26b277dae66e906feb0f6b83df4b7c0ca58888a1..570949cdda6255f731ad965bdef16b6945b42aab 100644 --- a/languages/bn.ini +++ b/languages/bn.ini @@ -95,6 +95,7 @@ Back to Search Results = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ ফলাফলে ফি Backtrace = "বà§à¦¯à¦¾à¦•-টà§à¦°à§‡à¦¸" Bag = "সমà§à¦à¦¾à¦°" Balance = "à¦à¦¾à¦°à¦¸à¦¾à¦®à§à¦¯" +Barcode = "বারকোডেড অধিগà§à¦°à¦¹à¦£ সংখà§à¦¯à¦¾" basic_search_keep_filters = "বরà§à¦¤à¦®à¦¾à¦¨ ফিলà§à¦Ÿà¦¾à¦° বজায় রাখà§à¦¨ " Be the first to leave a comment = "পà§à¦°à¦¥à¦®à¦œà¦¨ হিসাবে মনà§à¦¤à¦¬à§à¦¯ করà§à¦¨" Be the first to tag this record = "পà§à¦°à¦¥à¦®à¦œà¦¨ হিসাবে টà§à¦¯à¦¾à¦— করà§à¦¨" @@ -184,6 +185,7 @@ Check Recall = "গà§à¦°à¦¨à§à¦¥ তলব করà§à¦¨" Checked Out = "গà§à¦°à¦¨à§à¦¥ পà§à¦°à¦¦à¦¾à¦¨ হয়েছে" Checked Out Items = "উপাদানগà§à¦²à¦¿ পà§à¦°à¦¦à¦¾à¦¨ হয়েছে" Checkedout = "গà§à¦°à¦¨à§à¦¥ পà§à¦°à¦¦à¦¾à¦¨ হয়েছে" +Checkout Date = "গà§à¦°à¦¨à§à¦¥ পà§à¦°à¦¦à¦¾à¦¨à§‡à¦° তারিখ" Chicago Citation = "শিকাগো সà§à¦Ÿà¦¾à¦‡à¦²à§‡ সাইটেশন" child_record_count = "%%count%% records" child_records = "সূচিপতà§à¦° / টà§à¦•à¦°à§‹" @@ -208,6 +210,7 @@ collection_disambiguation = "à¦à¦•à¦¾à¦§à¦¿à¦• সমনà§à¦¬à¦¯à¦¼à§‡ সং collection_empty = "দেখনোর মত কোন উপাদান নেই।" collection_view_record = "নথি দেখà§à¦¨" Collections = "সংগà§à¦°à¦¹à¦—à§à¦²à¦¿" +comment_anonymous_user = "অনামা বà§à¦¯à¦¾à¦¬à¦¹à§à¦¯à¦¾à¦°à¦•à¦¾à¦°à§€" comment_error_load = "তà§à¦°à§à¦Ÿà¦¿: মনà§à¦¤à¦¬à§à¦¯ তালিকাতে আবার লেখা যাবে না" comment_error_save = "তà§à¦°à§à¦Ÿà¦¿: মনà§à¦¤à¦¬à§à¦¯ সংরকà§à¦·à¦£ করা যাবেনা" Comments = "মনà§à¦¤à¦¬à§à¦¯ করà§à¦¨" @@ -258,6 +261,11 @@ date_year_placeholder = "বছর" Debug Information = "তà§à¦°à§à¦Ÿà¦¿ মà§à¦•à§à¦¤ তথà§à¦¯" del_search = "অনà§à¦¸à¦¨à§à¦§à§€à¦¤ দল অপসারণ করà§à¦¨" Delete = "মà§à¦›à§à¦¨" +delete_account_confirm = "আপনি কি নিশà§à¦šà¦¿à¦¤ যে অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ মà§à¦›à§‡ ফেলবেন?" +delete_account_description_html = "সংরকà§à¦·à¦¿à¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ à¦à¦¬à¦‚ পছনà§à¦¦ তালিকাগà§à¦²à¦¿ মà§à¦›à§‡ ফেলা হবে। পà§à¦°à§Ÿà§‹à¦œà¦¨ হলে আবার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ খà§à¦²à¦¤à§‡ পারবেন।" +delete_account_failure = "অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ মোছা গেলো না।" +delete_account_success_message = "আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ মোছা হলো। পà§à¦°à¦¸à§à¦¥à¦¾à¦¨ পরà§à¦¬ শà§à¦°à§ হচà§à¦›à§‡ ...." +delete_account_title = "মà§à¦›à§‡ যাওয়া অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ" delete_all = "সমসà§à¦¤ কিছৠমà§à¦›à§à¦¨" delete_comment_failure = "মনà§à¦¤à¦¬à§à¦¯ মোছা যাবে না।" delete_comment_success = "মনà§à¦¤à¦¬à§à¦¯ মোছা হয়েছে।" @@ -279,6 +287,8 @@ Document Type = "পà§à¦°à¦²à§‡à¦–ের ধরণ" DOI = "ডিওআই" doi_detected_html = "পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤ পà§à¦°à¦²à§‡à¦–ে DOI রয়েছে। পà§à¦°à¦²à§‡à¦– উপলবà§à¦§ করার জনà§à¦¯ à¦à¦–ানে কà§à¦²à¦¿à¦• করà§à¦¨: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ কà§à¦·à§‡à¦¤à§à¦° সংযোজন করà§à¦¨" +draw_searchbox_end = "ডà§à¦°à§Ÿà¦¿à¦‚ শেষ করার জনà§à¦¯ মাউস রিলিজ করà§à¦¨à¥¤" +draw_searchbox_start = "আয়তকà§à¦·à§‡à¦¤à§à¦° অঙà§à¦•à¦¨à§‡à¦° জনà§à¦¯ মাউস কà§à¦²à¦¿à¦• à¦à¦¬à¦‚ ডà§à¦°à§à¦¯à¦¾à¦— করà§à¦¨à¥¤" Due = "বাকি" Due Date = "তারিখ বাকি আছে" DVD = "ডিà¦à¦¿à¦¡à¦¿" @@ -345,11 +355,10 @@ export_fail = "আপনার উপাদানগà§à¦²à¦¿ রপà§à¦¤à¦¾à¦¨ export_invalid_format = "নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ রপà§à¦¤à¦¾à¦¨à¦¿ বিনà§à¦¯à¦¾à¦¸à¦Ÿà¦¿ à¦à¦‡ রেকরà§à¦¡à¦Ÿà¦¿à¦•à§‡ সমরà§à¦¥à¦¨ করে না।" export_missing = "কিছৠডাটা পাওয়া যাচà§à¦›à§‡ না। আপনার উপাদানগà§à¦²à¦¿ à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ করাযাবে না।" export_no_formats = "à¦à¦‡ রেকরà§à¦¡à¦Ÿà¦¿ à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ করা যাবে না।" -export_redirect = "Start export to %%service%%" -export_refworks = "উলà§à¦²à¦¿à¦–িত কাজগà§à¦²à¦¿ করার জনà§à¦¯ à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ শà§à¦°à§ করà§à¦¨" export_save = "ফাইল সংরকà§à¦·à¦£ করà§à¦¨" export_selected = "নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ ফাইলটি à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨" export_selected_favorites = "নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ পছনà§à¦¦ তালিকাগà§à¦²à¦¿ à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ করà§à¦¨" +export_send = "Start export to %%service%%" export_success = "à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ সমà§à¦ªà§‚রà§à¦¨ হয়েছে" export_unsupported_format = "অসমরà§à¦¥à¦¿à¦¤ à¦à¦•à§à¦¸à¦ªà§‹à¦°à§à¦Ÿ বিনà§à¦¯à¦¾à¦¸" external_auth_heading = "সাবসà§à¦•à§à¦°à¦¾à¦‡à¦¬ করা গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার সমà§à¦ªà¦¦à§‡à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨" @@ -502,12 +511,14 @@ ill_request_processed = "সমà§à¦ªà§‚রà§à¦¨à¦Ÿà¦¿ পà§à¦°à¦•à§à¦°à¦¿à§Ÿ ill_request_profile_html = "আনà§à¦¤à¦ƒà¦—à§à¦°à¦¨à§à¦¥à¦¾à¦—ার ঋণ অনà§à¦°à§‹à¦§à§‡à¦° জনà§à¦¯ তথà§à¦¯ , অনà§à¦—à§à¦°à¦¹ করে ঠিক করà§à¦¨ <a href="%%url%%">Library Catalog Profile</a>." ill_request_submit_text = "অনà§à¦°à§‹à¦§ করà§à¦¨" Illustrated = "ছবিযà§à¦•à§à¦¤" +ils_action_unavailable = "অসমরà§à¦¥à¦¿à¦¤ অনà§à¦°à§‹à¦§à¥¤ আপনার সকà§à¦°à¦¿à§Ÿ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার সদসà§à¦¯à¦ªà¦¤à§à¦° à¦à¦‡ সà§à¦¬à¦¿à¦§à¦¾ পà§à¦°à¦¦à¦¾à¦¨ করে না।" ils_connection_failed = "অটোমেশন সফটওয়à§à¦¯à¦¾à¦°à§‡à¦° সঙà§à¦—ে সমà§à¦ªà¦°à§à¦• ছিনà§à¦¨ হয়েছে। আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ সমà§à¦ªà¦°à§à¦•à¦¿à¦¤ তথà§à¦¯ উপলবà§à¦§ নয়। সমাধানের জনà§à¦¯ আপনার গà§à¦°à¦¨à§à¦¥à¦¾à¦—ারে যোগাযোগ করà§à¦¨à¥¤" ils_offline_holdings_message = "হোলà§à¦¡à¦¿à¦‚স à¦à¦¬à¦‚ উপাদানটির পà§à¦°à¦¾à¦ªà§à¦¯à¦¤à¦¾ বরà§à¦¤à¦®à¦¾à¦¨à§‡ অনà§à¦ªà¦²à¦¬à§à¦§à¥¤ আপনার সবরকম অসà§à¦¬à¦¿à¦§à¦¾à¦° জনà§à¦¯ আমরা দà§à¦ƒà¦–িত à¦à¦¬à¦‚ অনà§à¦¯à¦¾à¦¨à§à¦¯ সহায়তার জনà§à¦¯ আমাদের সাথে যোগাযোগ করà§à¦¨à¦ƒ" ils_offline_home_message = "আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡à¦° বিশদ বিবরণ à¦à¦¬à¦‚ জীবনà§à¦¤ উপাদানটির তথà§à¦¯ à¦à¦‡ সময়ে অনà§à¦ªà¦²à¦¬à§à¦§ হতে পারে। আপনার সবরকম অসà§à¦¬à¦¿à¦§à¦¾à¦° জনà§à¦¯ আমরা দà§à¦ƒà¦–িত à¦à¦¬à¦‚ অনà§à¦¯à¦¾à¦¨à§à¦¯ সহায়তার জনà§à¦¯ আমাদের সাথে যোগাযোগ করà§à¦¨à¦ƒ" ils_offline_login_message = "আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡à¦° বিশদ বিবরণ à¦à¦‡ সময়ে অনà§à¦ªà¦²à¦¬à§à¦§ হতে পারে। আপনার সবরকম অসà§à¦¬à¦¿à¦§à¦¾à¦° জনà§à¦¯ আমরা দà§à¦ƒà¦–িত à¦à¦¬à¦‚ অনà§à¦¯à¦¾à¦¨à§à¦¯ সহায়তার জনà§à¦¯ আমাদের সাথে যোগাযোগ করà§à¦¨à¦ƒ" ils_offline_status = "বরà§à¦¤à¦®à¦¾à¦¨à§‡ আমাদের গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦ªà¦¨à¦¾ পদà§à¦§à¦¤à¦¿à¦Ÿà¦¿ রকà§à¦·à¦£à¦¾à¦¬à§‡à¦•à§à¦·à¦£à§‡à¦° অধীনে।" ils_offline_title = "পদà§à¦§à¦¤à¦¿à¦Ÿà¦¿ রকà§à¦·à¦£à¦¾à¦¬à§‡à¦•à§à¦·à¦£à§‡à¦° অধীনে" +ils_transaction_history_disabled = "আপনার সকà§à¦°à¦¿à§Ÿ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার সদসà§à¦¯à¦ªà¦¤à§à¦° সঞà§à¦šà¦¾à¦°à¦¨ ইতিহাসের সà§à¦¬à¦¿à¦§à¦¾ পà§à¦°à¦¦à¦¾à¦¨ করে না।" Import Record = "সংগà§à¦°à¦¹à§€à¦¤ তথà§à¦¯" in = "মধà§à¦¯à§‡" In This Collection = "à¦à¦‡ সংগà§à¦°à¦¹à§‡à¦° মধà§à¦¯à§‡" @@ -582,6 +593,8 @@ list_access_denied = "আপনার à¦à¦‡ তালিকা দেখার list_edit_name_required = "শেষ নামটা পà§à¦°à§Ÿà§‹à¦œà¦¨" load_tag_error = "তà§à¦°à§à¦Ÿà¦¿à¦ƒ টà§à¦¯à¦¾à¦— লোড করা যাচà§à¦›à§‡ না" Loading = "লোডিং" +Loan History = "সঞà§à¦šà¦¾à¦°à¦¨ ইতিহাস" +loan_history_empty = "সঞà§à¦šà¦¾à¦°à¦¨ ইতিহাস থেকে আপনার নামে কোনো ঋণ পাওয়া যায়নি।" Local Login = "সà§à¦¥à¦¾à¦¨à¦¿à§Ÿ লগইন" local_login_desc = "আপনার তৈরী করা সাইটির জনà§à¦¯ বà§à¦¯à¦¬à¦¹à§ƒà¦¤ নাম à¦à¦¬à¦‚ পাসওয়ারà§à¦¡ পà§à¦°à§Ÿà§‹à¦— করà§à¦¨à¥¤" Located = "অবসà§à¦¥à¦¿à¦¤" @@ -609,6 +622,7 @@ Message From Sender = "বারà§à¦¤à¦¾à¦Ÿà¦¿ পà§à¦°à§‡à¦°à¦•à§‡à¦° থে Metadata Prefix = "মেটাডাটা পà§à¦°à¦¿à¦«à¦¿à¦•à§à¦¸" Microfilm = "মাইকà§à¦°à§‹à¦«à¦¿à¦²à§à¦®" MLA Citation = "à¦à¦®à¦à¦²à¦ সাইটেশন" +Mobile Number = "মোবাইল নমà§à¦¬à¦°" mobile_link = "আপনি à¦à¦•à¦Ÿà¦¿ মোবাইল ডিà¦à¦¾à¦‡à¦¸à§‡à¦° মাধà§à¦¯à¦®à§‡ দেখতে পারেন, আপনি কি দেখবেন?" Monograph Title = "মনোগà§à¦°à¦¾à¦« আখà§à¦¯à¦¾" more = "আরও" @@ -638,6 +652,8 @@ New Items = "নতà§à¦¨ উপাদানগà§à¦²à¦¿" New Title = "নতà§à¦¨ আখà§à¦¯à¦¾" new_password = "নতà§à¦¨ পাসওইয়ারà§à¦¡" new_password_success = "আপনার পাসওয়ারà§à¦¡à¦Ÿà¦¿ সফলà¦à¦¾à¦¬à§‡ পরিবরà§à¦¤à¦¨ করা হয়েছে" +new_user_welcome_subject = "আপনার নতà§à¦¨ সদসà§à¦¯à¦ªà¦¦ (অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ) à¦à¦–ানে পাবেন %%library%%" +new_user_welcome_text = "সà§à¦¬à¦¾à¦—ত জানাই %%library%%. à¦à¦•à¦Ÿà¦¿ নতà§à¦¨ অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ %%firstname%% %%lastname%% à¦à¦‡ নামে খোলা হলো। আপনার বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ পরিচয় হলো - %%username%%. অনà§à¦—à§à¦°à¦¹ করে পাসওয়ারà§à¦¡à¦Ÿà¦¿ ঠিক করà§à¦¨ : %%url%%" Newspaper = "সংবাদপতà§à¦°" Next = "পরবরà§à¦¤à§€" No citations are available for this record = "à¦à¦‡ রেকরà§à¦¡à§‡à¦° জনà§à¦¯ কোন সাইটেশন নেই" @@ -787,6 +803,7 @@ Range slider = "বà§à¦¯à¦ªà§à¦¤à¦¿ সà§à¦²à¦¾à¦‡à¦¡à¦¾à¦°" Read the full review online... = "পূরà§à¦£ পরà§à¦¯à¦¾à¦²à§‹à¦šà¦¨à¦¾à¦° জনà§à¦¯ অনলাইনে পড়à§à¦¨ ..." Recall This = "মনে করà§à¦¨" recaptcha_not_passed = "CAPTCHA অনà§à¦¤à§à¦¤à§€à¦°à§à¦£" +recently_returned_channel_title = "সদà§à¦¯ ফেরত দেয়া পà§à¦°à¦²à§‡à¦–গà§à¦²à¦¿ " Record Citations = "সাইটেশন নথি" Record Count = "নথির সংখà§à¦¯à¦¾" Record Type = "নথির পà§à¦°à¦•à¦¾à¦°" @@ -804,8 +821,17 @@ recovery_new_disabled = "à¦à¦‡ সময়ে আপনার পাসওয recovery_title = "পাসওয়ারà§à¦¡ পà§à¦¨à¦°à§à¦¦à§à¦§à¦¾à¦° করà§à¦¨" recovery_too_soon = "অনেকগà§à¦²à¦¿ পà§à¦¨à¦°à§à¦¦à§à¦§à¦¾à¦° অনà§à¦°à§‹à¦§ করা হয়েছে, দয়া করে পরে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨" recovery_user_not_found = "আমরা আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ খà§à¦à¦œà§‡ পাইনি" +rectangle_center_message = "আলোকিত আয়তকà§à¦·à§‡à¦¤à§à¦°à§‡à¦° মধà§à¦¯à¦¬à¦°à§à¦¤à§€ সà§à¦¥à¦¾à¦¨" Refine Results = "ফলাফল পরিমারà§à¦œà¦¨ করà§à¦¨" Region = "à¦à¦²à¦¾à¦•à¦¾ à¦à¦¿à¦¤à§à¦¤à¦¿à¦•" +relais_available = "à¦à¦‡ পà§à¦°à¦²à§‡à¦–টি আনà§à¦¤à¦ƒà¦—à§à¦°à¦¨à§à¦¥à¦¾à¦—ার ঋণের মাধà§à¦¯à¦®à§‡ পাওয়া যেতে পারে। আপনি কি অনà§à¦°à§‹à¦§ জানাতে চান?" +relais_checking = "পà§à¦°à¦²à§‡à¦–টি উপলবà§à¦§ কি না খোà¦à¦œ করা হচà§à¦›à§‡ ..." +relais_error_html = "à¦à¦‡ অনà§à¦°à§‹à¦§à¦Ÿà¦¿ পালনে অসà§à¦¬à¦¿à¦§à¦¾ রয়েছে। <a href="%%url%%" target='new'>à¦à¦–ানে</a> কà§à¦²à¦¿à¦• করে আনà§à¦¤à¦ƒà¦—à§à¦°à¦¨à§à¦¥à¦¾à¦—ার ঋণের অনà§à¦°à§‹à¦§ জানান।" +relais_request = "আনà§à¦¤à¦ƒà¦—à§à¦°à¦¨à§à¦¥à¦¾à¦—ার ঋণের অনà§à¦°à§‹à¦§t" +relais_requesting = "অনà§à¦°à§‹à¦§ জানানো হচà§à¦›à§‡..." +relais_search = "আনà§à¦¤à¦ƒà¦—à§à¦°à¦¨à§à¦¥à¦¾à¦—ার ঋণ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" +relais_success_label = "সà§à¦¬à§€à¦•à§ƒà¦¤à¦¿:" +relais_success_message = "অনà§à¦°à§‹à¦§ কà§à¦°à¦® #%%id%% তৈরি করা হলো। সà§à¦¬à§€à¦•à§ƒà¦¤à¦¿ ই-মেইল পাঠানো হচà§à¦›à§‡à¥¤" Related Author = "সংশà§à¦²à¦¿à¦·à§à¦Ÿ লেখক" Related Items = "সমà§à¦ªà¦°à§à¦•à¦¿à¦¤ উপাদান" Related Subjects = "সমà§à¦ªà¦°à§à¦•à¦¿à¦¤ বিষয়" @@ -834,12 +860,14 @@ Requests = "অনà§à¦°à§‹à¦§" Reserves = "অরকà§à¦·à¦¿à¦¤" Reserves Search = "আরকà§à¦·à¦¿à¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" Reserves Search Results = "আরকà§à¦·à¦¿à¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ ফলাফল" +result_checkbox_label = "পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤ পà§à¦°à¦²à§‡à¦– সংখà§à¦¯à¦¾ %%number%%" result_count = "%%count%% ফলাফল" Results = "ফলাফল" results = "ফলাফল" Results for = "ফলাফলের জনà§à¦¯" Results per page = "পৃষà§à¦ া পà§à¦°à¦¤à¦¿ ফলাফল" Resumption Token = "টোকেন পà§à¦¨à¦°à¦¾à¦°à¦®à§à¦" +Return Date = "ফেরত দেবার তারিখ" Review by = "পরà§à¦¯à¦¾à¦²à§‹à¦šà¦¨à¦¾ অনà§à¦¯à¦¾à§Ÿà§€" Reviews = "পরà§à¦¯à¦¾à¦²à§‹à¦šà¦¨à¦¾" Save = "সংরকà§à¦·à¦£ করà§à¦¨" @@ -911,8 +939,14 @@ sort_author = "লেখক" sort_author_author = "বরà§à¦£à¦¾à¦¨à§à¦•à§à¦°à¦®à¦¿à¦•" sort_author_relevance = "জনপà§à¦°à¦¿à¦¯à¦¼à¦¤à¦¾" sort_callnumber = "ডাকসংখà§à¦¯à¦¾" +sort_checkout_date_asc = "পà§à¦°à¦²à§‡à¦– পà§à¦°à¦¦à¦¾à¦¨à§‡à¦° তারিখ (পà§à¦°à¦¾à¦¤à¦¨ তারিখ আগে)" +sort_checkout_date_desc = "পà§à¦°à¦²à§‡à¦– পà§à¦°à¦¦à¦¾à¦¨à§‡à¦° তারিখ (নতà§à¦¨ তারিখ আগে)" sort_count = "ফলাফল গণনা" +sort_due_date_asc = "পà§à¦°à¦²à§‡à¦– পà§à¦°à¦¤à§à¦¯à¦¾à¦¬à¦°à§à¦¤à¦¨à§‡à¦° তারিখ (পà§à¦°à¦¾à¦¤à¦¨ তারিখ আগে)" +sort_due_date_desc = "পà§à¦°à¦²à§‡à¦– পà§à¦°à¦¤à§à¦¯à¦¾à¦¬à¦°à§à¦¤à¦¨à§‡à¦° তারিখ (নতà§à¦¨ তারিখ আগে)" sort_relevance = "পà§à¦°à¦¾à¦¸à¦™à§à¦—িকতা" +sort_return_date_asc = "পà§à¦°à¦²à§‡à¦– ফেরত দেওয়ার তারিখ (পà§à¦°à¦¾à¦¤à¦¨ তারিখ আগে)" +sort_return_date_desc = "পà§à¦°à¦²à§‡à¦– ফেরত দেওয়ার তারিখ (নতà§à¦¨ তারিখ আগে)" sort_title = "আখà§à¦¯à¦¾" sort_year = "তারিখ অধোগামী অনà§à¦¯à¦¾à§Ÿà§€ সাজান" sort_year asc = "তারিখ ঊরà§à¦§à§à¦¬à¦—ামী অনà§à¦¯à¦¾à§Ÿà§€ সাজান" @@ -1026,6 +1060,7 @@ total_tags = "মোট টà§à¦¯à¦¾à¦— " total_users = "মোট বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€" Transliterated Title = "বরà§à¦£à¦¾à¦¨à§à¦¤à¦°à¦¿à¦¤ আখà§à¦¯à¦¾" tree_search_limit_reached_html = "আপনার অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° ফলাফল অনেক বেশি, তাই তা রেখাচিতà§à¦°à§‡à¦° আকারে পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤à¥¤ Showing only the first <b>%%limit%%</b> items. For a full search click <a id="fullSearchLink" href="%%url%%" target="_blank">here.</a>" +trending_items_channel_title = "পà§à¦°à¦²à§‡à¦– চাহিদা ধারা" unique_tags = "সà§à¦¬à¦¤à¦¨à§à¦¤à§à¦° টà§à¦¯à¦¾à¦— " University Library = "বিশà§à¦¬à¦¬à¦¿à¦¦à§à¦¯à¦¾à¦²à§Ÿ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার" Unknown = "অজà§à¦žà¦¾à¦¤" diff --git a/languages/ca.ini b/languages/ca.ini index bbb232d79cb70580cbdb1cef27ebd02ed08396d1..a838eda0f94e32bb74a373d469c699d46ad9f11c 100644 --- a/languages/ca.ini +++ b/languages/ca.ini @@ -105,6 +105,7 @@ Back to Search Results = "Tornar als resultats de la cerca" Backtrace = "Retrocedir" Bag = "Bossa" Balance = "Balanç" +Barcode = "Codi de barres" basic_search_keep_filters = "Conservar els meus filtres actuals" Be the first to leave a comment = "Sigues el primer a deixar un comentari" Be the first to tag this record = "Sigues el primer a etiquetar aquest registre" @@ -184,14 +185,17 @@ catalog_login_desc = "Enter your library catalog credentials." CD = "CD" Change Password = "Canviar contrasenya" channel_add_more = "Afegeix més canals com aquest" +channel_browse = "Veure més registres" channel_expand = "Explora canals relacionats" channel_explore = "Explora canals" channel_search = "Mostrar elements com a resultats de cerca" +channel_searchbox_label = "Cercar a més canals:" Check Hold = "Comprovar fons" Check Recall = "Comprovar recordatoris" Checked Out = "Retirat" Checked Out Items = "Exemplars retirats" Checkedout = "Retirat" +Checkout Date = "Data del préstec" Chicago Citation = "Chicago Style Citation" child_record_count = "%%count%% registres" child_records = "Continguts/components" @@ -216,6 +220,7 @@ collection_disambiguation = "S'han trobat diferentes col·leccions coincidents" collection_empty = "Sense exemplars que mostrar." collection_view_record = "Veure Registre" Collections = "Colleccions" +comment_anonymous_user = "Anonim" comment_error_load = "Error: No s’ha pogut recuperar la llista de comentaris" comment_error_save = "Error: No s’ha pogut guardar el comentari" Comments = "Comentaris" @@ -266,6 +271,11 @@ date_year_placeholder = "Y" Debug Information = "Informació de depuració" del_search = "Eliminar cerca de grup" Delete = "Eliminar" +delete_account_confirm = "Estas segur que vols eliminar aquest compte?" +delete_account_description_html = "Cerques guardades i llistes seran eliminades. Encara pots crear un nou compte més endavant si vol." +delete_account_failure = "No s'ha pogut eliminar el compte." +delete_account_success_message = "S'ha eliminat el seu compte. Sortir..." +delete_account_title = "Elimina el compte" delete_all = "Esborrar tot" delete_comment_failure = "No es pot borrar el comentari." delete_comment_success = "Comentari borrat." @@ -287,6 +297,8 @@ Document Type = "Tipus de document" DOI = "DOI" doi_detected_html = "La vostra cerca sembla que conté un DOI. Feu clic aquà per consultar la disponibilitat del recurs: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Dibuixar quadre de cerca" +draw_searchbox_end = "Solta el ratolà per deixar de dibuixar." +draw_searchbox_start = "Clica i solta per dibuixar un rectangle." Due = "Venciment" Due Date = "Data de venciment" DVD = "DVD" @@ -299,6 +311,7 @@ edit_list_fail = "Disculpeu, no podeu editar aquesta llista" edit_list_success = "LLista actualitzada correctament." Edition = "Edició" eds_expander_fulltext = "Incloure el text complet a la cerca" +eds_expander_relatedsubjects = "Veure matèries similars" eds_expander_thesaurus = "Afegir conceptes relacionats" eds_limiter_FC = "Només el catà leg" eds_limiter_FC1 = "Només el repositori institucional" @@ -352,11 +365,10 @@ export_fail = "Els vostres Ãtems no s’han exportat" export_invalid_format = "El format d' exportació seleccionat no està suportat per aquest registre." export_missing = "S’han perdut algunes dades. Els vostres Ãtems no s’han exportat." export_no_formats = "Aquest registre no permet exportacions." -export_redirect = "Començar a exportar a %%service%%" -export_refworks = "Començar exportació a RefWorks" export_save = "Guardar fitxer" export_selected = "Exportar seleccionats" export_selected_favorites = "Exportar favorits seleccionats" +export_send = "Començar a exportar a %%service%%" export_success = "Exportació completada" export_unsupported_format = "Format d’exportació no suportat" external_auth_heading = "Dibuixar quadre de cerca" @@ -509,12 +521,14 @@ ill_request_processed = "Processat" ill_request_profile_html = "Per informació sobre la petició de prèstec, si us plau configura la seva <a href="%%url%%">configuració de catà leg de biblioteca</a>." ill_request_submit_text = "Demanar" Illustrated = "Il·lustrat" +ils_action_unavailable = "La funcionalitat solicitada no està activada." ils_connection_failed = "El catà leg està fora de servei temporalment per tasques de manteniment" ils_offline_holdings_message = "La informació de disponibilitat dels exemplars no està disponible en aquests moments, sentim les inconveniències:" ils_offline_home_message = "El seu compte i la informació dels exemplars no estarà accessible temporalment, preguem que ens disculpi les molèsties, en breu estarem actius:" ils_offline_login_message = "El seu compte i la informació dels exemplars no estarà accessible temporalment, preguem que ens disculpi les molèsties, en breu estarem actius:" ils_offline_status = "El catà leg està fora de servei temporalment per tasques de manteniment" ils_offline_title = "Sistema fora de servei per tasques de manteniment" +ils_transaction_history_disabled = "Historial de préstecs no está activada per aquest compte." Import Record = "Importar registre" in = "a" In This Collection = "En aquesta col·lecció" @@ -589,6 +603,8 @@ list_access_denied = "No teniu permisos per visualitzar aquesta llista." list_edit_name_required = "Cal nom de llista." load_tag_error = "Error: no es poden carregar etiquetes" Loading = "Carregant" +Loan History = "Historial de préstecs" +loan_history_empty = "No hi ha cap préstec al seu historial." Local Login = "Autentificació local" local_login_desc = "Introdueixi el usuari i contrasenya que ha creat per accedir a aquest servei." Located = "Localitzat" @@ -616,6 +632,7 @@ Message From Sender = "Missatge del remitent" Metadata Prefix = "Metadata Prefix" Microfilm = "Microfilm" MLA Citation = "Cita MLA" +Mobile Number = "Número mòbil" mobile_link = "Sembla un dispositiu mòbil, vols accedir al OPAC mòbil?" Monograph Title = "TÃtol de monografia" more = "més" @@ -636,6 +653,7 @@ My Holds = "Les meves reserves" My Profile = "El meu perfil" Narrow Search = "Refinar cerca" navigate_back = "Back" +nearby_items = "Exemplars a prop "%%title%%"" Need Help? = "Necessites ajuda?" New Item Feed = "Alerta nous Ãtems" New Item Search = "Cerca nous Ãtems" @@ -644,6 +662,8 @@ New Items = "Nous Ãtems" New Title = "Nou tÃtol" new_password = "Nova contrasenya" new_password_success = "Ha canviat la seva contrasenya" +new_user_welcome_subject = "El seu nou compte a %%library%%" +new_user_welcome_text = "Benvingut a %%library%%. Un nou compte s'ha creat %%firstname%% %%lastname%%. El seu usuari %%username%%. Si us plau introdueixi la contrasenya: %%url%%" Newspaper = "Diari" Next = "Següent" No citations are available for this record = "No hi ha citacions disponibles per aquest registre" @@ -741,6 +761,8 @@ past_days = "Anterior %%range%% DÃas" PDF Full Text = "PDF a text complet" peer_reviewed = "Peer Reviewed" peer_reviewed_limit = "Limitar a articles de revistes revisades per experts" +permission_denied = "Ha demanat una acció, pero no té permisos suficients." +permission_denied_title = "No autoritzat" Phone Number = "Número de telèfon" Photo = "Foto" Physical Description = "Descripció fÃsica" @@ -791,6 +813,7 @@ Range slider = "Rang mòbil" Read the full review online... = "Llegiu la resenya completa en lÃnia..." Recall This = "Reclamar aquest" recaptcha_not_passed = "CAPTCHA invà lid" +recently_returned_channel_title = "Retornat fa poc" Record Citations = "Cites del registre" Record Count = "Número de registres" Record Type = "Tipus de registre" @@ -808,8 +831,17 @@ recovery_new_disabled = "No es pot recuperar la contrasenya en aquests moments." recovery_title = "Recuperació de la contrasenya" recovery_too_soon = "Massa peticions de recuperació, si us plau torna a internar-ho en 5 minuts" recovery_user_not_found = "Usuari no registrat" +rectangle_center_message = "This is the center point for the highlighted rectangle" Refine Results = "Refinar resultats" Region = "Regió" +relais_available = "Aquest registre està disponible amb prèstec interbibliotecari. Voldira demanar-ho?" +relais_checking = "Comprovant disponivilitat..." +relais_error_html = "Hi ha hagut un problema amb la seva sol·licitut. Clickeu <a href="%%url%%" target='new'>here</a> per demanar-lo via préstec interbibliotecari.</a>" +relais_request = "Solicitud de préstec interbibliotecari" +relais_requesting = "Demanant..." +relais_search = "Cerca de préstec interbibliotecari" +relais_success_label = "Confirmació:" +relais_success_message = "Sol·licitut id #%%id%% was created. Rebrà un email de confirmació." Related Author = "Autor relacionat" Related Items = "Ãtems relacionats" Related Subjects = "Matèries relacionades" @@ -838,12 +870,14 @@ Requests = "Peticions" Reserves = "Reserves" Reserves Search = "Cerca de reserves" Reserves Search Results = "Resultats de la cerca de reserves" +result_checkbox_label = "Selecciona el número de resultat %%number%%" result_count = "%%count%% results" Results = "Resultats" results = "resultats" Results for = "Resultats per" Results per page = "Resultats per pà gina" Resumption Token = "Resumption Token" +Return Date = "Data de retorn" Review by = "Revisat per" Reviews = "Resenyes" Save = "Guardar" @@ -915,8 +949,14 @@ sort_author = "Autor" sort_author_author = "Alfabèticament" sort_author_relevance = "Popularitat" sort_callnumber = "Topogrà fic" +sort_checkout_date_asc = "Data de préstec (més antic primer)" +sort_checkout_date_desc = "Data de préstec (més recent primer)" sort_count = "Total resultats" +sort_due_date_asc = "Data de venciment (més tard primer)" +sort_due_date_desc = "Data de venciment (més propera primer)" sort_relevance = "Importà ncia" +sort_return_date_asc = "Data de retorn (més antiga primer)" +sort_return_date_desc = "Data de retorn (més propera primer)" sort_title = "TÃtol" sort_year = "Data Descendent" sort_year asc = "Data Ascendent" @@ -1030,6 +1070,7 @@ total_tags = "Etiquetes totals" total_users = "Usuaris totals" Transliterated Title = "TÃtol transliterat" tree_search_limit_reached_html = "La seva cerca ha retornat massa registres per veure'ls a l'arbre de col·leccions. Veure els primers <b>%%limit%%</b> exemplars. Per una cerca completa cliqueu <a id="fullSearchLink" href="%%url%%" target="_blank">aquÃ.</a>" +trending_items_channel_title = "més populars" unique_tags = "Etiquetes úniques" University Library = "Biblioteca università ria" Unknown = "Desconegut" diff --git a/languages/cs.ini b/languages/cs.ini index 2c71b31ba30c98df805b9ef62f22c92ae381a809..e9cf6212802939822a279b986ded2b16fa1072db 100644 --- a/languages/cs.ini +++ b/languages/cs.ini @@ -1,4 +1,4 @@ -; Translation: opensource.knihovna.cz, Michal Denár, Josef Moravec, last updated 18.12.2012 +; Translation: opensource.knihovna.cz, Michal Denár, Josef Moravec, last updated 5. 6. 2018 Abstract = "Abstrakt" Access = "PÅ™Ãstup" Access URL = "PÅ™Ãstupová URL adresa" @@ -92,6 +92,7 @@ Back to Search Results = "ZpÄ›t k výsledkům vyhledávánÃ" Backtrace = "Podrobnosti" Bag = "KoÅ¡Ãk" Balance = "Dluh/Kredit" +Barcode = "Čárový kód" basic_search_keep_filters = "Zachovat souÄasné nastavenà filtrů" Be the first to leave a comment = "BuÄte prvnÃ, kdo okomentuje tento záznam" Be the first to tag this record = "BuÄte prvnÃ, kdo otaguje tento záznam" @@ -171,14 +172,17 @@ catalog_login_desc = "Vložte pÅ™Ãstupové údaje do katalogu knihovny." CD = "CD" Change Password = "ZmÄ›nit heslo" channel_add_more = "PÅ™idat dalÅ¡Ã podobné pohledy" +channel_browse = "Procházet dalÅ¡Ã záznamy" channel_expand = "Zobrazit souvisejÃcà pohledy" channel_explore = "Grafické procházenà katalogu" channel_search = "Zobrazit jako výsledek vyhledávánÃ" +channel_searchbox_label = "Vyhledat dalÅ¡Ã pohledy:" Check Hold = "Zjistit stav rezervacÃ" Check Recall = "Zjistit stav objednávek" Checked Out = "VypůjÄeno" Checked Out Items = "VýpůjÄky" Checkedout = "VýpůjÄky" +Checkout Date = "Datum vypůjÄenÃ" Chicago Citation = "Styl Chicago" child_record_count = "%%count%% záznamů" child_records = "Obsah/Äásti" @@ -203,6 +207,7 @@ collection_disambiguation = "Bylo nalezeno nÄ›kolik odpovÃdajÃcÃch sbÃrek" collection_empty = "SbÃrka je prázdná." collection_view_record = "Zobrazit záznam" Collections = "SbÃrky" +comment_anonymous_user = "Anonym" comment_error_load = "PÅ™i zÃskávánà komentářů doÅ¡lo k chybÄ›" comment_error_save = "PÅ™i ukládánà komentáře doÅ¡lo k chybÄ›" Comments = "Komentáře" @@ -253,6 +258,11 @@ date_year_placeholder = "y" Debug Information = "Informace o chybÄ›" del_search = "Odstranit vyhledávacà skupinu" Delete = "Odstranit" +delete_account_confirm = "Opravdu chcete odstranit Váš úÄet?" +delete_account_description_html = "Uložená vyhledávánà i seznam oblÃbených budou odstranÄ›ny. Pokud budete chtÃt můžete si pozdÄ›ji založit nový úÄet." +delete_account_failure = "ÚÄet se nepodaÅ™ilo odstranit." +delete_account_success_message = "Váš úÄet byl úspěšnÄ› odstranÄ›n. OdhlaÅ¡uji..." +delete_account_title = "Odstranit úÄet" delete_all = "Odstranit vÅ¡e" delete_comment_failure = "Komentář nelze smazat." delete_comment_success = "Komentář smazán." @@ -274,6 +284,8 @@ Document Type = "Druh dokumentu" DOI = "DOI" doi_detected_html = "Zdá se, že Váš vyhledávacà dotaz obsahuje DOI. Pokud chcete ověřit dostupnost odpovÃdajÃcÃho digitálnÃho dokumentu, kliknÄ›te zde: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "OznaÄit vyhledávané územÃ" +draw_searchbox_end = "PusÅ¥te tlaÄÃtko myÅ¡i pro dokonÄenÃ." +draw_searchbox_start = "KliknÄ›te a táhnÄ›te pro oznaÄenà oblasti" Due = "do" Due Date = "PůjÄeno do" DVD = "DVD" @@ -286,6 +298,7 @@ edit_list_fail = "Nemáte oprávnÄ›nà upravovat seznam." edit_list_success = "Seznam byl upraven." Edition = "VydánÃ" eds_expander_fulltext = "Prohledávat také plné texty Älánků" +eds_expander_relatedsubjects = "Zobrazit podobná témata" eds_expander_thesaurus = "PoužÃt také pÅ™Ãbuzné výrazy" eds_limiter_FC = "Pouze katalog" eds_limiter_FC1 = "Pouze intitucionálnà repozitář" @@ -339,11 +352,10 @@ export_fail = "Položky se nebyly exportovány" export_invalid_format = "Tuto položku nelze exportovat ve vybraném formátu." export_missing = "Chybà nÄ›které údaje. Položky nebylo možné exportovat." export_no_formats = "Položku nelze exportovat." -export_redirect = "ZaÄÃt export do %%service%%" -export_refworks = "ZaÄÃt export do RefWorks" export_save = "Uložit soubor" export_selected = "Exportovat vybrané" export_selected_favorites = "Exportovat vybrané oblÃbené položky" +export_send = "ZaÄÃt export do %%service%%" export_success = "Export byl úspěšný" export_unsupported_format = "Nepodporovaný exportnà formát." external_auth_heading = "PÅ™Ãstup k licencovaným zdrojům" @@ -496,12 +508,14 @@ ill_request_processed = "Zpracováno" ill_request_profile_html = "Pro informace o meziknihovnÃch výpůjÄkách je nutné mÃt <a href="%%url%%">úÄet v knihovnÄ›</a>." ill_request_submit_text = "Objednat MVS" Illustrated = "Ilustrováno" +ils_action_unavailable = "Pro aktuálnÄ› nastavený Ätenářský průkaz nenà tato funkce dostupná." ils_connection_failed = "PrávÄ› probÃhá údržba knihovnÃho systému." ils_offline_holdings_message = "V souÄasné dobÄ› nejsou dostupné informace o dostupnosti. Omlouváme se Vám za nepÅ™Ãjemnosti. Neváhejte nás kontaktovat a my se pokusÃme zjistit požadované informace jinou cestou:" ils_offline_home_message = "V souÄasné dobÄ› nelze zobrazit údaje z VaÅ¡eho úÄtu a aktuálnà informace o dostupnosti dokumentů. Omlouváme se. Neváhejte nás kontaktovat a my se pokusÃme zjistit požadované informace jinou cestou:" ils_offline_login_message = "V souÄasné dobÄ› nelze zobrazit údaje z VaÅ¡eho úÄtu. Omlouváme se. Neváhejte nás kontaktovat a my se pokusÃme zjistit požadované informace jinou cestou:" ils_offline_status = "PrávÄ› probÃhá údržba knihovnÃho systému." ils_offline_title = "PrávÄ› probÃhá údržba systému" +ils_transaction_history_disabled = "Historie výpůjÄek nenà pro aktuálnÄ› nastavený Ätenářský průkaz dostupná." Import Record = "Importovat záznam" in = "v poli" In This Collection = "Zúžit v rámci této sbÃrky" @@ -576,6 +590,8 @@ list_access_denied = "Nemáte oprávnÄ›nà zobrazit tento seznam." list_edit_name_required = "Je nutné zadat název seznamu." load_tag_error = "Chyba: nelze zobrazit tagy " Loading = "NaÄÃtá se" +Loan History = "Historie výpůjÄek" +loan_history_empty = "Nemáte žádné výpůjÄky." Local Login = "MÃstnà pÅ™ihlášenÃ" local_login_desc = "Vložte pÅ™ihlaÅ¡ovacà údaje, pomocà kterých jste provedli registraci na tÄ›chto stránkách." Located = "UmÃstÄ›nÃ" @@ -603,6 +619,7 @@ Message From Sender = "Zpráva od odesÃlatele" Metadata Prefix = "Prefix metadat" Microfilm = "Mikrofilm" MLA Citation = "Citace podle MLA" +Mobile Number = "ÄŒÃslo mobilnÃho telefonu" mobile_link = "PravdÄ›podobnÄ› použÃváte mobilnà zaÅ™ÃzenÃ. Chcete pÅ™epnout do mobilnà verze?" Monograph Title = "Název monografie" more = "vÃce" @@ -623,6 +640,7 @@ My Holds = "Rezervace" My Profile = "Profil" Narrow Search = "UpÅ™esnit hledánÃ" navigate_back = "ZpÄ›t" +nearby_items = "Items Near "%%title%%"" Need Help? = "Hledáte pomoc?" New Item Feed = "Nové tituly v katalogu" New Item Search = "Hledánà v novinkách" @@ -631,6 +649,8 @@ New Items = "Nové tituly v katalogu" New Title = "Nový název" new_password = "Nové heslo" new_password_success = "VaÅ¡e heslo bylo úspěšnÄ› zmÄ›nÄ›no" +new_user_welcome_subject = "Váš nový úÄet v knihovnÄ›: %%library%%" +new_user_welcome_text = "VÃtejte v knihovnÄ›: %%library%%. PrávÄ› se Vám podaÅ™ilo založit úÄet na jméno %%firstname%% %%lastname%%. VaÅ¡e pÅ™ihlaÅ¡ovacà jméno je %%username%%. Heslo si můžete nastavit na této stránce: %%url%%" Newspaper = "Noviny" Next = "DalÅ¡Ã" No citations are available for this record = "Pro tento záznam nejsou dostupné žádné citace" @@ -728,6 +748,8 @@ past_days = "Za poslednÃch %%range%% dnů" PDF Full Text = "Plný text ve formátu PDF" peer_reviewed = "Recenzováno" peer_reviewed_limit = "Omezit na Älánky z recenzovaných Äasopisů" +permission_denied = "Pokusili jste se provést operaci, ke které bohužel nemáte dostateÄné opravnÄ›nÃ." +permission_denied_title = "PÅ™Ãstup zamÃtnut" Phone Number = "Telefonnà ÄÃslo" Photo = "Fotografie" Physical Description = "Fyzický popis" @@ -778,6 +800,7 @@ Range slider = "VýbÄ›r rozsahu" Read the full review online... = "PÅ™eÄÃst celou recenzi online..." Recall This = "Rezervovat" recaptcha_not_passed = "Kód CAPTCHA nesouhlasÃ." +recently_returned_channel_title = "Nedávno vrácené" Record Citations = "Citace záznamu" Record Count = "PoÄet záznamů" Record Type = "Typ záznamu" @@ -795,8 +818,17 @@ recovery_new_disabled = "V tuto chvÃli nenà možné heslo obnovit." recovery_title = "Obnovenà hesla" recovery_too_soon = "Bylo zadáno pÅ™ÃliÅ¡ mnoho požadavků na obnovenà hesla, zkuste to, prosÃm, pozdÄ›ji." recovery_user_not_found = "Uživatelský úÄet se nepodaÅ™ilo nalézt" +rectangle_center_message = "Toto je stÅ™ed vyznaÄené oblasti" Refine Results = "UpÅ™esnit hledánÃ" Region = "Oblast" +relais_available = "Tento dokument je dostupný v rámci meziknihovnà výpůjÄnà služby. Chcete jej objednat?" +relais_checking = "ZjiÅ¡Å¥uji dostupnost..." +relais_error_html = "PÅ™i zpracovánà požadavku se vyskytla chyba. KliknÄ›te <a href="%%url%%" target='new'>zde</a> pro objednánà dokumentu na stránkách systému pro meziknihovnà výpůjÄky.</a>" +relais_request = "Objednávka meziknihovnà výpůjÄky" +relais_requesting = "Objednávám..." +relais_search = "Hledat v meziknihovnÃch výpůjÄkách" +relais_success_label = "PotvrzenÃ:" +relais_success_message = "Objednávka ÄÃslo #%%id%% byla vytvoÅ™ena. E-mailem obdržÃte potvrzenÃ" Related Author = "SouvisejÃcà autoÅ™i" Related Items = "PÅ™Ãbuzné jednotky" Related Subjects = "SouvisejÃcà témata" @@ -825,12 +857,14 @@ Requests = "Požadavky" Reserves = "Rezervace" Reserves Search = "Vyhledávánà rezervacÃ" Reserves Search Results = "Výsledky vyhledávánà rezervacÃ" +result_checkbox_label = "Vybrat výsledek ÄÃslo %%number%%" result_count = "%%count%% výsledků" Results = "Výsledky" results = "výsledků" Results for = "výsledků pro" Results per page = "PoÄet výsledků na stránku" Resumption Token = "Odnovenà průkazky" +Return Date = "Datum vrácenÃ" Review by = "Recenze z" Reviews = "Recenze" Save = "Uložit" @@ -902,8 +936,14 @@ sort_author = "Autor" sort_author_author = "AbecednÄ›" sort_author_relevance = "OblÃbenost" sort_callnumber = "Signatury" +sort_checkout_date_asc = "Data vypůjÄená vzestupnÄ›" +sort_checkout_date_desc = "Data vypůjÄenà sestupnÄ›" sort_count = "PoÄet výsledků" +sort_due_date_asc = "TermÃnu pro vrácenà vzestupnÄ›" +sort_due_date_desc = "TermÃnu pro vrácenà sestupnÄ›" sort_relevance = "Relevance" +sort_return_date_asc = "Data vrácenà vzestupnÄ›" +sort_return_date_desc = "Data vrácenà sestupnÄ›" sort_title = "Název" sort_year = "Podle data sestupnÄ›" sort_year asc = "Podle data vzestupnÄ›" @@ -1017,6 +1057,7 @@ total_tags = "Celkem tagů" total_users = "Celkem uživatelů" Transliterated Title = "Transliterovaný název" tree_search_limit_reached_html = "Na váš dotaz bylo nalezeno pÅ™ÃliÅ¡ mnoho výsledků, proto je nebylo možné vÅ¡echny zobrazit ve stromové struktuÅ™e. Bylo zobrazeno pouze prvnÃch <b>%%limit%%</b> položek. Pro zobrazenà vÅ¡ech výsledků kliknÄ›te <a id="fullSearchLink" href="%%url%%" target="_blank">zde.</a>" +trending_items_channel_title = "O co je teÄ zájem" unique_tags = "Unikátnà tagy" University Library = "Univerzitnà knihovna" Unknown = "Neznámo" @@ -1039,7 +1080,7 @@ View Full Collection = "Zobrazit celou sbÃrku" View Full Record = "Zobrazit úplný záznam" View in EDS = "Zobrazit v EDS" View online: Full view Book Preview from the Hathi Trust = "Prohlédnout online: Celá recenze na Hathi Trust" -View Record = "Prohlédnou záznam" +View Record = "Prohlédnout záznam" View Records = "Prohlédnout záznamy" View this record in EBSCOhost = "Zobrazit záznam v databázi EBSCOhost" view_already_selected = "Typ zobrazenà %%current%% je již vybrán" diff --git a/languages/cy.ini b/languages/cy.ini index 653eb94df3085d753dd8d49368aebbcbba283518..d5df9bb43fb9672bf81aa5cde3ddabf44d7dc9f7 100644 --- a/languages/cy.ini +++ b/languages/cy.ini @@ -94,6 +94,7 @@ Back to Search Results = "Yn ôl at Ganlyniadau'r Chwiliad" Backtrace = "Yn ôl" Bag = "Bag" Balance = "Balans" +Barcode = "Cod Bar" basic_search_keep_filters = "Cadw fy hidlwyr presennol" Be the first to leave a comment = "Byddwch y cyntaf i adael sylw" Be the first to tag this record = "Byddwch y cyntaf i dagio'r cofnod hwn" @@ -183,6 +184,7 @@ Check Recall = "Gwirio am Adalw" Checked Out = "Ar Fenthyg" Checked Out Items = "Eitemau ar Fenthyg" Checkedout = "Ar Fenthyg" +Checkout Date = "Dyddiad Derbyn " Chicago Citation = "Dyfyniad Arddull Chicago" child_record_count = "%%count%% o gofnodion" child_records = "Cynnwys/darnau" @@ -207,6 +209,7 @@ collection_disambiguation = "Daethpwyd o hyd i nifer o gasgliadau sy'n cyfateb" collection_empty = "Dim eitemau i'w dangos" collection_view_record = "Gweld Cofnod" Collections = "Casgliadau" +comment_anonymous_user = "Di-enw" comment_error_load = "Gwall: Ni ellir Ail-lunio'r Rhestr Sylwadau" comment_error_save = "Gwall: Ni ellir Cadw Sylwadau" Comments = "Sylwadau" @@ -257,6 +260,11 @@ date_year_placeholder = "C" Debug Information = "Dadfygio Gwybodaeth" del_search = "Gwaredu Grŵp Chwilio" Delete = "Dileu" +delete_account_confirm = "Ydych chi'n sicr eich bod yn dymuno dileu eich cyfrif?" +delete_account_description_html = "Caiff chwiliadau a gadwyd a rhestrau ffefrynnau eu dileu. Gallwch sefydlu cyfrif newydd o hyd pe dymunech." +delete_account_failure = "Wedi methu â dileu'r cyfrif." +delete_account_success_message = "Mae eich cyfrif wedi cael ei ddileu. Allgofnodi..." +delete_account_title = "Dileu'r Cyfrif" delete_all = "Dileu Pob Un" delete_comment_failure = "Methu â dileu sylw" delete_comment_success = "Dilëwyd y sylw" @@ -278,6 +286,8 @@ Document Type = "Math o Ddogfen" DOI = "DOI" doi_detected_html = "Ymddengys bod eich chwiliad yn cynnwys DOI. Cliciwch yma i weld argaeledd yr adnodd:<a href="%%url%%">%%doi%%</a>" Draw Search Box = "Llunio Blwch Chwilio" +draw_searchbox_end = "Gadewch y llygoden i fynd i orffen tynnu'r llun." +draw_searchbox_start = "Cliciwch a llusgwch i dynnu llun o betryal." Due = "Erbyn" Due Date = "Dyddiad Cau" DVD = "DVD" @@ -344,11 +354,10 @@ export_fail = "Ni allfudwyd eich eitemau" export_invalid_format = "Nid yw'r cofnod hwn yn cefnogi'r fformat allforio a ddewiswyd" export_missing = "Roedd peth data ar goll. Ni allfudwyd eich eitemau." export_no_formats = "Nid yw'r cofnod hwn yn cefnogi allforio" -export_redirect = "Dechrau allforio i %%service%%" -export_refworks = "Dechrau allforio i RefWorks" export_save = "Cadw Ffeil" export_selected = "Allfudo'r detholiad" export_selected_favorites = "Allfudo Ffefrynnau Dethol" +export_send = "Dechrau allforio i %%service%%" export_success = "Allfudo'n Llwyddiannus" export_unsupported_format = "Fformat Allfudo na Chefnogwyd" external_auth_heading = "Mynediad i ddeunydd trwyddedig" @@ -501,12 +510,14 @@ ill_request_processed = "Proseswyd" ill_request_profile_html = "Am wybodaeth am gais am Fenthyciad Rhwnglyfrgellol, rhaid i chi sefydlu'ch <a href="%%url%%">Proffil Catalog Llyfrgell</a>." ill_request_submit_text = "Cyflwyno'r Cais" Illustrated = "Darluniadol" +ils_action_unavailable = "Nid yw'r weithred y gofynnwyd amdani ar gael gyda'r cerdyn llyfrgell gweithredol." ils_connection_failed = "Rydym yn gwneud gwaith cynnal a chadw ar ein System Rheoli'r Llyfrgell ar hyn o bryd" ils_offline_holdings_message = "Nid yw gwybodaeth am y stoc nac am argaeledd yr eitemau ar gael ar hyn o bryd. Rydym yn ymddiheuro am unrhyw anhwylustod. Cysylltwch â ni am ragor o wybodaeth." ils_offline_home_message = "Ni fydd manylion eich cyfrif na gwybodaeth fyw am eitemau ar gael yn ystod y cyfnod hwn. Rydym yn ymddiheuro am unrhyw anhwylustod. Cysylltwch â ni am ragor o wybodaeth." ils_offline_login_message = "Ni fydd manylion eich cyfrif ar gael yn ystod y cyfnod hwn. Rydym yn ymddiheuro am unrhyw anhwylustod. Cysylltwch â ni am ragor o wybodaeth." ils_offline_status = "Rydym yn gwneud gwaith cynnal a chadw ar ein System Rheoli'r Llyfrgell ar hyn o bryd" ils_offline_title = "Gwaith Cynnal a Chadw ar y Gweill" +ils_transaction_history_disabled = "Nid yw'r hanes benthyciadau wedi'i alluogi ar gyfer y cerdyn llyfrgell gweithredol. " Import Record = "Mewnforio Cofnod" in = "mewn" In This Collection = "Yn y Casgliad Hwn" @@ -581,6 +592,8 @@ list_access_denied = "Nid oes hawl gennych edrych ar y rhestr hwn" list_edit_name_required = "Mae angen enw'r rhestr" load_tag_error = "Gwall: Ni Ellir Llwytho'r Tagiau" Loading = "Llwytho" +Loan History = "Hanes Benthyciadau " +loan_history_empty = "Nid oes gennych unrhyw fenthyciadau yn yr hanes benthyciadau." Local Login = "Mewngofnodi Lleol" local_login_desc = "Rhowch yr enw defnyddiwr a chyfrinair y creoch ar gyfer y wefan hon." Located = "Wedi'i leoli" @@ -608,6 +621,7 @@ Message From Sender = "Neges gan yr Anfonwr" Metadata Prefix = "Rhagddodiad Metadata" Microfilm = "Microffilm" MLA Citation = "Dyfyniad MLA" +Mobile Number = "Rhif ffôn symudol" mobile_link = "Ymddengys eich bod ar declyn symudol; newid i olwg symudol?" Monograph Title = "Teitl y Monograff" more = "rhagor" @@ -637,6 +651,8 @@ New Items = "Eitemau Newydd" New Title = "Teitl Newydd" new_password = "Cyfrinair Newydd" new_password_success = "Llwyddwyd i newid eich cyfrinair." +new_user_welcome_subject = "Eich cyfrif newydd yn %%library%%" +new_user_welcome_text = "Croeso i %%library%%. Mae cyfrif newydd wedi'i agor ar gyfer %%firstname%% %%lastname%%. Eich enw defnyddiwr yw %%username%%. Rhowch gyfrinair ar y dudalen hon: %%url%%" Newspaper = "Papur Newydd" Next = "Nesaf" No citations are available for this record = "Dim dyfyniadau ar gael ar gyfer y cofnod hwn" @@ -786,6 +802,7 @@ Range slider = "Detholwr Amrediad" Read the full review online... = "Darllen yr arolwg cyfan ar-lein..." Recall This = "Adalw hwn" recaptcha_not_passed = "Methwyd y prawf CAPTCHA" +recently_returned_channel_title = "Dychwelwyd yn Ddiweddar" Record Citations = "Cofnodi Dyfyniadau" Record Count = "Nifer y Cofnodion" Record Type = "Math Cofnod" @@ -803,8 +820,17 @@ recovery_new_disabled = "Ni chaniateir i chi newid eich cyfrinair ar hyn o bryd" recovery_title = "Adfer Cyfrinair" recovery_too_soon = "Gwnaed gormod o geisiadau adfer, rhowch gynnig eto'n nes ymlaen." recovery_user_not_found = "Methwyd â dod o hyd i'ch cyfrif" +rectangle_center_message = "Dyma ganolbwynt y petryal a uwcholeuir " Refine Results = "Mireinio'r Canlyniadau" Region = "Rhanbarth" +relais_available = "Mae'r eitem hon ar gael trwy Fenthyciad Rhynglyfrgellol. Hoffech chi wneud cais amdani?" +relais_checking = "Gwirio argaeledd..." +relais_error_html = "Cafwyd problem o ran y cais hwn. Cliciwch <a href="%%url%%" target='new'>yma</a> i wneud cais am yr eitem hon gan ddefnyddio gwefan Benthyciad Rhynglyfrgellol." +relais_request = "Cais am Fenthyciad Rhynglyfrgellol" +relais_requesting = "Gwneud cais am..." +relais_search = "Chwiliwch Fenthyciadau Rhynglyfrgellol" +relais_success_label = "Cadarnhad:" +relais_success_message = "Crewyd cais adnabod #%%id%%. Byddwch yn derbyn cadarnhad drwy e-bost." Related Author = "Awdur Perthynol" Related Items = "Eitemau Perthynol" Related Subjects = "Pynciau Perthynol" @@ -833,12 +859,14 @@ Requests = "Ceisiadau" Reserves = "Ar Gadw" Reserves Search = "Chwilio Ar Gadw" Reserves Search Results = "Canlyniadau Chwilio Ar Gadw" +result_checkbox_label = "Dewiswch ganlyniad rhif %%number%%" result_count = "%%count%% canlyniadau" Results = "Canlyniadau" results = "canlyniadau" Results for = "Canlyniadau ar gyfer" Results per page = "Canlyniadau y tudalen" Resumption Token = "Tocyn Ail-gychwyn" +Return Date = "Dyddiad a Ddychwelwyd" Review by = "Adolygiad gan" Reviews = "Adolygiadau" Save = "Cadw" @@ -910,8 +938,14 @@ sort_author = "Awdur" sort_author_author = "Trefn y wyddor" sort_author_relevance = "Poblogrwydd" sort_callnumber = "Rhif Galw" +sort_checkout_date_asc = "Dyddiad Derbyn (hynaf yn gyntaf)" +sort_checkout_date_desc = "Dyddiad Derbyn (newydd yn gyntaf)" sort_count = "Cyfri Canlyniadau" +sort_due_date_asc = "Dyddiad i'w Ddychwelyd (hynaf yn gyntaf)" +sort_due_date_desc = "Dyddiad i'w Ddychwelyd (newydd yn gyntaf)" sort_relevance = "Perthnasedd" +sort_return_date_asc = "Dyddiad a Ddychwelwyd (hynaf yn gyntaf)" +sort_return_date_desc = "Dyddiad a Ddychwelwyd (newydd yn gyntaf)" sort_title = "Teitl" sort_year = "Dyddiad Trefn Ddisgynnol" sort_year asc = "Dyddiad Trefn Esgynnol" @@ -1025,6 +1059,7 @@ total_tags = "Cyfanswm Tagiau" total_users = "Cyfanswm Defnyddwyr" Transliterated Title = "Teitl Wedi'i Drawslythrennu" tree_search_limit_reached_html = "Roedd gormod o ganlyniadau i'w dangos yn y goeden. Ond yn dangos y <b>%%limit%%</b> eitem gyntaf. I chwilio'r cyfan, cliciwch <a id=fullSearchLink" href="%%url%%" target="_blank">yma.</a>"" +trending_items_channel_title = "Eitemau sy'n Trendio" unique_tags = "Tagiau Unigryw" University Library = "Llyfrgell y Brifysgol" Unknown = "Anhysbys" diff --git a/languages/da.ini b/languages/da.ini index 696096ac54eda9a1929d6af86821d8e6655699bf..767dbd599e899134fbf781b9ccddd87c2d90288d 100644 --- a/languages/da.ini +++ b/languages/da.ini @@ -232,7 +232,6 @@ export_download = "Download Fil" export_exporting = "Der dannes en eksportfil" export_fail = "Dine emner blev ikke eksporteret" export_missing = "Der skete en fejl. Dine emner blev ikke eksporteret." -export_refworks = "Begynd eksport til RefWorks" export_save = "Gem Fil" export_selected = "Eksport valgte" export_selected_favorites = "Eksport valgte favoritter" diff --git a/languages/de.ini b/languages/de.ini index 03fab866d8b9330fb834bfa7e558b5775762db73..c397cf2320e339c60f5e1f0e102c8651a7622f25 100644 --- a/languages/de.ini +++ b/languages/de.ini @@ -93,6 +93,7 @@ Back to Search Results = "Zurück zum Suchergebnis" Backtrace = "Ablaufverfolgung" Bag = "Korb" Balance = "Saldo" +Barcode = "Strichcode" basic_search_keep_filters = "Behalte die Filtereinstellungen." Be the first to leave a comment = "Schreiben Sie den ersten Kommentar" Be the first to tag this record = "Fügen Sie den ersten Tag hinzu" @@ -182,6 +183,7 @@ Check Recall = "Möglichkeit einer Vormerkung prüfen" Checked Out = "Ausgeliehen" Checked Out Items = "Ausgeliehene Medien" Checkedout = "Ausgliehene Medien" +Checkout Date = "Ausleihdatum" Chicago Citation = "Chicago Zitierstil" child_record_count = "%%count%% Datensätze" child_records = "Inhalte/Bestandteile" @@ -206,6 +208,7 @@ collection_disambiguation = "Mehrere zutreffende Bestände gefunden" collection_empty = "Keine Inhalte anzuzeigen." collection_view_record = "Datensatz ansehen" Collections = "Bestände" +comment_anonymous_user = "Anonym" comment_error_load = "Fehler: Laden der Kommentareinträge fehlgeschlagen" comment_error_save = "Fehler: Speichern des Kommentars fehlgeschlagen" Comments = "Kommentare" @@ -256,6 +259,11 @@ date_year_placeholder = "J" Debug Information = "Debug Information" del_search = "Suchgruppe entfernen" Delete = "Löschen" +delete_account_confirm = "Möchten Sie ihr Konto wirklich löschen?" +delete_account_description_html = "Gespeicherten Suchen und Favoritenlisten werden gelöscht. Sie können bei Bedarf später einen neues Konto eröffnen." +delete_account_failure = "Löschen des Kontos fehlgeschlagen." +delete_account_success_message = "Ihr Konto wurde gelöscht. Sie werden ausgeloggt..." +delete_account_title = "Konto löschen" delete_all = "Alles löschen" delete_comment_failure = "Der Kommentar konnte nicht gelöscht werden." delete_comment_success = "Der Kommentar wurde gelöscht." @@ -277,6 +285,8 @@ Document Type = "Publikationsart" DOI = "DOI" doi_detected_html = "Ihre Suche scheint eine DOI zu enthalten. Prüfen Sie die Online-Verfügbarkeit hier: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Suchbereich definieren" +draw_searchbox_end = "Lassen Sie die Maus los um die Zeichnung abzuschliessen." +draw_searchbox_start = "Klicken und ziehen um ein Rechteck zu zeichnen." Due = "Rückgabe bis" Due Date = "Rückgabedatum" DVD = "DVD" @@ -343,11 +353,10 @@ export_fail = "Ihre Datensätze wurden nicht exportiert" export_invalid_format = "Das ausgewählte Exportformat steht bei diesem Datensatz nicht zur Verfügung." export_missing = "Fehlende Angaben. Ihre Datensätze wurden nicht exportiert." export_no_formats = "Dieser Datensatz kann nicht exportiert werden." -export_redirect = "Exportiere nach %%service%%" -export_refworks = "Export nach RefWorks" export_save = "Datei speichern" export_selected = "Auswahl exportieren" export_selected_favorites = "Ausgewählte Favoriten exportieren" +export_send = "Exportiere nach %%service%%" export_success = "Export abgeschlossen" export_unsupported_format = "Nichtunterstütztes Exportformat" external_auth_heading = "Zugriff auf lizenzpflichtige Inhalte" @@ -500,12 +509,15 @@ ill_request_processed = "Bearbeitet" ill_request_profile_html = "Um die Fernleihe zu nutzen, richten Sie bitte ihr <a href="%%url%%">Bibliothekskatalog-Profil</a> ein." ill_request_submit_text = "Anfrage abschicken" Illustrated = "Abbildungen" +ils_account_create_error = "Es konnte kein Konto in unserem Bibliotheksverwaltungssystem für Sie erstellt werden. Wir entschuldigen uns für die Umstände und stehen für weitere Fragen gerne zur Verfügung." +ils_action_unavailable = "Diese Funktion ist für das aktuelle Bibliothekskonto nicht verfügbar." ils_connection_failed = "Unser Bibliotheksverwaltungssystem ist momentan wegen Wartungsarbeiten nicht verfügbar." ils_offline_holdings_message = "Bestandes- und Verfügbarkeitsinformationen können momentan leider nicht angezeigt werden. Wir entschuldigen uns für die Umstände und stehen für weitere Fragen gerne zur Verfügung:" ils_offline_home_message = "Ihr persönliches Konto und die Ausleihinformationen stehen während dieser Zeit nicht zur Verfügung. Wir entschuldigen uns für die Umstände und stehen für weitere Fragen gerne zur Verfügung:" ils_offline_login_message = "Ihr persönliches Konto steht während dieser Zeit nicht zur Verfügung. Wir entschuldigen uns für die Umstände und stehen für weitere Fragen gerne zur Verfügung:" ils_offline_status = "Unser Bibliotheksverwaltungssystem ist momentan wegen Wartungsarbeiten nicht verfügbar." ils_offline_title = "Wegen Wartungsarbeiten nicht verfügbar" +ils_transaction_history_disabled = "Die Ãœbersicht der bisherigen Ausleihen ist für das aktuelle Bibliothekskonto nicht aktiviert." Import Record = "Datensatz importieren" in = "in" In This Collection = "In diesem Bestand" @@ -580,6 +592,8 @@ list_access_denied = "Sie sind nicht berechtigt diese Liste anzusehen." list_edit_name_required = "Name für die Liste fehlt!" load_tag_error = "Fehler: Laden der Tags fehlgeschlagen" Loading = "Wird geladen" +Loan History = "Bisherige Ausleihen" +loan_history_empty = "Sie haben keine bisherigen Ausleihen." Local Login = "Lokale Anmeldung" local_login_desc = "Geben Sie ihren Benutzernamen und ihr Passwort für diese Webseite ein." Located = "Standort" @@ -607,6 +621,7 @@ Message From Sender = "Nachricht vom Sender" Metadata Prefix = "Metadaten Präfix" Microfilm = "Mikrofilm" MLA Citation = "MLA Zitierstil" +Mobile Number = "Handynummer" mobile_link = "Sie scheinen ein Mobilgerät zu benutzen. Möchten Sie zur Ansicht für Mobilgeräte wechseln?" Monograph Title = "Titel der Monografie" more = "mehr" @@ -636,6 +651,8 @@ New Items = "Neuerscheinungen" New Title = "Späterer Titel" new_password = "Neues Passwort" new_password_success = "Ihr Passwort wurde erfolgreich geändert" +new_user_welcome_subject = "Ihr neuer Account bei %%library%%" +new_user_welcome_text = "Willommen bei %%library%%. Für %%firstname%% %%lastname%% wurde ein neues Konto eröffnet. Ihr Benutzername lautet %%username%%. Bitte setzen Sie ein Passwort auf folgender Seite: %%url%%" Newspaper = "Zeitung" Next = "Nächster" No citations are available for this record = "Für diesen Datensatz kann kein Zitat erstellt werden" @@ -785,6 +802,7 @@ Range slider = "Bereichswähler" Read the full review online... = "Die vollständige Rezension online lesen..." Recall This = "Vormerken" recaptcha_not_passed = "CAPTCHA nicht korrekt eingegeben" +recently_returned_channel_title = "Kürzlich zurückgekommen" Record Citations = "Zitate" Record Count = "Satzanzahl" Record Type = "Datenart" @@ -802,8 +820,17 @@ recovery_new_disabled = "Sie verfügen momentan nicht über die Berechtigung ihr recovery_title = "Passwort zurücksetzen" recovery_too_soon = "Es wurden zuviele Anfragen gestellt um das Passwort zurückzusetzen. Bitte versuchen Sie es später wieder" recovery_user_not_found = "Ihr Konto wurde nicht gefunden" +rectangle_center_message = "Mittelpunkt des hervorgehobenen Rechts" Refine Results = "Treffer weiter einschränken" Region = "Region" +relais_available = "Das Medium ist per Fernleihe verfügbar. Möchten Sie das Exemplar anfordern?" +relais_checking = "Ãœberprüfe Verfügbarkeit..." +relais_error_html = "Bei der Bestellung ist ein Problem aufgetreten. Klicken Sie <a href="%%url%%" target='new'>hier</a> um das Bestellformular für die Fernleihe aufzurufen.</a>" +relais_request = "Bestellung per Fernleihe" +relais_requesting = "Anfrage..." +relais_search = "Suche für die Fernleihe" +relais_success_label = "Bestätigung:" +relais_success_message = "Bestellnummer %%id%% wurde angelegt. Sie erhalten eine Bestätigung per E-Mail." Related Author = "Ähnliche Verfasser" Related Items = "Ähnliche Datensätze" Related Subjects = "Ähnliche Schlagworte" @@ -832,12 +859,14 @@ Requests = "Anfragen" Reserves = "Vormerkungen" Reserves Search = "Suche Vormerkungen" Reserves Search Results = "Suchergebnisse Vormerkungen" +result_checkbox_label = "Bitte wählen Sie die Treffernummer %%number%%" result_count = "%%count%% Treffer" Results = "Treffer" results = "Treffer" Results for = "Ergebnis für" Results per page = "Treffer pro Seite" Resumption Token = "Resumption Token" +Return Date = "Rückgabedatum" Review by = "Rezension von" Reviews = "Rezensionen" Save = "Speichern" @@ -909,8 +938,14 @@ sort_author = "Verfasser" sort_author_author = "Alphabetisch" sort_author_relevance = "Häufigkeit" sort_callnumber = "Signatur" +sort_checkout_date_asc = "Ausleihdatum (älteste zuerst)" +sort_checkout_date_desc = "Ausleihdatum (neuste zuerst)" sort_count = "Anzahl Treffer" +sort_due_date_asc = "Ausleihfrist (älteste zuerst)" +sort_due_date_desc = "Ausleihfrist (neuste zuerst)" sort_relevance = "Relevanz" +sort_return_date_asc = "Rückgabedatum (älteste zuerst)" +sort_return_date_desc = "Rückgabedatum (neuste zuerst)" sort_title = "Titel" sort_year = "Nach Datum, absteigend" sort_year asc = "Nach Datum, aufsteigend" @@ -1024,6 +1059,7 @@ total_tags = "Total Tags" total_users = "Total Benutzer" Transliterated Title = "Transliterierter Titel" tree_search_limit_reached_html = "Ihr Suche ergab zuviele Treffer, um die gesamte Hierarchie anzeigen zu können. Es werden nur the ersten <b>%%limit%%</b> Treffer angezeigt. Für eine vollständige Suche klicken Sie <a id="fullSearchLink" href="%%url%%" target="_blank">hier.</a>" +trending_items_channel_title = "Beliebte Medien" unique_tags = "Einmalige Tags" University Library = "Universitätsbibliothek" Unknown = "Unbekannt" diff --git a/languages/el.ini b/languages/el.ini index 5b3ec6789af37f5b724a11cf080144e69b473bc9..4171aa9048b294285a5ac4bbebb8cd2fffe7ac38 100644 --- a/languages/el.ini +++ b/languages/el.ini @@ -94,6 +94,7 @@ Back to Search Results = "ΕπιστÏοφή στα αποτελÎσματα α Backtrace = "Διαγνωστική πληÏοφοÏία" Bag = "Καλάθι" Balance = "Οφειλόμενα" +Barcode = "Ραβδοκώδικας" basic_search_keep_filters = "ΔιατήÏηση φίλτÏων" Be the first to leave a comment = "καταχωÏήστε σχόλιο Ï€Ïώτοι" Be the first to tag this record = "ΚαταχωÏήστε ετικÎτα Ï€Ïώτοι" @@ -183,6 +184,7 @@ Check Recall = "Επιλογή ανακληθÎντων" Checked Out = "Μη διαθÎσιμο" Checked Out Items = "ΔανεισμÎνα τεκμήÏια" Checkedout = "ΔανεισμÎνο" +Checkout Date = "ΗμεÏομηνία δανεισμοÏ" Chicago Citation = "ΠαÏαπομπή Chicago Style" child_record_count = "%%count%% εγγÏαφÎÏ‚" child_records = "ΠεÏιεχόμενα / Τμήματα" @@ -207,6 +209,7 @@ collection_disambiguation = "Î’ÏÎθηκαν πεÏισσότεÏες από μ collection_empty = "Δεν βÏÎθηκαν τεκμήÏια." collection_view_record = "Εμφάνιση εγγÏαφής" Collections = "ΣυλλογÎÏ‚" +comment_anonymous_user = "Ανώνυμος" comment_error_load = "Σφάλμα: Το σχόλιο δε βÏÎθηκε" comment_error_save = "Σφάλμα: Το σχόλιο δεν αποθηκεÏτηκε" Comments = "Σχόλια" @@ -257,6 +260,11 @@ date_year_placeholder = "Ε" Debug Information = "ΠληÏοφοÏίες Debug" del_search = "ΚατάÏγηση ομάδας" Delete = "ΔιαγÏαφή" +delete_account_confirm = "ΣίγουÏα θÎλετε να διαγÏάψετε τον λογαÏιασμό σας;" +delete_account_description_html = "Οι αποθηκευμÎνες αναζητήσεις και οι λίστες με τα αγαπημÎνα θα διαγÏαφοÏν. ΜποÏείτε να δημιουÏγήσετε νÎο λογαÏιασμό αÏγότεÏα, εαν το επιθυμείτε" +delete_account_failure = "Η διαγÏαφή του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï Î±Ï€Îτυχε" +delete_account_success_message = "Ο λογαÏιασμός σας διαγÏάφηκε. ΑποσÏνδεση..." +delete_account_title = "ΔιαγÏαφή λογαÏιασμοÏ" delete_all = "ΔιαγÏαφή όλων" delete_comment_failure = "Το σχόλιο δεν ήταν δυνατόν να διαγÏαφεί." delete_comment_success = "Το σχόλιο διαγÏάφηκε." @@ -278,6 +286,8 @@ Document Type = "ΤÏπος εγγÏάφου" DOI = "DOI" doi_detected_html = "Η αναζήτησή σας φαίνεται να πεÏιÎχει DOI. κάντε κλικ εδώ για να ελÎγξετε τη διαθεσιμότητα της πηγής: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Σχεδιάστε την πεÏιοχή αναζήτησης" +draw_searchbox_end = "Αφήστε το ποντίκι για να ολοκληÏωθεί η σχεδίαση" +draw_searchbox_start = "Πατήστε και σÏÏετε με το ποντίκι για να σχεδιάσετε Îνα πλαίσιο" Due = "ΕπιστÏοφή" Due Date = "ΗμεÏομηνία επιστÏοφής" DVD = "DVD" @@ -344,11 +354,10 @@ export_fail = "Δεν Îγινε εξαγωγή των τεκμηÏίων σας export_invalid_format = "Η μοÏφή εξαγωγής που επιλÎξατε δεν υποστηÏίζεται για αυτή την εγγÏαφή." export_missing = "Ελλιπή στοιχεία. Δεν Îγινε εξαγωγή των τεκμηÏίων σας" export_no_formats = "Αυτή η εγγÏαφή δεν είναι δυνατόν να εξαχθεί." -export_redirect = "ΈναÏξη εξαγωγής σε %%service%%" -export_refworks = "ΈναÏξη εξαγωγής στο RefWorks" export_save = "Αποθήκευση αÏχείου" export_selected = "Εξαγωγή επιλεγμÎνων" export_selected_favorites = "Εξαγωγή επιλεγμÎνων αγαπημÎνων" +export_send = "ΈναÏξη εξαγωγής σε %%service%%" export_success = "Η εξαγωγή ολοκληÏώθηκε" export_unsupported_format = "Αυτός ο Ï„Ïπος αÏχείου δεν υποστηÏίζεται για εξαγωγή" external_auth_heading = "Î Ïόσβαση σε αδειοδοτημÎνο υλικό" @@ -501,12 +510,14 @@ ill_request_processed = "ΕπεξεÏγασμÎνο" ill_request_profile_html = "Για πληÏοφοÏίες σχετικά με τον διαδανεισμό, συνδεθείτε με τον <a href="%%url%%">λογαÏιασμό</a> της Βιβλιοθήκης σας." ill_request_submit_text = "Υποβολή αιτήματος" Illustrated = "ΕικονογÏαφημÎνο" +ils_action_unavailable = "Η λειτουÏγία που επιλÎξατε δεν είναι διαθÎσιμη για τη συγκεκÏιμÎνη κάÏτα βιβλιοθήκης" ils_connection_failed = "Ο κατάλογος της βιβλιοθήκης βÏίσκεται σε κατάσταση συντήÏησης." ils_offline_holdings_message = "Η κατάσταση των τεκμηÏίων δεν είναι διαθÎσιμη αυτή τη στιγμή. ΖητοÏμε συγγνώμη για το Ï€Ïόβλημα. Επικοινωνήστε μαζί μας αν χÏειάζεστε βοήθεια:" ils_offline_home_message = "Τα στοιχεία του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚ και η κατάσταση των τεκμηÏίων δεν είναι διαθÎσιμα αυτή τη στιγμή. ΖητοÏμε συγγνώμη για το Ï€Ïόβλημα. Επικοινωνήστε μαζί μας αν χÏειάζεστε βοήθεια:" ils_offline_login_message = "Τα στοιχεία του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚ δεν είναι διαθÎσιμα αυτή τη στιγμή. ΖητοÏμε συγγνώμη για το Ï€Ïόβλημα. Επικοινωνήστε μαζί μας αν χÏειάζεστε βοήθεια:" ils_offline_status = "Ο κατάλογος της βιβλιοθήκης βÏίσκεται σε κατάσταση συντήÏησης." ils_offline_title = "ΣÏστημα σε κατάσταση συντήÏησης" +ils_transaction_history_disabled = "Το ιστοÏικό δανεισμών δεν είναι διαθÎσιμο για τη συγκεκÏιμÎνη κάÏτα βιβλιοθήκης" Import Record = "Εισαγωγή εγγÏαφής" in = "σε" In This Collection = "Σε αυτή τη συλλογή" @@ -581,6 +592,8 @@ list_access_denied = "Δεν Îχετε Ï€Ïόσβαση σε αυτή τη λί list_edit_name_required = "Î ÏÎπει να δώσετε όνομα λίστας." load_tag_error = "Σφάλμα. Αδυναμία φόÏτωσης ετικετών" Loading = "ΦοÏτώνει..." +Loan History = "ΙστοÏικό δανεισμών" +loan_history_empty = "Δεν υπάÏχουν καταχωÏήσεις στο ιστοÏικό δανεισμών" Local Login = "Είσοδος τοπικά" local_login_desc = "Εισάγετε τα στοιχεία εισόδου του Ï„Î¿Ï€Î¹ÎºÎ¿Ï ÏƒÎ±Ï‚ λογαÏιασμοÏ." Located = "Î’Ïίσκεται σε" @@ -608,6 +621,7 @@ Message From Sender = "Μήνυμα από αποστολÎα" Metadata Prefix = "Î Ïόθεμα μεταδεδομÎνων" Microfilm = "ΜικÏοφίλμ" MLA Citation = "ΠαÏαπομπή MLA" +Mobile Number = "ΑÏιθμός κινητοÏ" mobile_link = "Αλλαγή σε Ï€Ïοβολή για φοÏητÎÏ‚ συσκευÎÏ‚" Monograph Title = "Τίτλος μονογÏαφίας" more = "πεÏισσότεÏα" @@ -637,6 +651,8 @@ New Items = "ÎÎα τεκμήÏια" New Title = "ÎÎος τίτλος" new_password = "ÎÎος κωδικός" new_password_success = "Ο κωδικός σας άλλαξε επιτυχώς" +new_user_welcome_subject = "Ο νÎος λογαÏιασμός σας στη %%library%%" +new_user_welcome_text = "ΚαλωσήÏθατε στη %%library%%. ΔημιουÏγήσατε νÎο λογαÏιασμό για το ονοματεπώνυμο %%firstname%% %%lastname%%, με όνομα χÏήστη %%username%%. Για να οÏίσετε τον κωδικό Ï€Ïόσβασης μεταβείτε στη σελίδα: %%url%%" Newspaper = "ΕφημεÏίδα" Next = "Επόμενα" No citations are available for this record = "Δεν υπάÏχουν αναφοÏÎÏ‚ για αυτή την εγγÏαφή" @@ -786,6 +802,7 @@ Range slider = "ΧειÏιστήÏιο εÏÏους" Read the full review online... = "Διαβάστε ολόκληÏη την κÏιτική online..." Recall This = "Κάντε ανάκληση" recaptcha_not_passed = "ΛανθασμÎνο CAPTCHA" +recently_returned_channel_title = "ΕπιστÏάφηκαν Ï€Ïόσφατα" Record Citations = "ΑναφοÏÎÏ‚ εγγÏαφής" Record Count = "ΑÏιθμός εγγÏαφών" Record Type = "ΤÏπος εγγÏαφής" @@ -803,8 +820,17 @@ recovery_new_disabled = "Δεν Îχετε τη δυνατότητα να αλλ recovery_title = "Ανάκτηση κωδικοÏ" recovery_too_soon = "Έχουν ήδη υποβληθεί πολλά αιτήματα ανάκτησης, δοκιμάστε αÏγότεÏα" recovery_user_not_found = "Ο λογαÏιασμός σας δεν βÏÎθηκε" +rectangle_center_message = "Αυτό είναι το κÎντÏο του επιλεγμÎνου πλαισίου" Refine Results = "ΠεÏιοÏισμός αποτελεσμάτων" Region = "ΧώÏα / ΠεÏιοχή" +relais_available = "Το τεκμήÏιο αυτό είναι διαθÎσιμο μÎσω διαδανεισμοÏ. ΘÎλετε να το ζητήσετε;" +relais_checking = "Έλεγχος διαθεσιμότητας..." +relais_error_html = "ΠαÏουσιάστηκε Ï€Ïόβλημα με αυτό το αίτημα. Πατήστε <a href="%%url%%" target='new'>εδώ</a> για να κάνετε το αίτημα μÎσω της ιστοσελίδας διαδανεισμοÏ" +relais_request = "Αίτημα διαδανεισμοÏ" +relais_requesting = "Αίτημα σε εξÎλιξη..." +relais_search = "Αναζήτηση για διαδανεισμό" +relais_success_label = "Επιβεβαίωση:" +relais_success_message = "Το αίτημα υποβλήθηκε με αÏιθμό #%%id%%. ΣÏντομα θα λάβετε μήνυμα επιβεβαίωσης" Related Author = "Σχετικοί συγγÏαφείς" Related Items = "Σχετικά τεκμήÏια" Related Subjects = "Σχετικά θÎματα" @@ -833,12 +859,14 @@ Requests = "Αιτήσεις" Reserves = "ΚÏατήσεις" Reserves Search = "Αναζήτηση κÏατήσεων" Reserves Search Results = "ΑποτελÎσματα αναζήτησης κÏατήσεων" +result_checkbox_label = "Επιλογή αποτελÎσματος με αÏιθμό %%number%%" result_count = "%%count%% αποτελÎσματα" Results = "ΑποτελÎσματα" results = "αποτελÎσματα" Results for = "ΑποτελÎσματα για" Results per page = "ΑποτελÎσματα ανά σελίδα" Resumption Token = "ΑναγνωÏιστικό ανάκτησης" +Return Date = "ΗμεÏομηνία επιστÏοφής" Review by = "ΚÏιτική από:" Reviews = "ΚÏιτικÎÏ‚" Save = "Αποθήκευση" @@ -910,8 +938,14 @@ sort_author = "Ανά συγγÏαφÎα" sort_author_author = "Αλφαβητικά" sort_author_relevance = "Ανά δημοτικότητα" sort_callnumber = "Ανα Ταξιθετικό ΑÏιθμό" +sort_checkout_date_asc = "Ημ/νια Î´Î±Î½ÎµÎ¹ÏƒÎ¼Î¿Ï (αÏξουσα)" +sort_checkout_date_desc = "Ημ/νια Î´Î±Î½ÎµÎ¹ÏƒÎ¼Î¿Ï (φθίνουσα)" sort_count = "ΑÏιθμός αποτελεσμάτων" +sort_due_date_asc = "Ημ/νια επιστÏοφής (αÏξουσα)" +sort_due_date_desc = "Ημ/νια επιστÏοφής (φθίνουσα)" sort_relevance = "Ανά σχετικότητα" +sort_return_date_asc = "ΕπιστÏάφηκε (αÏξουσα)" +sort_return_date_desc = "ΕπιστÏάφηκε (φθίνουσα)" sort_title = "Ανά Τίτλο" sort_year = "Ανά ΗμεÏομηνία (φθιν.)" sort_year asc = "Ανά ΗμεÏομηνία (αυξ.)" @@ -1025,6 +1059,7 @@ total_tags = "ΣÏνολο ετικετών" total_users = "ΣÏνολο χÏηστών" Transliterated Title = "ΜεταγλωττισμÎνος Τίτλος" tree_search_limit_reached_html = "Η αναζήτηση επÎστÏεψε πάÏα πολλά αποτελÎσματα. Εμφανίζονται μόνο τα Ï€Ïώτα <b>%%limit%%</b>. Δείτε όλα τα αποτελÎσματα <a id" +trending_items_channel_title = "ΕπίκαιÏα τεκμήÏια" unique_tags = "ΜοναδικÎÏ‚ ετικÎτες" University Library = "Πανεπιστημιακή Βιβλιοθήκη" Unknown = "Άγνωστο" diff --git a/languages/en.ini b/languages/en.ini index 2a7afb2fbd546017caeb5a789575d115f9d48375..ef5698ecd00740b4f3511bc91ca309e21abf042a 100644 --- a/languages/en.ini +++ b/languages/en.ini @@ -93,6 +93,7 @@ Back to Search Results = "Back to Search Results" Backtrace = "Backtrace" Bag = "Bag" Balance = "Balance" +Barcode = "Barcode" basic_search_keep_filters = "Retain current filters" Be the first to leave a comment = "Be the first to leave a comment" Be the first to tag this record = "Be the first to tag this record" @@ -182,6 +183,7 @@ Check Recall = "Check Recall" Checked Out = "Checked Out" Checked Out Items = "Checked Out Items" Checkedout = "Checked Out" +Checkout Date = "Checkout Date" Chicago Citation = "Chicago Style Citation" child_record_count = "%%count%% records" child_records = "Contents/pieces" @@ -206,6 +208,7 @@ collection_disambiguation = "Found Multiple Matching Collections" collection_empty = "No items to display." collection_view_record = "View Record" Collections = "Collections" +comment_anonymous_user = "Anonymous" comment_error_load = "Error: Could Not Redraw Comment List" comment_error_save = "Error: Could Not Save Comment" Comments = "Comments" @@ -230,6 +233,7 @@ confirm_storage_retrieval_request_cancel_selected_text = "Do you wish to cancel Contents = "Contents" Contributing Source = "Contributing Source" Contributors = "Contributors" +Coordinates = "Coordinates" Copies = "Copies" Copy = "Copy" Copyright = "Copyright" @@ -256,6 +260,11 @@ date_year_placeholder = "Y" Debug Information = "Debug Information" del_search = "Remove Search Group" Delete = "Delete" +delete_account_confirm = "Are you sure you want to delete your account?" +delete_account_description_html = "Saved searches and favorite lists will be deleted. You can still establish a new account later if you wish." +delete_account_failure = "Failed to delete the account." +delete_account_success_message = "Your account has been deleted. Logging out..." +delete_account_title = "Delete Account" delete_all = "Delete All" delete_comment_failure = "Could not delete comment." delete_comment_success = "Comment deleted." @@ -277,6 +286,8 @@ Document Type = "Document Type" DOI = "DOI" doi_detected_html = "Your search appears to contain a DOI. Click here to check the availability of the resource: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Draw Search Box" +draw_searchbox_end = "Release mouse to finish drawing." +draw_searchbox_start = "Click and drag to select an area." Due = "Due" Due Date = "Due Date" DVD = "DVD" @@ -343,12 +354,11 @@ export_fail = "Your items were not exported" export_invalid_format = "The selected export format is not supported by this record." export_missing = "Some data was missing. Your items were not exported." export_no_formats = "This record does not support export." -export_redirect = "Start export to %%service%%" -export_refworks = "Start export to RefWorks" export_save = "Save File" export_selected = "Export Selected" export_selected_favorites = "Export Selected Favorites" -export_success = "Export Complete" +export_send = "Send to %%service%%" +export_success = "Export Ready" export_unsupported_format = "Unsupported Export Format" external_auth_heading = "Access to licensed material" external_auth_login_message = "Login to access licensed material" @@ -500,12 +510,15 @@ ill_request_processed = "Processed" ill_request_profile_html = "For interlibrary loan request information, please establish your <a href="%%url%%">Library Catalog Profile</a>." ill_request_submit_text = "Place Request" Illustrated = "Illustrated" +ils_account_create_error = "Your account could not be created in our library management system. If the problem persists, please contact your library." +ils_action_unavailable = "The requested function is not available with the active library card." ils_connection_failed = "Connection to the library management system failed. Information related to your library account cannot be displayed. If the problem persists, please contact your library." ils_offline_holdings_message = "Holdings and item availability information is currently unavailable. Please accept our apologies for any inconvenience this may cause and contact us for further assistance:" ils_offline_home_message = "Your account details and live item information will be unavailable during this time. Please accept our apologies for any inconvenience this may cause and contact us for further assistance:" ils_offline_login_message = "Your account details will be unavailable during this time. Please accept our apologies for any inconvenience this may cause and contact us for further assistance:" ils_offline_status = "Our Library Management System is currently under maintenance." ils_offline_title = "System Under Maintenance" +ils_transaction_history_disabled = "Loan history is not enabled for the active library card." Import Record = "Import Record" in = "in" In This Collection = "In This Collection" @@ -581,6 +594,8 @@ list_edit_name_required = "List name is required." list_item_delete = "Delete item from list" load_tag_error = "Error: Could Not Load Tags" Loading = "Loading" +Loan History = "Loan History" +loan_history_empty = "You do not have any loans in the loan history." Local Login = "Local Login" local_login_desc = "Enter the username and password you created for this site." Located = "Located" @@ -608,6 +623,7 @@ Message From Sender = "Message From Sender" Metadata Prefix = "Metadata Prefix" Microfilm = "Microfilm" MLA Citation = "MLA Citation" +Mobile Number = "Mobile Number" mobile_link = "You appear to be on a mobile device; switch to mobile view?" Monograph Title = "Monograph Title" more = "more" @@ -637,6 +653,8 @@ New Items = "New Items" New Title = "New Title" new_password = "New Password" new_password_success = "Your password has successfully been changed" +new_user_welcome_subject = "Your new account at %%library%%" +new_user_welcome_text = "Welcome to %%library%%. A new account has been opened for %%firstname%% %%lastname%%. Your username is %%username%%. Please set a password on this page: %%url%%" Newspaper = "Newspaper" Next = "Next" No citations are available for this record = "No citations are available for this record" @@ -786,6 +804,7 @@ Range slider = "Range slider" Read the full review online... = "Read the full review online..." Recall This = "Recall This" recaptcha_not_passed = "CAPTCHA not passed" +recently_returned_channel_title = "Recently Returned" Record Citations = "Record Citations" Record Count = "Record Count" Record Type = "Record Type" @@ -803,8 +822,17 @@ recovery_new_disabled = "You are not allowed to change your password at this tim recovery_title = "Password Recovery" recovery_too_soon = "Too many recovery requests have been made, please try again later" recovery_user_not_found = "We could not find your account" +rectangle_center_message = "This is the center point for the highlighted rectangle" Refine Results = "Refine Results" Region = "Region" +relais_available = "This item is available through Interlibrary Loan. Would you like to request it?" +relais_checking = "Checking availability..." +relais_error_html = "There was a problem with this request. Click <a href="%%url%%" target='new'>here</a> to request this item using the Interlibrary Loan website.</a>" +relais_request = "Interlibrary Loan Request" +relais_requesting = "Requesting..." +relais_search = "Search Interlibrary Loan" +relais_success_label = "Confirmation:" +relais_success_message = "Request id #%%id%% was created. You will receive a confirmation email." Related Author = "Related Author" Related Items = "Related Items" Related Subjects = "Related Subjects" @@ -833,12 +861,14 @@ Requests = "Requests" Reserves = "Reserves" Reserves Search = "Reserves Search" Reserves Search Results = "Reserves Search Results" +result_checkbox_label = "Select result number %%number%%" result_count = "%%count%% results" Results = "Results" results = "results" Results for = "Results for" Results per page = "Results per page" Resumption Token = "Resumption Token" +Return Date = "Return Date" Review by = "Review by" Reviews = "Reviews" Save = "Save" @@ -910,8 +940,14 @@ sort_author = "Author" sort_author_author = "Alphabetical" sort_author_relevance = "Popularity" sort_callnumber = "Call Number" +sort_checkout_date_asc = "Checkout Date (oldest first)" +sort_checkout_date_desc = "Checkout Date (newest first)" sort_count = "Result Count" +sort_due_date_asc = "Due Date (oldest first)" +sort_due_date_desc = "Due Date (newest first)" sort_relevance = "Relevance" +sort_return_date_asc = "Return Date (oldest first)" +sort_return_date_desc = "Return Date (newest first)" sort_title = "Title" sort_year = "Date Descending" sort_year asc = "Date Ascending" @@ -1025,6 +1061,7 @@ total_tags = "Total Tags" total_users = "Total Users" Transliterated Title = "Transliterated Title" tree_search_limit_reached_html = "Your search returned too many results to display in the tree. Showing only the first <b>%%limit%%</b> items. For a full search click <a id="fullSearchLink" href="%%url%%" target="_blank">here.</a>" +trending_items_channel_title = "Trending Items" unique_tags = "Unique Tags" University Library = "University Library" Unknown = "Unknown" diff --git a/languages/es.ini b/languages/es.ini index 3afebc22a0aa3d8ebb35d24f3d6fb7d29ff944dd..0f2309e4c1c7cb5d1abf80e3e84d87f534a45a20 100644 --- a/languages/es.ini +++ b/languages/es.ini @@ -94,6 +94,7 @@ Back to Search Results = "Volver a Resultados de Búsqueda" Backtrace = "Rastrear" Bag = "Bolsa" Balance = "Balance" +Barcode = "Código de Barras" basic_search_keep_filters = "retener mis filtros" Be the first to leave a comment = "Sea el primero en dejar un comentario" Be the first to tag this record = "Sea el primero en etiquetar este registro" @@ -183,6 +184,7 @@ Check Recall = "Comprobar Solicitud" Checked Out = "Prestado" Checked Out Items = "Copias Prestadas" Checkedout = "Prestados" +Checkout Date = "Fecha de Préstamo" Chicago Citation = "Citación estilo Chicago" child_record_count = "Registros %%count%%" child_records = "Contenido/piezas" @@ -207,6 +209,7 @@ collection_disambiguation = "Se han encontrado múltiples coincidencias de colec collection_empty = "No hay elementos a desplegar." collection_view_record = "Ver registro" Collections = "Colecciones" +comment_anonymous_user = "Anónimo" comment_error_load = "Error: no podemos visualizar comentario" comment_error_save = "Error: no podemos guardar comentario" Comments = "Comentarios" @@ -257,6 +260,11 @@ date_year_placeholder = "A" Debug Information = "Información Depurada" del_search = "Borrar Grupo de Búsqueda" Delete = "Borrar" +delete_account_confirm = "¿Seguro que quieres eliminar tu cuenta?" +delete_account_description_html = "Las búsquedas guardadas y listas de favoritos se eliminarán. Aún puede establecer una nueva cuenta más adelante si lo desea" +delete_account_failure = "Error al eliminar la cuenta" +delete_account_success_message = "Tu cuenta ha sido eliminada. Saliendo de tu cuenta..." +delete_account_title = "Borrar Cuenta" delete_all = "Borrar Todo" delete_comment_failure = "No se puedo eliminar comentario." delete_comment_success = "Comentario eliminado." @@ -278,6 +286,8 @@ Document Type = "Tipo de Documento" DOI = "DOI" doi_detected_html = "Su búsqueda parece contener un DOI. Haga clic aquà para ver la disponibilidad del recurso: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Dibujar cajón de búqueda" +draw_searchbox_end = "Suelte el mouse para finalizar el dibujo." +draw_searchbox_start = "Haga clic y arrastre para dibujar un rectángulo." Due = "Vencimiento" Due Date = "Fecha de Vencimiento" DVD = "DVD" @@ -344,11 +354,10 @@ export_fail = "Sus elementos no fueron exportados." export_invalid_format = "Formato de exportación no válido." export_missing = "Algún dato falta. Sus elementos no fueron exportados" export_no_formats = "Este registro no soporta exportación." -export_redirect = "Iniciar exportación a %%service%%" -export_refworks = "Exportar a RefWorks" export_save = "Guardar Archivo" export_selected = "Exportar los Seleccionados" export_selected_favorites = "Exportar los Favoritos Seleccionados." +export_send = "Iniciar exportación a %%service%%" export_success = "Exportación Completada" export_unsupported_format = "Formato de Exportación no soportado." external_auth_heading = "Acceso a material licenciado" @@ -501,12 +510,14 @@ ill_request_processed = "Procesado" ill_request_profile_html = "Para información de solicitud de préstamo interbibliotecario, por favor establecer su <a href="%%url%%">Perfil del Catálogo de la biblioteca</a>" ill_request_submit_text = "Lugar de Solicitud" Illustrated = "Ilustrado" +ils_action_unavailable = "La función solicitada no está disponible con la tarjeta de biblioteca activa." ils_connection_failed = "Nuestro Sistema de Biblioteca se encuentra en mantenimiento." ils_offline_holdings_message = "En este momento no hay información de existencias y disponibilidad de copias. Por favor acepte nuestras disculpas por los inconvenientes causados, contáctenos para una mayor información." ils_offline_home_message = "El detalle de su cuenta e información de sus elementos podrÃan no estar disponibles en este momento. Por favor acepte nuestras disculpas por los inconvenientes causados, contáctenos para una mayor información." ils_offline_login_message = "El detalle de su cuenta podrÃa no estar disponible en este momento. Por favor acepte nuestras disculpas por los inconvenientes causados, contáctenos para una mayor información." ils_offline_status = "Nuestro Sistema de Biblioteca se encuentra en mantenimiento." ils_offline_title = "Sistema en mantenimiento" +ils_transaction_history_disabled = "El historial de préstamos no está habilitado para la tarjeta de biblioteca activa." Import Record = "Registro Importado" in = "en" In This Collection = "En esta colección" @@ -581,6 +592,8 @@ list_access_denied = "Sin permiso para ver esta lista" list_edit_name_required = "Nombre de lista es requerido." load_tag_error = "Error: No podrá cargar etiquetas" Loading = "Cargando" +Loan History = "Historial de Préstamos" +loan_history_empty = "No tiene ningún préstamo en el historial de préstamos." Local Login = "Ingreso Local" local_login_desc = "Ingresar nombre de usuario y contraseña creado para este sitio." Located = "Ubicado" @@ -608,6 +621,7 @@ Message From Sender = "Mensaje del Remitente" Metadata Prefix = "Prefijo de metadatos" Microfilm = "Microfilme" MLA Citation = "Cita MLA" +Mobile Number = "Número de teléfono móvil" mobile_link = "Parece estar en un dispositivo móvil; cambiar a vista de móvil?" Monograph Title = "TÃtulo de MonografÃa" more = "más" @@ -637,6 +651,8 @@ New Items = "Nuevos ejemplares" New Title = "Nuevo TÃtulo" new_password = "Nueva Contraseña" new_password_success = "Su contraseña ha sido cambiada con éxito" +new_user_welcome_subject = "Su nueva cuenta en %%library%%" +new_user_welcome_text = "Bienvenido a %%library%%. Se ha abierto una nueva cuenta para %%firstname%% %%lastname%%. Tu nombre de usuario es %%username%%. Establezca una contraseña en esta página: %%url%%" Newspaper = "Periódico" Next = "Siguiente" No citations are available for this record = "No hay citas disponibles para este registro" @@ -786,6 +802,7 @@ Range slider = "Rango de deslizamiento" Read the full review online... = "Leer en lÃnea la crÃtica completa..." Recall This = "Recordar esto" recaptcha_not_passed = "CAPTCHA inválida" +recently_returned_channel_title = "Recientemente devuelto" Record Citations = "Registro de Citas" Record Count = "Conteo de registro" Record Type = "Tipo de registro" @@ -803,8 +820,17 @@ recovery_new_disabled = "Está impedido de cambiar su contraseña en este moment recovery_title = "Contraseña recuperada" recovery_too_soon = "Se han realizado demasiadas solicitudes de recuperación, por favor inténtelo más tarde" recovery_user_not_found = "No hemos podido encontrar su cuenta" +rectangle_center_message = "Este es el punto central del rectángulo resaltado" Refine Results = "Limitar resultados" Region = "Región" +relais_available = "Este Ãtem está disponible a través de préstamo interbibliotecario. ¿Te gustarÃa solicitarlo?" +relais_checking = "Comprobando disponibilidad..." +relais_error_html = "Hubo un problema con esta solicitud. Haga clic <a href="%%url%%" target='new'>aquÃ</a> para solicitar este Ãtem utilizando el sitio web de préstamo interbibliotecario." +relais_request = "Solicitud de préstamo interbibliotecario" +relais_requesting = "Solicitando ..." +relais_search = "Buscar de préstamo interbibliotecario" +relais_success_label = "Confirmación:" +relais_success_message = "Se ha creado el id solicitado %%id%%. Recibirá un email de confirmación." Related Author = "Autor relacionado" Related Items = "Ejemplares relacionados" Related Subjects = "Materias Relacionadas" @@ -833,12 +859,14 @@ Requests = "Solicitar" Reserves = "Reservas" Reserves Search = "Buscar Reservas" Reserves Search Results = "Resultado de Búsquedas de Reservas" +result_checkbox_label = "Seleccione el número de resultado %%number%%" result_count = "%%count%% resultados" Results = "Resultados" results = "Resultados" Results for = "Resultados para" Results per page = "resultados por página" Resumption Token = "Reanudación de muestra" +Return Date = "Fecha de Devolución" Review by = "Reseña de" Reviews = "CrÃticas" Save = "Guardar" @@ -910,8 +938,14 @@ sort_author = "Autor" sort_author_author = "Alfabético" sort_author_relevance = "Popularidad" sort_callnumber = "Número de clasificación" +sort_checkout_date_asc = "Fecha de Préstamo (el más antiguo primero)" +sort_checkout_date_desc = "Fecha de Préstamo (el más reciente primero)" sort_count = "Resultados del conteo" +sort_due_date_asc = "Fecha de vencimiento (el más antiguo primero)" +sort_due_date_desc = "Fecha de vencimiento (la más reciente primero)" sort_relevance = "Relevancia" +sort_return_date_asc = "Fecha de devolución (el más antiguo primero)" +sort_return_date_desc = "Fecha de devolución (el más reciente primero)" sort_title = "TÃtulo" sort_year = "Fecha Descendente" sort_year asc = "Fecha Ascendente" @@ -1025,6 +1059,7 @@ total_tags = "Total de etiquetas" total_users = "Total de usuarios" Transliterated Title = "TÃtulo Transliterado" tree_search_limit_reached_html = "Su búsqueda tiene muchos resultados para desplegar en el árbol. Mostrando solo los primeros <b>%%limit%%</b> elementos. Para una búsqueda completa clic <a id="fullSearchLink" href="%%url%%" target="_blank">aquÃ.</a>" +trending_items_channel_title = "Itemes de tendencia" unique_tags = "Etiquetas únicas" University Library = "Biblioteca Universitaria" Unknown = "Desconocido" diff --git a/languages/eu.ini b/languages/eu.ini index 257ca16aba3006116715e431df1a4471265b74f1..629f2fab457fb0f72961d21412fbb656e99a5775 100644 --- a/languages/eu.ini +++ b/languages/eu.ini @@ -714,6 +714,7 @@ Back to Search Results = "Bilaketaren emaitzetara itzuli" Backtrace = "Aztarna" Bag = "Poltsa" Balance = "Balantzea" +Barcode = "Barra-kodea" basic_search_keep_filters = "Iragazkia indarrean dago" Be the first to leave a comment = "Izan zaitez lehena ohar bat uzten" Be the first to tag this record = "Izan zaitez lehena erregistro honi etiketa jartzen" @@ -803,6 +804,7 @@ Check Recall = "Comprobar Solicitud" Checked Out = "Maileguan" Checked Out Items = "Mailegatutako aleak" Checkedout = "Maileguan" +Checkout Date = "Mailegu-Data" Chicago Citation = "Chicago Style aipamena" child_record_count = "%%count%% erregistroak" child_records = "Edukiak/piezak" @@ -827,6 +829,7 @@ collection_disambiguation = "Bilduma gehiegi aurkitu dira" collection_empty = "Inongo item-ik ez dago ikusteko." collection_view_record = "Ikusi erregistroa" Collections = "Bildumak" +comment_anonymous_user = "Anonimoa" comment_error_load = "Errorea: ezin dugu iruzkina erakutsi" comment_error_save = "Errorea: ezin dugu iruzkina gorde" Comments = "Iruzkinak" @@ -877,6 +880,11 @@ date_year_placeholder = "A" Debug Information = "Arazketa-informazioa" del_search = "Ezabatu" Delete = "Ezabatu" +delete_account_confirm = "Seguru zaude zure kontua ezabatu nahi duzula?" +delete_account_description_html = "Gordetako bilaketak eta gogokoen zerrendak ezabatuko dira. Oraindik Kontu berri bat sor dezakezu geroago nahi baduzu." +delete_account_failure = "Akatsa kontua ezabatzerakoan." +delete_account_success_message = "Zure kontua ezabatu da. Zure kontutik irtenez..." +delete_account_title = "Kontua Ezabatu" delete_all = "Ezabatu guztia" delete_comment_failure = "Iruzkina agian ez dago ezabaturik." delete_comment_success = "Iruzkina ongi ezabatu da." @@ -898,6 +906,8 @@ Document Type = "Dokumentu mota" DOI = "DOI" doi_detected_html = "Badirudi zure bilaketak DOI duela. Egin klik hemen baliabidearen erabilgarritasun-egoera ikusteko: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Marraztu Bilatze Kaxa" +draw_searchbox_end = "Sagua askatu marraztea bukatzeko." +draw_searchbox_start = "Klik egin hemen eta arrastatu laukizuzen bat marrazteko." Due = "Noiz arte" Due Date = "Itzulketa data" DVD = "DVD" @@ -964,11 +974,10 @@ export_fail = "Zure dokumentuak esportatu dira" export_invalid_format = "Aukeratutako esportatze formatua ezin da aukeratu erregistro honetarako." export_missing = "Daturen bat falta da. Zure dokumentuak ez dira esportatu" export_no_formats = "Esportatzea ezin da erabili erregistro honekin." -export_redirect = "Hasi esportatzen hona%%service%%" -export_refworks = "Exportar a RefWorks" export_save = "Fitxategia gorde" export_selected = "Aukeratutakoak esportatu" export_selected_favorites = "Gogokoenak esportatu." +export_send = "Hasi esportatzen hona%%service%%" export_success = "Esportazioa burutua" export_unsupported_format = "Esportazio formatu hori ezin da erabili." external_auth_heading = "Baimendutako materialerako sarbidea" @@ -1121,12 +1130,14 @@ ill_request_processed = "Prozesatua" ill_request_profile_html = "Liburutegi arteko maileguari buruzo informazio gehiagorako, mesedez sartu zure liburutegi profila hemen <a href="%%url%%">Liburutegiaren katalogo profila</a>." ill_request_submit_text = "Egin eskaera" Illustrated = "Irudiduna" +ils_action_unavailable = "Eskatutako funtzioa ez dago erabiltzeko moduan liburutegi aktiboko txartelarekin." ils_connection_failed = "Mantentze lanak direla eta, gure Liburutegia Kudeatzeko Sistema ez dago erabilgarri." ils_offline_holdings_message = "Item-en erabilgarritasunari buruzko informazioa ez dabil momento honetan. Mesedez, barkatu eragozpenak. Nahi baduzu, kontakta dezakezu zerbitzu teknikoarekin laguntza gehiagorako:" ils_offline_home_message = "Zure kontuaren xehetasunei buruzko informazioa eta item informazio eguneratuta ezin da kontsultatu momentu honetan. Mesedez, barkatu eragozpenak. Nahi baduzu, kontakta dezakezu zerbitzu teknikoarekin laguntza gehiagorako:" ils_offline_login_message = "Zure kontuaren xehetasunei buruzko informazioa ez dabil. Mesedez, barkatu eragozpenak. Nahi baduzu, kontakta dezakezu zerbitzu teknikoarekin laguntza gehiagorako:" ils_offline_status = "Mantentze lanak direla eta, gure Liburutegia Kudeatzeko Sistema ez dago erabilgarri." ils_offline_title = "Sistema mantentze lanetan ari da" +ils_transaction_history_disabled = "Mailegu-Historia ez dago erabiltzeko moduan liburutegi aktiboko txartelerako." Import Record = "Erregistroa hartu" in = "non" In This Collection = "Bilduma honetan" @@ -1201,6 +1212,8 @@ list_access_denied = "Zerrenda hau ikusteko baimenik ez" list_edit_name_required = "Zerrendaren izena beharrezkoa." load_tag_error = "Errorea: etiketa ezin da kargatu." Loading = "Lanean" +Loan History = "Mailegu-Historia" +loan_history_empty = "ez duzu mailegurik mailegu-historian." Local Login = "Tokiko sarbidea" local_login_desc = "Idatzi hemen sartzeko zure erabiltzailearen izena eta pasahitza." Located = "Kokapena" @@ -1228,6 +1241,7 @@ Message From Sender = "Igorlearen mezua" Metadata Prefix = "Metadata Aurrizki" Microfilm = "Mikrofilma" MLA Citation = "MLA aipamena" +Mobile Number = "Telefono Mugikorra" mobile_link = "Badirudi mugikorretik zaudela konektatuta, mugikorretarako bista gaitu?" Monograph Title = "Monografiaren izenburua" more = "Gehiago" @@ -1257,6 +1271,8 @@ New Items = "Berriak liburutegietan" New Title = "Izenburu berria" new_password = "Pasahitza berria" new_password_success = "Zure pasahitza aldatu da" +new_user_welcome_subject = "Zure berri kontua %%library%%" +new_user_welcome_text = "Ongi etorria %%library%%. Kontu berri bat ireki da %%firstname%% %%lastname%%. zure erabiltzailea da %%username%%. Mesedez Konfiguratu orrialde honetan pasahitza: %%url%%" Newspaper = "Egunkaria" Next = "Hurrengoa" No citations are available for this record = "Erregistro honetarako ez dago oharrik" @@ -1406,6 +1422,7 @@ Range slider = "Rango de Deslizamiento" Read the full review online... = "Azterketa osoa linean irakurri" Recall This = "Jakinarazi" recaptcha_not_passed = "CAPTCHA ez da gainditu" +recently_returned_channel_title = "Oraintsu itzulita" Record Citations = "Dokumentuaren aipamena" Record Count = "Erregistro-zenbatzea" Record Type = "Erregistro mota" @@ -1423,8 +1440,17 @@ recovery_new_disabled = "Ezin duzu pasahitza aldatu une honetan" recovery_title = "Pasahitza berreskuratzea" recovery_too_soon = "Eskaera gehiegi, mesedez saiatu berriro geroago" recovery_user_not_found = "Ezin dugu aurkitu zure kontua" +rectangle_center_message = "Hau laukizuzen nabarmenduaren erdigunea da" Refine Results = "Findu emaitzak" Region = "Eskualdea" +relais_available = "Artikulu hau liburutegien arteko maileguaren bidez libre dago. Eskatzea gustatuko litzaizuke?" +relais_checking = "Erabilgarritasuna begiratuz..." +relais_error_html = "Eskaera honekin arazo bat egon zen. Klik egin <a href="%%url%%" target='new'>here</a> Eskatu artikulu hau liburutegien arteko maileguaren webgunean erabiliz." +relais_request = "Liburutegien arteko maileguaren eskaera" +relais_requesting = "Eskatuz..." +relais_search = "Liburutegien arteko mailegua bilatzea" +relais_success_label = "Egiaztapena:" +relais_success_message = "erabiltzailearen identifikatzaileko eskaera #%%id%% Sortu zen. Zuk egiaztapen posta elektroniko bat jasoko duzu." Related Author = "Antzeko egilea" Related Items = "Antzeko dokumentuak" Related Subjects = "Antzeko gaiak" @@ -1453,12 +1479,14 @@ Requests = "Eskaerak" Reserves = "Erreserbak" Reserves Search = "Erresenben bilaketa" Reserves Search Results = "Signaturaren araberako bilaketaren emaitza" +result_checkbox_label = "Aukera ezazu emaitza-zenbakia%%number%%" result_count = "%%count%% emaitzak" Results = "Emaitzak" results = "emaitzak" Results for = "Emaitzak" Results per page = "Orren araberako emaitza" Resumption Token = "Berrekitzeko Token-a" +Return Date = " Itzultzeko data" Review by = "Berraztertu" Reviews = "Iruzkinak" Save = "Gorde" @@ -1530,8 +1558,14 @@ sort_author = "Egilea" sort_author_author = "Alfabetikoa" sort_author_relevance = "Onarpena" sort_callnumber = "Sailkapena" +sort_checkout_date_asc = "Mailegu-Data (zaharrena lehenik)" +sort_checkout_date_desc = "Mailegu-Data (Berriena lehenik)" sort_count = "Emaitzen kontaketa" +sort_due_date_asc = "Epemuga (Zaharrena lehenik)" +sort_due_date_desc = "Epemuga (berriena lehenik)" sort_relevance = "Garrantzia" +sort_return_date_asc = " Itzultzeko data (zaharrena lehenik)" +sort_return_date_desc = " Itzultzeko data (berriena lehenik)" sort_title = "Izenburua" sort_year = "Berrienatik atzera" sort_year asc = "Zaharrenetik aurrera" @@ -1645,6 +1679,7 @@ total_tags = "Etiketen guztizkoa" total_users = "Erabiltzaileen guztizkoa" Transliterated Title = "Titulu letraldatua" tree_search_limit_reached_html = "Zure bilaketak aurkitu zituen erakusteko emaitza gehiegi. Erakusten dira bakarrik lehenengo <b>%%limit%%</b> items. Emaitza gehiago ikusteko, sakatu <a id="fullSearchLink" href="%%url%%" target="_blank">here.</a>" +trending_items_channel_title = "Artikulu-Joerak" unique_tags = "Etiketa bakarrak" University Library = "Liburutegi unibertsitarioa" Unknown = "Ezezaguna" diff --git a/languages/fi.ini b/languages/fi.ini index a024e5bcfdbaf4d3838c524a1c6ee90e3df91a3f..84e21ab0b20d264878a2ba87c9e0c9db2d9975b3 100644 --- a/languages/fi.ini +++ b/languages/fi.ini @@ -92,6 +92,7 @@ Back to Search Results = "Takaisin hakutuloksiin" Backtrace = "Kutsupino" Bag = "Kori" Balance = "Maksettavaa" +Barcode = "Viivakoodi" basic_search_keep_filters = "Säilytä käytössä oleva rajaus" Be the first to leave a comment = "Lisää ensimmäinen kommentti" Be the first to tag this record = "Lisää ensimmäinen tagi" @@ -181,6 +182,7 @@ Check Recall = "Tarkista varaus" Checked Out = "Lainat" Checked Out Items = "Lainat" Checkedout = "Lainat" +Checkout Date = "Lainauspäivä" Chicago Citation = "Chicago-tyylinen lähdeviittaus" child_record_count = "%%count%% tietuetta" child_records = "Sisältö/kappaleet" @@ -205,6 +207,7 @@ collection_disambiguation = "Löytyi useita vastaavia kokoelmia" collection_empty = "Ei näytettävää." collection_view_record = "Näytä tietue" Collections = "Kokoelmat" +comment_anonymous_user = "Anonyymi" comment_error_load = "Virhe: Kommenttilistaa ei voitu muuttaa" comment_error_save = "Virhe: Kommenttia ei voitu tallentaa" Comments = "Kommentit" @@ -261,6 +264,11 @@ date_year_placeholder = "V" Debug Information = "Virheraportti" del_search = "Poista hakuryhmä" Delete = "Poista" +delete_account_confirm = "Haluatko varmasti poistaa tilisi?" +delete_account_description_html = "Tallennetut haut ja suosikkilistat poistetaan. Halutessasi voit luoda uuden tilin myöhemmin." +delete_account_failure = "Tilin poistaminen epäonnistui." +delete_account_success_message = "Tili poistettu. Kirjaudutaan ulos..." +delete_account_title = "Poista tili" delete_all = "Poista kaikki" delete_comment_failure = "Kommentin poistaminen epäonnistui." delete_comment_success = "Kommentti poistettu." @@ -282,6 +290,8 @@ Document Type = "Dokumentin tyyppi" DOI = "DOI" doi_detected_html = "Hakusi näyttää sisältävän DOI-tunnisteen. Klikkaa tästä tarkistaaksesi saatavuuden: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Valitse alue" +draw_searchbox_end = "Lopeta raahaaminen, kun valinta on valmis." +draw_searchbox_start = "Klikkaa ja raahaa valitaksesi alueen." Due = "Eräpäivä" Due Date = "Eräpäivä" DVD = "DVD" @@ -348,11 +358,10 @@ export_fail = "Tietueiden vienti ei onnistunut" export_invalid_format = "Valittu vientimuoto ei ole käytettävissä tälle aineistolle." export_missing = "Tietoja puuttuu. Tietueiden vienti ei onnistunut." export_no_formats = "Tämä aineisto ei tue vientitoimintoa." -export_redirect = "Aloita vienti kohteeseen %%service%%" -export_refworks = "Vie RefWorksiin" export_save = "Tallenna tiedosto" export_selected = "Vie valitut" export_selected_favorites = "Vie valitut suosikit" +export_send = "Lähetä palveluun %%service%%" export_success = "Vienti valmis" export_unsupported_format = "Vienti valitussa muodossa ei onnistu" external_auth_heading = "Pääsy lisensioituun aineistoon" @@ -505,12 +514,14 @@ ill_request_processed = "Käsitelty" ill_request_profile_html = "Kirjaudu <a href="%%url%%">kirjastokortilla</a> nähdäksesi kaukolainatilaukset." ill_request_submit_text = "Tee kaukolainatilaus" Illustrated = "Kuvitus" +ils_action_unavailable = "Toiminto ei ole saatavissa käytössä olevalla kirjastokortilla." ils_connection_failed = "Kirjastojärjestelmään ei saatu yhteyttä. Tietoja, jotka liittyvät tiliisi kirjastossa, ei voida näyttää. Jos ongelma jatkuu, ota yhteyttä kirjastoon." ils_offline_holdings_message = "Saatavuustiedot eivät ole juuri nyt käytettävissä. Pahoittelemme tästä aiheutunutta vaivaa. Voitte ottaa yhteyttä:" ils_offline_home_message = "Tilitietosi ja ajantasaiset saatavuustiedot ovat poissa käytöstä tämän ajan. Pahoittelemme tästä aiheutunutta vaivaa. Voitte ottaa yhteyttä:" ils_offline_login_message = "Tilitietosi ovat poissa käytöstä tämän ajan. Pahoittelemme tästä aiheutunutta vaivaa. Voitte ottaa yhteyttä:" ils_offline_status = "Kirjastojärjestelmä on juuri nyt pois käytöstä." ils_offline_title = "Järjestelmä pois käytöstä" +ils_transaction_history_disabled = "Lainaushistoriaa ei ole otettu käyttöön valitulla kirjastokortilla." Import Record = "Tuo tietue" in = "kentästä" In This Collection = "Tässä kokoelmassa" @@ -585,6 +596,8 @@ list_access_denied = "Sinulla ei ole oikeuksia katsoa tätä listaa." list_edit_name_required = "Listan nimi tarvitaan." load_tag_error = "Virhe: Tagien lataaminen epäonnistui" Loading = "Lataa" +Loan History = "Lainaushistoria" +loan_history_empty = "Ei tietoja lainaushistoriassa." Local Login = "Paikallinen kirjautuminen" local_login_desc = "Syötä käyttäjätunnus ja salasana, jotka loit tätä sivustoa varten." Located = "Sijainti" @@ -612,6 +625,7 @@ Message From Sender = "Viesti lähettäjältä" Metadata Prefix = "Metadatan etuliite" Microfilm = "Mikrofilmi" MLA Citation = "MLA-viite" +Mobile Number = "Matkapuhelin" mobile_link = "Vaikuttaa siltä, että käytät mobiililaitetta. Siirrytäänkö mobiiliversioon?" Monograph Title = "Kirjan nimi" more = "lisää" @@ -641,6 +655,8 @@ New Items = "Uutuusluettelo" New Title = "Uusi nimeke" new_password = "Uusi salasana" new_password_success = "Salasana vaihdettu" +new_user_welcome_subject = "Uusi tili organisaatiossa %%library%%" +new_user_welcome_text = "Tervetuloa käyttäjäksi organisaatioon %%library%%. Uusi tili on luotu nimellä %%firstname%% %%lastname%%. Käyttäjätunnuksesi on %%username%%. Aseta salasana seuraavalla sivulla: %%url%%" Newspaper = "Sanomalehti" Next = "Seuraava" No citations are available for this record = "Ei lainauksia saatavissa tälle tietueelle" @@ -790,6 +806,7 @@ Range slider = "Välin säätö" Read the full review online... = "Lue arvostelu kokonaisuudessaan..." Recall This = "Tee varaus" recaptcha_not_passed = "CAPTCHA-tarkistus virheellinen" +recently_returned_channel_title = "Äskettäin palautetut" Record Citations = "Tietueen sitaatit" Record Count = "Tietueiden lukumäärä" Record Type = "Tietueen tyyppi" @@ -807,6 +824,7 @@ recovery_new_disabled = "Salasanan vaihtaminen ei ole sallittua tällä hetkell recovery_title = "Salasanan palautus" recovery_too_soon = "Liian monta salasanan palautusyritystä. Yritä myöhemmin uudelleen." recovery_user_not_found = "Tiliäsi ei löytynyt" +rectangle_center_message = "Valitun alueen keskikohta" Refine Results = "Tarkenna hakua" Region = "Alue" Related Author = "Liittyvä tekijä" @@ -837,12 +855,14 @@ Requests = "Varauksia" Reserves = "Rajatut" Reserves Search = "Rajattujen haku" Reserves Search Results = "Rajattujen haun tulokset" +result_checkbox_label = "Valitse hakutulos %%number%%" result_count = "%%count%% tulosta" Results = "Tulokset" results = "tuloksesta" Results for = "Tulokset haulle" Results per page = "Tuloksia sivulla" Resumption Token = "Resumption Token" +Return Date = "Palautuspäivä" Review by = "Arvostellut" Reviews = "Arvostelut" Save = "Tallenna" @@ -914,8 +934,14 @@ sort_author = "Tekijä" sort_author_author = "Aakkosellinen" sort_author_relevance = "Relevanssi" sort_callnumber = "Luokka" +sort_checkout_date_asc = "Lainauspäivä (vanhin ensin)" +sort_checkout_date_desc = "Lainauspäivä (uusin ensin)" sort_count = "Lukumäärä" +sort_due_date_asc = "Eräpäivä (vanhin ensin)" +sort_due_date_desc = "Eräpäivä (uusin ensin)" sort_relevance = "Relevanssi" +sort_return_date_asc = "Palautuspäivä (vanhin ensin)" +sort_return_date_desc = "Palautuspäivä (uusin ensin)" sort_title = "Nimeke" sort_year = "Aika (uusimmat ensin)" sort_year asc = "Aika (vanhimmat ensin)" @@ -1029,6 +1055,7 @@ total_tags = "Tageja yhteensä" total_users = "Käyttäjiä yhteensä" Transliterated Title = "Translitteroitu nimeke" tree_search_limit_reached_html = "Hakusi tuotti liikaa tuloksia puussa näytettäväksi. Näytetään vain ensimmäiset <b>%%limit%%</b> kohdetta. Klikkaa <a id="fullSearchLink" href="%%url%%" target="_blank">tästä</a> nähdäksesi koko hakutuloksen." +trending_items_channel_title = "Nousussa" unique_tags = "Yksilöllisiä tageja yhteensä" University Library = "Yliopiston kirjasto" Unknown = "Tuntematon" diff --git a/languages/fr.ini b/languages/fr.ini index e7617f58d4a14566612045bd3a588b4b5af7c1dd..548303f33b70dfac06dc49c3bfba17f094fa557a 100644 --- a/languages/fr.ini +++ b/languages/fr.ini @@ -3,7 +3,7 @@ Abstract = "Résumé" Access = "Accès" Access URL = "URL d'accès" -access_denied = "Accès interdit." +access_denied = "Accès non autorisé." Accession Number = "Numéro d'inventaire" Account = "Compte lecteur" account_block_options_missing = "Certains choix ont été retirés en raison d'un blocage sur votre compte. Détails : %%details%%" @@ -93,6 +93,7 @@ Back to Search Results = "Retourner aux résultats de recherche" Backtrace = "Historique" Bag = "Panier" Balance = "Solde du compte" +Barcode = "Code-barres" basic_search_keep_filters = "Je garde les filtres." Be the first to leave a comment = "Soyez le premier à ajouter un commentaire" Be the first to tag this record = "Soyez le premier à ajouter un tag" @@ -172,14 +173,17 @@ catalog_login_desc = "Entrez vos codes d'accès au catalogue" CD = "CD" Change Password = "Modifier le mot de passe" channel_add_more = "Afficher davantage de canaux de ce type" +channel_browse = "Afficher davantage de notices" channel_expand = "Explorer des canaux similaires" channel_explore = "Explorer avec les canaux" channel_search = "Afficher les éléments dans la liste de résultats" +channel_searchbox_label = "Chercher d'autres canaux:" Check Hold = "Voir les réservations" Check Recall = "Voir les rappels" Checked Out = "Emprunté" Checked Out Items = "Documents empruntés" Checkedout = "Documents empruntés" +Checkout Date = "Date d'emprunt" Chicago Citation = "Style de citation Chicago" child_record_count = "%%count%% notices" child_records = "Contenu" @@ -204,6 +208,7 @@ collection_disambiguation = "Plusieurs collections correspondantes ont été tro collection_empty = "Pas d'éléments à afficher." collection_view_record = "Voir la notice" Collections = "Collections" +comment_anonymous_user = "Anonyme" comment_error_load = "Erreur: Impossible de mettre à jour la liste des commentaires" comment_error_save = "Erreur: Enregistrement du commentaire impossible" Comments = "Commentaires" @@ -254,6 +259,11 @@ date_year_placeholder = "A" Debug Information = "Debug informations" del_search = "Effacer un groupe de recherche" Delete = "Supprimer" +delete_account_confirm = "Êtes-vous sûr-e de vouloir supprimer votre compte ?" +delete_account_description_html = "Les recherches enregistrées et les listes de favoris seront supprimés. Si vous le souhaitez, vous pourrez créer un nouveau compte par la suite." +delete_account_failure = "Échec lors de la suppression du compte." +delete_account_success_message = "Votre compte a été supprimé. Déconnexion en cours..." +delete_account_title = "Supprimer le compte" delete_all = "Tout supprimer" delete_comment_failure = "Suppression impossible." delete_comment_success = "Commentaire supprimé." @@ -275,6 +285,8 @@ Document Type = "Type de document" DOI = "DOI" doi_detected_html = "Il semble que votre recherche contienne un DOI. Pour vérifier la disponibilité en ligne de cette ressource, cliquez ici: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Dessiner la boîte de recherche" +draw_searchbox_end = "Relâchez la souris pour terminer le dessin." +draw_searchbox_start = "Cliquez et tirez la souris pour tracer un rectangle." Due = "Délais" Due Date = "Délais d'emprunt" DVD = "DVD" @@ -287,6 +299,7 @@ edit_list_fail = "Hélas vous n'êtes pas autorisé à modifier cette liste" edit_list_success = "La liste a été mise à jour avec succès." Edition = "Édition" eds_expander_fulltext = "Chercher aussi à l'intérieur des articles" +eds_expander_relatedsubjects = "Élargir à des sujets en relation" eds_expander_thesaurus = "Appliquer les termes associés" eds_limiter_FC = "Seulement le catalogue" eds_limiter_FC1 = "Seulement le dépôt institutionnel" @@ -340,11 +353,10 @@ export_fail = "Les données n'ont pas été exportées" export_invalid_format = "Ce format d'export n'est pas disponible pour la notice." export_missing = "Certaines informations étaient manquantes, les données n'ont pas été exportées." export_no_formats = "Cette notice ne peut être exportée." -export_redirect = "Démarrer l'export vers %%service%%" -export_refworks = "Démarrer l'export vers Refworks" export_save = "Enregistrer le fichier" export_selected = "Exporter la sélection" export_selected_favorites = "Exporter les favoris sélectionnés" +export_send = "Démarrer l'export vers %%service%%" export_success = "Export terminé" export_unsupported_format = "Format d'export non supporté" external_auth_heading = "Accès aux contenus qui font l'objet d'une licence" @@ -497,12 +509,14 @@ ill_request_processed = "Traité" ill_request_profile_html = "Pour les informations sur les demandes de prêt entre bibliothèques, merci de créer un <a href="%%url%%">profil dans le catalogue de la bibliothèque</a>." ill_request_submit_text = "Faire une demande" Illustrated = "Illustré" +ils_action_unavailable = "La fonctionnalité demandée n'est pas disponible avec la carte de bibliothèque utilisée." ils_connection_failed = "Notre système de gestion de bibliothèque est en cours de maintenance." ils_offline_holdings_message = "Les réservations et la disponibilité des documents sont momentanément indisponibles. Veuillez nous excuser pour la gêne occasionnée et nous contacter pour toute assistance :" ils_offline_home_message = "Votre compte personnel et les données sur vos emprunts seront indisponibles pendant ce temps. Veuillez nous excuser pour la gêne occasionnée et nous contacter pour toute assistance :" ils_offline_login_message = "Votre compte personnel sera indisponible pendant ce temps. Veuillez nous excuser pour la gêne occasionnée et nous contacter pour toute assistance :" ils_offline_status = "Notre système de gestion de bibliothèque est en cours de maintenance." ils_offline_title = "Opération de maintenance en cours" +ils_transaction_history_disabled = "L'historique des emprunts n'est pas activé pour la carte de bibliothèque utilisée." Import Record = "Importer les notices" in = "dans" In This Collection = "Dans cette collection" @@ -577,6 +591,8 @@ list_access_denied = "Vous n'avez pas les droits pour consulter cette liste." list_edit_name_required = "Le nom de la liste est obligatoire." load_tag_error = "Erreur: Chargement des tags impossible" Loading = "Chargement en cours" +Loan History = "Historique des emprunts" +loan_history_empty = "Il n'y a pas d'emprunts dans votre historique." Local Login = "Identifiant local" local_login_desc = "Entrez l'identifiant et le mot de passe que vous avez créés pour ce site." Located = "Localisé" @@ -604,6 +620,7 @@ Message From Sender = "Message de l'expéditeur " Metadata Prefix = "Préfixe des métadonnées" Microfilm = "Microfilm" MLA Citation = "Style de citation MLA" +Mobile Number = "Numéro de téléphone portable" mobile_link = "Vous semblez naviguer depuis un appareil mobile, voulez-vous basculer vers l'interface mobile ?" Monograph Title = "Titre de monographie" more = "plus" @@ -624,6 +641,7 @@ My Holds = "Mes réservations" My Profile = "Mon profil" Narrow Search = "Restreindre la recherche" navigate_back = "Retour" +nearby_items = "Documents proches de "%%title%%"" Need Help? = "Besoin d'aide ?" New Item Feed = "Flux RSS des nouveautés" New Item Search = "Recherche des nouveautés" @@ -632,6 +650,8 @@ New Items = "Nouveautés" New Title = "Nouveau titre" new_password = "Nouveau mot de passe" new_password_success = "Mot de passe mis à jour avec succès" +new_user_welcome_subject = "Votre nouveau compte à la %%library%%" +new_user_welcome_text = "Bienvenue à %%library%%. Un nouveau compte a été ouvert pour %%firstname%% %%lastname%%. Votre nom d'utilisateur est %%username%%. Merci de créer un mot de passe sur cette page: %%url%%" Newspaper = "Journal" Next = "Suivant" No citations are available for this record = "Pas de citation disponible pour cette notice" @@ -729,6 +749,8 @@ past_days = "Depuis %%range%% Jours" PDF Full Text = "Texte intégral en PDF" peer_reviewed = "Validé par les pairs" peer_reviewed_limit = "Limiter aux articles de revues à comité de lecture" +permission_denied = "Vous n'avez pas la permission d'accéder à la page ou à la fonctionnalité demandée." +permission_denied_title = "Autorisation refusée" Phone Number = "Numéro de téléphone" Photo = "Photo" Physical Description = "Description matérielle" @@ -779,6 +801,7 @@ Range slider = "Curseur" Read the full review online... = "Lire l'avis complet en ligne..." Recall This = "Rappeler" recaptcha_not_passed = "L'identification du CAPTCHA a échoué" +recently_returned_channel_title = "Revenus récemment" Record Citations = "Citations de notices" Record Count = "Nombre de notices" Record Type = "Type de notice" @@ -796,8 +819,17 @@ recovery_new_disabled = "Vous n'êtes pas autorisé à modifier votre mot de pas recovery_title = "Récupération de mot de passe" recovery_too_soon = "Trop d'essais de récupération de mot de passe. Veuillez réessayer plus tard" recovery_user_not_found = "Nous n'avons pu identifier votre compte" +rectangle_center_message = "Point central du rectangle surligné" Refine Results = "Affiner les résultats" Region = "Région" +relais_available = "Ce document est disponible par le Prêt entre bibliothèques. Voulez-vous faire une demande ?" +relais_checking = "Vérification de la disponibilité..." +relais_error_html = "Un problème est arrivé pendant votre demande. Cliquez <a href="%%url%%" target='new'>ici</a> pour demander ce document en utilisant le site Web du Prêt entre bibliothèques." +relais_request = "Demande de prêt entre bibliothèques" +relais_requesting = "Demande en cours..." +relais_search = "Recherche pour le Prêt entre bibliothèques" +relais_success_label = "Confirmation:" +relais_success_message = "La demande numéro #%%id%% a été créée. Vous recevrez un courriel de confirmation." Related Author = "Auteurs similaires" Related Items = "Documents similaires" Related Subjects = "Sujets similaires" @@ -826,12 +858,14 @@ Requests = "Demandes" Reserves = "Réserves" Reserves Search = "Rechercher dans les réserves" Reserves Search Results = "Résultats de la recherche dans les réserves" +result_checkbox_label = "Merci de choisir la recherche numéro %%number%%" result_count = "%%count%% résultats" Results = "Résultats" results = "résultats" Results for = "Résultats de" Results per page = "Résultats par page" Resumption Token = "Jeton (Resumption Token)" +Return Date = "Date de retour" Review by = "Recension de" Reviews = "Recensions" Save = "Sauvegarder" @@ -903,8 +937,14 @@ sort_author = "Auteur" sort_author_author = "Alphabétique" sort_author_relevance = "Pertinence" sort_callnumber = "Cote" +sort_checkout_date_asc = "Date d'emprunt (par ordre croissant)" +sort_checkout_date_desc = "Date d'emprunt (par ordre décroissant)" sort_count = "Nombre de résultats" +sort_due_date_asc = "Date de retour prévue (par ordre croissant)" +sort_due_date_desc = "Date de retour prévue (par ordre décroissant)" sort_relevance = "Pertinence" +sort_return_date_asc = "Date de retour (par ordre croissant)" +sort_return_date_desc = "Date de retour (par ordre décroissant)" sort_title = "Titre" sort_year = "Date (décroissante)" sort_year asc = "Date (croissante)" @@ -1018,6 +1058,7 @@ total_tags = "Nombre total de tags" total_users = "Nombre total d'utilisateurs" Transliterated Title = "Titre translittéré" tree_search_limit_reached_html = "Trop de réponses pour l'affichage arborescent. Seuls les <b>%%limit%%</b> premiers éléments sont affichés. Pour une recherche complète, cliquez <a id="fullSearchLink" href="%%url%%" target="_blank">ici.</a>" +trending_items_channel_title = "Documents populaires" unique_tags = "Tags uniques" University Library = "Bibliothèque universitaire" Unknown = "Inconnue" diff --git a/languages/ga.ini b/languages/ga.ini index ac06a1e5e25d610e6ff3bceb00c9ae418ade9a31..1d9c9d4f863f80999c68c6d39f135c4d1cccb4c2 100644 --- a/languages/ga.ini +++ b/languages/ga.ini @@ -219,7 +219,6 @@ export_download = "Ãoslódáil Comhad" export_exporting = "Comhad Easpórtála á Chruthú" export_fail = "NÃor easpórtáladh do chuid mÃreanna" export_missing = "Bhà roinnt sonraà in easnamh. NÃor easpórtáladh do chuid mÃreanna." -export_refworks = "Tosaigh an Easpórtáil go Refworks" export_save = "Sábháil Comhad" export_selected = "Easpórtáil MÃreanna Roghnaithe" export_selected_favorites = "Easpórtáil Ceanáin Roghnaithe" diff --git a/languages/gl.ini b/languages/gl.ini index 2098bde5bb1a32f6ce83330d51f01e377ca1a9ee..3d49a0d670069da18c0a95024b26fca2333d6c17 100644 --- a/languages/gl.ini +++ b/languages/gl.ini @@ -322,11 +322,10 @@ export_fail = "Os seus elementos non foron exportados" export_invalid_format = "Formato de exportación non válido" export_missing = "Algún dato falta. Os seus elementos non foron exportados" export_no_formats = "Este rexistro non soporta exportación" -export_redirect = "Iniciar exportación a %%service%%" -export_refworks = "Exportar a RefWorks" export_save = "Gardar Arquivo" export_selected = "Exportar os seleccionados" export_selected_favorites = "Exportar os favoritos seleccionados" +export_send = "Iniciar exportación a %%service%%" export_success = "Exportación Completada" export_unsupported_format = "Formato de Exportación non soportado" FAQs = "Preguntas Frecuentes" diff --git a/languages/he.ini b/languages/he.ini index 55a886ef306658f1f9d78cd8af038be78e231bc9..436df6167a72434c261d85fab2b60b5af71e4580 100644 --- a/languages/he.ini +++ b/languages/he.ini @@ -253,11 +253,10 @@ export_fail = "×”×¤×¨×™×˜×™× ×©×œ×š ×œ× ×™×•×¦×ו" export_invalid_format = "פורמט ×”×™×¦×•× ×”× ×‘×—×¨ ×œ× × ×ª×ž×š עבור הרשומה הזו." export_missing = "חלק ×ž×”× ×ª×•× ×™× ×—×¡×¨×™×. ×”×¤×¨×™×˜×™× ×©×œ×š ×œ× ×™×•×¦×ו" export_no_formats = "רשומה זו ×œ× ×ª×•×ž×›×ª ביצו×." -export_redirect = "התחלת ×™×¦×•× ×œ%%service%%" -export_refworks = "התחלת ×™×¦×•× ×œ-RefWorks" export_save = "שמירת הקובץ" export_selected = "×™×¦×•× × ×‘×—×¨×™×" export_selected_favorites = "×™×¦×•× ×ž×•×¢×“×¤×™× × ×‘×—×¨×™×" +export_send = "התחלת ×™×¦×•× ×œ%%service%%" export_success = "×”×™×¦×•× ×”×¦×œ×™×—" export_unsupported_format = "פורמט ×™×¦×•× ××™× ×• × ×ª×ž×š" FAQs = "ש×לות × ×¤×•×¦×•×ª" diff --git a/languages/it.ini b/languages/it.ini index 1d4389ae648b46f02ec9fc85e8427bb7271f3476..858d5aa36e24a4dade22f0f2964707b0167602b5 100644 --- a/languages/it.ini +++ b/languages/it.ini @@ -93,6 +93,7 @@ Back to Search Results = "Ritorna ai risultati della ricerca" Backtrace = "Resoconto" Bag = "Carrello" Balance = "Bilancio" +Barcode = "Codice a barre" basic_search_keep_filters = "Mantieni i filtri attuali" Be the first to leave a comment = "Lascia un commento" Be the first to tag this record = "puoi essere il primo ad aggiungerne! " @@ -182,6 +183,7 @@ Check Recall = "Controlla le richieste" Checked Out = "Non disponibile" Checked Out Items = "Documenti presi in prestito" Checkedout = "Preso in prestito" +Checkout Date = "Data prestito" Chicago Citation = "Stile di citazione Chicago" child_record_count = "%%count%% record" child_records = "Contenuto/pezzi" @@ -206,6 +208,7 @@ collection_disambiguation = "Trovate più collezioni corrispondenti ai criteri d collection_empty = "Non ci sono elementi da mostrare." collection_view_record = "Vedi il record" Collections = "Collezioni" +comment_anonymous_user = "Anonimo" comment_error_load = "Errore: lista commenti non accessibile" comment_error_save = "Errore: commento non salvato" Comments = "Commenti" @@ -256,6 +259,11 @@ date_year_placeholder = "Y" Debug Information = "Informazione di debug" del_search = "Rimuovi il gruppo di ricerca" Delete = "Cancella" +delete_account_confirm = "Sei sicuro di voler cancellare i tuo account ?" +delete_account_description_html = "Le ricerche salvate e le liste di favoriti saranno cancellate. Puoi creare un nuovo account successivamente se lo desideri." +delete_account_failure = "Cancellazione dell'account fallita." +delete_account_success_message = "Il tuo account è stato cancellato. Scollegati ..." +delete_account_title = "Account cancellato" delete_all = "Cancella tutti" delete_comment_failure = "NOn puoi cancellare questo commento." delete_comment_success = "Commento cancellato." @@ -277,6 +285,8 @@ Document Type = "Tipo di documento" DOI = "DOI" doi_detected_html = "La tua ricerca sembra contenere un DOI. Clicca qui per controllare la disponibilità della risorsa: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Definisci riquadro ricerca" +draw_searchbox_end = "Rilascia il mouse per finire il disegno." +draw_searchbox_start = "Clicca e trascina per disegnare un rettangolo." Due = "Scadere" Due Date = "Data di scadenza" DVD = "DVD" @@ -343,11 +353,10 @@ export_fail = "L'esportazione è fallita" export_invalid_format = "Questo record non può essere esportato nel formato selezionato." export_missing = "Alcuni dati erano mancanti; l'esportazione è fallita." export_no_formats = "Questo record non può essere esportato." -export_redirect = "Esporta verso %%service%%" -export_refworks = "Esporta a Refworks" export_save = "Salva" export_selected = "Esporta i selezionati" export_selected_favorites = "Esporta i preferiti selezionati" +export_send = "Esporta verso %%service%%" export_success = "Esportazione completata correttamente" export_unsupported_format = "Formato di esportazione non supportato" external_auth_heading = "Accesso s materiale sottoposto a licenza" @@ -500,12 +509,14 @@ ill_request_processed = "Elaborato" ill_request_profile_html = "Per informazioni sulle richieste di prestito interbibliotecario, vai al tuo <a href="%%url%%">profilo nel catalogo della biblioteca</a>." ill_request_submit_text = "Fai una richiesta" Illustrated = "Illustrato" +ils_action_unavailable = "La funzione richiesta non è disponibile con la tessera di biblioteca ora attiva." ils_connection_failed = "Il sistema di gestione della nostra biblioteca in questo momento è in manutenzione." ils_offline_holdings_message = "Le informazioni sulla disponibilità delle copie non sono disponibili. Ci scusiamo per l'inconveniente. Per assistenza puoi contattarci:" ils_offline_home_message = "I dettagli del tuo account e delle tue transazioni non saranno disponibili in questo momento. Ci scusiamo per l'inconveniente. Per assistenza puoi contattarci:" ils_offline_login_message = "I dettagli del tuo account non saranno disponibili in questo momento. Ci scusiamo per l'inconveniente. Per assistenza puoi contattarci:" ils_offline_status = "Il sistema di gestione della nostra biblioteca in questo momento è in manutenzione." ils_offline_title = "Sistema in manutenzione" +ils_transaction_history_disabled = "Lo storico dei prestiti non è attivo nella presente tessera di biblioteca." Import Record = "Importa record" in = "in" In This Collection = "In questa collezione" @@ -580,6 +591,8 @@ list_access_denied = "Non hai il permesso di visualizzare questa lista." list_edit_name_required = "E' richiesto il nome della lista." load_tag_error = "Errore: i Tag non possono essere caricati" Loading = "Caricamento" +Loan History = "Storico dei prestiti" +loan_history_empty = "Non hai alcun prestito nello storico dei prestiti." Local Login = "Login locale" local_login_desc = "Inserisci le credenziali che hai creato per questo sito." Located = "Localizzazione" @@ -607,6 +620,7 @@ Message From Sender = "Messaggio: " Metadata Prefix = "Metadata Prefix" Microfilm = "Microfilm" MLA Citation = "Citazione MLA" +Mobile Number = "Numero di cellulare" mobile_link = "Passare alla visualizzazione per smartphone e tablet?" Monograph Title = "Titolo del libro" more = "espandi" @@ -636,6 +650,8 @@ New Items = "Nuovi documenti" New Title = "Nuovo titolo" new_password = "Nuova Password" new_password_success = "La tua password è stata cambiata con successo" +new_user_welcome_subject = "Il tuo nuovo account presso la %%library%%" +new_user_welcome_text = "Benvenuto in %%library%%. Un nuovo account è stato attivato per %%firstname%% %%lastname%%. Il tuo username è %%username%%. Per favore definisci una password in questa pagina: %%url%%" Newspaper = "Quotidiano" Next = "Successivo" No citations are available for this record = "Nessuna citazione disponibile per questo record" @@ -785,6 +801,7 @@ Range slider = "Scorri la serie" Read the full review online... = "Leggi tutta la recensione online..." Recall This = "Prenota" recaptcha_not_passed = "CAPTCHA non corretto" +recently_returned_channel_title = "Rientrato di recente" Record Citations = "Citazioni del record" Record Count = "Conteggio dei record" Record Type = "Tipo record" @@ -802,8 +819,17 @@ recovery_new_disabled = "In questo momento non puoi cambiare la tua password" recovery_title = "Rigenera la password" recovery_too_soon = "Sono state fatte troppe richieste; riprova più tardi" recovery_user_not_found = "Non riusciamo a trovare il tuo account" +rectangle_center_message = "Questo è il centro per il rettangolo evidenziato" Refine Results = "Raffina i risultati" Region = "Regione" +relais_available = "Questa copia è disponibile attraverso il servizio ILL. Vuoi richiederla?" +relais_checking = "Sto controllando la disponibilità ..." +relais_error_html = "C'è un problam con questa richiesta. Clicca <a href="%%url%%" target='new'>qui</a> per richiedere questa copia usando il sito dell'ILL.</a>" +relais_request = "Richiesta di prestito ILL" +relais_requesting = "In richiesta ..." +relais_search = "Cerca attraverso l'ILL" +relais_success_label = "Conferma:" +relais_success_message = "La richiesta con id #%%id%% è stata creata. Riceverai una mial di conferma." Related Author = "Nomi associati" Related Items = "Documenti correlati" Related Subjects = "Soggetti correlati" @@ -832,12 +858,14 @@ Requests = "Richieste" Reserves = "Reserve" Reserves Search = "Reserves Search" Reserves Search Results = "Reserves Search Results" +result_checkbox_label = "Seleziona il risultato numero %%number%%" result_count = "%%count%% risultati" Results = "Resultati" results = "risultati" Results for = "Risultati per" Results per page = "Risultati per pagina" Resumption Token = "Resumption Token" +Return Date = "Data rientro" Review by = "Recensione da" Reviews = "Recensioni" Save = "Salva" @@ -909,8 +937,14 @@ sort_author = "Autore" sort_author_author = "Alfabetico" sort_author_relevance = "Popolarità " sort_callnumber = "Collocazione" +sort_checkout_date_asc = "Data prestito (più vecchia prima)" +sort_checkout_date_desc = "Data prestito (nuova prima)" sort_count = "Conta risultati" +sort_due_date_asc = "Data scadenza prestito (più vecchia prima)" +sort_due_date_desc = "Data scadenza prestito (nuova prima)" sort_relevance = "Rilevanza" +sort_return_date_asc = "Data rientro (più vecchia prima)" +sort_return_date_desc = "Data rientro (nuova prima)" sort_title = "Titolo" sort_year = "Data (discendente)" sort_year asc = "Data (ascendente)" @@ -1024,6 +1058,7 @@ total_tags = "Totale tag" total_users = "Totale utenti" Transliterated Title = "Titolo translitterato" tree_search_limit_reached_html = "La tua ricerca restituisce troppi risultati per permetterti di visualizzarne l'albero. Questi sono i primi <b>%%limit%%</b> risultati. Per una ricerca completa <a id="fullSearchLink" href="%%url%%" target="_blank">clicca qui.</a>" +trending_items_channel_title = "Copie ad alto utilizzo rientrate di recente" unique_tags = "Tag unici" University Library = "Biblioteca Universitaria" Unknown = "Sconosciuto" diff --git a/languages/ja.ini b/languages/ja.ini index b14c0c7ca189c637fc71c1d8ef13a46683b62875..2d0c1c622ab4a2b7401ea45fb3d7591681ef99d2 100644 --- a/languages/ja.ini +++ b/languages/ja.ini @@ -94,6 +94,7 @@ Back to Search Results = "検索çµæžœã«æˆ»ã‚‹" Backtrace = "ãƒãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹" Bag = "カート" Balance = "残高" +Barcode = "ãƒãƒ¼ã‚³ãƒ¼ãƒ‰" basic_search_keep_filters = "ç¾åœ¨ã®çµžè¾¼ã¿ã‚’ä¿æŒã™ã‚‹" Be the first to leave a comment = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã¸ã®åˆã‚ã¦ã®ã‚³ãƒ¡ãƒ³ãƒˆã‚’付ã‘ã¾ã›ã‚“ã‹" Be the first to tag this record = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã¸ã®åˆã‚ã¦ã®ã‚¿ã‚°ã‚’付ã‘ã¾ã›ã‚“ã‹" @@ -183,6 +184,7 @@ Check Recall = "è¿”å´è«‹æ±‚確èª" Checked Out = "貸出ä¸" Checked Out Items = "貸出ä¸è³‡æ–™" Checkedout = "貸出ä¸" +Checkout Date = "貸出日" Chicago Citation = "シカゴスタイル引用形" child_record_count = "%%count%%レコード" child_records = "内容注記" @@ -207,6 +209,7 @@ collection_disambiguation = "該当ã™ã‚‹ã‚³ãƒ¬ã‚¯ã‚·ãƒ§ãƒ³ãŒè¤‡æ•°ã‚ã‚Šã¾ã™ collection_empty = "表示ã™ã¹ãアイテムãŒã‚ã‚Šã¾ã›ã‚“。" collection_view_record = "レコードã®é–²è¦§" Collections = "コレクション" +comment_anonymous_user = "ç„¡å" comment_error_load = "エラー: コメントリストをå†è¡¨ç¤ºã§ãã¾ã›ã‚“ã§ã—ãŸ" comment_error_save = "エラー: コメントをä¿å˜ã§ãã¾ã›ã‚“ã§ã—ãŸ" Comments = "コメント" @@ -257,6 +260,11 @@ date_year_placeholder = "Y" Debug Information = "デãƒãƒƒã‚°æƒ…å ±" del_search = "検索グループを削除" Delete = "削除" +delete_account_confirm = "本当ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’削除ã—ã¦ã‚‚よã„ã§ã™ã‹?" +delete_account_description_html = "ä¿å˜æ¸ˆã®æ¤œç´¢ã¨ãŠæ°—ã«å…¥ã‚Šãƒªã‚¹ãƒˆã¯å‰Šé™¤ã•ã‚Œã¾ã™ã€‚å¿…è¦ã§ã‚ã‚Œã°ã€å¾Œã§æ–°ã—ã„アカウントを作æˆã§ãã¾ã™ã€‚" +delete_account_failure = "アカウントを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" +delete_account_success_message = "アカウントを削除ã—ã¾ã—ãŸã€‚ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã™ã€‚" +delete_account_title = "アカウントã®å‰Šé™¤" delete_all = "ã™ã¹ã¦ã®å‰Šé™¤" delete_comment_failure = "コメントを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" delete_comment_success = "コメントを削除ã—ã¾ã—ãŸã€‚" @@ -278,6 +286,8 @@ Document Type = "文書タイプ" DOI = "DOI" doi_detected_html = "æ¤œç´¢é …ç›®ã«DOIãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚次ã®ãƒªãƒ³ã‚¯ã‚’クリックã—ã¦è³‡æ–™ãŒåˆ©ç”¨å¯èƒ½ã‹ãƒã‚§ãƒƒã‚¯ã—ã¦ãã ã•ã„。: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "検索ボックスをドãƒãƒ¼ã—ã¾ã™ã€‚" +draw_searchbox_end = "æ画を終ãˆã‚‹ã«ã¯ãƒžã‚¦ã‚¹ã‚’離ã—ã¦ãã ã•ã„。" +draw_searchbox_start = "四角をæãã«ã¯ã‚¯ãƒªãƒƒã‚¯ã—ã¦ãƒ‰ãƒ©ãƒƒã‚°ã—ã¦ãã ã•ã„。" Due = "è¿”å´æ—¥" Due Date = "è¿”å´æ—¥" DVD = "DVD" @@ -344,11 +354,10 @@ export_fail = "エクスãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“ã§ã—ãŸ" export_invalid_format = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã¯é¸æŠžã—ãŸå½¢å¼ã§ã¯ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“。" export_missing = "紛失データãŒã‚ã‚Šã€ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" export_no_formats = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã¯ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“。" -export_redirect = "%%service%%ã¸ã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始" -export_refworks = "RefWorksã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ" export_save = "ファイルをä¿å˜" export_selected = "é¸æŠžã‚¢ã‚¤ãƒ†ãƒ をエクスãƒãƒ¼ãƒˆ" export_selected_favorites = "é¸æŠžã—ãŸãŠæ°—ã«å…¥ã‚Šã‚’エクスãƒãƒ¼ãƒˆ" +export_send = "%%service%%ã¸ã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始" export_success = "エクスãƒãƒ¼ãƒˆå®Œäº†" export_unsupported_format = "未対応ã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆ" external_auth_heading = "ライセンス資料ã«ã‚¢ã‚¯ã‚»ã‚¹" @@ -501,12 +510,14 @@ ill_request_processed = "処ç†ä¸" ill_request_profile_html = "ILLリクエストを行ã†ã«ã¯ã€<a href="%%url%%">図書館目録プãƒãƒ•ã‚£ãƒ¼ãƒ«</a>を作æˆã—ã¦ãã ã•ã„。" ill_request_submit_text = "リクエストã®ä¾é ¼" Illustrated = "図表" +ils_action_unavailable = "ã”希望ã®æ©Ÿèƒ½ã¯ã“ã®å›³æ›¸é¤¨ã‚«ãƒ¼ãƒ‰ã§ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。" ils_connection_failed = "ç¾åœ¨ã€å›³æ›¸é¤¨ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã¯ä¿å®ˆä½œæ¥ä¸ã§ã™ã€‚" ils_offline_holdings_message = "ç¾åœ¨ã€äºˆç´„ã¨è²¸å‡ºæƒ…å ±ã®é–²è¦§ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。ã”ä¸ä¾¿ã‚’ãŠã‹ã‘ã—大変申ã—訳ã‚ã‚Šã¾ã›ã‚“。詳細ã¯æ‹…当ã«ã”連絡ãã ã•ã„:" ils_offline_home_message = "ç¾åœ¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ±ã¨ã‚¢ã‚¤ãƒ†ãƒ æƒ…å ±ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。ã”ä¸ä¾¿ã‚’ãŠã‹ã‘ã—大変申ã—訳ã‚ã‚Šã¾ã›ã‚“。詳細ã¯æ‹…当ã«ã”連絡ãã ã•ã„:" ils_offline_login_message = "ç¾åœ¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ±ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。ã”ä¸ä¾¿ã‚’ãŠã‹ã‘ã—大変申ã—訳ã‚ã‚Šã¾ã›ã‚“。詳細ã¯æ‹…当ã«ã”連絡ãã ã•ã„:" ils_offline_status = "ç¾åœ¨ã€å›³æ›¸é¤¨ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã¯ä¿å®ˆä½œæ¥ä¸ã§ã™ã€‚" ils_offline_title = "システムä¿å®ˆä½œæ¥ä¸" +ils_transaction_history_disabled = "ã“ã®å›³æ›¸é¤¨ã‚«ãƒ¼ãƒ‰ã§ã¯è²¸å‡ºå±¥æ´ã‚’有効ã«ã§ãã¾ã›ã‚“。" Import Record = "レコードã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ" in = ":" In This Collection = "ã“ã®ã‚³ãƒ¬ã‚¯ã‚·ãƒ§ãƒ³" @@ -581,6 +592,8 @@ list_access_denied = "ã“ã®ãƒªã‚¹ãƒˆã®é–²è¦§ã‚’許ã•ã‚Œã¦ã„ã¾ã›ã‚“。" list_edit_name_required = "リストåを指定ã—ã¦ãã ã•ã„。" load_tag_error = "エラー: ã‚¿ã‚°ã‚’ãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸ" Loading = "ãƒãƒ¼ãƒ‰ä¸" +Loan History = "貸出履æ´" +loan_history_empty = "貸出履æ´ã«è²¸å‡ºè¨˜éŒ²ãŒã‚ã‚Šã¾ã›ã‚“。" Local Login = "VuFindã®ãƒã‚°ã‚¤ãƒ³" local_login_desc = "ã“ã®ã‚µã‚¤ãƒˆã§ä½œæˆã—ãŸãƒ¦ãƒ¼ã‚¶åã¨ãƒ‘スワードを入力ã—ã¦ãã ã•ã„。" Located = "é…æž¶å ´æ‰€" @@ -608,6 +621,7 @@ Message From Sender = "é€ä¿¡è€…ã‹ã‚‰ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸" Metadata Prefix = "メタデータプリフィクス" Microfilm = "マイクãƒãƒ•ã‚£ãƒ«ãƒ " MLA Citation = "MLA引用形å¼" +Mobile Number = "モãƒã‚¤ãƒ«ç•ªå·" mobile_link = "æºå¸¯ç”¨ã®è¡¨ç¤ºã«åˆ‡ã‚Šæ›¿ãˆã¾ã™ã‹?" Monograph Title = "モノグラフタイトル" more = "ã‚‚ã£ã¨è¦‹ã‚‹" @@ -637,6 +651,8 @@ New Items = "æ–°ç€è³‡æ–™" New Title = "æ–°ç€è³‡æ–™" new_password = "新パスワード" new_password_success = "パスワードãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸã€‚" +new_user_welcome_subject = "%%library%%ã«ãŠã‘ã‚‹æ–°ã—ã„アカウント" +new_user_welcome_text = "%%library%%ã«ã‚ˆã†ã“ã。%%lastname%%%%firstname%%ã•ã‚“ã®æ–°ã—ã„アカウントを作æˆã—ã¾ã—ãŸã€‚ユーザåã¯%%username%%ã§ã™ã€‚%%url%%ã§ãƒ‘スワードをè¨å®šã—ã¦ãã ã•ã„。" Newspaper = "æ–°èž" Next = "次ページ" No citations are available for this record = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã«ã¯å¼•ç”¨ãŒã‚ã‚Šã¾ã›ã‚“。" @@ -786,6 +802,7 @@ Range slider = "レンジスライダ" Read the full review online... = "書評全文をオンラインã§é–²è¦§..." Recall This = "è¿”å´è«‹æ±‚ã™ã‚‹" recaptcha_not_passed = "CAPTCHAãŒä¸€è‡´ã—ã¾ã›ã‚“" +recently_returned_channel_title = "最新ã®è¿”å´" Record Citations = "レコードã®å¼•ç”¨å½¢" Record Count = "レコード数" Record Type = "レコードã®ã‚¿ã‚¤ãƒ—" @@ -803,8 +820,17 @@ recovery_new_disabled = "ç¾åœ¨ã€ãƒ‘スワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“。" recovery_title = "パスワードã®å†è¨å®š" recovery_too_soon = "å†è¨å®šå›žæ•°ã®ä¸Šé™ã‚’超ãˆã¾ã—ãŸã€‚ã—ã°ã‚‰ã時間をãŠã„ã¦å†åº¦å®Ÿè¡Œã—ã¦ãã ã•ã„。" recovery_user_not_found = "指定ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å˜åœ¨ã—ã¾ã›ã‚“。" +rectangle_center_message = "ã“ã‚Œã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆã•ã‚ŒãŸå››è§’ã®ä¸å¿ƒç‚¹ã§ã™ã€‚" Refine Results = "çµæžœã®çµžã‚Šè¾¼ã¿" Region = "地ç†åŒºåˆ†" +relais_available = "ã“ã®ã‚¢ã‚¤ãƒ†ãƒ ã¯å›³æ›¸é¤¨é–“貸出を通ã˜ã¦åˆ©ç”¨å¯èƒ½ã§ã™ã€‚リクエストã—ã¾ã™ã‹?" +relais_checking = "利用å¯èƒ½ã‹èª¿æŸ»ä¸..." +relais_error_html = "ã“ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã§å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚<a href="%%url%%" target='new'>ã“ã“</a>をクリックã—ã¦ã€å›³æ›¸é¤¨é–“貸出サイトã§ã“ã®ã‚¢ã‚¤ãƒ†ãƒ をリクエストã—ã¦ãã ã•ã„。" +relais_request = "図書館間貸出リクエスト" +relais_requesting = "リクエスト処ç†ä¸..." +relais_search = "図書館間貸出を検索" +relais_success_label = "確èª:" +relais_success_message = "リクエストID: #%%id%% ãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚確èªãƒ¡ãƒ¼ãƒ«ã‚’発é€ã—ã¾ã—ãŸã€‚" Related Author = "関連著者" Related Items = "関連資料" Related Subjects = "関連主題" @@ -833,12 +859,14 @@ Requests = "リクエスト" Reserves = "予約" Reserves Search = "予約資料検索" Reserves Search Results = "予約資料検索çµæžœ" +result_checkbox_label = "çµæžœç•ªå·: %%number%%ã‚’é¸æŠž" result_count = "%%count%% çµæžœ" Results = "çµæžœ" results = "çµæžœ" Results for = "検索語:" Results per page = "1ページã®è¡¨ç¤ºä»¶æ•°" Resumption Token = "リサンプション・トークン" +Return Date = "è¿”å´æ—¥" Review by = "レビュー者:" Reviews = "レビュー" Save = "ä¿å˜" @@ -910,8 +938,14 @@ sort_author = "è‘—è€…é †" sort_author_author = "ã‚¢ãƒ«ãƒ•ã‚¡ãƒ™ãƒƒãƒˆé †" sort_author_relevance = "äººæ°—é †" sort_callnumber = "請求記å·é †" +sort_checkout_date_asc = "è²¸å‡ºæ—¥ï¼ˆæ˜‡é †ï¼‰" +sort_checkout_date_desc = "貸出日(é™é †ï¼‰" sort_count = "集計çµæžœ" +sort_due_date_asc = "è¿”å´æœŸé™æ—¥ï¼ˆæ˜‡é †ï¼‰" +sort_due_date_desc = "è¿”å´æœŸé™æ—¥ï¼ˆé™é †ï¼‰" sort_relevance = "é©åˆé †" +sort_return_date_asc = "è¿”å´æ—¥ï¼ˆæ˜‡é †ï¼‰" +sort_return_date_desc = "è¿”å´æ—¥ï¼ˆé™é †ï¼‰" sort_title = "ã‚¿ã‚¤ãƒˆãƒ«é †" sort_year = "出版年é™é †" sort_year asc = "å‡ºç‰ˆå¹´æ˜‡é †" @@ -1025,6 +1059,7 @@ total_tags = "ã‚¿ã‚°ç·æ•°" total_users = "利用者ç·æ•°" Transliterated Title = "ç¿»å—タイトル" tree_search_limit_reached_html = "検索çµæžœãŒè¡¨ç¤ºå¯èƒ½ä»¶æ•°ã‚’超éŽã—ã¾ã—ãŸã€‚最åˆã®<b>%%limit%%</b>件を表示ã—ã¦ã„ã¾ã™ã€‚検索çµæžœã‚’絞り込むã«ã¯ã€<a id="fullSearchLink" href="%%url%%" target="_blank">詳細検索</a>ã‚’ã”利用ãã ã•ã„。" +trending_items_channel_title = "トレンドアイテム" unique_tags = "ユニークタグ" University Library = "大å¦å›³æ›¸é¤¨" Unknown = "ä¸æ˜Ž" diff --git a/languages/native.ini b/languages/native.ini index e925043b3eb10677fe575d381938cf438495f800..1d9a3aa3acb87e6f661fccf8aa2f1b29731a8f3c 100644 --- a/languages/native.ini +++ b/languages/native.ini @@ -10,6 +10,7 @@ Danish = "Dansk" Dutch = "Nederlands" English = "English" Finnish = "Suomi" +Flemish Dutch = "Nederlands (B)" French = "Français" Galician = "Galego" German = "Deutsch" diff --git a/languages/nl-be.ini b/languages/nl-be.ini new file mode 100644 index 0000000000000000000000000000000000000000..b2815f63e78241ee42027afa480b1465c7e75347 --- /dev/null +++ b/languages/nl-be.ini @@ -0,0 +1,2 @@ +@parent_ini = "nl.ini" +Mobile Number = "GSM nummer" diff --git a/languages/nl.ini b/languages/nl.ini index 9fdb4fe478f8c3ce06fe566482a87da581618968..e84ba354b51308d3cefac9379f365ce98aeb6f5f 100644 --- a/languages/nl.ini +++ b/languages/nl.ini @@ -94,6 +94,7 @@ Back to Search Results = "Terug naar zoekresultaten" Backtrace = "Backtrace" Bag = "Tas" Balance = "Balans" +Barcode = "Barcode" basic_search_keep_filters = "Bewaar mijn huidige filters" Be the first to leave a comment = "Wees de eerste die reageert" Be the first to tag this record = "Wees de eerste die dit record labelt" @@ -183,6 +184,7 @@ Check Recall = "Controleer de terugroeping" Checked Out = "Uitgeleend" Checked Out Items = "Uitgeleende artikelen" Checkedout = "Uitgeleend" +Checkout Date = "Uitleendatum" Chicago Citation = "Chicago Style citaat" child_record_count = "%%count%% records" child_records = "Inhoud/stuks" @@ -207,6 +209,7 @@ collection_disambiguation = "Meerdere Overeenkomstige Collecties Gevonden" collection_empty = "Geen items af the beelden" collection_view_record = "Bekijk Record" Collections = "Collecties" +comment_anonymous_user = "Anoniem" comment_error_load = "Foutmelding: De commentarenlijst kon niet worden vernieuwd" comment_error_save = "Foutmelding: Commentaar werd niet opgeslagen" Comments = "Commentaar" @@ -257,6 +260,11 @@ date_year_placeholder = "J" Debug Information = "Debug Informatie" del_search = "Verwijder zoekveldengroep" Delete = "Verwijderen" +delete_account_confirm = "Weet je zeker dat je je account wil verwijderen?" +delete_account_description_html = "Bewaarde zoekopdrachten en favorieten zullen worden verwijderd. Je kunt wel nog een nieuw account aanmaken als je dat wil." +delete_account_failure = "Verwijdering van het account is mislukt." +delete_account_success_message = "Je account is verwijderd. Je wordt afgemeld..." +delete_account_title = "Verwijder dit account"." delete_all = "Alles Verwijderen" delete_comment_failure = "Opmerking kon niet worden verwijderd" delete_comment_success = "Opmerking verwijderd" @@ -278,6 +286,8 @@ Document Type = "Document Type" DOI = "DOI" doi_detected_html = "Jouw zoekopdracht bevat een DOI. Klik hier om de beschikbaarheid van de bron na te gaan: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Teken Zoek-vak" +draw_searchbox_end = "Laat de muis los om te stoppen met tekenen" +draw_searchbox_start = "Klik en sleep om een rechthoek te tekenen." Due = "Inleverdatum" Due Date = "Inleverdatum" DVD = "DVD" @@ -344,11 +354,10 @@ export_fail = "Jouw items werden niet geëxporteerd" export_invalid_format = "Het geselecteerde exporteer-format is niet beschikbaar voor dit record" export_missing = "De gegevens waren niet volledig. Jouw items werden niet geëxporteerd." export_no_formats = "Dit record ondersteunt exporteren niet" -export_redirect = "Start exporteren naar %%service%%" -export_refworks = "Start export naar RefWorks" export_save = "Bestand opslaan" export_selected = "Het geselecteerde exporteren" export_selected_favorites = "Geselecteerde favorieten exporteren" +export_send = "Start exporteren naar %%service%%" export_success = "Export afgerond" export_unsupported_format = "niet ondersteund Export Format" external_auth_heading = "Toegang tot gelicentieerd materiaal" @@ -501,12 +510,14 @@ ill_request_processed = "Verwerkt" ill_request_profile_html = "Voor meer info over IBL-aanvragen, meld je eerst aan." ill_request_submit_text = "Plaats aanvraag" Illustrated = "Geïllustreerd" +ils_action_unavailable = "De gevraagde opdracht is niet mogelijk met deze bibliotheekkaart." ils_connection_failed = "Ons bibliotheek beheerssysteem is momenteel in onderhoud." ils_offline_holdings_message = "Reserveringen en beschikbaarheid van items momenteel niet beschikbaar. Met onze excuses. Contacteer ons voor hulp:" ils_offline_home_message = "Jouw accountgegevens en huidige item informatie zullen onbeschikbaar zijn gedurende deze tijd. Gelieve ons te verontschuldigen voor het ongemak en contacteer ons voor hulp:" ils_offline_login_message = "Jouw accountgegevens zullen gedurende die tijd onbereikbaar zijn. Gelieve ons te verontschuldigen voor het ongemak en contacteer ons voor hulp:" ils_offline_status = "Ons bibliotheek beheerssysteem is momenteel in onderhoud." ils_offline_title = "Het systeem is offline vanwege onderhoudswerken" +ils_transaction_history_disabled = "De uitleengeschiedenis is niet geactiveerd voor deze bibliotheekkaart." Import Record = "Importeer record" in = "in" In This Collection = "In deze collectie" @@ -581,6 +592,8 @@ list_access_denied = "Je hebt geen toestemming om deze lijst te zien." list_edit_name_required = "Lijstnaam moet worden ingegeven." load_tag_error = "Foutmelding: de Tags konden niet worden niet opgehaald" Loading = "Wordt geladen" +Loan History = "Uitleningen" +loan_history_empty = "Er zijn geen uitleningen" Local Login = "Plaatselijke login" local_login_desc = "voer jouw gebruikersnaam en wachtwoord voor deze site in." Located = "Locatie" @@ -608,6 +621,7 @@ Message From Sender = "Bericht van zender" Metadata Prefix = "Metadata Prefix" Microfilm = "Microfilm" MLA Citation = "MLA citatie" +Mobile Number = "Mobiel nummer" mobile_link = "Je gebruikt een mobiel apparaat; switchen naar de mobiele versie?" Monograph Title = "Titel monografie" more = "meer" @@ -637,6 +651,8 @@ New Items = "Nieuwe items" New Title = "Nieuwe titel" new_password = "Nieuw wachtwoord" new_password_success = "Succes! Jouw wachtwoord is aangepast." +new_user_welcome_subject = "Uw nieuwe account in de %%library%%" +new_user_welcome_text = "Welkom in de %%library%%. Er werd een nieuw account geopend voor %%firstname%% %%lastname%%. Je gebruikersnaam is %%username%%. Maak een paswoord aan op deze pagina: %%url%%" Newspaper = "Dagblad" Next = "Volgende" No citations are available for this record = "Er zijn geen citaties beschikbaar voor dit record." @@ -786,6 +802,7 @@ Range slider = "Reikwijdte schuifbalk" Read the full review online... = "Lees volledige bespreking online..." Recall This = "Hou dit vast" recaptcha_not_passed = "Onjuiste CAPTCHA" +recently_returned_channel_title = "Onlangs ingeleverd" Record Citations = "Record citatie" Record Count = "Aantal records" Record Type = "Document type" @@ -803,8 +820,17 @@ recovery_new_disabled = "Je kan jouw wachtwoord op dit ogenblik niet wijzigen" recovery_title = "Wachtwoord herstellen" recovery_too_soon = "Teveel herstelvragen werden verzonden, probeer later opnieuw" recovery_user_not_found = "We vinden jouw account niet" +rectangle_center_message = "Dit is het middelpunt voor de rechthoek." Refine Results = "Verfijn jouw resultaten" Region = "Gebied" +relais_available = "Dit item is beschikbaar via IBL (Interbibliothecair leenverkeer). Wil je het aanvragen?" +relais_checking = "Beschikbaarheid wordt gecontroleerd..." +relais_error_html = "Er was een probleem i.v.m. deze aanvraag. Klik <a href="%%url%%" target='new'>hier</a> om het item aan te vragen via Interbibliothecair Leenverkeer." +relais_request = "Interbibliothecaire Leenaanvraag" +relais_requesting = "Je aanvraag loopt..." +relais_search = "Zoek Interbibliothecaire uitleen" +relais_success_label = "Bevestiging:" +relais_success_message = "Er werd een aanvraag id #%%id%% gecreëerd. Je krijgt hiervan een mail ter bevestiging." Related Author = "Gerelateerde Auteur" Related Items = "Gerelateerde Items" Related Subjects = "Gerelateerde Onderwerpen" @@ -833,12 +859,14 @@ Requests = "Aanvragen" Reserves = "Reserveringen" Reserves Search = "Zoekopdracht Reserveringen" Reserves Search Results = "Zoekresultaten voor Reserveringen" +result_checkbox_label = "Selecteer resultaatnummer %%number%%" result_count = "%%count%% resultaten" Results = "Resultaten" results = "resultaten" Results for = "Resultaten voor" Results per page = "Resultaten per pagina" Resumption Token = "Hervatting Token" +Return Date = "Inleverdatum" Review by = "Review door" Reviews = "Reviews" Save = "Opslaan" @@ -910,8 +938,14 @@ sort_author = "Auteur" sort_author_author = "Alfabetisch" sort_author_relevance = "populariteit" sort_callnumber = "Plaatsingsnummer" +sort_checkout_date_asc = "Uitleendatum (oudste eerst)" +sort_checkout_date_desc = "Uitleendatum (recentste eerst)" sort_count = "Aantal Resultaten" +sort_due_date_asc = "Vervaldatum (oudste eerst)" +sort_due_date_desc = "Vervaldatum (recentste eerst)" sort_relevance = "Relevantie" +sort_return_date_asc = "Inleverdatum (oudste eerst)" +sort_return_date_desc = "Inleverdatum (recentste eerst)" sort_title = "Titel" sort_year = "Datum Aflopend" sort_year asc = "Datum Oplopend" @@ -1025,6 +1059,7 @@ total_tags = "Totaal want. Tags" total_users = "Totaal aant. gebruikers" Transliterated Title = "Omgezette Titel" tree_search_limit_reached_html = "Jouw zoekopdracht leverde teveel resultaten op om weer te geven in de boomstructuur. De eerste <b>%%limit%%</b> items worden getoond. Voor de volledige lijst klik <a id="fullSearchLink" href="%%url%%" target="_blank">hier.</a>" +trending_items_channel_title = "Populaire items" unique_tags = "Unieke tags" University Library = "Universiteitsbibliotheek" Unknown = "Onbekend" diff --git a/languages/pl.ini b/languages/pl.ini index a64ccf0d69fe566267754343821e35650c3496ed..05345b1da770c5bfb7365656a66dcd07103b01d6 100644 --- a/languages/pl.ini +++ b/languages/pl.ini @@ -155,6 +155,7 @@ Back to Search Results = "Powrót do rezultatów" Backtrace = "Åšlad wsteczny" Bag = "Schowek" Balance = "Saldo" +Barcode = "Kod kreskowy" basic_search_keep_filters = "Zatrzymaj filtry" Be the first to leave a comment = "Napisz pierwszy komentarz" Be the first to tag this record = "DoÅ‚Ä…cz pierwszÄ… etykiete" @@ -244,6 +245,7 @@ Check Recall = "Sprawdź możliwość odwoÅ‚ania" Checked Out = "Wypożyczone" Checked Out Items = "Wypożyczone książki" Checkedout = "Wypożyczone" +Checkout Date = "Okres wypożyczenia" Chicago Citation = "Styl cytowania Chicago" child_record_count = "%%count%% zapisów" child_records = "Zapisy" @@ -268,6 +270,7 @@ collection_disambiguation = "Znaleziono kilka trafnych kolekcji" collection_empty = "Nie pokazuj zawartoÅ›ci" collection_view_record = "Zobacz rekord" Collections = "Zbiory" +comment_anonymous_user = "Anonimowy komentarz" comment_error_load = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ zaÅ‚adować komentarzy" comment_error_save = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ zapisać komentarza" Comments = "Komentarze" @@ -318,6 +321,11 @@ date_year_placeholder = "Y" Debug Information = "Informacje Debug" del_search = "Usunąć wyszukiwanie" Delete = "UsuÅ„" +delete_account_confirm = "Chcesz usunąć swoje konto czytelnika?" +delete_account_description_html = "Historia wyszukiwania i listy ulubionych książek zostanÄ… usuniÄ™te. JeÅ›li chcesz, możesz zaÅ‚ożyć nowe konto czytelnika później." +delete_account_failure = "Nie udaÅ‚o siÄ™ usunąć Twojego konta czytelnika." +delete_account_success_message = "Twoje konto czytelnika zostaÅ‚o usuniÄ™te. Wylogowujemy ciÄ™..." +delete_account_title = "UsuÅ„ konto czytelnika" delete_all = "UsuÅ„ wszystko" delete_comment_failure = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ usunąć kommentarza." delete_comment_success = "Kommentarz zostaÅ‚ usuniÄ™ty." @@ -339,6 +347,8 @@ Document Type = "Typ dokumentu" DOI = "DOI" doi_detected_html = "Twoje wyszukiwanie widocznie zawiera identyfikator DOI. Kliknij tutaj aby sprawdzić dostÄ™pność: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Zaznacz zasiÄ™g wyszukiwania" +draw_searchbox_end = "Puść myszkÄ™, aby zakoÅ„czyć rysunek." +draw_searchbox_start = "Kliknij i przeciÄ…gnij, aby narysować prostokÄ…t." Due = "do" Due Date = "Termin zwrotu" DVD = "DVD" @@ -405,11 +415,10 @@ export_fail = "Zapisy nie zostaÅ‚y eksportowane" export_invalid_format = "Wybrany format nie stoi do dyspozycji dla tego zapisu." export_missing = "Brak danych. Nie udaÅ‚o siÄ™ eksportować zapisów." export_no_formats = "Nie jest możliwe eksportowanie tego zapisu." -export_redirect = "Eksportuj do %%service%%" -export_refworks = "Eksportuj cytat do RefWorks" export_save = "Zapisz plik" export_selected = "Eksportuj zaznaczone książki" export_selected_favorites = "Eksportuj zaznaczone ulubione książki" +export_send = "Eksportuj do %%service%%" export_success = "Eksport zakoÅ„czony" export_unsupported_format = "Niewspomagany format eksportu" external_auth_heading = "Logowanie do peÅ‚nego dostÄ™pu" @@ -562,12 +571,14 @@ ill_request_processed = "Przetworzony" ill_request_profile_html = "Aby otrzymać informacje o zamówieniu miÄ™dzybibliotecznym, utwórz <a href="%%url%%">konto czytelnika</a>." ill_request_submit_text = "Zamów" Illustrated = "Ilustracje" +ils_action_unavailable = "Żądana funkcja nie jest dostÄ™pna dla twojej karty czytelnika." ils_connection_failed = "Niestety! Z powodu przeglÄ…du technicznego system jest niedostÄ™pny." ils_offline_holdings_message = "Niestety! Status dostÄ™pu obecnie nie stoi do dyspozycji - skontaktuj siÄ™ z bibliotekÄ…." ils_offline_home_message = "Niestety! Konto czytelnika oraz status dostÄ™pu obecnie nie sÄ… do dostÄ™pne - skontaktuj siÄ™ z bibliotekÄ…." ils_offline_login_message = "Niestety! Konto czytelnika obecnie nie jest dostÄ™pne - skontaktuj siÄ™ z bibliotekÄ…." ils_offline_status = "Niestety! Z powodu przeglÄ…du technicznego system jest niedostÄ™pny." ils_offline_title = "Z powodu przeglÄ…du technicznego niedostÄ™pne" +ils_transaction_history_disabled = "Historia wypożyczonych książek nie jest dostÄ™pna dla twojej karty czytelnika." Import Record = "Importuj zapis" in = "w" In This Collection = "w tej kolekcji" @@ -642,6 +653,8 @@ list_access_denied = "Nie masz odpowiednich uprawieÅ„ do oglÄ…dania tej listy." list_edit_name_required = "Brakuje nazwy listy!" load_tag_error = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ zaÅ‚adować etykiety." Loading = "Åaduje siÄ™..." +Loan History = "Historia wypożyczonych książek" +loan_history_empty = "Nie masz nic w historii wypożyczonych książek." Local Login = "Lokalne logowanie" local_login_desc = "Podaj nazwÄ™ użytkownika i hasÅ‚o, które stworzyÅ‚eÅ› na tej stronie." Located = "Zlokalizowane" @@ -669,6 +682,7 @@ Message From Sender = "Wiadomość od nadajnika" Metadata Prefix = "Prefiks opisu" Microfilm = "Mikrofilm" MLA Citation = "Styl cytowania MLA" +Mobile Number = "Numer telefonu komórkowego" mobile_link = "Widocznie używasz urzÄ…dzenia mobilnego. Czy chcesz zmienić na wersjÄ™ mobilnÄ…?" Monograph Title = "TytuÅ‚ monografii" more = "WiÄ™cej" @@ -698,6 +712,8 @@ New Items = "Nowe nabytki" New Title = "Nowy tytuÅ‚" new_password = "Nowe hasÅ‚o" new_password_success = "HasÅ‚o zostaÅ‚o zmienione" +new_user_welcome_subject = "Twoje konto czytelnika w %%library%%" +new_user_welcome_text = "Witamy w %%library%%. UtworzyliÅ›my nowe konto czytelnika dla %%firstname%% %%lastname%%. Twoja nazwa użytkownika jest %%username%%. Utwórz hasÅ‚o na tej stronie: %%url%%" Newspaper = "Gazeta" Next = "NastÄ™pna" No citations are available for this record = "Dla tego zapisu nie można stworzyć cytatu." @@ -847,6 +863,7 @@ Range slider = "Slider" Read the full review online... = "Przeczytaj caÅ‚Ä… recenzjÄ™..." Recall This = "OdwoÅ‚aj" recaptcha_not_passed = "CAPTCHA nie zweryfikowany" +recently_returned_channel_title = "Niedawno zwrócone" Record Citations = "Cytaty zapisu" Record Count = "Ilość zapisów" Record Type = "Typ zapisu" @@ -864,8 +881,17 @@ recovery_new_disabled = "Nie możesz zmienić hasÅ‚a w tym momencie" recovery_title = "Odzyskanie hasÅ‚a" recovery_too_soon = "WysÅ‚ano za dużo żądaÅ„ odzyskania. Spróbuj później" recovery_user_not_found = "Nie znaleźliÅ›my takiego konta" +rectangle_center_message = "To jest punkt Å›rodkowy podÅ›wietlonego prostokÄ…ta." Refine Results = "Redukuj rezultaty" Region = "Region" +relais_available = "Ta książka jest dostÄ™pna przez wypożyczenie miÄ™dzybiblioteczne. Chcesz zamówić?" +relais_checking = "Sprawdzamy dostÄ™pność..." +relais_error_html = "WystÄ…piÅ‚ problem przy zamówieniu. Kliknij <a href="%%url%%" target='new'>here</a> aby zamówić tÄ… książke za pomocÄ… wypożyczalni miÄ™dzybibliotecznej.</a>" +relais_request = "Zamówienie wypożyczenia miÄ™dzybibliotecznego" +relais_requesting = "Zamawiamy..." +relais_search = "Wyszukaj wypożyczenie miÄ™dzybiblioteczne" +relais_success_label = "Potwierdzenie:" +relais_success_message = "Twoje zamówienie ma identyfikator #%%id%. Otrzymasz potwierdzenie E-mailem." Related Author = "Podobni autorzy" Related Items = "Podobne zapisy" Related Subjects = "Podobne hasÅ‚a" @@ -894,12 +920,14 @@ Requests = "Zamówienia" Reserves = "Rezerwacje" Reserves Search = "Wyszukiwanie rezerwacji" Reserves Search Results = "Rezultaty rezerwacji" +result_checkbox_label = "Wybierz numer wyniku %%number%%" result_count = "%%count%% Rezultatów" Results = "Rezultaty" results = "Rezultaty" Results for = "Rezultaty dla" Results per page = "Rezultaty na stronÄ™" Resumption Token = "Token" +Return Date = "Data zwrotu" Review by = "Recenzja od" Reviews = "Recenzje" Save = "Zapisz" @@ -971,8 +999,14 @@ sort_author = "Autor" sort_author_author = "Alfabetycznie" sort_author_relevance = "CzÄ™stotliwość" sort_callnumber = "Sygnatura" +sort_checkout_date_asc = "Data wypożyczenia (najpierw najstarsze)" +sort_checkout_date_desc = "Data wypożyczenia (najpierw najnowsze)" sort_count = "Liczba rezultatów" +sort_due_date_asc = "Termin zwrotu (najpierw najstarsze)" +sort_due_date_desc = "Termin zwrotu (najpierw najnowsze)" sort_relevance = "Ważność" +sort_return_date_asc = "Data zwrotu (najpierw najstarsze)" +sort_return_date_desc = "Data zwrotu (najpierw najnowsze)" sort_title = "TytuÅ‚" sort_year = "WedÅ‚ug najnowszych" sort_year asc = "WedÅ‚ug najstarszych" @@ -1086,6 +1120,7 @@ total_tags = "Suma etykiet" total_users = "Suma użytkowników" Transliterated Title = "TytuÅ‚ transliterowany" tree_search_limit_reached_html = "Wyszukiwanie wykazaÅ‚o za dużo rezultatów aby pokazać caÅ‚Ä… hierarchiÄ™. Widoczne jest <b>%%limit%%</b> rezultatów. Aby zobaczyć wszystkie wyniki, kliknij <a id="fullSearchLink" href="%%url%%" target="_blank">tutaj.</a>" +trending_items_channel_title = "Modne książki" unique_tags = "Niepowtarzalne etykiety" University Library = "Biblioteka Uniwersytecka" Unknown = "Nieznany" diff --git a/languages/pt-br.ini b/languages/pt-br.ini index db9b31c486689f6e4239cc898f59d5f2c7138e8d..bb5331ce7d1a08499d72edee48ca94541bcdf719 100644 --- a/languages/pt-br.ini +++ b/languages/pt-br.ini @@ -93,6 +93,7 @@ Back to Search Results = "Voltar à lista de resultados" Backtrace = "Caminho" Bag = "Cesto" Balance = "Balanço" +Barcode = "Código de Barra" basic_search_keep_filters = "Restringir a busca atual / manter filtros" Be the first to leave a comment = "Seja o primeiro a deixar um comentário" Be the first to tag this record = "seja o primeiro a adicionar uma tag" @@ -182,6 +183,7 @@ Check Recall = "Ver Pedido de Devolução" Checked Out = "Emprestado" Checked Out Items = "Itens emprestados" Checkedout = "Emprestado" +Checkout Date = "Data de saÃda" Chicago Citation = "Citação norma Chicago" child_record_count = "%%count%% registros" child_records = "Conteúdos/Exemplares" @@ -206,6 +208,7 @@ collection_disambiguation = "Encontradas várias coleções correspondentes" collection_empty = "Não há registros para mostrar." collection_view_record = "Ver Registro" Collections = "Coleções" +comment_anonymous_user = "Anonimos" comment_error_load = "Erro: Não foi possÃvel apagar a lista de comentários" comment_error_save = "Erro: Não foi possÃvel salvar o comentário" Comments = "Comentários" @@ -256,6 +259,11 @@ date_year_placeholder = "A" Debug Information = "Debug Information" del_search = "Retirar o grupo de busca" Delete = "Apagar" +delete_account_confirm = "Tem certeza que quer remover sua conta?" +delete_account_description_html = "Buscas salva e listas de favoritos serão removidas. Você pode estabelecer uma nova conta mais tarde se desejar." +delete_account_failure = "Remoção da conta falhou." +delete_account_success_message = "Sua conta foi deletada. Saia do sistema" +delete_account_title = "Remover Conta" delete_all = "Deletar todo" delete_comment_failure = "Não foi possÃvel apagar o comentário." delete_comment_success = "Comentário apagado." @@ -277,6 +285,8 @@ Document Type = "Tipo de documento" DOI = "DOI" doi_detected_html = "Sua busca aparenta conter um DOI. Clique aqui para checar a disponibilidade do recurso: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Desenhar Caixa de pesquisa" +draw_searchbox_end = "Libere o mouse para terminar o arrastar." +draw_searchbox_start = "Clique e arraste para desenhar um retângulo." Due = "Fim do empréstimo" Due Date = "Data fim do empréstimo" DVD = "DVD" @@ -343,11 +353,10 @@ export_fail = "Os itens não foram exportados" export_invalid_format = "O formato de exportação selecionado não é suportado por este registro." export_missing = "Alguns dados foram perdidos. Os itens não foram exportados." export_no_formats = "Este registro não suporta a exportação." -export_redirect = "A exportar para o serviço %%service%%" -export_refworks = "A exportar para o RefWorks" export_save = "Salvar arquivo" export_selected = "Exportar selec." export_selected_favorites = "Exportar os Favoritos selecionados" +export_send = "A exportar para o serviço %%service%%" export_success = "Exportação concluÃda" export_unsupported_format = "Formato de exportação não suportado" external_auth_heading = "Acesso a material licenciado" @@ -500,12 +509,14 @@ ill_request_processed = "Processado" ill_request_profile_html = "Para informação sobre Pedido de Débito Interbiblioteca, por favor estabeleça seu <a href="%%url%%">Perfil no Catálogo da Biblioteca</a>." ill_request_submit_text = "Colocar Pedido" Illustrated = "Ilustrado" +ils_action_unavailable = "A função requerida não está disponÃvel com o cartão de biblioteca ativo." ils_connection_failed = "O sistema está em manutenção." ils_offline_holdings_message = "A informação da disponibilidade do(s) exemplar(es) não está disponÃvel de momento; por favor, aceite as nossas desculpas por qualquer inconveniente que isso possa causar e contate-nos para obter ajuda:" ils_offline_home_message = "Os dados da sua conta e informação em tempo real de existências dos itens não estão disponÃveis de momento; por favor, aceite as nossas desculpas por qualquer inconveniente que isso possa causar e contate-nos para obter assistência:" ils_offline_login_message = "Os dados da sua conta estarão indisponÃveis durante este tempo Por favor, aceitem nossas desculpas por qualquer inconveniente que isso possa causar e contate-nos para obter assistência:" ils_offline_status = "O sistema está em manutenção." ils_offline_title = "Sistema em Manutenção" +ils_transaction_history_disabled = "O histórico de emprestimo não está disponÃvel para o cartão de biblioteca ativo." Import Record = "Importar Registro" in = "em" In This Collection = "Nesta coleção" @@ -580,6 +591,8 @@ list_access_denied = "Você não tem permissão para ver esta lista." list_edit_name_required = "É necessário indicar o nome da lista." load_tag_error = "Erro: não foi possÃvel carregar as Tags" Loading = "Carregando" +Loan History = "Histórico de empréstimo" +loan_history_empty = "Você não tem nenhum emprestimo no histórico." Local Login = "Login local" local_login_desc = "Entre com o usuário e senha criado neste site." Located = "Localizado" @@ -607,6 +620,7 @@ Message From Sender = "Remetente" Metadata Prefix = "Prefixo de Metadados" Microfilm = "Microfilme" MLA Citation = "Citação norma MLA" +Mobile Number = "Número de celular" mobile_link = "Parece estar utilizando um dispositivo móvel; mudar para formato mobile?" Monograph Title = "TÃtulo de Monografia" more = "Mais" @@ -636,6 +650,8 @@ New Items = "Novos itens" New Title = "Novo tÃtulo" new_password = "Nova Senha" new_password_success = "Sua Senha foi alterada com sucesso" +new_user_welcome_subject = "Sua nova conta na %%library%%" +new_user_welcome_text = "Bem vindo a %%library%%. Uma nova conta foi criada para %%firstname%% %%lastname%%. sua identificação é %%username%%. Por favor, coloque uma senha nesta páginga: %%url%%" Newspaper = "Jornal" Next = "Seguinte" No citations are available for this record = "Não há citações disponÃveis para este registro" @@ -785,6 +801,7 @@ Range slider = "Ajuste do Intervalo" Read the full review online... = "Leia a análise completa linha ..." Recall This = "Reservar" recaptcha_not_passed = "Código CAPTCHA não coincidiu" +recently_returned_channel_title = "Recentemente Devolvidos" Record Citations = "Citações do registro" Record Count = "Número de Registros" Record Type = "Tipo de registro" @@ -802,8 +819,17 @@ recovery_new_disabled = "Não é permitido a troca de senha neste momento" recovery_title = "Recuperação de Senha" recovery_too_soon = "Muitos pedidos de recuperação feitos, por favor, tente mais tarde" recovery_user_not_found = "Conta não encontrada" +rectangle_center_message = "Este é o ponto centra do retângul marcado" Refine Results = "Refinar Resultados" Region = "Região" +relais_available = "Este item está disponÃvel por meio do empréstimo entre bibliotecas. Gostaria de requisitá-lo?" +relais_checking = "Verificando disponibilidade..." +relais_error_html = "Houve um problema com seu pedido. Clique em <a href="%%url%%" target='new'>here</a> para pedir esse item usando o site de empréstimo entre bibliotecas.</a>" +relais_request = "Pedido de Empréstimo entre Bibliotecas" +relais_requesting = "Pedindo..." +relais_search = "Buscar Emprestimo entre Bibliotecas" +relais_success_label = "Confirmação:" +relais_success_message = "Identificação do pedido #%%id%% foi criado. Você receberá uma confirmação por E-mail." Related Author = "Autor relacionado" Related Items = "Itens Relacionados" Related Subjects = "Assuntos relacionados" @@ -832,12 +858,14 @@ Requests = "Pedidos" Reserves = "Reservas" Reserves Search = "Busca de Reservas" Reserves Search Results = "Resultados da Busca de Reservas" +result_checkbox_label = "Selcionar número de resultado %%number%%" result_count = "%%count%% resultados" Results = "Resultados" results = "resultados" Results for = "Resultados para" Results per page = "Resultados por página" Resumption Token = "Token de retomada" +Return Date = "Data de Devolução" Review by = "Analisado por" Reviews = "Análises" Save = "Gravar" @@ -909,8 +937,14 @@ sort_author = "Autor" sort_author_author = "Ordem alfabética" sort_author_relevance = "Popularidade" sort_callnumber = "Ãrea" +sort_checkout_date_asc = "Data de saÃda (o mais antigo primeiro)" +sort_checkout_date_desc = "Dara de saÃda (o mais recente primeiro)" sort_count = "Contagem de resultados" +sort_due_date_asc = "Data de Vencimento (o mais antigo primeiro)" +sort_due_date_desc = "Data de Vencimento (o mais recente primeiro)" sort_relevance = "Relevância" +sort_return_date_asc = "Data de Devolução (o mais antigo primeiro)" +sort_return_date_desc = "Data de Devolução (o mais recente primeiro)" sort_title = "TÃtulo" sort_year = "Data Descendente" sort_year asc = "Data Ascendente" @@ -1024,6 +1058,7 @@ total_tags = "Total de tags" total_users = "Total de Usuários" Transliterated Title = "TÃtulo Transliterado" tree_search_limit_reached_html = "A sua busca devolveu muitos resultados para mostrar em árvore. Mostrando apenas os primeiros <b>%%limit%%</b> registros. Para uma busca completa clique <a id="fullSearchLink" href="%%url%%" target="_blank">aqui.</a>" +trending_items_channel_title = "Itens de Tendência" unique_tags = "Tags únicas" University Library = "Biblioteca Universitária" Unknown = "online" diff --git a/languages/pt.ini b/languages/pt.ini index 5bd591bb6b668dd7b6f1f743366611b7a46e9e42..5a4573dd36e9218ef28a76a1e202e44a64255bff 100644 --- a/languages/pt.ini +++ b/languages/pt.ini @@ -312,11 +312,10 @@ export_fail = "Os itens não foram exportados" export_invalid_format = "O formato de exportação selecionado não é suportado por este registo." export_missing = "Alguns dados foram perdidos. Os itens não foram exportados." export_no_formats = "Este registo não suporta a exportação." -export_redirect = "A exportar para o serviço %%service%%" -export_refworks = "A exportar para o RefWorks" export_save = "Guardar arquivo" export_selected = "Exportar selec." export_selected_favorites = "Exportar os Favoritos seleccionados" +export_send = "A exportar para o serviço %%service%%" export_success = "Exportação concluÃda" export_unsupported_format = "Formato de exportação não suportado" FAQs = "FAQs" diff --git a/languages/ru.ini b/languages/ru.ini index faeaa7ced6f45681f163302cbc2c9d645f59c4a5..509f8576e74c2334d699de5a434ca90a945b6fd3 100644 --- a/languages/ru.ini +++ b/languages/ru.ini @@ -343,11 +343,10 @@ export_fail = "Ваши документы не ÑкÑпортированы" export_invalid_format = "Выбранный формат ÑкÑпорта не поддерживаетÑÑ Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð¹ запиÑи." export_missing = "Пропущены некоторые данные. Ваши документы не поддерживаютÑÑ." export_no_formats = "Ðта запиÑÑŒ не поддерживает ÑкÑпорт." -export_redirect = "ЗапуÑтите ÑкÑпорт в %%ÑервиÑ%%" -export_refworks = "ЗапуÑтите ÑкÑпорт в RefWorks" export_save = "Сохранить файл" export_selected = "ÐкÑпорт выбран" export_selected_favorites = "ÐкÑпорт выбранных Избранных" +export_send = "ЗапуÑтите ÑкÑпорт в %%ÑервиÑ%%" export_success = "ÐкÑпорт завершен" export_unsupported_format = "Ðеподдерживаемый формат ÑкÑпорта" FAQs = "ЧаÑто задаваемые вопроÑÑ‹" diff --git a/languages/sl.ini b/languages/sl.ini index b3e7cd1e235e387785637348265ec3fb7352296b..e6a362659b099774bb869454b035f7186c6524eb 100644 --- a/languages/sl.ini +++ b/languages/sl.ini @@ -268,11 +268,10 @@ export_fail = "VaÅ¡e knjige/Älanki niso bili izvoženi" export_invalid_format = "Zadetek ne podpira izbrani izvozni format." export_missing = "Manjkajo nekateri podatki. VaÅ¡i knjige/Älanki niso bili izvoženi." export_no_formats = "Ta zadetek ne podpira izvoz." -export_redirect = "PriÄni izvoz v %%service%%" -export_refworks = "PriÄni izvoz v RefWorks" export_save = "Shrani datoteko" export_selected = "Izvoz izbran" export_selected_favorites = "Izvozi izbrane priljubljene" +export_send = "PriÄni izvoz v %%service%%" export_success = "Izvoz konÄan" export_unsupported_format = "Nepodprt izvozni format" FAQs = "Pogosta vpraÅ¡anja" diff --git a/languages/sv.ini b/languages/sv.ini index 0d40848a35a92cfa8b2bba7618e37704460d49e8..75368406c95fbd169bfd3c435560311fb8ca8633 100644 --- a/languages/sv.ini +++ b/languages/sv.ini @@ -92,6 +92,7 @@ Back to Search Results = "Tillbaka till sökresultaten" Backtrace = "Anropsstacken" Bag = "Korg" Balance = "Att betala" +Barcode = "Streckkod" basic_search_keep_filters = "Spara filtreringen i bruk" Be the first to leave a comment = "Lägg till första kommentaren" Be the first to tag this record = "Lägg till första taggen" @@ -181,6 +182,7 @@ Check Recall = "Kolla Ã¥terkallelse" Checked Out = "LÃ¥n" Checked Out Items = "LÃ¥n" Checkedout = "LÃ¥n" +Checkout Date = "UtlÃ¥ningsdag" Chicago Citation = "Chicago-stil citat" child_record_count = "%%count%% poster" child_records = "InnehÃ¥ll/delar" @@ -205,6 +207,7 @@ collection_disambiguation = "Found Multiple Matching Collections" collection_empty = "Inget att visa" collection_view_record = "Visa posten" Collections = "Samlingar" +comment_anonymous_user = "Anonym" comment_error_load = "Fel: kommentarlistan kunde inte ändras" comment_error_save = "Fel: kommentaren kunde inte sparas" Comments = "Kommentarer" @@ -256,6 +259,11 @@ date_year_placeholder = "Ã…" Debug Information = "Felrapport" del_search = "Radera sökgruppen" Delete = "Radera" +delete_account_confirm = "Är du säker pÃ¥ att du vill radera ditt konto?" +delete_account_description_html = "Sparade sökningar och favoritlistor raderas. Om du vill kan du skapa ett nytt konto senare." +delete_account_failure = "Radering av kontot misslyckades." +delete_account_success_message = "Kontot raderades. Loggar ut..." +delete_account_title = "Radera kontot" delete_all = "Radera alla" delete_comment_failure = "Radering av kommentaren misslyckades." delete_comment_success = "Kommentaren har raderats." @@ -277,6 +285,8 @@ Document Type = "Dokument typ" DOI = "DOI" doi_detected_html = "Din sökning verkar innehÃ¥lla en DOI. Klicka denna länk för att kolla tillgängligheten: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Välj region" +draw_searchbox_end = "Avsluta dra för att komplettera begränsningen." +draw_searchbox_start = "Klicka och dra för att begränsa omrÃ¥det." Due = "Förfaller" Due Date = "Förfallodag" DVD = "DVD" @@ -343,11 +353,10 @@ export_fail = "Exporten av poster misslyckades" export_invalid_format = "Det valda exportformatet är inte i bruk för detta material." export_missing = "Uppgifter saknas. Exporten av poster misslyckades." export_no_formats = "Detta material stöder inte exportfunktionen" -export_redirect = "Börja export till %%service%%" -export_refworks = "Exportera till RefWorks" export_save = "Spara posten" export_selected = "Exportera valda" export_selected_favorites = "Exportera valda favoriter" +export_send = "Skicka till %%service%%" export_success = "Exporten färdig" export_unsupported_format = "Export i det valda formatet är inte möjlig" external_auth_heading = "Behörighet att komma Ã¥t licensierat material" @@ -500,12 +509,14 @@ ill_request_processed = "Behandlad" ill_request_profile_html = "Logga in med din <a href="%%url%%">bibliotekskort</a> för att se fjärrlÃ¥nbeställningar." ill_request_submit_text = "Beställ" Illustrated = "Illustrerad" +ils_action_unavailable = "Den begärda Ã¥tgärden är inte tillgänglig med det aktiva bibliotekskortet." ils_connection_failed = "Anslutning till bibliotekssystemet misslyckades. Information relaterad till ditt bibliotekskonto kan inte visas. Kontakta kundtjänst om problemet kvarstÃ¥r." ils_offline_holdings_message = "Holdings and item availability information is currently unavailable. Please accept our apologies for any inconvenience this may cause and contact us for further assistance:" ils_offline_home_message = "Your account details and live item information will be unavailable during this time. Please accept our apologies for any inconvenience this may cause and contact us for further assistance:" ils_offline_login_message = "Your account details will be unavailable during this time. Please accept our apologies for any inconvenience this may cause and contact us for further assistance:" ils_offline_status = "Our Library Management System is currently under maintenance." ils_offline_title = "System Under Maintenance" +ils_transaction_history_disabled = "UtlÃ¥ningshistoriken är inte aktiverat för det aktiva bibliotekskortet." Import Record = "Importera posten" in = "i fältet" In This Collection = "I denna samling" @@ -580,6 +591,8 @@ list_access_denied = "Du har inte rättigheter att se denna lista." list_edit_name_required = "Namn pÃ¥ lista behövs." load_tag_error = "Fel: taggar kunde inte hämtas." Loading = "Laddar" +Loan History = "UtlÃ¥ningshistorik" +loan_history_empty = "Du har inga lÃ¥n i utlÃ¥ningshistoriken." Local Login = "Lokal login" local_login_desc = "Ange användarnamn och lösenord du skapade för den här webbplatsen." Located = "Placering" @@ -607,6 +620,7 @@ Message From Sender = "Meddelande frÃ¥n" Metadata Prefix = "Metadata Prefix" Microfilm = "Mikrofilm" MLA Citation = "MLA-referens" +Mobile Number = "Mobiltelefon" mobile_link = "You appear to be on a mobile device; switch to mobile view?" Monograph Title = "Monografis titel" more = "mer" @@ -636,6 +650,8 @@ New Items = "Nytt i katalogen" New Title = "Ny titel" new_password = "Nytt lösenord" new_password_success = "Lösenordet har ändrats" +new_user_welcome_subject = "Ditt nytt konto i %%library%%" +new_user_welcome_text = "Välkommen till %%library%%. En ny konto har skapats för %%firstname%% %%lastname%%. Ditt användarnamn är %%username%%. Vänligen lägg till ett lösenord pÃ¥ denna sidan: %%url%%" Newspaper = "Tidning" Next = "Nästa" No citations are available for this record = "Inga hänvisningar tillgängliga för denna post" @@ -785,6 +801,7 @@ Range slider = "Justera intervallet" Read the full review online... = "Läs hela recensionen online..." Recall This = "Reservera denna" recaptcha_not_passed = "CAPTCHA inte klarat" +recently_returned_channel_title = "Nyligen returnerad" Record Citations = "Hänvisningar gällande denna post" Record Count = "Antal poster" Record Type = "Posttyp" @@ -802,6 +819,7 @@ recovery_new_disabled = "Det är inte tillÃ¥tet att byta lösenord vid denna tid recovery_title = "Hämtning av lösenord" recovery_too_soon = "Fär mÃ¥nga hämntningsbegäranden har placerats. Var vänligen försök igen senare." recovery_user_not_found = "Ditt konto hittades inte" +rectangle_center_message = "Mittpunkt för begränsat omrÃ¥de" Refine Results = "Förfina resultatet" Region = "Region" Related Author = "Relaterad upphovsman" @@ -832,12 +850,14 @@ Requests = "Reserveringar" Reserves = "Material i begränsad användning" Reserves Search = "Sök material i begränsad användning" Reserves Search Results = "Sökresultat för material i begränsad användning" +result_checkbox_label = "Välj resultat nummer %%number%%" result_count = "%%count%% resultat" Results = "Resultat" results = "resultat" Results for = "Resultat för sökningen" Results per page = "Resultat per sida" Resumption Token = "Resumption Token" +Return Date = "Ã…terlämningsdag" Review by = "Recensent" Reviews = "Recensioner" Save = "Spara" @@ -909,8 +929,14 @@ sort_author = "Upphovsman" sort_author_author = "Alfabetiskt" sort_author_relevance = "Relevans" sort_callnumber = "Signum" +sort_checkout_date_asc = "UtlÃ¥ningsdag (äldst först)" +sort_checkout_date_desc = "UtlÃ¥ningsdag (nyast först)" sort_count = "Antal" +sort_due_date_asc = "Förfallodag (äldst först)" +sort_due_date_desc = "Förfallodag (nyast först)" sort_relevance = "Relevans" +sort_return_date_asc = "Returneringsdag (äldst först)" +sort_return_date_desc = "Returneringsdag (nyast först)" sort_title = "Titel" sort_year = "Tid (nyaste först)" sort_year asc = "Tid (äldsta först)" @@ -1024,6 +1050,7 @@ total_tags = "Totalantal taggar" total_users = "Totalantar användare" Transliterated Title = "Translittererad titel" tree_search_limit_reached_html = "Din sökning gav för mÃ¥nga resultat att visa i trädet. Visar de första <b>%%limit%%</b> resultat. Klicka <a id="fullSearchLink" href="%%url%%" target="_blank">här</a> för att se alla resultat." +trending_items_channel_title = "Hett nu" unique_tags = "Unika taggar" University Library = "Universitetsbibliotek" Unknown = "Okänd" diff --git a/languages/tr.ini b/languages/tr.ini index 3a9834a7656eb47e31185e13551a796a42c33a17..281acabadf876fdc30128090829d3c374d10e781 100644 --- a/languages/tr.ini +++ b/languages/tr.ini @@ -104,6 +104,7 @@ Back to Search Results = "Tarama Sonuçlarına Dön" Backtrace = "Geri izleme" Bag = "Sepet" Balance = "Denge" +Barcode = "Barkod" basic_search_keep_filters = "Güncel filitrelerimle devam et." Be the first to leave a comment = "Ä°lk yorumlayan siz olun" Be the first to tag this record = "Ä°lk siz ekleyin" @@ -193,6 +194,7 @@ Check Recall = "Ä°adeleri Kontrol Et" Checked Out = "Ödünçte" Checked Out Items = "Ä°ade Ettiklerim" Checkedout = "Ödünç VerilmiÅŸ" +Checkout Date = "Ödünç Verme Tarihi" Chicago Citation = "Chicago Stili Alıntı" child_record_count = "%%count%% Kayıt" child_records = "İçindekiler/parçalar" @@ -217,6 +219,7 @@ collection_disambiguation = "Çoklu eÅŸleÅŸtirilmiÅŸ koleksiyonlar bulundu" collection_empty = "Görüntülenecek kayıt yok." collection_view_record = "Kayıt Görüntüle" Collections = "Koleksiyonlar" +comment_anonymous_user = "Anonim" comment_error_load = "Yorum Yüklenemedi" comment_error_save = "Yorum Saklanmadı" Comments = "Yorumlar" @@ -267,6 +270,11 @@ date_year_placeholder = "Y" Debug Information = "Hata Bilgisi" del_search = "Tarama Alanı Sil" Delete = "Sil" +delete_account_confirm = "Hesabınızı silmek istediÄŸinizden eminmisiniz?" +delete_account_description_html = "Kayıtlı aramalar ve favori listeler silinecek. Ä°sterseniz daha sonra yeni bir hesap oluÅŸturabilirsiniz." +delete_account_failure = "Hesap silinemedi" +delete_account_success_message = "Hesabınız silinmiÅŸtir. Çıkış yapın..." +delete_account_title = "Hesabı Sil" delete_all = "Tümünü Sil" delete_comment_failure = "Yorum silinemedi." delete_comment_success = "Yorum silindi." @@ -288,6 +296,8 @@ Document Type = "Doküman Türü" DOI = "DOI" doi_detected_html = "Aramanızın bir DOI içerdiÄŸi görülüyor. Kaynağın kullanılabilirliÄŸini kontrol etmek için burayı tıklayın: <a href="%%url%%">%%doi%%</a>" Draw Search Box = "Arama Kutusu Düzenle" +draw_searchbox_end = "Çizimi bitirmek için fareyi serbest bırakın." +draw_searchbox_start = "Dikdörtgen çizmek için tıklayın ve sürükleyin." Due = "Ä°ade Tarihi" Due Date = "Ä°ade tarihi" DVD = "DVD" @@ -354,11 +364,10 @@ export_fail = "Kayıtlarınız aktarılamadı" export_invalid_format = "SeçilmiÅŸ ihraç formatı bu kayıt tarafından desteklenmiyor." export_missing = "Bazı veriler eksik. Kayıtlarınız aktarılamadı." export_no_formats = "Bu kayıt ihracç desteklemiyor." -export_redirect = "%%service%% e ihraç etmeyi baÅŸlat" -export_refworks = "RefWorks'e ihraç iÅŸlemine baÅŸla" export_save = "Dosyayı Sakla" export_selected = "Aktarım seçildi" export_selected_favorites = "SeçilmiÅŸ Favorileri Aktar" +export_send = "%%service%% e ihraç etmeyi baÅŸlat" export_success = "Aktarım Tamamlandı" export_unsupported_format = "Desteklenmeyen Aktarım Formatı" external_auth_heading = "Lisanslı materyale eriÅŸim" @@ -511,12 +520,14 @@ ill_request_processed = "Ä°ÅŸlendi" ill_request_profile_html = "Kütüphaneler arası ödünç alma istek bilgileri için, Lütfen <a href="%%url%%">Kütüphane Katalog Profiline Tıklayınız</a>." ill_request_submit_text = "Ä°stekte bulunun" Illustrated = "Resimli" +ils_action_unavailable = "Ä°stenen iÅŸlev aktif kütüphane kartında mevcut deÄŸildir." ils_connection_failed = "Kütüphane sistemimiz bakımda." ils_offline_holdings_message = "Kopya kayıtları ve kayıların durum bilgileri ÅŸu anda eriÅŸilebilir deÄŸil. Bunun için özür dileriz, daha fazla yardım için bizimle irtibata geçebilirsiniz:" ils_offline_home_message = "Åžu anda hesap bilgileriniz ve kayıt bilgileri eriÅŸilebilir deÄŸil. Bunun için özür dileriz, daha fazla yardım için bizimle irtibata geçebilirsiniz:" ils_offline_login_message = "Hesabınıza ait bilgiler ÅŸu anda eriÅŸilebilir deÄŸil. Bunun için özür dileriz, daha fazla yardım için bizimle irtibata geçebilirsiniz:" ils_offline_status = "Kütüphane sistemimiz bakımda." ils_offline_title = "Sistem Bakımda" +ils_transaction_history_disabled = "Aktif kütüphane kartı için Mdünç verme geçmiÅŸi etkin deÄŸil." Import Record = "Kaydı Al" in = "ile" In This Collection = "Bu Koleksiyonda" @@ -591,6 +602,8 @@ list_access_denied = "Bu listeyi görüntülemeye yetkiniz yok." list_edit_name_required = "Liste düzenleme adı gerekli" load_tag_error = "Etiket yükleme hatası" Loading = "Yüklüyor..." +Loan History = "Ödünç Verme GeçmiÅŸi" +loan_history_empty = "Ödünç verme geçmiÅŸinizde hiç ödünç verme yok.." Local Login = "Yerel Giriþ" local_login_desc = "Bu sayfa için oluþturduðunuz kullanýcý adý ve þifrenizi giriniz." Located = "BulunduÄŸu Yer" @@ -618,6 +631,7 @@ Message From Sender = "Gönderenin Mesajı" Metadata Prefix = "Metadata Öneki" Microfilm = "Mikrofilm" MLA Citation = "MLA Alıntı" +Mobile Number = "Mobil Numarası" mobile_link = "Mobil cihazda görünüyorsunuz; mobil görünümüne geçmek istermisiniz?" Monograph Title = "Monograf BaÅŸlığı" more = "daha fazla" @@ -647,6 +661,8 @@ New Items = "Yeni Gelen Materyaller" New Title = "Yeni BaÅŸlık" new_password = "Yeni Åžifre" new_password_success = "Åžifreniz baÅŸarı ile deÄŸiÅŸtirildi" +new_user_welcome_subject = "%%library%% deki yeni hesabınız" +new_user_welcome_text = "%%library%% ye HoÅŸ Geldiniz. %%firstname%% %%lastname%% için yeni hesabınız açılmıştır. Kullanıcı adınız %%username%%. %%url%% bu baÄŸlantıya tıklayarak ÅŸifrenizi oluÅŸturunuz" Newspaper = "Gazete" Next = "Sonraki" No citations are available for this record = "Bu kayıt için alıntı yapılmamış" @@ -796,6 +812,7 @@ Range slider = "Sınırlandırma Düzeyi" Read the full review online... = "EleÅŸtirinin tamamını online oku..." Recall This = "Rezerve" recaptcha_not_passed = "CAPTCHA kodu uygun deÄŸil" +recently_returned_channel_title = "Son Zamanlarda Ä°ade Edilenler" Record Citations = "Alıntılar" Record Count = "Kayıt Sayısı" Record Type = "Kayıt Türü" @@ -813,8 +830,17 @@ recovery_new_disabled = "Åžu anda ÅŸifrenizi deÄŸiÅŸtirmeye izininiz yok" recovery_title = "Åžifre Kurtarma" recovery_too_soon = "Çok fazla kurtarma isteÄŸi yapıldı, lütfen daha sonra tekrar deneyiniz" recovery_user_not_found = "Hesabınız bulunamadı" +rectangle_center_message = "Vurgulanan dikdörtgen için orta nokta budur" Refine Results = "Sonuçları Daraltın" Region = "Bölge" +relais_available = "Bu kayda Kütüphanelerarası Ödünç Verme yoluyla ulaşılabilir. Talep etmek ister misiniz?" +relais_checking = "KullanılabilirliÄŸi kontrol ediliyor..." +relais_error_html = "Bu istekle ilgili bir sorun oluÅŸtu. <a href="%%url%%" target='new'>Buraya tıklayın</a> Kütüphaneler arası ödünç verme web sitesini kullanarak bu öğeyi istemek için.</a>" +relais_request = "Kütüphaneler Arası Ödünç Verme Ä°steÄŸi" +relais_requesting = "Ä°steniyor..." +relais_search = "Kütüphanelerarası Ödünç Vermeyi Tara" +relais_success_label = "Onayla:" +relais_success_message = "#%%id%% Ä°stek talebi oluÅŸturuldu. Bununla ilgili bir onay maili alacaksınız." Related Author = "Ä°lgili Yazar" Related Items = "Ä°lgili Kayıtlar" Related Subjects = "Ä°lgili Konular" @@ -843,12 +869,14 @@ Requests = "Ä°stekler" Reserves = "Rezerve" Reserves Search = "Rezerve Tara" Reserves Search Results = "Rezerve Tarama Sonucu" +result_checkbox_label = "%%number%% Sonuç numarasını seç" result_count = "%%count%% sonuçlar" Results = "Sonuçlar" results = "sonuçlar" Results for = "Results for" Results per page = "Her Sayfadaki Sonuçlar" Resumption Token = "KalInan Yer Belirteci" +Return Date = "Ä°ade Tarihi" Review by = "EleÅŸtri" Reviews = "EleÅŸtiriler" Save = "Sakla" @@ -920,8 +948,14 @@ sort_author = "Yazar" sort_author_author = "Yazarı Yazara Göre Sırala" sort_author_relevance = "Yazar ilgililiÄŸine göre sırala" sort_callnumber = "Yer Numarası" +sort_checkout_date_asc = "Ödünç Verme Tarihi (Ä°lkönce eski)" +sort_checkout_date_desc = "Ödünç Verme Tarihi (Ä°lkönce yeni)" sort_count = "Sonuç Sayısı" +sort_due_date_asc = "Gecikme Tarihi (Ä°lkönce eski)" +sort_due_date_desc = "Gecikme Tarihi (Ä°lkönce yeni)" sort_relevance = "Ä°lgili" +sort_return_date_asc = "Ä°ade Tarihi (Ä°lkönce eski)" +sort_return_date_desc = "Ä°ade Tarihi (Ä°lkönce yeni)" sort_title = "Materyal Adı" sort_year = "Tarih-Azalan" sort_year asc = "Tarih-Artan" @@ -1035,6 +1069,7 @@ total_tags = "Toplam Etiketler" total_users = "Toplam Kullanýcalr" Transliterated Title = "Çeviri BaÅŸlığı" tree_search_limit_reached_html = "Taramanız çok fazla sonuç verdi. Bu yapıda gösterilemiyor. Sadece ilk <b>%%limit%%</b> kayıt gösteriliyor. Tüm tarama için <a id="fullSearchLink" href="%%url%%" target="_blank">'e tıklayın.</a>" +trending_items_channel_title = "Trend Olan Kayıtlar" unique_tags = "Benzersiz Etiketler" University Library = "Ãœniversite Kütüphanesi" Unknown = "Bilinmiyor" diff --git a/languages/zh-cn.ini b/languages/zh-cn.ini index 8a9e6401942f57fa43ddd9ee2e41fa0d1af6de47..dc9cdc022324ed3ecc6b42c0c6df2c5ff0cfe2a7 100644 --- a/languages/zh-cn.ini +++ b/languages/zh-cn.ini @@ -219,7 +219,6 @@ export_download = "下载文件" export_exporting = "创建导出的文件" export_fail = "您的项目没有导出" export_missing = "一些数æ®ä¸¢å¤±. 您的项目没有导出." -export_refworks = "开始导出到RefWorks" export_save = "ä¿å˜æ–‡ä»¶" export_selected = "导出所选" export_selected_favorites = "导出选定的最爱" diff --git a/languages/zh.ini b/languages/zh.ini index dcb8b1af028d799ca3095178afcf9a0f0e0e3841..8b8c43efb1ca9dc252ebf3682e6b846d3a09861d 100644 --- a/languages/zh.ini +++ b/languages/zh.ini @@ -219,7 +219,6 @@ export_download = "下載文件" export_exporting = "創建導出的文件" export_fail = "æ‚¨çš„é …ç›®æ²’æœ‰å°Žå‡º" export_missing = "一些數據丟失. æ‚¨çš„é …ç›®æ²’æœ‰å°Žå‡º." -export_refworks = "開始導出到RefWorks" export_save = "ä¿å˜æ–‡ä»¶" export_selected = "導出所é¸" export_selected_favorites = "導出é¸å®šçš„最愛" diff --git a/module/VuFind/Module.php b/module/VuFind/Module.php index 5fba39ce42bf7e1c5b9f9cf7259ba99357b0ce3c..97f335a89ca0e11702dd740d4392a73a1266ae51 100644 --- a/module/VuFind/Module.php +++ b/module/VuFind/Module.php @@ -2,7 +2,7 @@ /** * ZF2 module definition for the VuFind application * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org */ namespace VuFind; + use Zend\Mvc\MvcEvent; /** diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index 0fd1efe5090c9c9470a1ad77b8ed748423121e50..40ea0d9483f61ea0041a48ab78809070e22cea97 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -5,7 +5,7 @@ $config = [ 'router' => [ 'routes' => [ 'default' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/[:controller[/[:action]]]', 'constraints' => [ @@ -18,8 +18,22 @@ $config = [ ], ], ], + 'alma-webhook' => [ + 'type' => 'Zend\Router\Http\Segment', + 'options' => [ + 'route' => '/Alma/Webhook/[:almaWebhookAction]', + 'constraints' => [ + 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + ], + 'defaults' => [ + 'controller' => 'Alma', + 'action' => 'Webhook', + ], + ], + ], 'content-page' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/Content/[:page]', 'constraints' => [ @@ -32,7 +46,7 @@ $config = [ ], ], 'legacy-alphabrowse-results' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/AlphaBrowse/Results', 'defaults' => [ @@ -42,17 +56,17 @@ $config = [ ] ], 'legacy-bookcover' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/bookcover.php', 'defaults' => [ - 'controller' => 'cover', + 'controller' => 'Cover', 'action' => 'Show', ] ] ], 'legacy-summonrecord' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/Summon/Record', 'defaults' => [ @@ -62,7 +76,7 @@ $config = [ ] ], 'legacy-worldcatrecord' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/WorldCat/Record', 'defaults' => [ @@ -72,7 +86,7 @@ $config = [ ] ], 'soap-shibboleth-logout-notification-handler' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/soap/shiblogout', 'defaults' => [ @@ -105,158 +119,350 @@ $config = [ ], 'controllers' => [ 'factories' => [ - 'ajax' => 'VuFind\Controller\Factory::getAjaxController', - 'alphabrowse' => 'VuFind\Controller\Factory::getAlphabrowseController', - 'author' => 'VuFind\Controller\Factory::getAuthorController', - 'authority' => 'VuFind\Controller\Factory::getAuthorityController', - 'browse' => 'VuFind\Controller\Factory::getBrowseController', - 'browzine' => 'VuFind\Controller\Factory::getBrowZineController', - 'cart' => 'VuFind\Controller\Factory::getCartController', - 'channels' => 'VuFind\Controller\Factory::getChannelsController', - 'collection' => 'VuFind\Controller\Factory::getCollectionController', - 'collections' => 'VuFind\Controller\Factory::getCollectionsController', - 'combined' => 'VuFind\Controller\Factory::getCombinedController', - 'confirm' => 'VuFind\Controller\Factory::getConfirmController', - 'content' => 'VuFind\Controller\Factory::getContentController', - 'cover' => 'VuFind\Controller\Factory::getCoverController', - 'eds' => 'VuFind\Controller\Factory::getEdsController', - 'edsrecord' => 'VuFind\Controller\Factory::getEdsrecordController', - 'eit' => 'VuFind\Controller\Factory::getEITController', - 'eitrecord' => '\VuFind\Controller\Factory::getEITrecordController', - 'error' => 'VuFind\Controller\Factory::getErrorController', - 'externalauth' => 'VuFind\Controller\Factory::getExternalAuthController', - 'feedback' => 'VuFind\Controller\Factory::getFeedbackController', - 'help' => 'VuFind\Controller\Factory::getHelpController', - 'hierarchy' => 'VuFind\Controller\Factory::getHierarchyController', - 'index' => 'VuFind\Controller\Factory::getIndexController', - 'install' => 'VuFind\Controller\Factory::getInstallController', - 'libguides' => 'VuFind\Controller\Factory::getLibGuidesController', - 'librarycards' => 'VuFind\Controller\Factory::getLibraryCardsController', - 'missingrecord' => 'VuFind\Controller\Factory::getMissingrecordController', - 'my-research' => 'VuFind\Controller\Factory::getMyResearchController', - 'oai' => 'VuFind\Controller\Factory::getOaiController', - 'pazpar2' => 'VuFind\Controller\Factory::getPazpar2Controller', - 'primo' => 'VuFind\Controller\Factory::getPrimoController', - 'primorecord' => 'VuFind\Controller\Factory::getPrimorecordController', - 'qrcode' => 'VuFind\Controller\Factory::getQRCodeController', - 'record' => 'VuFind\Controller\Factory::getRecordController', - 'records' => 'VuFind\Controller\Factory::getRecordsController', - 'search' => 'VuFind\Controller\Factory::getSearchController', - 'shibbolethlogoutnotification' => 'VuFind\Controller\Factory::getShibbolethLogoutNotificationController', - 'summon' => 'VuFind\Controller\Factory::getSummonController', - 'summonrecord' => 'VuFind\Controller\Factory::getSummonrecordController', - 'tag' => 'VuFind\Controller\Factory::getTagController', - 'upgrade' => 'VuFind\Controller\Factory::getUpgradeController', - 'web' => 'VuFind\Controller\Factory::getWebController', - 'worldcat' => 'VuFind\Controller\Factory::getWorldcatController', - 'worldcatrecord' => 'VuFind\Controller\Factory::getWorldcatrecordController', + 'VuFind\Controller\AjaxController' => 'VuFind\Controller\AjaxControllerFactory', + 'VuFind\Controller\AlmaController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\AlphabrowseController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\AuthorController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\AuthorityController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\BrowseController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', + 'VuFind\Controller\BrowZineController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\CartController' => 'VuFind\Controller\CartControllerFactory', + 'VuFind\Controller\ChannelsController' => 'VuFind\Controller\ChannelsControllerFactory', + 'VuFind\Controller\CollectionController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', + 'VuFind\Controller\CollectionsController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', + 'VuFind\Controller\CombinedController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ConfirmController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ContentController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\CoverController' => 'VuFind\Controller\CoverControllerFactory', + 'VuFind\Controller\EdsController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\EdsrecordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\EITController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\EITrecordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ErrorController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ExternalAuthController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\FeedbackController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\Search2Controller' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\Search2recordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\HelpController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\HierarchyController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\IndexController' => 'VuFind\Controller\IndexControllerFactory', + 'VuFind\Controller\InstallController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\LibGuidesController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\LibraryCardsController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\MissingrecordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\MyResearchController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\OaiController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\Pazpar2Controller' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\PrimoController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\PrimorecordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\QRCodeController' => 'VuFind\Controller\QRCodeControllerFactory', + 'VuFind\Controller\RecordController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', + 'VuFind\Controller\RecordsController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\RelaisController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\SearchController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ShibbolethLogoutNotificationController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\SummonController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\SummonrecordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\TagController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\UpgradeController' => 'VuFind\Controller\UpgradeControllerFactory', + 'VuFind\Controller\WebController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\WorldcatController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\WorldcatrecordController' => 'VuFind\Controller\AbstractBaseFactory', + ], + 'aliases' => [ + 'AJAX' => 'VuFind\Controller\AjaxController', + 'ajax' => 'VuFind\Controller\AjaxController', + 'Alma' => 'VuFind\Controller\AlmaController', + 'alma' => 'VuFind\Controller\AlmaController', + 'Alphabrowse' => 'VuFind\Controller\AlphabrowseController', + 'alphabrowse' => 'VuFind\Controller\AlphabrowseController', + 'Author' => 'VuFind\Controller\AuthorController', + 'author' => 'VuFind\Controller\AuthorController', + 'Authority' => 'VuFind\Controller\AuthorityController', + 'authority' => 'VuFind\Controller\AuthorityController', + 'Browse' => 'VuFind\Controller\BrowseController', + 'browse' => 'VuFind\Controller\BrowseController', + 'BrowZine' => 'VuFind\Controller\BrowZineController', + 'browzine' => 'VuFind\Controller\BrowZineController', + 'Cart' => 'VuFind\Controller\CartController', + 'cart' => 'VuFind\Controller\CartController', + 'Channels' => 'VuFind\Controller\ChannelsController', + 'channels' => 'VuFind\Controller\ChannelsController', + 'Collection' => 'VuFind\Controller\CollectionController', + 'collection' => 'VuFind\Controller\CollectionController', + 'Collections' => 'VuFind\Controller\CollectionsController', + 'collections' => 'VuFind\Controller\CollectionsController', + 'Combined' => 'VuFind\Controller\CombinedController', + 'combined' => 'VuFind\Controller\CombinedController', + 'Confirm' => 'VuFind\Controller\ConfirmController', + 'confirm' => 'VuFind\Controller\ConfirmController', + 'Content' => 'VuFind\Controller\ContentController', + 'content' => 'VuFind\Controller\ContentController', + 'Cover' => 'VuFind\Controller\CoverController', + 'cover' => 'VuFind\Controller\CoverController', + 'EDS' => 'VuFind\Controller\EdsController', + 'eds' => 'VuFind\Controller\EdsController', + 'EdsRecord' => 'VuFind\Controller\EdsrecordController', + 'edsrecord' => 'VuFind\Controller\EdsrecordController', + 'EIT' => 'VuFind\Controller\EITController', + 'eit' => 'VuFind\Controller\EITController', + 'EITRecord' => 'VuFind\Controller\EITrecordController', + 'eitrecord' => 'VuFind\Controller\EITrecordController', + 'Error' => 'VuFind\Controller\ErrorController', + 'error' => 'VuFind\Controller\ErrorController', + 'ExternalAuth' => 'VuFind\Controller\ExternalAuthController', + 'externalauth' => 'VuFind\Controller\ExternalAuthController', + 'Feedback' => 'VuFind\Controller\FeedbackController', + 'feedback' => 'VuFind\Controller\FeedbackController', + 'Search2' => 'VuFind\Controller\Search2Controller', + 'search2' => 'VuFind\Controller\Search2Controller', + 'Search2Record' => 'VuFind\Controller\Search2recordController', + 'search2record' => 'VuFind\Controller\Search2recordController', + 'Help' => 'VuFind\Controller\HelpController', + 'help' => 'VuFind\Controller\HelpController', + 'Hierarchy' => 'VuFind\Controller\HierarchyController', + 'hierarchy' => 'VuFind\Controller\HierarchyController', + 'Index' => 'VuFind\Controller\IndexController', + 'index' => 'VuFind\Controller\IndexController', + 'Install' => 'VuFind\Controller\InstallController', + 'install' => 'VuFind\Controller\InstallController', + 'LibGuides' => 'VuFind\Controller\LibGuidesController', + 'libguides' => 'VuFind\Controller\LibGuidesController', + 'LibraryCards' => 'VuFind\Controller\LibraryCardsController', + 'librarycards' => 'VuFind\Controller\LibraryCardsController', + 'MissingRecord' => 'VuFind\Controller\MissingrecordController', + 'missingrecord' => 'VuFind\Controller\MissingrecordController', + 'MyResearch' => 'VuFind\Controller\MyResearchController', + 'myresearch' => 'VuFind\Controller\MyResearchController', + 'OAI' => 'VuFind\Controller\OaiController', + 'oai' => 'VuFind\Controller\OaiController', + 'Pazpar2' => 'VuFind\Controller\Pazpar2Controller', + 'pazpar2' => 'VuFind\Controller\Pazpar2Controller', + 'Primo' => 'VuFind\Controller\PrimoController', + 'primo' => 'VuFind\Controller\PrimoController', + 'PrimoRecord' => 'VuFind\Controller\PrimorecordController', + 'primorecord' => 'VuFind\Controller\PrimorecordController', + 'QRCode' => 'VuFind\Controller\QRCodeController', + 'qrcode' => 'VuFind\Controller\QRCodeController', + 'Record' => 'VuFind\Controller\RecordController', + 'record' => 'VuFind\Controller\RecordController', + 'Records' => 'VuFind\Controller\RecordsController', + 'records' => 'VuFind\Controller\RecordsController', + 'Relais' => 'VuFind\Controller\RelaisController', + 'relais' => 'VuFind\Controller\RelaisController', + 'Search' => 'VuFind\Controller\SearchController', + 'search' => 'VuFind\Controller\SearchController', + 'ShibbolethLogoutNotification' => 'VuFind\Controller\ShibbolethLogoutNotificationController', + 'shibbolethlogoutnotification' => 'VuFind\Controller\ShibbolethLogoutNotificationController', + 'Summon' => 'VuFind\Controller\SummonController', + 'summon' => 'VuFind\Controller\SummonController', + 'SummonRecord' => 'VuFind\Controller\SummonrecordController', + 'summonrecord' => 'VuFind\Controller\SummonrecordController', + 'Tag' => 'VuFind\Controller\TagController', + 'tag' => 'VuFind\Controller\TagController', + 'Upgrade' => 'VuFind\Controller\UpgradeController', + 'upgrade' => 'VuFind\Controller\UpgradeController', + 'Web' => 'VuFind\Controller\WebController', + 'web' => 'VuFind\Controller\WebController', + 'Worldcat' => 'VuFind\Controller\WorldcatController', + 'worldcat' => 'VuFind\Controller\WorldcatController', + 'WorldcatRecord' => 'VuFind\Controller\WorldcatrecordController', + 'worldcatrecord' => 'VuFind\Controller\WorldcatrecordController', ], ], 'controller_plugins' => [ 'factories' => [ - 'favorites' => 'VuFind\Controller\Plugin\Factory::getFavorites', - 'flashmessenger' => 'VuFind\Controller\Plugin\Factory::getFlashMessenger', - 'followup' => 'VuFind\Controller\Plugin\Factory::getFollowup', - 'holds' => 'VuFind\Controller\Plugin\Factory::getHolds', - 'newitems' => 'VuFind\Controller\Plugin\Factory::getNewItems', - 'ILLRequests' => 'VuFind\Controller\Plugin\Factory::getILLRequests', - 'permission' => 'VuFind\Controller\Plugin\Factory::getPermission', - 'recaptcha' => 'VuFind\Controller\Plugin\Factory::getRecaptcha', - 'reserves' => 'VuFind\Controller\Plugin\Factory::getReserves', - 'result-scroller' => 'VuFind\Controller\Plugin\Factory::getResultScroller', - 'storageRetrievalRequests' => 'VuFind\Controller\Plugin\Factory::getStorageRetrievalRequests', + 'VuFind\Controller\Plugin\DbUpgrade' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Controller\Plugin\Favorites' => 'VuFind\Controller\Plugin\Factory::getFavorites', + 'VuFind\Controller\Plugin\Followup' => 'VuFind\Controller\Plugin\Factory::getFollowup', + 'VuFind\Controller\Plugin\Holds' => 'VuFind\Controller\Plugin\Factory::getHolds', + 'VuFind\Controller\Plugin\ILLRequests' => 'VuFind\Controller\Plugin\Factory::getILLRequests', + 'VuFind\Controller\Plugin\NewItems' => 'VuFind\Controller\Plugin\Factory::getNewItems', + 'VuFind\Controller\Plugin\Permission' => 'VuFind\Controller\Plugin\Factory::getPermission', + 'VuFind\Controller\Plugin\Recaptcha' => 'VuFind\Controller\Plugin\Factory::getRecaptcha', + 'VuFind\Controller\Plugin\Renewals' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Controller\Plugin\Reserves' => 'VuFind\Controller\Plugin\Factory::getReserves', + 'VuFind\Controller\Plugin\ResultScroller' => 'VuFind\Controller\Plugin\Factory::getResultScroller', + 'VuFind\Controller\Plugin\StorageRetrievalRequests' => 'VuFind\Controller\Plugin\Factory::getStorageRetrievalRequests', + 'Zend\Mvc\Plugin\FlashMessenger\FlashMessenger' => 'VuFind\Controller\Plugin\Factory::getFlashMessenger', ], - 'invokables' => [ - 'db-upgrade' => 'VuFind\Controller\Plugin\DbUpgrade', + 'aliases' => [ + 'dbUpgrade' => 'VuFind\Controller\Plugin\DbUpgrade', + 'favorites' => 'VuFind\Controller\Plugin\Favorites', + 'flashMessenger' => 'Zend\Mvc\Plugin\FlashMessenger\FlashMessenger', + 'followup' => 'VuFind\Controller\Plugin\Followup', + 'holds' => 'VuFind\Controller\Plugin\Holds', + 'ILLRequests' => 'VuFind\Controller\Plugin\ILLRequests', + 'newItems' => 'VuFind\Controller\Plugin\NewItems', + 'permission' => 'VuFind\Controller\Plugin\Permission', + 'recaptcha' => 'VuFind\Controller\Plugin\Recaptcha', 'renewals' => 'VuFind\Controller\Plugin\Renewals', - ] + 'reserves' => 'VuFind\Controller\Plugin\Reserves', + 'resultScroller' => 'VuFind\Controller\Plugin\ResultScroller', + 'storageRetrievalRequests' => 'VuFind\Controller\Plugin\StorageRetrievalRequests', + ], ], 'service_manager' => [ 'allow_override' => true, 'factories' => [ - 'VuFind\AccountCapabilities' => 'VuFind\Service\Factory::getAccountCapabilities', - 'VuFind\AuthManager' => 'VuFind\Auth\Factory::getManager', - 'VuFind\AuthPluginManager' => 'VuFind\Service\Factory::getAuthPluginManager', - 'VuFind\AutocompletePluginManager' => 'VuFind\Service\Factory::getAutocompletePluginManager', - 'VuFind\CacheManager' => 'VuFind\Service\Factory::getCacheManager', - 'VuFind\Cart' => 'VuFind\Service\Factory::getCart', - 'VuFind\ChannelProviderPluginManager' => 'VuFind\Service\Factory::getChannelProviderPluginManager', - 'VuFind\Config' => 'VuFind\Service\Factory::getConfig', - 'VuFind\ContentPluginManager' => 'VuFind\Service\Factory::getContentPluginManager', - 'VuFind\ContentAuthorNotesPluginManager' => 'VuFind\Service\Factory::getContentAuthorNotesPluginManager', - 'VuFind\ContentCoversPluginManager' => 'VuFind\Service\Factory::getContentCoversPluginManager', - 'VuFind\ContentExcerptsPluginManager' => 'VuFind\Service\Factory::getContentExcerptsPluginManager', - 'VuFind\ContentReviewsPluginManager' => 'VuFind\Service\Factory::getContentReviewsPluginManager', - 'VuFind\CookieManager' => 'VuFind\Service\Factory::getCookieManager', - 'VuFind\Cover\Router' => 'VuFind\Service\Factory::getCoverRouter', - 'VuFind\DateConverter' => 'VuFind\Service\Factory::getDateConverter', - 'VuFind\DbAdapter' => 'VuFind\Service\Factory::getDbAdapter', - 'VuFind\DbAdapterFactory' => 'VuFind\Service\Factory::getDbAdapterFactory', - 'VuFind\DbRowPluginManager' => 'VuFind\Service\Factory::getDbRowPluginManager', - 'VuFind\DbTablePluginManager' => 'VuFind\Service\Factory::getDbTablePluginManager', - 'VuFind\Export' => 'VuFind\Service\Factory::getExport', + 'ProxyManager\Configuration' => 'VuFind\Service\Factory::getProxyConfig', + 'VuFind\AjaxHandler\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Auth\ILSAuthenticator' => 'VuFind\Auth\ILSAuthenticatorFactory', + 'VuFind\Auth\Manager' => 'VuFind\Auth\ManagerFactory', + 'VuFind\Auth\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Autocomplete\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Autocomplete\Suggester' => 'VuFind\Autocomplete\SuggesterFactory', + 'VuFind\Cache\Manager' => 'VuFind\Cache\ManagerFactory', + 'VuFind\Cart' => 'VuFind\CartFactory', + 'VuFind\ChannelProvider\ChannelLoader' => 'VuFind\ChannelProvider\ChannelLoaderFactory', + 'VuFind\ChannelProvider\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Config\AccountCapabilities' => 'VuFind\Config\AccountCapabilitiesFactory', + 'VuFind\Config\PluginManager' => 'VuFind\Config\PluginManagerFactory', + 'VuFind\Config\SearchSpecsReader' => 'VuFind\Config\YamlReaderFactory', + 'VuFind\Config\YamlReader' => 'VuFind\Config\YamlReaderFactory', + 'VuFind\Connection\Relais' => 'VuFind\Connection\RelaisFactory', + 'VuFind\Connection\WorldCatUtils' => 'VuFind\Connection\WorldCatUtilsFactory', + 'VuFind\Content\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Content\AuthorNotes\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Content\Covers\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Content\Excerpts\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Content\Reviews\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Content\Summaries\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Content\TOC\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\ContentBlock\BlockLoader' => 'VuFind\ContentBlock\BlockLoaderFactory', + 'VuFind\ContentBlock\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Cookie\CookieManager' => 'VuFind\Cookie\CookieManagerFactory', + 'VuFind\Cover\CachingProxy' => 'VuFind\Cover\CachingProxyFactory', + 'VuFind\Cover\Generator' => 'VuFind\Cover\GeneratorFactory', + 'VuFind\Cover\Layer\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Cover\Loader' => 'VuFind\Cover\LoaderFactory', + 'VuFind\Cover\Router' => 'VuFind\Cover\RouterFactory', + 'VuFind\Crypt\HMAC' => 'VuFind\Crypt\HMACFactory', + 'VuFind\Date\Converter' => 'VuFind\Service\DateConverterFactory', + 'VuFind\Db\AdapterFactory' => 'VuFind\Service\ServiceWithConfigIniFactory', + 'VuFind\Db\Row\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Db\Table\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Export' => 'VuFind\ExportFactory', 'VuFind\Favorites\FavoritesService' => 'VuFind\Favorites\FavoritesServiceFactory', - 'VuFind\HierarchyDriverPluginManager' => 'VuFind\Service\Factory::getHierarchyDriverPluginManager', - 'VuFind\HierarchyTreeDataFormatterPluginManager' => 'VuFind\Service\Factory::getHierarchyTreeDataFormatterPluginManager', - 'VuFind\HierarchyTreeDataSourcePluginManager' => 'VuFind\Service\Factory::getHierarchyTreeDataSourcePluginManager', - 'VuFind\HierarchyTreeRendererPluginManager' => 'VuFind\Service\Factory::getHierarchyTreeRendererPluginManager', - 'VuFind\Http' => 'VuFind\Service\Factory::getHttp', - 'VuFind\HMAC' => 'VuFind\Service\Factory::getHMAC', - 'VuFind\ILSAuthenticator' => 'VuFind\Auth\Factory::getILSAuthenticator', - 'VuFind\ILSConnection' => 'VuFind\Service\Factory::getILSConnection', - 'VuFind\ILSDriverPluginManager' => 'VuFind\Service\Factory::getILSDriverPluginManager', - 'VuFind\ILSHoldLogic' => 'VuFind\Service\Factory::getILSHoldLogic', - 'VuFind\ILSHoldSettings' => 'VuFind\Service\Factory::getILSHoldSettings', - 'VuFind\ILSTitleHoldLogic' => 'VuFind\Service\Factory::getILSTitleHoldLogic', - 'VuFind\Logger' => 'VuFind\Log\LoggerFactory', - 'VuFind\Mailer' => 'VuFind\Mailer\Factory', - 'VuFind\ProxyConfig' => 'VuFind\Service\Factory::getProxyConfig', - 'VuFind\Recaptcha' => 'VuFind\Service\Factory::getRecaptcha', - 'VuFind\RecommendPluginManager' => 'VuFind\Service\Factory::getRecommendPluginManager', - 'VuFind\RecordCache' => 'VuFind\Service\Factory::getRecordCache', - 'VuFind\RecordDriverPluginManager' => 'VuFind\Service\Factory::getRecordDriverPluginManager', - 'VuFind\RecordLoader' => 'VuFind\Service\Factory::getRecordLoader', - 'VuFind\RecordRouter' => 'VuFind\Service\Factory::getRecordRouter', - 'VuFind\RecordTabPluginManager' => 'VuFind\Service\Factory::getRecordTabPluginManager', - 'VuFind\RelatedPluginManager' => 'VuFind\Service\Factory::getRelatedPluginManager', - 'VuFind\ResolverDriverPluginManager' => 'VuFind\Service\Factory::getResolverDriverPluginManager', - 'VuFind\Role\PermissionManager' => 'VuFind\Service\Factory::getPermissionManager', - 'VuFind\Role\PermissionDeniedManager' => 'VuFind\Service\Factory::getPermissionDeniedManager', - 'VuFind\Search' => 'VuFind\Service\Factory::getSearchService', - 'VuFind\Search\BackendManager' => 'VuFind\Service\Factory::getSearchBackendManager', - 'VuFind\Search\Memory' => 'VuFind\Service\Factory::getSearchMemory', - 'VuFind\SearchOptionsPluginManager' => 'VuFind\Service\Factory::getSearchOptionsPluginManager', - 'VuFind\SearchParamsPluginManager' => 'VuFind\Service\Factory::getSearchParamsPluginManager', - 'VuFind\SearchResultsPluginManager' => 'VuFind\Service\Factory::getSearchResultsPluginManager', - 'VuFind\SearchRunner' => 'VuFind\Service\Factory::getSearchRunner', - 'VuFind\SearchSpecsReader' => 'VuFind\Service\Factory::getSearchSpecsReader', - 'VuFind\SearchTabsHelper' => 'VuFind\Service\Factory::getSearchTabsHelper', - 'VuFind\SessionManager' => 'VuFind\Session\ManagerFactory', - 'VuFind\SessionPluginManager' => 'VuFind\Service\Factory::getSessionPluginManager', - 'VuFind\SMS' => 'VuFind\SMS\Factory', - 'VuFind\Solr\Writer' => 'VuFind\Service\Factory::getSolrWriter', - 'VuFind\Tags' => 'VuFind\Service\Factory::getTags', - 'VuFind\Translator' => 'VuFind\Service\Factory::getTranslator', - 'VuFind\WorldCatUtils' => 'VuFind\Service\Factory::getWorldCatUtils', - 'VuFind\YamlReader' => 'VuFind\Service\Factory::getYamlReader', - ], - 'invokables' => [ - 'VuFind\HierarchicalFacetHelper' => 'VuFind\Search\Solr\HierarchicalFacetHelper', - 'VuFind\IpAddressUtils' => 'VuFind\Net\IpAddressUtils', - 'VuFind\Session\Settings' => 'VuFind\Session\Settings', + 'VuFind\GeoFeatures\BasemapConfig' => 'VuFind\GeoFeatures\AbstractConfigFactory', + 'VuFind\GeoFeatures\MapTabConfig' => 'VuFind\GeoFeatures\AbstractConfigFactory', + 'VuFind\GeoFeatures\MapSelectionConfig' => 'VuFind\GeoFeatures\AbstractConfigFactory', + 'VuFind\Hierarchy\Driver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Hierarchy\TreeDataFormatter\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Hierarchy\TreeDataSource\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Hierarchy\TreeRenderer\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\ILS\Connection' => 'VuFind\ILS\ConnectionFactory', + 'VuFind\ILS\Driver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\ILS\Logic\Holds' => 'VuFind\ILS\Logic\LogicFactory', + 'VuFind\ILS\Logic\TitleHolds' => 'VuFind\ILS\Logic\LogicFactory', + 'VuFind\ILS\HoldSettings' => 'VuFind\ILS\HoldSettingsFactory', + 'VuFind\Log\Logger' => 'VuFind\Log\LoggerFactory', + 'VuFind\Mailer\Mailer' => 'VuFind\Mailer\Factory', + 'VuFind\Net\IpAddressUtils' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\QRCode\Loader' => 'VuFind\QRCode\LoaderFactory', + 'VuFind\Recommend\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Record\Cache' => 'VuFind\Record\CacheFactory', + 'VuFind\Record\Loader' => 'VuFind\Record\LoaderFactory', + 'VuFind\Record\Router' => 'VuFind\Record\RouterFactory', + 'VuFind\RecordDriver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\RecordTab\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Related\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Resolver\Driver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Role\PermissionManager' => 'VuFind\Role\PermissionManagerFactory', + 'VuFind\Role\PermissionDeniedManager' => 'VuFind\Role\PermissionDeniedManagerFactory', + 'VuFind\Search\BackendManager' => 'VuFind\Search\BackendManagerFactory', + 'VuFind\Search\History' => 'VuFind\Search\HistoryFactory', + 'VuFind\Search\Memory' => 'VuFind\Search\MemoryFactory', + 'VuFind\Search\FacetCache\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Search\Options\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Search\Params\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Search\Results\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Search\Solr\HierarchicalFacetHelper' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Search\SearchRunner' => 'VuFind\Search\SearchRunnerFactory', + 'VuFind\Search\SearchTabsHelper' => 'VuFind\Search\SearchTabsHelperFactory', + 'VuFind\Service\ReCaptcha' => 'VuFind\Service\ReCaptchaFactory', + 'VuFind\Session\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Session\Settings' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\SMS\SMSInterface' => 'VuFind\SMS\Factory', + 'VuFind\Solr\Writer' => 'VuFind\Solr\WriterFactory', + 'VuFind\Tags' => 'VuFind\TagsFactory', + 'VuFind\Validator\Csrf' => 'VuFind\Validator\CsrfFactory', + 'VuFindHttp\HttpService' => 'VuFind\Service\Factory::getHttp', + 'VuFindSearch\Service' => 'VuFind\Service\Factory::getSearchService', + 'Zend\Db\Adapter\Adapter' => 'VuFind\Service\Factory::getDbAdapter', + 'Zend\Mvc\I18n\Translator' => 'VuFind\Service\Factory::getTranslator', + 'Zend\Session\SessionManager' => 'VuFind\Session\ManagerFactory', ], 'initializers' => [ - 'VuFind\ServiceManager\Initializer::initInstance', + 'VuFind\ServiceManager\ServiceInitializer', ], 'aliases' => [ - 'mvctranslator' => 'VuFind\Translator', - 'translator' => 'VuFind\Translator', + 'VuFind\AccountCapabilities' => 'VuFind\Config\AccountCapabilities', + 'VuFind\AuthManager' => 'VuFind\Auth\Manager', + 'VuFind\AuthPluginManager' => 'VuFind\Auth\PluginManager', + 'VuFind\AutocompletePluginManager' => 'VuFind\Autocomplete\PluginManager', + 'VuFind\CacheManager' => 'VuFind\Cache\Manager', + 'VuFind\ChannelProviderPluginManager' => 'VuFind\ChannelProvider\PluginManager', + 'VuFind\Config' => 'VuFind\Config\PluginManager', + 'VuFind\ContentPluginManager' => 'VuFind\Content\PluginManager', + 'VuFind\ContentAuthorNotesPluginManager' => 'VuFind\Content\AuthorNotes\PluginManager', + 'VuFind\ContentCoversPluginManager' => 'VuFind\Content\Covers\PluginManager', + 'VuFind\ContentExcerptsPluginManager' => 'VuFind\Content\Excerpts\PluginManager', + 'VuFind\ContentReviewsPluginManager' => 'VuFind\Content\Reviews\PluginManager', + 'VuFind\ContentSummariesPluginManager' => 'VuFind\Content\Summaries\PluginManager', + 'VuFind\ContentTOCPluginManager' => 'VuFind\Content\TOC\PluginManager', + 'VuFind\CookieManager' => 'VuFind\Cookie\CookieManager', + 'VuFind\DateConverter' => 'VuFind\Date\Converter', + 'VuFind\DbAdapter' => 'Zend\Db\Adapter\Adapter', + 'VuFind\DbAdapterFactory' => 'VuFind\Db\AdapterFactory', + 'VuFind\DbRowPluginManager' => 'VuFind\Db\Row\PluginManager', + 'VuFind\DbTablePluginManager' => 'VuFind\Db\Table\PluginManager', + 'VuFind\HierarchicalFacetHelper' => 'VuFind\Search\Solr\HierarchicalFacetHelper', + 'VuFind\HierarchyDriverPluginManager' => 'VuFind\Hierarchy\Driver\PluginManager', + 'VuFind\HierarchyTreeDataFormatterPluginManager' => 'VuFind\Hierarchy\TreeDataFormatter\PluginManager', + 'VuFind\HierarchyTreeDataSourcePluginManager' => 'VuFind\Hierarchy\TreeDataSource\PluginManager', + 'VuFind\HierarchyTreeRendererPluginManager' => 'VuFind\Hierarchy\TreeRenderer\PluginManager', + 'VuFind\HMAC' => 'VuFind\Crypt\HMAC', + 'VuFind\Http' => 'VuFindHttp\HttpService', + 'VuFind\ILSAuthenticator' => 'VuFind\Auth\ILSAuthenticator', + 'VuFind\ILSConnection' => 'VuFind\ILS\Connection', + 'VuFind\ILSDriverPluginManager' => 'VuFind\ILS\Driver\PluginManager', + 'VuFind\ILSHoldLogic' => 'VuFind\ILS\Logic\Holds', + 'VuFind\ILSHoldSettings' => 'VuFind\ILS\HoldSettings', + 'VuFind\ILSTitleHoldLogic' => 'VuFind\ILS\Logic\TitleHolds', + 'VuFind\IpAddressUtils' => 'VuFind\Net\IpAddressUtils', + 'VuFind\Logger' => 'VuFind\Log\Logger', + 'VuFind\Mailer' => 'VuFind\Mailer\Mailer', + 'VuFind\ProxyConfig' => 'ProxyManager\Configuration', + 'VuFind\Recaptcha' => 'VuFind\Service\ReCaptcha', + 'VuFind\RecommendPluginManager' => 'VuFind\Recommend\PluginManager', + 'VuFind\RecordCache' => 'VuFind\Record\Cache', + 'VuFind\RecordDriverPluginManager' => 'VuFind\RecordDriver\PluginManager', + 'VuFind\RecordLoader' => 'VuFind\Record\Loader', + 'VuFind\RecordRouter' => 'VuFind\Record\Router', + 'VuFind\RecordTabPluginManager' => 'VuFind\RecordTab\PluginManager', + 'VuFind\RelatedPluginManager' => 'VuFind\Related\PluginManager', + 'VuFind\ResolverDriverPluginManager' => 'VuFind\Resolver\Driver\PluginManager', + 'VuFind\Search' => 'VuFindSearch\Service', + 'VuFind\SearchOptionsPluginManager' => 'VuFind\Search\Options\PluginManager', + 'VuFind\SearchParamsPluginManager' => 'VuFind\Search\Params\PluginManager', + 'VuFind\SearchResultsPluginManager' => 'VuFind\Search\Results\PluginManager', + 'VuFind\SearchRunner' => 'VuFind\Search\SearchRunner', + 'VuFind\SearchSpecsReader' => 'VuFind\Config\SearchSpecsReader', + 'VuFind\SearchTabsHelper' => 'VuFind\Search\SearchTabsHelper', + 'VuFind\SessionManager' => 'Zend\Session\SessionManager', + 'VuFind\SessionPluginManager' => 'VuFind\Session\PluginManager', + 'VuFind\SMS' => 'VuFind\SMS\SMSInterface', + 'VuFind\Translator' => 'Zend\Mvc\I18n\Translator', + 'VuFind\WorldCatUtils' => 'VuFind\Connection\WorldCatUtils', + 'VuFind\YamlReader' => 'VuFind\Config\YamlReader', + 'Zend\Validator\Csrf' => 'VuFind\Validator\Csrf', ], ], 'translator' => [], 'view_helpers' => [ 'initializers' => [ - 'VuFind\ServiceManager\Initializer::initZendPlugin', + 'VuFind\ServiceManager\ServiceInitializer', ], ], 'view_manager' => [ @@ -273,9 +479,7 @@ $config = [ // unrelated to specific Zend Framework 2 components). 'vufind' => [ // The config reader is a special service manager for loading .ini files: - 'config_reader' => [ - 'abstract_factories' => ['VuFind\Config\PluginFactory'], - ], + 'config_reader' => [ /* see VuFind\Config\PluginManager for defaults */ ], // PostgreSQL sequence mapping 'pgsql_seq_mapping' => [ 'comments' => ['id', 'comments_id_seq'], @@ -295,380 +499,37 @@ $config = [ // This section contains service manager configurations for all VuFind // pluggable components: 'plugin_managers' => [ - 'auth' => [ - 'abstract_factories' => ['VuFind\Auth\PluginFactory'], - 'factories' => [ - 'choiceauth' => 'VuFind\Auth\Factory::getChoiceAuth', - 'facebook' => 'VuFind\Auth\Factory::getFacebook', - 'ils' => 'VuFind\Auth\Factory::getILS', - 'multiils' => 'VuFind\Auth\Factory::getMultiILS', - 'shibboleth' => 'VuFind\Auth\Factory::getShibboleth' - ], - 'invokables' => [ - 'cas' => 'VuFind\Auth\CAS', - 'database' => 'VuFind\Auth\Database', - 'ldap' => 'VuFind\Auth\LDAP', - 'multiauth' => 'VuFind\Auth\MultiAuth', - 'sip2' => 'VuFind\Auth\SIP2', - ], - 'aliases' => [ - // for legacy 1.x compatibility - 'db' => 'Database', - 'sip' => 'Sip2', - ], - ], - 'autocomplete' => [ - 'abstract_factories' => ['VuFind\Autocomplete\PluginFactory'], - 'factories' => [ - 'solr' => 'VuFind\Autocomplete\Factory::getSolr', - 'solrauth' => 'VuFind\Autocomplete\Factory::getSolrAuth', - 'solrcn' => 'VuFind\Autocomplete\Factory::getSolrCN', - 'solrreserves' => 'VuFind\Autocomplete\Factory::getSolrReserves', - ], - 'invokables' => [ - 'none' => 'VuFind\Autocomplete\None', - 'oclcidentities' => 'VuFind\Autocomplete\OCLCIdentities', - 'tag' => 'VuFind\Autocomplete\Tag', - ], - 'aliases' => [ - // for legacy 1.x compatibility - 'noautocomplete' => 'None', - 'oclcidentitiesautocomplete' => 'OCLCIdentities', - 'solrautocomplete' => 'Solr', - 'solrauthautocomplete' => 'SolrAuth', - 'solrcnautocomplete' => 'SolrCN', - 'solrreservesautocomplete' => 'SolrReserves', - 'tagautocomplete' => 'Tag', - ], - ], - 'channelprovider' => [ - 'factories' => [ - 'alphabrowse' => 'VuFind\ChannelProvider\Factory::getAlphaBrowse', - 'facets' => 'VuFind\ChannelProvider\Factory::getFacets', - 'listitems' => 'VuFind\ChannelProvider\Factory::getListItems', - 'random' => 'VuFind\ChannelProvider\Factory::getRandom', - 'similaritems' => 'VuFind\ChannelProvider\Factory::getSimilarItems', - ] - ], - 'content' => [ - 'factories' => [ - 'authornotes' => 'VuFind\Content\Factory::getAuthorNotes', - 'excerpts' => 'VuFind\Content\Factory::getExcerpts', - 'reviews' => 'VuFind\Content\Factory::getReviews', - ], - ], - 'content_authornotes' => [ - 'factories' => [ - 'syndetics' => 'VuFind\Content\AuthorNotes\Factory::getSyndetics', - 'syndeticsplus' => 'VuFind\Content\AuthorNotes\Factory::getSyndeticsPlus', - ], - ], - 'content_excerpts' => [ - 'factories' => [ - 'syndetics' => 'VuFind\Content\Excerpts\Factory::getSyndetics', - 'syndeticsplus' => 'VuFind\Content\Excerpts\Factory::getSyndeticsPlus', - ], - ], - 'content_covers' => [ - 'factories' => [ - 'amazon' => 'VuFind\Content\Covers\Factory::getAmazon', - 'booksite' => 'VuFind\Content\Covers\Factory::getBooksite', - 'buchhandel' => 'VuFind\Content\Covers\Factory::getBuchhandel', - 'contentcafe' => 'VuFind\Content\Covers\Factory::getContentCafe', - 'syndetics' => 'VuFind\Content\Covers\Factory::getSyndetics', - ], - 'invokables' => [ - 'google' => 'VuFind\Content\Covers\Google', - 'librarything' => 'VuFind\Content\Covers\LibraryThing', - 'localfile' => 'VuFind\Content\Covers\LocalFile', - 'openlibrary' => 'VuFind\Content\Covers\OpenLibrary', - 'summon' => 'VuFind\Content\Covers\Summon', - ], - ], - 'content_reviews' => [ - 'factories' => [ - 'amazon' => 'VuFind\Content\Reviews\Factory::getAmazon', - 'amazoneditorial' => 'VuFind\Content\Reviews\Factory::getAmazonEditorial', - 'booksite' => 'VuFind\Content\Reviews\Factory::getBooksite', - 'syndetics' => 'VuFind\Content\Reviews\Factory::getSyndetics', - 'syndeticsplus' => 'VuFind\Content\Reviews\Factory::getSyndeticsPlus', - ], - 'invokables' => [ - 'guardian' => 'VuFind\Content\Reviews\Guardian', - ], - ], - 'db_row' => [ - 'factories' => [ - 'changetracker' => 'VuFind\Db\Row\Factory::getChangeTracker', - 'comments' => 'VuFind\Db\Row\Factory::getComments', - 'externalsession' => 'VuFind\Db\Row\Factory::getExternalSession', - 'oairesumption' => 'VuFind\Db\Row\Factory::getOaiResumption', - 'record' => 'VuFind\Db\Row\Factory::getRecord', - 'resource' => 'VuFind\Db\Row\Factory::getResource', - 'resourcetags' => 'VuFind\Db\Row\Factory::getResourceTags', - 'search' => 'VuFind\Db\Row\Factory::getSearch', - 'session' => 'VuFind\Db\Row\Factory::getSession', - 'tags' => 'VuFind\Db\Row\Factory::getTags', - 'user' => 'VuFind\Db\Row\Factory::getUser', - 'usercard' => 'VuFind\Db\Row\Factory::getUserCard', - 'userlist' => 'VuFind\Db\Row\Factory::getUserList', - 'userresource' => 'VuFind\Db\Row\Factory::getUserResource', - ], - ], - 'db_table' => [ - 'abstract_factories' => ['VuFind\Db\Table\PluginFactory'], - 'factories' => [ - 'changetracker' => 'VuFind\Db\Table\Factory::getChangeTracker', - 'comments' => 'VuFind\Db\Table\Factory::getComments', - 'externalsession' => 'VuFind\Db\Table\Factory::getExternalSession', - 'oairesumption' => 'VuFind\Db\Table\Factory::getOaiResumption', - 'record' => 'VuFind\Db\Table\Factory::getRecord', - 'resource' => 'VuFind\Db\Table\Factory::getResource', - 'resourcetags' => 'VuFind\Db\Table\Factory::getResourceTags', - 'search' => 'VuFind\Db\Table\Factory::getSearch', - 'session' => 'VuFind\Db\Table\Factory::getSession', - 'tags' => 'VuFind\Db\Table\Factory::getTags', - 'user' => 'VuFind\Db\Table\Factory::getUser', - 'usercard' => 'VuFind\Db\Table\Factory::getUserCard', - 'userlist' => 'VuFind\Db\Table\Factory::getUserList', - 'userresource' => 'VuFind\Db\Table\Factory::getUserResource', - ], - ], - 'hierarchy_driver' => [ - 'factories' => [ - 'default' => 'VuFind\Hierarchy\Driver\Factory::getHierarchyDefault', - 'flat' => 'VuFind\Hierarchy\Driver\Factory::getHierarchyFlat', - ], - ], - 'hierarchy_treedataformatter' => [ - 'invokables' => [ - 'json' => 'VuFind\Hierarchy\TreeDataFormatter\Json', - 'xml' => 'VuFind\Hierarchy\TreeDataFormatter\Xml', - ], - ], - 'hierarchy_treedatasource' => [ - 'factories' => [ - 'solr' => 'VuFind\Hierarchy\TreeDataSource\Factory::getSolr', - ], - 'invokables' => [ - 'xmlfile' => 'VuFind\Hierarchy\TreeDataSource\XMLFile', - ], - ], - 'hierarchy_treerenderer' => [ - 'factories' => [ - 'jstree' => 'VuFind\Hierarchy\TreeRenderer\Factory::getJSTree' - ], - ], - 'ils_driver' => [ - 'abstract_factories' => ['VuFind\ILS\Driver\PluginFactory'], - 'factories' => [ - 'aleph' => 'VuFind\ILS\Driver\Factory::getAleph', - 'daia' => 'VuFind\ILS\Driver\Factory::getDAIA', - 'demo' => 'VuFind\ILS\Driver\Factory::getDemo', - 'horizon' => 'VuFind\ILS\Driver\Factory::getHorizon', - 'horizonxmlapi' => 'VuFind\ILS\Driver\Factory::getHorizonXMLAPI', - 'lbs4' => 'VuFind\ILS\Driver\Factory::getLBS4', - 'multibackend' => 'VuFind\ILS\Driver\Factory::getMultiBackend', - 'noils' => 'VuFind\ILS\Driver\Factory::getNoILS', - 'paia' => 'VuFind\ILS\Driver\Factory::getPAIA', - 'koha' => 'VuFind\ILS\Driver\Factory::getKoha', - 'kohailsdi' => 'VuFind\ILS\Driver\Factory::getKohaILSDI', - 'sierrarest' => 'VuFind\ILS\Driver\Factory::getSierraRest', - 'symphony' => 'VuFind\ILS\Driver\Factory::getSymphony', - 'unicorn' => 'VuFind\ILS\Driver\Factory::getUnicorn', - 'voyager' => 'VuFind\ILS\Driver\Factory::getVoyager', - 'voyagerrestful' => 'VuFind\ILS\Driver\Factory::getVoyagerRestful', - ], - 'invokables' => [ - 'amicus' => 'VuFind\ILS\Driver\Amicus', - 'claviussql' => 'VuFind\ILS\Driver\ClaviusSQL', - 'evergreen' => 'VuFind\ILS\Driver\Evergreen', - 'innovative' => 'VuFind\ILS\Driver\Innovative', - 'newgenlib' => 'VuFind\ILS\Driver\NewGenLib', - 'polaris' => 'VuFind\ILS\Driver\Polaris', - 'sample' => 'VuFind\ILS\Driver\Sample', - 'sierra' => 'VuFind\ILS\Driver\Sierra', - 'virtua' => 'VuFind\ILS\Driver\Virtua', - 'xcncip2' => 'VuFind\ILS\Driver\XCNCIP2', - ], - ], - 'recommend' => [ - 'abstract_factories' => ['VuFind\Recommend\PluginFactory'], - 'factories' => [ - 'authorfacets' => 'VuFind\Recommend\Factory::getAuthorFacets', - 'authorinfo' => 'VuFind\Recommend\Factory::getAuthorInfo', - 'authorityrecommend' => 'VuFind\Recommend\Factory::getAuthorityRecommend', - 'catalogresults' => 'VuFind\Recommend\Factory::getCatalogResults', - 'collectionsidefacets' => 'VuFind\Recommend\Factory::getCollectionSideFacets', - 'dplaterms' => 'VuFind\Recommend\Factory::getDPLATerms', - 'europeanaresults' => 'VuFind\Recommend\Factory::getEuropeanaResults', - 'expandfacets' => 'VuFind\Recommend\Factory::getExpandFacets', - 'favoritefacets' => 'VuFind\Recommend\Factory::getFavoriteFacets', - 'mapselection' => 'VuFind\Recommend\Factory::getMapSelection', - 'sidefacets' => 'VuFind\Recommend\Factory::getSideFacets', - 'randomrecommend' => 'VuFind\Recommend\Factory::getRandomRecommend', - 'summonbestbets' => 'VuFind\Recommend\Factory::getSummonBestBets', - 'summondatabases' => 'VuFind\Recommend\Factory::getSummonDatabases', - 'summonresults' => 'VuFind\Recommend\Factory::getSummonResults', - 'summontopics' => 'VuFind\Recommend\Factory::getSummonTopics', - 'switchquery' => 'VuFind\Recommend\Factory::getSwitchQuery', - 'topfacets' => 'VuFind\Recommend\Factory::getTopFacets', - 'visualfacets' => 'VuFind\Recommend\Factory::getVisualFacets', - 'webresults' => 'VuFind\Recommend\Factory::getWebResults', - 'worldcatidentities' => 'VuFind\Recommend\Factory::getWorldCatIdentities', - ], - 'invokables' => [ - 'alphabrowselink' => 'VuFind\Recommend\AlphaBrowseLink', - 'channels' => 'VuFind\Recommend\Channels', - 'doi' => 'VuFind\Recommend\DOI', - 'europeanaresultsdeferred' => 'VuFind\Recommend\EuropeanaResultsDeferred', - 'facetcloud' => 'VuFind\Recommend\FacetCloud', - 'libraryh3lp' => 'VuFind\Recommend\Libraryh3lp', - 'openlibrarysubjects' => 'VuFind\Recommend\OpenLibrarySubjects', - 'openlibrarysubjectsdeferred' => 'VuFind\Recommend\OpenLibrarySubjectsDeferred', - 'pubdatevisajax' => 'VuFind\Recommend\PubDateVisAjax', - 'removefilters' => 'VuFind\Recommend\RemoveFilters', - 'resultgooglemapajax' => 'VuFind\Recommend\Deprecated', - 'spellingsuggestions' => 'VuFind\Recommend\SpellingSuggestions', - 'summonbestbetsdeferred' => 'VuFind\Recommend\SummonBestBetsDeferred', - 'summondatabasesdeferred' => 'VuFind\Recommend\SummonDatabasesDeferred', - 'summonresultsdeferred' => 'VuFind\Recommend\SummonResultsDeferred', - 'switchtype' => 'VuFind\Recommend\SwitchType', - 'worldcatterms' => 'VuFind\Recommend\Deprecated', - ], - ], - 'recorddriver' => [ - 'abstract_factories' => ['VuFind\RecordDriver\PluginFactory'], - 'factories' => [ - 'eds' => 'VuFind\RecordDriver\Factory::getEDS', - 'eit' => 'VuFind\RecordDriver\Factory::getEIT', - 'missing' => 'VuFind\RecordDriver\Factory::getMissing', - 'pazpar2' => 'VuFind\RecordDriver\Factory::getPazpar2', - 'primo' => 'VuFind\RecordDriver\Factory::getPrimo', - 'solrauth' => 'VuFind\RecordDriver\Factory::getSolrAuth', - 'solrdefault' => 'VuFind\RecordDriver\Factory::getSolrDefault', - 'solrmarc' => 'VuFind\RecordDriver\Factory::getSolrMarc', - 'solrmarcremote' => 'VuFind\RecordDriver\Factory::getSolrMarcRemote', - 'solrreserves' => 'VuFind\RecordDriver\Factory::getSolrReserves', - 'solrweb' => 'VuFind\RecordDriver\Factory::getSolrWeb', - 'summon' => 'VuFind\RecordDriver\Factory::getSummon', - 'worldcat' => 'VuFind\RecordDriver\Factory::getWorldCat', - ], - 'invokables' => [ - 'browzine' => 'VuFind\RecordDriver\BrowZine', - 'libguides' => 'VuFind\RecordDriver\LibGuides', - ], - ], - 'recordtab' => [ - 'abstract_factories' => ['VuFind\RecordTab\PluginFactory'], - 'factories' => [ - 'collectionhierarchytree' => 'VuFind\RecordTab\Factory::getCollectionHierarchyTree', - 'collectionlist' => 'VuFind\RecordTab\Factory::getCollectionList', - 'excerpt' => 'VuFind\RecordTab\Factory::getExcerpt', - 'hierarchytree' => 'VuFind\RecordTab\Factory::getHierarchyTree', - 'holdingsils' => 'VuFind\RecordTab\Factory::getHoldingsILS', - 'holdingsworldcat' => 'VuFind\RecordTab\Factory::getHoldingsWorldCat', - 'map' => 'VuFind\RecordTab\Factory::getMap', - 'preview' => 'VuFind\RecordTab\Factory::getPreview', - 'reviews' => 'VuFind\RecordTab\Factory::getReviews', - 'similaritemscarousel' => 'VuFind\RecordTab\Factory::getSimilarItemsCarousel', - 'usercomments' => 'VuFind\RecordTab\Factory::getUserComments', - ], - 'invokables' => [ - 'description' => 'VuFind\RecordTab\Description', - 'staffviewarray' => 'VuFind\RecordTab\StaffViewArray', - 'staffviewmarc' => 'VuFind\RecordTab\StaffViewMARC', - 'toc' => 'VuFind\RecordTab\TOC', - ], - 'initializers' => [ - 'ZfcRbac\Initializer\AuthorizationServiceInitializer' - ], - ], - 'related' => [ - 'abstract_factories' => ['VuFind\Related\PluginFactory'], - 'factories' => [ - 'similar' => 'VuFind\Related\Factory::getSimilar', - 'worldcatsimilar' => 'VuFind\Related\Factory::getWorldCatSimilar', - ], - 'invokables' => [ - 'channels' => 'VuFind\Related\Channels', - 'editions' => 'VuFind\Related\Deprecated', - 'worldcateditions' => 'VuFind\Related\Deprecated', - ], - ], - 'resolver_driver' => [ - 'abstract_factories' => ['VuFind\Resolver\Driver\PluginFactory'], - 'factories' => [ - '360link' => 'VuFind\Resolver\Driver\Factory::getThreesixtylink', - 'ezb' => 'VuFind\Resolver\Driver\Factory::getEzb', - 'sfx' => 'VuFind\Resolver\Driver\Factory::getSfx', - 'redi' => 'VuFind\Resolver\Driver\Factory::getRedi', - ], - 'invokables' => [ - 'demo' => 'VuFind\Resolver\Driver\Demo', - ], - 'aliases' => [ - 'threesixtylink' => '360link', - ], - ], - 'search_backend' => [ - 'factories' => [ - 'BrowZine' => 'VuFind\Search\Factory\BrowZineBackendFactory', - 'EDS' => 'VuFind\Search\Factory\EdsBackendFactory', - 'EIT' => 'VuFind\Search\Factory\EITBackendFactory', - 'LibGuides' => 'VuFind\Search\Factory\LibGuidesBackendFactory', - 'Pazpar2' => 'VuFind\Search\Factory\Pazpar2BackendFactory', - 'Primo' => 'VuFind\Search\Factory\PrimoBackendFactory', - 'Solr' => 'VuFind\Search\Factory\SolrDefaultBackendFactory', - 'SolrAuth' => 'VuFind\Search\Factory\SolrAuthBackendFactory', - 'SolrReserves' => 'VuFind\Search\Factory\SolrReservesBackendFactory', - 'SolrWeb' => 'VuFind\Search\Factory\SolrWebBackendFactory', - 'Summon' => 'VuFind\Search\Factory\SummonBackendFactory', - 'WorldCat' => 'VuFind\Search\Factory\WorldCatBackendFactory', - ], - 'aliases' => [ - // Allow Solr core names to be used as aliases for services: - 'authority' => 'SolrAuth', - 'biblio' => 'Solr', - 'reserves' => 'SolrReserves', - // Legacy: - 'VuFind' => 'Solr', - ] - ], - 'search_options' => [ - 'abstract_factories' => ['VuFind\Search\Options\PluginFactory'], - 'factories' => [ - 'eds' => 'VuFind\Search\Options\Factory::getEDS', - ], - ], - 'search_params' => [ - 'abstract_factories' => ['VuFind\Search\Params\PluginFactory'], - 'factories' => [ - 'solr' => 'VuFind\Search\Params\Factory::getSolr', - ], - ], - 'search_results' => [ - 'abstract_factories' => ['VuFind\Search\Results\PluginFactory'], - 'factories' => [ - 'favorites' => 'VuFind\Search\Results\Factory::getFavorites', - 'solr' => 'VuFind\Search\Results\Factory::getSolr', - 'tags' => 'VuFind\Search\Results\Factory::getTags', - ], - ], - 'session' => [ - 'abstract_factories' => ['VuFind\Session\PluginFactory'], - 'invokables' => [ - 'database' => 'VuFind\Session\Database', - 'file' => 'VuFind\Session\File', - 'memcache' => 'VuFind\Session\Memcache', - ], - 'aliases' => [ - // for legacy 1.x compatibility - 'filesession' => 'File', - 'memcachesession' => 'Memcache', - 'mysqlsession' => 'Database', - ], - ] + 'ajaxhandler' => [ /* see VuFind\AjaxHandler\PluginManager for defaults */ ], + 'auth' => [ /* see VuFind\Auth\PluginManager for defaults */ ], + 'autocomplete' => [ /* see VuFind\Autocomplete\PluginManager for defaults */ ], + 'channelprovider' => [ /* see VuFind\ChannelProvider\PluginManager for defaults */ ], + 'content' => [ /* see VuFind\Content\PluginManager for defaults */ ], + 'content_authornotes' => [ /* see VuFind\Content\AuthorNotes\PluginManager for defaults */ ], + 'content_covers' => [ /* see VuFind\Content\Covers\PluginManager for defaults */ ], + 'content_excerpts' => [ /* see VuFind\Content\Excerpts\PluginManager for defaults */ ], + 'content_reviews' => [ /* see VuFind\Content\Reviews\PluginManager for defaults */ ], + 'content_summaries' => [ /* see VuFind\Content\Summaries\PluginManager for defaults */ ], + 'content_toc' => [ /* see VuFind\Content\TOC\PluginManager for defaults */ ], + 'contentblock' => [ /* see VuFind\ContentBlock\PluginManager for defaults */ ], + 'cover_layer' => [ /* see VuFind\Cover\Layer\PluginManager for defaults */ ], + 'db_row' => [ /* see VuFind\Db\Row\PluginManager for defaults */ ], + 'db_table' => [ /* see VuFind\Db\Table\PluginManager for defaults */ ], + 'hierarchy_driver' => [ /* see VuFind\Hierarchy\Driver\PluginManager for defaults */ ], + 'hierarchy_treedataformatter' => [ /* see VuFind\Hierarchy\TreeDataFormatter\PluginManager for defaults */ ], + 'hierarchy_treedatasource' => [ /* see VuFind\Hierarchy\TreeDataSource\PluginManager for defaults */ ], + 'hierarchy_treerenderer' => [ /* see VuFind\Hierarchy\TreeRenderer\PluginManager for defaults */ ], + 'ils_driver' => [ /* See VuFind\ILS\Driver\PluginManager for defaults */ ], + 'recommend' => [ /* See VuFind\Recommend\PluginManager for defaults */ ], + 'recorddriver' => [ /* See VuFind\RecordDriver\PluginManager for defaults */ ], + 'recordtab' => [ /* See VuFind\RecordTab\PluginManager for defaults */ ], + 'related' => [ /* See VuFind\Related\PluginManager for defaults */ ], + 'resolver_driver' => [ /* See VuFind\Resolver\Driver\PluginManager for defaults */ ], + 'search_backend' => [ /* See VuFind\Search\BackendRegistry for defaults */ ], + 'search_facetcache' => [ /* See VuFind\Search\FacetCache\PluginManager for defaults */ ], + 'search_options' => [ /* See VuFind\Search\Options\PluginManager for defaults */ ], + 'search_params' => [ /* See VuFind\Search\Params\PluginManager for defaults */ ], + 'search_results' => [ /* See VuFind\Search\Results\PluginManager for defaults */ ], + 'session' => [ /* see VuFind\Session\PluginManager for defaults */ ], ], // This section behaves just like recorddriver_tabs below, but is used for // the collection module instead of the standard record view. @@ -683,12 +544,13 @@ $config = [ ], // This section controls which tabs are used for which record driver classes. // Each sub-array is a map from a tab name (as used in a record URL) to a tab - // service (found in recordtab_plugin_manager, below). If a particular record - // driver is not defined here, it will inherit configuration from a configured - // parent class. The defaultTab setting may be used to specify the default - // active tab; if null, the value from the relevant .ini file will be used. - // You can also specify which tabs are loaded in the background when arriving - // at a record tabs view with backgroundLoadedTabs as a list of tab indexes. + // service (found in recordtab plugin manager settings above). If a + // particular record driver is not defined here, it will inherit + // configuration from a configured parent class. The defaultTab setting may + // be used to specify the default active tab; if null, the value from the + // relevant .ini file will be used. You can also specify which tabs are + // loaded in the background when arriving at a record tabs view with + // backgroundLoadedTabs as a list of tab indexes. 'recorddriver_tabs' => [ 'VuFind\RecordDriver\EDS' => [ 'tabs' => [ @@ -716,13 +578,19 @@ $config = [ ], 'defaultTab' => null, ], - 'VuFind\RecordDriver\SolrAuth' => [ + 'VuFind\RecordDriver\SolrAuthDefault' => [ + 'tabs' => [ + 'Details' => 'StaffViewArray', + ], + 'defaultTab' => null, + ], + 'VuFind\RecordDriver\SolrAuthMarc' => [ 'tabs' => [ 'Details' => 'StaffViewMARC', ], 'defaultTab' => null, ], - 'VuFind\RecordDriver\SolrDefault' => [ + 'VuFind\RecordDriver\DefaultRecord' => [ 'tabs' => [ 'Holdings' => 'HoldingsILS', 'Description' => 'Description', 'TOC' => 'TOC', 'UserComments' => 'UserComments', @@ -770,7 +638,7 @@ $config = [ ], // Authorization configuration: 'zfc_rbac' => [ - 'identity_provider' => 'VuFind\AuthManager', + 'identity_provider' => 'VuFind\Auth\Manager', 'guest_role' => 'guest', 'role_provider' => [ 'VuFind\Role\DynamicRoleProvider' => [ @@ -782,19 +650,7 @@ $config = [ 'VuFind\Role\DynamicRoleProvider' => 'VuFind\Role\DynamicRoleProviderFactory', ], ], - 'vufind_permission_provider_manager' => [ - 'factories' => [ - 'ipRange' => 'VuFind\Role\PermissionProvider\Factory::getIpRange', - 'ipRegEx' => 'VuFind\Role\PermissionProvider\Factory::getIpRegEx', - 'serverParam' => 'VuFind\Role\PermissionProvider\Factory::getServerParam', - 'shibboleth' => 'VuFind\Role\PermissionProvider\Factory::getShibboleth', - 'user' => 'VuFind\Role\PermissionProvider\Factory::getUser', - 'username' => 'VuFind\Role\PermissionProvider\Factory::getUsername', - ], - 'invokables' => [ - 'role' => 'VuFind\Role\PermissionProvider\Role', - ], - ], + 'vufind_permission_provider_manager' => [ /* see VuFind\Role\PermissionProvider\PluginManager for defaults */ ], ], ]; @@ -809,6 +665,7 @@ $recordRoutes = [ 'solrauthrecord' => 'Authority', 'summonrecord' => 'SummonRecord', 'worldcatrecord' => 'WorldcatRecord', + 'search2record' => 'Search2Record', // For legacy (1.x/2.x) compatibility: 'vufindrecord' => 'Record', @@ -845,20 +702,22 @@ $staticRoutes = [ 'LibraryCards/Home', 'LibraryCards/SelectCard', 'LibraryCards/DeleteCard', 'MyResearch/Account', 'MyResearch/ChangePassword', 'MyResearch/CheckedOut', - 'MyResearch/Delete', 'MyResearch/DeleteList', 'MyResearch/Edit', - 'MyResearch/Email', 'MyResearch/Favorites', 'MyResearch/Fines', - 'MyResearch/Holds', 'MyResearch/Home', - 'MyResearch/ILLRequests', 'MyResearch/Logout', + 'MyResearch/Delete', 'MyResearch/DeleteAccount', 'MyResearch/DeleteList', + 'MyResearch/Edit', 'MyResearch/Email', 'MyResearch/Favorites', + 'MyResearch/Fines', 'MyResearch/HistoricLoans', 'MyResearch/Holds', + 'MyResearch/Home', 'MyResearch/ILLRequests', 'MyResearch/Logout', 'MyResearch/NewPassword', 'MyResearch/Profile', 'MyResearch/Recover', 'MyResearch/SaveSearch', 'MyResearch/StorageRetrievalRequests', 'MyResearch/UserLogin', - 'MyResearch/Verify', + 'MyResearch/Verify', 'OAI/Server', 'Pazpar2/Home', 'Pazpar2/Search', 'Primo/Advanced', 'Primo/Home', 'Primo/Search', - 'QRCode/Show', 'QRCode/Unavailable', - 'OAI/Server', 'Pazpar2/Home', 'Pazpar2/Search', 'Records/Home', - 'Search/Advanced', 'Search/Email', 'Search/FacetList', 'Search/History', - 'Search/Home', 'Search/NewItem', 'Search/OpenSearch', 'Search/Reserves', - 'Search/ReservesFacetList', 'Search/Results', 'Search/Suggest', + 'QRCode/Show', 'QRCode/Unavailable', 'Records/Home', + 'Relais/Login', 'Relais/Request', + 'Search/Advanced', 'Search/CollectionFacetList', 'Search/Email', + 'Search/FacetList', 'Search/History', 'Search/Home', 'Search/NewItem', + 'Search/OpenSearch', 'Search/Reserves', 'Search/ReservesFacetList', + 'Search/Results', 'Search/Suggest', + 'Search2/Advanced', 'Search2/Home', 'Search2/Results', 'Summon/Advanced', 'Summon/FacetList', 'Summon/Home', 'Summon/Search', 'Tag/Home', 'Upgrade/Home', 'Upgrade/FixAnonymousTags', 'Upgrade/FixDuplicateTags', @@ -877,7 +736,7 @@ $routeGenerator->addStaticRoutes($config, $staticRoutes); // Add the home route last $config['router']['routes']['home'] = [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/', 'defaults' => [ diff --git a/module/VuFind/sql/migrations/pgsql/5.0/001-modify-comments-user-ref.sql b/module/VuFind/sql/migrations/pgsql/5.0/001-modify-comments-user-ref.sql new file mode 100644 index 0000000000000000000000000000000000000000..24b280001c38c200ca499701c175eee1dd648fdf --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/5.0/001-modify-comments-user-ref.sql @@ -0,0 +1,10 @@ +-- +-- Modifications to table `comments` +-- + +ALTER TABLE "comments" + ALTER COLUMN user_id DROP NOT NULL, + DROP CONSTRAINT comments_ibfk_1; + +ALTER TABLE comments + ADD CONSTRAINT comments_ibfk_1 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE SET NULL; diff --git a/module/VuFind/sql/migrations/pgsql/5.0/002-modify-user-columns.sql b/module/VuFind/sql/migrations/pgsql/5.0/002-modify-user-columns.sql new file mode 100644 index 0000000000000000000000000000000000000000..da5edf586708384f514593e1d03292a60300d460 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/5.0/002-modify-user-columns.sql @@ -0,0 +1,7 @@ +-- +-- Modifications to table `user` +-- + +ALTER TABLE "user" + ADD COLUMN last_login timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', + ADD COLUMN auth_method varchar(50) DEFAULT NULL; diff --git a/module/VuFind/sql/mysql.sql b/module/VuFind/sql/mysql.sql index d00b13cd4fc96b6246a19cfc4e1c921b234dd72d..57593a89c161a3c8797f8bb440aed1d856365ae8 100644 --- a/module/VuFind/sql/mysql.sql +++ b/module/VuFind/sql/mysql.sql @@ -35,14 +35,14 @@ CREATE TABLE `change_tracker` ( /*!40101 SET character_set_client = utf8 */; CREATE TABLE `comments` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL DEFAULT '0', + `user_id` int(11) DEFAULT NULL, `resource_id` int(11) NOT NULL DEFAULT '0', `comment` text NOT NULL, `created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `resource_id` (`resource_id`), - CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, + CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, CONSTRAINT `comments_ibfk_2` FOREIGN KEY (`resource_id`) REFERENCES `resource` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -136,7 +136,7 @@ CREATE TABLE `search` ( CREATE TABLE `session` ( `id` int(11) NOT NULL AUTO_INCREMENT, `session_id` varchar(128) DEFAULT NULL, - `data` text, + `data` mediumtext, `last_used` int(12) NOT NULL DEFAULT '0', `created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', PRIMARY KEY (`id`), @@ -198,6 +198,8 @@ CREATE TABLE `user` ( `home_library` varchar(100) NOT NULL DEFAULT '', `created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', `verify_hash` varchar(42) NOT NULL DEFAULT '', + `last_login` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', + `auth_method` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`), UNIQUE KEY `cat_id` (`cat_id`) diff --git a/module/VuFind/sql/pgsql.sql b/module/VuFind/sql/pgsql.sql index 15a7a251e454c4f2343d8179148fb96afa81d75d..76d41610e8dafd9c6a00ab16532f479ded9f9de5 100644 --- a/module/VuFind/sql/pgsql.sql +++ b/module/VuFind/sql/pgsql.sql @@ -6,7 +6,7 @@ DROP TABLE IF EXISTS "comments"; CREATE TABLE comments ( id SERIAL, -user_id int NOT NULL DEFAULT '0', +user_id int DEFAULT NULL, resource_id int NOT NULL DEFAULT '0', comment text NOT NULL, created timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', @@ -123,6 +123,8 @@ major varchar(100) NOT NULL DEFAULT '', home_library varchar(100) NOT NULL DEFAULT '', created timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', verify_hash varchar(42) NOT NULL DEFAULT '', +last_login timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', +auth_method varchar(50) DEFAULT NULL, PRIMARY KEY (id), UNIQUE (username), UNIQUE (cat_id) @@ -289,7 +291,7 @@ CREATE INDEX user_card_user_id_idx ON user_card (user_id); -- Constraints for table comments -- ALTER TABLE comments -ADD CONSTRAINT comments_ibfk_1 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE, +ADD CONSTRAINT comments_ibfk_1 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE SET NULL, ADD CONSTRAINT comments_ibfk_2 FOREIGN KEY (resource_id) REFERENCES resource (id) ON DELETE CASCADE; diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractBase.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractBase.php new file mode 100644 index 0000000000000000000000000000000000000000..40f6bcc9ffbca04da48242342ecea60e7ca02da5 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractBase.php @@ -0,0 +1,82 @@ +<?php +/** + * Abstract base AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Session\Settings as SessionSettings; + +/** + * Abstract base AJAX handler + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractBase implements AjaxHandlerInterface +{ + /** + * Session settings + * + * @var SessionSettings + */ + protected $sessionSettings = null; + + /** + * Prevent session writes -- this is designed to be called prior to time- + * consuming AJAX operations to help reduce the odds of a timing-related bug + * that causes the wrong version of session data to be written to disk (see + * VUFIND-716 for more details). + * + * @return void + */ + protected function disableSessionWrites() + { + if (null === $this->sessionSettings) { + throw new \Exception('Session settings object missing.'); + } + $this->sessionSettings->disableWrite(); + } + + /** + * Format a response array. + * + * @param mixed $response Response data + * @param int $httpCode HTTP status code (omit for default) + * + * @return array + */ + protected function formatResponse($response, $httpCode = null) + { + $arr = [$response]; + if ($httpCode !== null) { + $arr[] = $httpCode; + } + return $arr; + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserAction.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserAction.php new file mode 100644 index 0000000000000000000000000000000000000000..de6787717f6ca1be90382d5ef455d7438010d430 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserAction.php @@ -0,0 +1,87 @@ +<?php +/** + * Abstract base class for handlers depending on the ILS and a logged-in user. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Auth\ILSAuthenticator; +use VuFind\Db\Row\User; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\ILS\Connection; +use VuFind\Session\Settings as SessionSettings; + +/** + * Abstract base class for handlers depending on the ILS and a logged-in user. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractIlsAndUserAction extends AbstractBase + implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * ILS connection + * + * @var Connection + */ + protected $ils; + + /** + * ILS authenticator + * + * @var ILSAuthenticator + */ + protected $ilsAuthenticator; + + /** + * Logged in user (or false) + * + * @var User|bool + */ + protected $user; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Connection $ils ILS connection + * @param ILSAuthenticator $ilsAuthenticator ILS authenticator + * @param User|bool $user Logged in user (or false) + */ + public function __construct(SessionSettings $ss, Connection $ils, + ILSAuthenticator $ilsAuthenticator, $user + ) { + $this->sessionSettings = $ss; + $this->ils = $ils; + $this->ilsAuthenticator = $ilsAuthenticator; + $this->user = $user; + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..15fe7d0cb361c9bf64fd0bee8d932ffa69a32a9b --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for AbstractIlsAndUserAction AJAX handlers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for AbstractIlsAndUserAction AJAX handlers. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractIlsAndUserActionFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\ILS\Connection'), + $container->get('VuFind\Auth\ILSAuthenticator'), + $container->get('VuFind\Auth\Manager')->isLoggedIn() + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisAction.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisAction.php new file mode 100644 index 0000000000000000000000000000000000000000..9c94b71cb4244312ccc2e5209e8ea416f67d0cae --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisAction.php @@ -0,0 +1,77 @@ +<?php +/** + * Abstract Relais Ajax Action + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Connection\Relais; +use VuFind\Db\Row\User; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Session\Settings as SessionSettings; + +/** + * Abstract Relais Ajax Action + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractRelaisAction extends AbstractBase + implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Relais connector + * + * @var Relais + */ + protected $relais; + + /** + * Logged-in user (or null if none) + * + * @var User + */ + protected $user; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Relais $relais Relais connector + * @param User $user Logged in user (or null if none) + */ + public function __construct(SessionSettings $ss, Relais $relais, + User $user = null + ) { + $this->sessionSettings = $ss; + $this->relais = $relais; + $this->user = $user; + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7191dedc770f286acd82fbeeb74160052054132d --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for AbstractRelaisAction AJAX handlers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for AbstractRelaisAction AJAX handlers. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractRelaisActionFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $user = $container->get('VuFind\Auth\Manager')->isLoggedIn(); + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Connection\Relais'), + $user ?: null + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/AjaxHandlerInterface.php b/module/VuFind/src/VuFind/AjaxHandler/AjaxHandlerInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..041b3e308d5f0559ed1cf1cd55ea0d016d335bb3 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/AjaxHandlerInterface.php @@ -0,0 +1,58 @@ +<?php +/** + * AJAX handler interface + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * AJAX handler interface + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +interface AjaxHandlerInterface +{ + // define some status constants + const STATUS_HTTP_BAD_REQUEST = 400; // bad request + const STATUS_HTTP_NEED_AUTH = 401; // must login first + const STATUS_HTTP_FORBIDDEN = 403; // method is unavailable + const STATUS_HTTP_ERROR = 500; // an error occurred + const STATUS_HTTP_UNAVAILABLE = 503; // temporarily unavailable + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params); +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/CheckRequestIsValid.php b/module/VuFind/src/VuFind/AjaxHandler/CheckRequestIsValid.php new file mode 100644 index 0000000000000000000000000000000000000000..df2b2c4efe278a7818e36f677642ceab7d17268d --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/CheckRequestIsValid.php @@ -0,0 +1,143 @@ +<?php +/** + * "Check Request is Valid" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * "Check Request is Valid" AJAX handler + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CheckRequestIsValid extends AbstractIlsAndUserAction +{ + /** + * Status messages + * + * @var array + */ + protected $statuses = [ + 'ILLRequest' => [ + 'success' => 'ill_request_place_text', + 'failure' => 'ill_request_error_blocked', + ], + 'StorageRetrievalRequest' => [ + 'success' => 'storage_retrieval_request_place_text', + 'failure' => 'storage_retrieval_request_error_blocked', + ], + ]; + + /** + * Given a request type and a boolean success status, return an appropriate + * message. + * + * @param string $requestType Type of request being made + * @param bool $results Result status + * + * @return string + */ + protected function getStatusMessage($requestType, $results) + { + // If successful, return success message: + if ($results) { + return isset($this->statuses[$requestType]['success']) + ? $this->statuses[$requestType]['success'] + : 'request_place_text'; + } + // If unsuccessful, return failure message: + return isset($this->statuses[$requestType]['failure']) + ? $this->statuses[$requestType]['failure'] + : 'hold_error_blocked'; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $id = $params->fromQuery('id'); + $data = $params->fromQuery('data'); + $requestType = $params->fromQuery('requestType'); + if (empty($id) || empty($data)) { + return $this->formatResponse( + $this->translate('bulk_error_missing'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + // check if user is logged in + if (!$this->user) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + try { + $patron = $this->ilsAuthenticator->storedCatalogLogin(); + if ($patron) { + switch ($requestType) { + case 'ILLRequest': + $results = $this->ils + ->checkILLRequestIsValid($id, $data, $patron); + break; + case 'StorageRetrievalRequest': + $results = $this->ils + ->checkStorageRetrievalRequestIsValid($id, $data, $patron); + break; + default: + $results = $this->ils->checkRequestIsValid($id, $data, $patron); + break; + } + if (is_array($results)) { + $msg = $results['status']; + $results = $results['valid']; + } else { + $msg = $this->getStatusMessage($requestType, $results); + } + return $this->formatResponse( + ['status' => $results, 'msg' => $this->translate($msg)] + ); + } + } catch (\Exception $e) { + // Do nothing -- just fail through to the error message below. + } + + return $this->formatResponse( + $this->translate('An error has occurred'), self::STATUS_HTTP_ERROR + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/CommentRecord.php b/module/VuFind/src/VuFind/AjaxHandler/CommentRecord.php new file mode 100644 index 0000000000000000000000000000000000000000..7cb271109c46a5ee6f69cc71b8f4f2c768613414 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/CommentRecord.php @@ -0,0 +1,154 @@ +<?php +/** + * AJAX handler to comment on a record. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Controller\Plugin\Recaptcha; +use VuFind\Db\Row\User; +use VuFind\Db\Table\Resource; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use Zend\Mvc\Controller\Plugin\Params; + +/** + * AJAX handler to comment on a record. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CommentRecord extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Resource database table + * + * @var Resource + */ + protected $table; + + /** + * Recaptcha controller plugin + * + * @var Recaptcha + */ + protected $recaptcha; + + /** + * Logged in user (or false) + * + * @var User|bool + */ + protected $user; + + /** + * Are comments enabled? + * + * @var bool + */ + protected $enabled; + + /** + * Constructor + * + * @param Resource $table Resource database table + * @param Recaptcha $recaptcha Recaptcha controller plugin + * @param User|bool $user Logged in user (or false) + * @param bool $enabled Are comments enabled? + */ + public function __construct(Resource $table, Recaptcha $recaptcha, $user, + $enabled = true + ) { + $this->table = $table; + $this->recaptcha = $recaptcha; + $this->user = $user; + $this->enabled = $enabled; + } + + /** + * Is CAPTCHA valid? (Also returns true if CAPTCHA is disabled). + * + * @return bool + */ + protected function checkCaptcha() + { + // Not enabled? Report success! + if (!$this->recaptcha->active('userComments')) { + return true; + } + $this->recaptcha->setErrorMode('none'); + return $this->recaptcha->validate(); + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + // Make sure comments are enabled: + if (!$this->enabled) { + return $this->formatResponse( + $this->translate('Comments disabled'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + + if ($this->user === false) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + $id = $params->fromPost('id'); + $source = $params->fromPost('source', DEFAULT_SEARCH_BACKEND); + $comment = $params->fromPost('comment'); + if (empty($id) || empty($comment)) { + return $this->formatResponse( + $this->translate('bulk_error_missing'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + + if (!$this->checkCaptcha()) { + return $this->formatResponse( + $this->translate('recaptcha_not_passed'), + self::STATUS_HTTP_FORBIDDEN + ); + } + + $resource = $this->table->findResource($id, $source); + $commentId = $resource->addComment($comment, $this->user); + return $this->formatResponse(compact('commentId')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php b/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2e9e9b1d1c20e5346d6750e92beede1eef7c9cab --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * Factory for CommentRecord AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for CommentRecord AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CommentRecordFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + $controllerPluginManager = $container->get('ControllerPluginManager'); + $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + return new $requestedName( + $tablePluginManager->get('VuFind\Db\Table\Resource'), + $controllerPluginManager->get('VuFind\Controller\Plugin\Recaptcha'), + $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $capabilities->getCommentSetting() !== 'disabled' + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordComment.php b/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordComment.php new file mode 100644 index 0000000000000000000000000000000000000000..8bb5bc9604e349697adcf0746dbf38d680be1f8e --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordComment.php @@ -0,0 +1,123 @@ +<?php +/** + * AJAX handler to delete a comment on a record. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Db\Row\User; +use VuFind\Db\Table\Comments; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use Zend\Mvc\Controller\Plugin\Params; + +/** + * AJAX handler to delete a comment on a record. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DeleteRecordComment extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Comments database table + * + * @var Comments + */ + protected $table; + + /** + * Logged in user (or false) + * + * @var User|bool + */ + protected $user; + + /** + * Are comments enabled? + * + * @var bool + */ + protected $enabled; + + /** + * Constructor + * + * @param Comments $table Comments database table + * @param User|bool $user Logged in user (or false) + * @param bool $enabled Are comments enabled? + */ + public function __construct(Comments $table, $user, $enabled = true) + { + $this->table = $table; + $this->user = $user; + $this->enabled = $enabled; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + // Make sure comments are enabled: + if (!$this->enabled) { + return $this->formatResponse( + $this->translate('Comments disabled'), + self::STATUS_HTTP_FORBIDDEN + ); + } + + if ($this->user === false) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + $id = $params->fromQuery('id'); + if (empty($id)) { + return $this->formatResponse( + $this->translate('bulk_error_missing'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + if (!$this->table->deleteIfOwnedByUser($id, $this->user)) { + return $this->formatResponse( + $this->translate('edit_list_fail'), + self::STATUS_HTTP_FORBIDDEN + ); + } + + return $this->formatResponse(''); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php b/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..95cadcf5e32cbfa44a0344a1013ebb39ce801289 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php @@ -0,0 +1,74 @@ +<?php +/** + * Factory for DeleteRecordComment AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for DeleteRecordComment AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DeleteRecordCommentFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + return new $requestedName( + $tablePluginManager->get('VuFind\Db\Table\Comments'), + $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $capabilities->getCommentSetting() !== 'disabled' + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestions.php b/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestions.php new file mode 100644 index 0000000000000000000000000000000000000000..50ab877b489fccfc841019ce11616e3f1d920d53 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestions.php @@ -0,0 +1,79 @@ +<?php +/** + * "Get Autocomplete Suggestions" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Autocomplete\Suggester; +use VuFind\Session\Settings as SessionSettings; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Stdlib\Parameters; + +/** + * "Get Autocomplete Suggestions" AJAX handler + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetACSuggestions extends AbstractBase +{ + /** + * Autocomplete suggester + * + * @var Suggester + */ + protected $suggester; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Suggester $suggester Autocomplete suggester + */ + public function __construct(SessionSettings $ss, Suggester $suggester) + { + $this->sessionSettings = $ss; + $this->suggester = $suggester; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $query = new Parameters($params->fromQuery()); + $suggestions = $this->suggester->getSuggestions($query); + return $this->formatResponse(compact('suggestions')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d00b91d9a7b3959a7f3631910720e2792b36e77e --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for GetACSuggestions AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetACSuggestions AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetACSuggestionsFactory implements + \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Autocomplete\Suggester') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php b/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php new file mode 100644 index 0000000000000000000000000000000000000000..d211fe353fc79a62710a3885653d5ba5ed30fe6e --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php @@ -0,0 +1,122 @@ +<?php +/** + * "Get Facet Data" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Search\Results\PluginManager as ResultsManager; +use VuFind\Search\Solr\HierarchicalFacetHelper; +use VuFind\Session\Settings as SessionSettings; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Stdlib\Parameters; + +/** + * "Get Facet Data" AJAX handler + * + * Get hierarchical facet data for jsTree + * + * Parameters: + * facetName The facet to retrieve + * facetSort By default all facets are sorted by count. Two values are available + * for alternative sorting: + * top = sort the top level alphabetically, rest by count + * all = sort all levels alphabetically + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetFacetData extends AbstractBase +{ + /** + * Hierarchical facet helper + * + * @var HierarchicalFacetHelper + */ + protected $facetHelper; + + /** + * Solr search results manager + * + * @var ResultsManager + */ + protected $resultsManager; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param HierarchicalFacetHelper $fh Facet helper + * @param ResultsManager $rm Search results manager + */ + public function __construct(SessionSettings $ss, HierarchicalFacetHelper $fh, + ResultsManager $rm + ) { + $this->sessionSettings = $ss; + $this->facetHelper = $fh; + $this->resultsManager = $rm; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + + $facet = $params->fromQuery('facetName'); + $sort = $params->fromQuery('facetSort'); + $operator = $params->fromQuery('facetOperator'); + $backend = $params->fromQuery('source', DEFAULT_SEARCH_BACKEND); + + $results = $this->resultsManager->get($backend); + $paramsObj = $results->getParams(); + $paramsObj->addFacet($facet, null, $operator === 'OR'); + $paramsObj->initFromRequest(new Parameters($params->fromQuery())); + + $facets = $results->getFullFieldFacets([$facet], false, -1, 'count'); + if (empty($facets[$facet]['data']['list'])) { + $facets = []; + } else { + $facetList = $facets[$facet]['data']['list']; + + if (!empty($sort)) { + $this->facetHelper->sortFacetList($facetList, $sort == 'top'); + } + + $facets = $this->facetHelper->buildFacetArray( + $facet, $facetList, $results->getUrlQuery(), false + ); + } + return $this->formatResponse(compact('facets')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3517a19930b05b2d092b08f67018ecd1c2464d57 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for GetFacetData AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetFacetData AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetFacetDataFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Search\Solr\HierarchicalFacetHelper'), + $container->get('VuFind\Search\Results\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatus.php b/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatus.php new file mode 100644 index 0000000000000000000000000000000000000000..58116debd3d8a669623a0dedf0a307f7ab520461 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatus.php @@ -0,0 +1,99 @@ +<?php +/** + * "Get ILS Status" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\ILS\Connection; +use VuFind\Session\Settings as SessionSettings; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\View\Renderer\RendererInterface; + +/** + * "Get ILS Status" AJAX handler + * + * This will check the ILS for being online and will return the ils-offline + * template upon failure. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetIlsStatus extends AbstractBase +{ + /** + * ILS connection + * + * @var Connection + */ + protected $ils; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Connection $ils ILS connection + * @param RendererInterface $renderer View renderer + */ + public function __construct(SessionSettings $ss, Connection $ils, + RendererInterface $renderer + ) { + $this->sessionSettings = $ss; + $this->ils = $ils; + $this->renderer = $renderer; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); + if ($this->ils->getOfflineMode(true) == 'ils-offline') { + $offlineModeMsg = $params->fromPost( + 'offlineModeMsg', $params->fromQuery('offlineModeMsg') + ); + $html = $this->renderer + ->render('Helpers/ils-offline.phtml', compact('offlineModeMsg')); + } + return $this->formatResponse(['html' => $html ?? '']); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..75547b84c562eba219287f566379d536f48fb53e --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for GetIlsStatus AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetIlsStatus AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetIlsStatusFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\ILS\Connection'), + $container->get('ViewRenderer') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php new file mode 100644 index 0000000000000000000000000000000000000000..f4bb3289d83e096af65818fbad174d6d5b39d97d --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php @@ -0,0 +1,533 @@ +<?php +/** + * "Get Item Status" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Delis <cedelis@uillinois.edu> + * @author Tuan Nguyen <tuan@yorku.ca> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\ILS\Connection; +use VuFind\ILS\Logic\Holds; +use VuFind\Session\Settings as SessionSettings; +use Zend\Config\Config; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\View\Renderer\RendererInterface; + +/** + * "Get Item Status" AJAX handler + * + * This is responsible for printing the holdings information for a + * collection of records in JSON format. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Delis <cedelis@uillinois.edu> + * @author Tuan Nguyen <tuan@yorku.ca> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetItemStatuses extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Top-level configuration + * + * @var Config + */ + protected $config; + + /** + * ILS connection + * + * @var Connection + */ + protected $ils; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Holds logic + * + * @var Holds + */ + protected $holdLogic; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Config $config Top-level configuration + * @param Connection $ils ILS connection + * @param RendererInterface $renderer View renderer + * @param Holds $holdLogic Holds logic + */ + public function __construct(SessionSettings $ss, Config $config, Connection $ils, + RendererInterface $renderer, Holds $holdLogic + ) { + $this->sessionSettings = $ss; + $this->config = $config; + $this->ils = $ils; + $this->renderer = $renderer; + $this->holdLogic = $holdLogic; + } + + /** + * Support method for getItemStatuses() -- filter suppressed locations from the + * array of item information for a particular bib record. + * + * @param array $record Information on items linked to a single bib record + * + * @return array Filtered version of $record + */ + protected function filterSuppressedLocations($record) + { + static $hideHoldings = false; + if ($hideHoldings === false) { + $hideHoldings = $this->holdLogic->getSuppressedLocations(); + } + + $filtered = []; + foreach ($record as $current) { + if (!in_array($current['location'] ?? null, $hideHoldings)) { + $filtered[] = $current; + } + } + return $filtered; + } + + /** + * Translate an array of strings using a prefix. + * + * @param string $transPrefix Translation prefix + * @param array $list List of values to translate + * + * @return array + */ + protected function translateList($transPrefix, $list) + { + $transList = []; + foreach ($list as $current) { + $transList[] = $this->translate( + $transPrefix . $current, [], $current + ); + } + return $transList; + } + + /** + * Support method for getItemStatuses() -- when presented with multiple values, + * pick which one(s) to send back via AJAX. + * + * @param array $rawList Array of values to choose from. + * @param string $mode config.ini setting -- first, all or msg + * @param string $msg Message to display if $mode == "msg" + * @param string $transPrefix Translator prefix to apply to values (false to + * omit translation of values) + * + * @return string + */ + protected function pickValue($rawList, $mode, $msg, $transPrefix = false) + { + // Make sure array contains only unique values: + $list = array_unique($rawList); + + // If there is only one value in the list, or if we're in "first" mode, + // send back the first list value: + if ($mode == 'first' || count($list) == 1) { + return $transPrefix + ? $this->translate($transPrefix . $list[0], [], $list[0]) + : $list[0]; + } elseif (count($list) == 0) { + // Empty list? Return a blank string: + return ''; + } elseif ($mode == 'all') { + // All values mode? Return comma-separated values: + return implode( + ",\t", + $transPrefix ? $this->translateList($transPrefix, $list) : $list + ); + } else { + // Message mode? Return the specified message, translated to the + // appropriate language. + return $this->translate($msg); + } + } + + /** + * Based on settings and the number of callnumbers, return callnumber handler + * Use callnumbers before pickValue is run. + * + * @param array $list Array of callnumbers. + * @param string $displaySetting config.ini setting -- first, all or msg + * + * @return string + */ + protected function getCallnumberHandler($list = null, $displaySetting = null) + { + if ($displaySetting == 'msg' && count($list) > 1) { + return false; + } + return isset($this->config->Item_Status->callnumber_handler) + ? $this->config->Item_Status->callnumber_handler + : false; + } + + /** + * Reduce an array of service names to a human-readable string. + * + * @param array $rawServices Names of available services. + * + * @return string + */ + protected function reduceServices(array $rawServices) + { + // Normalize, dedup and sort available services + $normalize = function ($in) { + return strtolower(preg_replace('/[^A-Za-z]/', '', $in)); + }; + $services = array_map($normalize, array_unique($rawServices)); + sort($services); + + // Do we need to deal with a preferred service? + $preferred = isset($this->config->Item_Status->preferred_service) + ? $normalize($this->config->Item_Status->preferred_service) : false; + if (false !== $preferred && in_array($preferred, $services)) { + $services = [$preferred]; + } + + return $this->renderer->render( + 'ajax/status-available-services.phtml', + ['services' => $services] + ); + } + + /** + * Support method for getItemStatuses() -- process a single bibliographic record + * for location settings other than "group". + * + * @param array $record Information on items linked to a single bib + * record + * @param array $messages Custom status HTML + * (keys = available/unavailable) + * @param string $locationSetting The location mode setting used for + * pickValue() + * @param string $callnumberSetting The callnumber mode setting used for + * pickValue() + * + * @return array Summarized availability information + */ + protected function getItemStatus($record, $messages, $locationSetting, + $callnumberSetting + ) { + // Summarize call number, location and availability info across all items: + $callNumbers = $locations = []; + $use_unknown_status = $available = false; + $services = []; + + foreach ($record as $info) { + // Find an available copy + if ($info['availability']) { + $available = true; + } + // Check for a use_unknown_message flag + if (isset($info['use_unknown_message']) + && $info['use_unknown_message'] == true + ) { + $use_unknown_status = true; + } + // Store call number/location info: + $callNumbers[] = $info['callnumber']; + $locations[] = $info['location']; + // Store all available services + if (isset($info['services'])) { + $services = array_merge($services, $info['services']); + } + } + + $callnumberHandler = $this->getCallnumberHandler( + $callNumbers, $callnumberSetting + ); + + // Determine call number string based on findings: + $callNumber = $this->pickValue( + $callNumbers, $callnumberSetting, 'Multiple Call Numbers' + ); + + // Determine location string based on findings: + $location = $this->pickValue( + $locations, $locationSetting, 'Multiple Locations', 'location_' + ); + + if (!empty($services)) { + $availability_message = $this->reduceServices($services); + } else { + $availability_message = $use_unknown_status + ? $messages['unknown'] + : $messages[$available ? 'available' : 'unavailable']; + } + + // Send back the collected details: + return [ + 'id' => $record[0]['id'], + 'availability' => ($available ? 'true' : 'false'), + 'availability_message' => $availability_message, + 'location' => htmlentities($location, ENT_COMPAT, 'UTF-8'), + 'locationList' => false, + 'reserve' => + ($record[0]['reserve'] == 'Y' ? 'true' : 'false'), + 'reserve_message' => $record[0]['reserve'] == 'Y' + ? $this->translate('on_reserve') + : $this->translate('Not On Reserve'), + 'callnumber' => htmlentities($callNumber, ENT_COMPAT, 'UTF-8'), + 'callnumber_handler' => $callnumberHandler + ]; + } + + /** + * Support method for getItemStatuses() -- process a single bibliographic record + * for "group" location setting. + * + * @param array $record Information on items linked to a single + * bib record + * @param array $messages Custom status HTML + * (keys = available/unavailable) + * @param string $callnumberSetting The callnumber mode setting used for + * pickValue() + * + * @return array Summarized availability information + */ + protected function getItemStatusGroup($record, $messages, $callnumberSetting) + { + // Summarize call number, location and availability info across all items: + $locations = []; + $use_unknown_status = $available = false; + foreach ($record as $info) { + // Find an available copy + if ($info['availability']) { + $available = $locations[$info['location']]['available'] = true; + } + // Check for a use_unknown_message flag + if (isset($info['use_unknown_message']) + && $info['use_unknown_message'] == true + ) { + $use_unknown_status = true; + $locations[$info['location']]['status_unknown'] = true; + } + // Store call number/location info: + $locations[$info['location']]['callnumbers'][] = $info['callnumber']; + } + + // Build list split out by location: + $locationList = false; + foreach ($locations as $location => $details) { + $locationCallnumbers = array_unique($details['callnumbers']); + // Determine call number string based on findings: + $callnumberHandler = $this->getCallnumberHandler( + $locationCallnumbers, $callnumberSetting + ); + $locationCallnumbers = $this->pickValue( + $locationCallnumbers, $callnumberSetting, 'Multiple Call Numbers' + ); + $locationInfo = [ + 'availability' => + $details['available'] ?? false, + 'location' => htmlentities( + $this->translate('location_' . $location, [], $location), + ENT_COMPAT, 'UTF-8' + ), + 'callnumbers' => + htmlentities($locationCallnumbers, ENT_COMPAT, 'UTF-8'), + 'status_unknown' => $details['status_unknown'] ?? false, + 'callnumber_handler' => $callnumberHandler + ]; + $locationList[] = $locationInfo; + } + + $availability_message = $use_unknown_status + ? $messages['unknown'] + : $messages[$available ? 'available' : 'unavailable']; + + // Send back the collected details: + return [ + 'id' => $record[0]['id'], + 'availability' => ($available ? 'true' : 'false'), + 'availability_message' => $availability_message, + 'location' => false, + 'locationList' => $locationList, + 'reserve' => + ($record[0]['reserve'] == 'Y' ? 'true' : 'false'), + 'reserve_message' => $record[0]['reserve'] == 'Y' + ? $this->translate('on_reserve') + : $this->translate('Not On Reserve'), + 'callnumber' => false + ]; + } + + /** + * Support method for getItemStatuses() -- process a failed record. + * + * @param array $record Information on items linked to a single bib record + * @param string $msg Availability message + * + * @return array Summarized availability information + */ + protected function getItemStatusError($record, $msg = '') + { + return [ + 'id' => $record[0]['id'], + 'error' => $this->translate($record[0]['error']), + 'availability' => false, + 'availability_message' => $msg, + 'location' => false, + 'locationList' => [], + 'reserve' => false, + 'reserve_message' => '', + 'callnumber' => false + ]; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $ids = $params->fromPost('id', $params->fromQuery('id', [])); + try { + $results = $this->ils->getStatuses($ids); + } catch (ILSException $e) { + // If the ILS fails, send an error response instead of a fatal + // error; we don't want to confuse the end user unnecessarily. + error_log($e->getMessage()); + foreach ($ids as $id) { + $results[] = [ + [ + 'id' => $id, + 'error' => 'An error has occurred' + ] + ]; + } + } + + if (!is_array($results)) { + // If getStatuses returned garbage, let's turn it into an empty array + // to avoid triggering a notice in the foreach loop below. + $results = []; + } + + // In order to detect IDs missing from the status response, create an + // array with a key for every requested ID. We will clear keys as we + // encounter IDs in the response -- anything left will be problems that + // need special handling. + $missingIds = array_flip($ids); + + // Load messages for response: + $messages = [ + 'available' => $this->renderer->render('ajax/status-available.phtml'), + 'unavailable' => + $this->renderer->render('ajax/status-unavailable.phtml'), + 'unknown' => $this->renderer->render('ajax/status-unknown.phtml') + ]; + + // Load callnumber and location settings: + $callnumberSetting = isset($this->config->Item_Status->multiple_call_nos) + ? $this->config->Item_Status->multiple_call_nos : 'msg'; + $locationSetting = isset($this->config->Item_Status->multiple_locations) + ? $this->config->Item_Status->multiple_locations : 'msg'; + $showFullStatus = isset($this->config->Item_Status->show_full_status) + ? $this->config->Item_Status->show_full_status : false; + + // Loop through all the status information that came back + $statuses = []; + foreach ($results as $recordNumber => $record) { + // Filter out suppressed locations: + $record = $this->filterSuppressedLocations($record); + + // Skip empty records: + if (count($record)) { + // Check for errors + if (!empty($record[0]['error'])) { + $current = $this + ->getItemStatusError($record, $messages['unknown']); + } elseif ($locationSetting === 'group') { + $current = $this->getItemStatusGroup( + $record, $messages, $callnumberSetting + ); + } else { + $current = $this->getItemStatus( + $record, $messages, $locationSetting, $callnumberSetting + ); + } + // If a full status display has been requested, append the HTML: + if ($showFullStatus) { + $current['full_status'] = $this->renderer->render( + 'ajax/status-full.phtml', [ + 'statusItems' => $record, + 'callnumberHandler' => $this->getCallnumberHandler() + ] + ); + } + $current['record_number'] = array_search($current['id'], $ids); + $statuses[] = $current; + + // The current ID is not missing -- remove it from the missing list. + unset($missingIds[$current['id']]); + } + } + + // If any IDs were missing, send back appropriate dummy data + foreach ($missingIds as $missingId => $recordNumber) { + $statuses[] = [ + 'id' => $missingId, + 'availability' => 'false', + 'availability_message' => $messages['unavailable'], + 'location' => $this->translate('Unknown'), + 'locationList' => false, + 'reserve' => 'false', + 'reserve_message' => $this->translate('Not On Reserve'), + 'callnumber' => '', + 'missing_data' => true, + 'record_number' => $recordNumber + ]; + } + + // Done + return $this->formatResponse(compact('statuses')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f0f50fc838b7a1a171b95f6e943ce7c6613580e4 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for GetItemStatus AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetItemStatus AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetItemStatusesFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Config\PluginManager')->get('config'), + $container->get('VuFind\ILS\Connection'), + $container->get('ViewRenderer'), + $container->get('VuFind\ILS\Logic\Holds') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php b/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php new file mode 100644 index 0000000000000000000000000000000000000000..bf22e820f581229ec24baea9dcfbc0ab729acaec --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php @@ -0,0 +1,95 @@ +<?php +/** + * "Get Library Pickup Locations" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * "Get Library Pickup Locations" AJAX handler + * + * Get pick up locations for a library + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetLibraryPickupLocations extends AbstractIlsAndUserAction +{ + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $id = $params->fromQuery('id'); + $pickupLib = $params->fromQuery('pickupLib'); + if (null === $id || null === $pickupLib) { + return $this->formatResponse( + $this->translate('bulk_error_missing'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + // check if user is logged in + if (!$this->user) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + try { + $patron = $this->ilsAuthenticator->storedCatalogLogin(); + if ($patron) { + $results = $this->ils + ->getILLPickupLocations($id, $pickupLib, $patron); + foreach ($results as &$result) { + if (isset($result['name'])) { + $result['name'] = $this->translate( + 'location_' . $result['name'], + [], + $result['name'] + ); + } + } + return $this->formatResponse(['locations' => $results]); + } + } catch (\Exception $e) { + // Do nothing -- just fail through to the error message below. + } + + return $this->formatResponse( + $this->translate('An error has occurred'), self::STATUS_HTTP_ERROR + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTML.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTML.php new file mode 100644 index 0000000000000000000000000000000000000000..1b41047796b4a915a0d9512cd28683bb76fabd00 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTML.php @@ -0,0 +1,88 @@ +<?php +/** + * AJAX handler to get list of comments for a record as HTML. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Record\Loader; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\View\Renderer\RendererInterface; + +/** + * AJAX handler to get list of comments for a record as HTML. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRecordCommentsAsHTML extends AbstractBase +{ + /** + * Record loader + * + * @var Loader + */ + protected $loader; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Constructor + * + * @param Connection $loader Record loader + * @param RendererInterface $renderer View renderer + */ + public function __construct(Loader $loader, RendererInterface $renderer) + { + $this->loader = $loader; + $this->renderer = $renderer; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $driver = $this->loader->load( + $params->fromQuery('id'), + $params->fromQuery('source', DEFAULT_SEARCH_BACKEND) + ); + $html = $this->renderer + ->render('record/comments-list.phtml', compact('driver')); + return $this->formatResponse(compact('html')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..aa5cad433b08f412c4b37497048db073ae0bbbdb --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for GetRecordCommentsAsHTML AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetRecordCommentsAsHTML AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRecordCommentsAsHTMLFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Record\Loader'), + $container->get('ViewRenderer') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php new file mode 100644 index 0000000000000000000000000000000000000000..e8c4a1a866d2b3938fa27ae90d9c570c638b9850 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php @@ -0,0 +1,138 @@ +<?php +/** + * "Get Record Details" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Record\Loader; +use VuFind\RecordTab\PluginManager as TabManager; +use Zend\Http\PhpEnvironment\Request; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\View\Renderer\RendererInterface; + +/** + * "Get Record Details" AJAX handler + * + * Get record for integrated list view. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRecordDetails extends AbstractBase +{ + /** + * ZF configuration + * + * @var array + */ + protected $config; + + /** + * Request + * + * @var Request + */ + protected $request; + + /** + * Record loader + * + * @var Loader + */ + protected $recordLoader; + + /** + * Record tab plugin manager + * + * @var TabManager + */ + protected $pluginManager; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Constructor + * + * @param array $config ZF configuration + * @param Request $request HTTP request + * @param Loader $loader Record loader + * @param TabManager $pm RecordTab plugin manager + * @param RendererInterface $renderer Renderer + */ + public function __construct(array $config, Request $request, Loader $loader, + TabManager $pm, RendererInterface $renderer + ) { + $this->config = $config; + $this->request = $request; + $this->recordLoader = $loader; + $this->pluginManager = $pm; + $this->renderer = $renderer; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $driver = $this->recordLoader + ->load($params->fromQuery('id'), $params->fromQuery('source')); + $viewtype = preg_replace( + '/\W/', '', trim(strtolower($params->fromQuery('type'))) + ); + + $details = $this->pluginManager->getTabDetailsForRecord( + $driver, + $this->config['vufind']['recorddriver_tabs'], + $this->request, + 'Information' + ); + + $html = $this->renderer->render( + "record/ajaxview-" . $viewtype . ".phtml", + [ + 'defaultTab' => $details['default'], + 'driver' => $driver, + 'tabs' => $details['tabs'], + 'backgroundTabs' => $this->pluginManager->getBackgroundTabNames( + $driver, $this->config['vufind']['recorddriver_tabs'] + ) + ] + ); + return $this->formatResponse(compact('html')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1174242c9abbdf59abc6b1dc775f558e02973b0c --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php @@ -0,0 +1,74 @@ +<?php +/** + * Factory for GetRecordDetails AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetRecordDetails AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRecordDetailsFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('Config'), + $container->get('Request'), + $container->get('VuFind\Record\Loader'), + $container->get('VuFind\RecordTab\PluginManager'), + $container->get('ViewRenderer') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordTags.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordTags.php new file mode 100644 index 0000000000000000000000000000000000000000..23444f211233e24fefb9939493a04d10a281df25 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordTags.php @@ -0,0 +1,113 @@ +<?php +/** + * AJAX handler to get all tags for a record as HTML. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Db\Row\User; +use VuFind\Db\Table\Tags; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\View\Renderer\RendererInterface; + +/** + * AJAX handler to get all tags for a record as HTML. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRecordTags extends AbstractBase +{ + /** + * Tags database table + * + * @var Tags + */ + protected $table; + + /** + * Logged in user (or false) + * + * @var User|bool + */ + protected $user; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Constructor + * + * @param Tags $table Tags table + * @param User|bool $user Logged in user (or false) + * @param RendererInterface $renderer View renderer + */ + public function __construct(Tags $table, $user, RendererInterface $renderer) + { + $this->table = $table; + $this->user = $user; + $this->renderer = $renderer; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $is_me_id = !$this->user ? null : $this->user->id; + + // Retrieve from database: + $tags = $this->table->getForResource( + $params->fromQuery('id'), + $params->fromQuery('source', DEFAULT_SEARCH_BACKEND), + 0, null, null, 'count', $is_me_id + ); + + // Build data structure for return: + $tagList = []; + foreach ($tags as $tag) { + $tagList[] = [ + 'tag' => $tag->tag, + 'cnt' => $tag->cnt, + 'is_me' => !empty($tag->is_me) + ]; + } + + $viewParams = ['tagList' => $tagList, 'loggedin' => (bool)$this->user]; + $html = $this->renderer->render('record/taglist', $viewParams); + return $this->formatResponse(compact('html')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8e4d746e203502885bf69a6fd3a535267dd4829f --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for GetRecordTags AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetRecordTags AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRecordTagsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + return new $requestedName( + $tablePluginManager->get('VuFind\Db\Table\Tags'), + $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $container->get('ViewRenderer') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php b/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php new file mode 100644 index 0000000000000000000000000000000000000000..82e82f02ad0bfdba517ce7e651e217e3ff97cd8a --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php @@ -0,0 +1,97 @@ +<?php +/** + * "Get Request Group Pickup Locations" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * "Get Request Group Pickup Locations" AJAX handler + * + * Get pick up locations for a request group + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetRequestGroupPickupLocations extends AbstractIlsAndUserAction +{ + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $id = $params->fromQuery('id'); + $requestGroupId = $params->fromQuery('requestGroupId'); + if (null === $id || null === $requestGroupId) { + return $this->formatResponse( + $this->translate('bulk_error_missing'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + // check if user is logged in + if (!$this->user) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + try { + if ($patron = $this->ilsAuthenticator->storedCatalogLogin()) { + $details = [ + 'id' => $id, + 'requestGroupId' => $requestGroupId + ]; + $results = $this->ils->getPickupLocations($patron, $details); + foreach ($results as &$result) { + if (isset($result['locationDisplay'])) { + $result['locationDisplay'] = $this->translate( + 'location_' . $result['locationDisplay'], + [], + $result['locationDisplay'] + ); + } + } + return $this->formatResponse(['locations' => $results]); + } + } catch (\Exception $e) { + // Do nothing -- just fail through to the error message below. + } + + return $this->formatResponse( + $this->translate('An error has occurred'), self::STATUS_HTTP_ERROR + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinks.php b/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinks.php new file mode 100644 index 0000000000000000000000000000000000000000..16483c1aa03c7db72ddde29ef1ad0b1abf6540dc --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinks.php @@ -0,0 +1,166 @@ +<?php +/** + * "Get Resolver Links" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author Graham Seaman <Graham.Seaman@rhul.ac.uk> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Resolver\Connection; +use VuFind\Resolver\Driver\PluginManager as ResolverManager; +use VuFind\Session\Settings as SessionSettings; +use Zend\Config\Config; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\View\Renderer\RendererInterface; + +/** + * "Get Resolver Links" AJAX handler + * + * Fetch Links from resolver given an OpenURL and format as HTML + * and output the HTML content in JSON object. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author Graham Seaman <Graham.Seaman@rhul.ac.uk> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetResolverLinks extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Resolver driver plugin manager + * + * @var ResolverManager + */ + protected $pluginManager; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Top-level VuFind configuration (config.ini) + * + * @var Config + */ + protected $config; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param ResolverManager $pm Resolver driver plugin manager + * @param RendererInterface $renderer View renderer + * @param Config $config Top-level VuFind configuration (config.ini) + */ + public function __construct(SessionSettings $ss, ResolverManager $pm, + RendererInterface $renderer, Config $config + ) { + $this->sessionSettings = $ss; + $this->pluginManager = $pm; + $this->renderer = $renderer; + $this->config = $config; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $openUrl = $params->fromQuery('openurl', ''); + $searchClassId = $params->fromQuery('searchClassId', ''); + + $resolverType = isset($this->config->OpenURL->resolver) + ? $this->config->OpenURL->resolver : 'other'; + if (!$this->pluginManager->has($resolverType)) { + return $this->formatResponse( + $this->translate("Could not load driver for $resolverType"), + self::STATUS_HTTP_ERROR + ); + } + $resolver = new Connection($this->pluginManager->get($resolverType)); + if (isset($this->config->OpenURL->resolver_cache)) { + $resolver->enableCache($this->config->OpenURL->resolver_cache); + } + $result = $resolver->fetchLinks($openUrl); + + // Sort the returned links into categories based on service type: + $electronic = $print = $services = []; + foreach ($result as $link) { + switch ($link['service_type'] ?? '') { + case 'getHolding': + $print[] = $link; + break; + case 'getWebService': + $services[] = $link; + break; + case 'getDOI': + // Special case -- modify DOI text for special display: + $link['title'] = $this->translate('Get full text'); + $link['coverage'] = ''; + case 'getFullTxt': + default: + $electronic[] = $link; + break; + } + } + + // Get the OpenURL base: + if (isset($this->config->OpenURL->url)) { + // Trim off any parameters (for legacy compatibility -- default config + // used to include extraneous parameters): + list($base) = explode('?', $this->config->OpenURL->url); + } else { + $base = false; + } + + $moreOptionsLink = $resolver->supportsMoreOptionsLink() + ? $resolver->getResolverUrl($openUrl) : ''; + + // Render the links using the view: + $view = [ + 'openUrlBase' => $base, 'openUrl' => $openUrl, 'print' => $print, + 'electronic' => $electronic, 'services' => $services, + 'searchClassId' => $searchClassId, + 'moreOptionsLink' => $moreOptionsLink + ]; + $html = $this->renderer->render('ajax/resolverLinks.phtml', $view); + + // output HTML encoded in JSON object + return $this->formatResponse(compact('html')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fc5072f68e45262a04ebb25fdf805b69a37010cf --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for GetResolverLinks AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetResolverLinks AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetResolverLinksFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Resolver\Driver\PluginManager'), + $container->get('ViewRenderer'), + $container->get('VuFind\Config\PluginManager')->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatuses.php b/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatuses.php new file mode 100644 index 0000000000000000000000000000000000000000..8f8dd2cf7d0cd9263e4a628107b93f47300db710 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatuses.php @@ -0,0 +1,153 @@ +<?php +/** + * "Get Save Statuses" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Db\Row\User; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Session\Settings as SessionSettings; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Mvc\Controller\Plugin\Url; + +/** + * "Get Save Statuses" AJAX handler + * + * Check one or more records to see if they are saved in one of the user's list. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetSaveStatuses extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Logged in user (or false) + * + * @var User|bool + */ + protected $user; + + /** + * URL helper + * + * @var Url + */ + protected $urlHelper; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param User|bool $user Logged in user (or false) + * @param Url $urlHelper URL helper + */ + public function __construct(SessionSettings $ss, $user, Url $urlHelper) + { + $this->sessionSettings = $ss; + $this->user = $user; + $this->urlHelper = $urlHelper; + } + + /** + * Format list object into array. + * + * @param array $list List data + * + * @return array + */ + protected function formatListData($list) + { + return [ + 'list_url' => + $this->urlHelper->fromRoute('userList', ['id' => $list['list_id']]), + 'list_title' => $list['list_title'], + ]; + } + + /** + * Obtain status data from the current logged-in user. + * + * @param array $ids IDs to retrieve + * @param array $sources Source data for IDs (parallel-indexed) + * + * @return array + */ + protected function getDataFromUser($ids, $sources) + { + $result = $checked = []; + foreach ($ids as $i => $id) { + $source = $sources[$i] ?? DEFAULT_SEARCH_BACKEND; + $selector = $source . '|' . $id; + + // We don't want to bother checking the same ID more than once, so + // use the $checked flag array to avoid duplicates: + if (!isset($checked[$selector])) { + $checked[$selector] = true; + + $data = $this->user->getSavedData($id, null, $source); + $result[$selector] = ($data && count($data) > 0) + ? array_map([$this, 'formatListData'], $data->toArray()) : []; + } + } + return $result; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + // check if user is logged in + if (!$this->user) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + // loop through each ID check if it is saved to any of the user's lists + $ids = $params->fromPost('id', $params->fromQuery('id', [])); + $sources = $params->fromPost('source', $params->fromQuery('source', [])); + if (!is_array($ids) || !is_array($sources)) { + return $this->formatResponse( + $this->translate('Argument must be array.'), + self::STATUS_HTTP_BAD_REQUEST + ); + } + $statuses = $this->getDataFromUser($ids, $sources); + return $this->formatResponse(compact('statuses')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7429b2565081b68238d0719c9778f7cc713fc837 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for GetSaveStatuses AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetSaveStatuses AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetSaveStatusesFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $container->get('ControllerPluginManager')->get('url') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php b/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php new file mode 100644 index 0000000000000000000000000000000000000000..79dc5421816ec2a255e0190c8d922b80ed086ecd --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php @@ -0,0 +1,158 @@ +<?php +/** + * "Get Visualization Data" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <crhallberg@gmail.com> + * @author Till Kinstler <kinstler@gbv.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Search\Solr\Results; +use VuFind\Session\Settings as SessionSettings; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Stdlib\Parameters; + +/** + * "Get Visualization Data" AJAX handler + * + * AJAX for timeline feature (PubDateVisAjax) + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <crhallberg@gmail.com> + * @author Till Kinstler <kinstler@gbv.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetVisData extends AbstractBase +{ + /** + * Solr search results object + * + * @var Results + */ + protected $results; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param Results $results Solr search results object + */ + public function __construct(SessionSettings $ss, Results $results) + { + $this->sessionSettings = $ss; + $this->results = $results; + } + + /** + * Extract details from applied filters. + * + * @param array $filters Current filter list + * @param array $dateFacets Objects containing the date ranges + * + * @return array + */ + protected function processDateFacets($filters, $dateFacets) + { + $result = []; + foreach ($dateFacets as $current) { + $from = $to = ''; + if (isset($filters[$current])) { + foreach ($filters[$current] as $filter) { + if (preg_match('/\[[\d\*]+ TO [\d\*]+\]/', $filter)) { + $range = explode(' TO ', trim($filter, '[]')); + $from = $range[0] == '*' ? '' : $range[0]; + $to = $range[1] == '*' ? '' : $range[1]; + break; + } + } + } + $result[$current] = [$from, $to]; + $result[$current]['label'] + = $this->results->getParams()->getFacetLabel($current); + } + return $result; + } + + /** + * Filter bad values from facet lists and add useful data fields. + * + * @param array $filters Current filter list + * @param array $fields Processed date information from processDateFacets + * + * @return array + */ + protected function processFacetValues($filters, $fields) + { + $facets = $this->results->getFullFieldFacets(array_keys($fields)); + $retVal = []; + foreach ($facets as $field => $values) { + $filter = $filters[$field][0] ?? null; + $newValues = [ + 'data' => [], + 'min' => $fields[$field][0] > 0 ? $fields[$field][0] : 0, + 'max' => $fields[$field][1] > 0 ? $fields[$field][1] : 0, + 'removalURL' => $this->results->getUrlQuery() + ->removeFacet($field, $filter)->getParams(false), + ]; + foreach ($values['data']['list'] as $current) { + // Only retain numeric values! + if (preg_match("/^[0-9]+$/", $current['value'])) { + $newValues['data'][] + = [$current['value'], $current['count']]; + } + } + $retVal[$field] = $newValues; + } + return $retVal; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $paramsObj = $this->results->getParams(); + $paramsObj->initFromRequest(new Parameters($params->fromQuery())); + foreach ($params->fromQuery('hf', []) as $hf) { + $paramsObj->addHiddenFilter($hf); + } + $paramsObj->getOptions()->disableHighlighting(); + $paramsObj->getOptions()->spellcheckEnabled(false); + $filters = $paramsObj->getFilters(); + $rawDateFacets = $params->fromQuery('facetFields'); + $dateFacets = empty($rawDateFacets) ? [] : explode(':', $rawDateFacets); + $fields = $this->processDateFacets($filters, $dateFacets); + $facets = $this->processFacetValues($filters, $fields); + return $this->formatResponse(compact('facets')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8835cf458ab893213a26d3152a19c0bb156dd8d4 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for GetVisData AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for GetVisData AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GetVisDataFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Search\Results\PluginManager')->get('Solr') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/KeepAlive.php b/module/VuFind/src/VuFind/AjaxHandler/KeepAlive.php new file mode 100644 index 0000000000000000000000000000000000000000..1bb71cd1f7d13a13bf7b14fe91fa59900ca5eb4f --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/KeepAlive.php @@ -0,0 +1,79 @@ +<?php +/** + * "Keep Alive" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Session\SessionManager; + +/** + * "Keep Alive" AJAX handler + * + * This is responsible for keeping the session alive whenever called + * (via JavaScript) + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class KeepAlive extends AbstractBase +{ + /** + * Session Manager + * + * @var SessionManager + */ + protected $sessionManager; + + /** + * Constructor + * + * @param SessionManager $sm Session manager + */ + public function __construct(SessionManager $sm) + { + $this->sessionManager = $sm; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function handleRequest(Params $params) + { + // Request ID from session to mark it active + $this->sessionManager->getId(); + return $this->formatResponse(true); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php b/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e229b158bfe8bb126896286864ba93ce2c131f16 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for KeepAlive AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for KeepAlive AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class KeepAliveFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get('Zend\Session\SessionManager')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php b/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..5f0e3295b8e5fcc9dd97d4174566ce1b72947541 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/PluginManager.php @@ -0,0 +1,131 @@ +<?php +/** + * AJAX handler plugin manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +/** + * AJAX handler plugin manager + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'checkRequestIsValid' => 'VuFind\AjaxHandler\CheckRequestIsValid', + 'commentRecord' => 'VuFind\AjaxHandler\CommentRecord', + 'deleteRecordComment' => 'VuFind\AjaxHandler\DeleteRecordComment', + 'getACSuggestions' => 'VuFind\AjaxHandler\GetACSuggestions', + 'getFacetData' => 'VuFind\AjaxHandler\GetFacetData', + 'getIlsStatus' => 'VuFind\AjaxHandler\GetIlsStatus', + 'getItemStatuses' => 'VuFind\AjaxHandler\GetItemStatuses', + 'getLibraryPickupLocations' => + 'VuFind\AjaxHandler\GetLibraryPickupLocations', + 'getRecordCommentsAsHTML' => 'VuFind\AjaxHandler\GetRecordCommentsAsHTML', + 'getRecordDetails' => 'VuFind\AjaxHandler\GetRecordDetails', + 'getRecordTags' => 'VuFind\AjaxHandler\GetRecordTags', + 'getRequestGroupPickupLocations' => + 'VuFind\AjaxHandler\GetRequestGroupPickupLocations', + 'getResolverLinks' => 'VuFind\AjaxHandler\GetResolverLinks', + 'getSaveStatuses' => 'VuFind\AjaxHandler\GetSaveStatuses', + 'getVisData' => 'VuFind\AjaxHandler\GetVisData', + 'keepAlive' => 'VuFind\AjaxHandler\KeepAlive', + 'recommend' => 'VuFind\AjaxHandler\Recommend', + 'relaisAvailability' => 'VuFind\AjaxHandler\RelaisAvailability', + 'relaisInfo' => 'VuFind\AjaxHandler\RelaisInfo', + 'relaisOrder' => 'VuFind\AjaxHandler\RelaisOrder', + 'systemStatus' => 'VuFind\AjaxHandler\SystemStatus', + 'tagRecord' => 'VuFind\AjaxHandler\TagRecord', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\AjaxHandler\CheckRequestIsValid' => + 'VuFind\AjaxHandler\AbstractIlsAndUserActionFactory', + 'VuFind\AjaxHandler\CommentRecord' => + 'VuFind\AjaxHandler\CommentRecordFactory', + 'VuFind\AjaxHandler\DeleteRecordComment' => + 'VuFind\AjaxHandler\DeleteRecordCommentFactory', + 'VuFind\AjaxHandler\GetACSuggestions' => + 'VuFind\AjaxHandler\GetACSuggestionsFactory', + 'VuFind\AjaxHandler\GetFacetData' => + 'VuFind\AjaxHandler\GetFacetDataFactory', + 'VuFind\AjaxHandler\GetIlsStatus' => + 'VuFind\AjaxHandler\GetIlsStatusFactory', + 'VuFind\AjaxHandler\GetItemStatuses' => + 'VuFind\AjaxHandler\GetItemStatusesFactory', + 'VuFind\AjaxHandler\GetLibraryPickupLocations' => + 'VuFind\AjaxHandler\AbstractIlsAndUserActionFactory', + 'VuFind\AjaxHandler\GetRecordCommentsAsHTML' => + 'VuFind\AjaxHandler\GetRecordCommentsAsHTMLFactory', + 'VuFind\AjaxHandler\GetRecordDetails' => + 'VuFind\AjaxHandler\GetRecordDetailsFactory', + 'VuFind\AjaxHandler\GetRecordTags' => + 'VuFind\AjaxHandler\GetRecordTagsFactory', + 'VuFind\AjaxHandler\GetRequestGroupPickupLocations' => + 'VuFind\AjaxHandler\AbstractIlsAndUserActionFactory', + 'VuFind\AjaxHandler\GetResolverLinks' => + 'VuFind\AjaxHandler\GetResolverLinksFactory', + 'VuFind\AjaxHandler\GetSaveStatuses' => + 'VuFind\AjaxHandler\GetSaveStatusesFactory', + 'VuFind\AjaxHandler\GetVisData' => 'VuFind\AjaxHandler\GetVisDataFactory', + 'VuFind\AjaxHandler\KeepAlive' => 'VuFind\AjaxHandler\KeepAliveFactory', + 'VuFind\AjaxHandler\Recommend' => 'VuFind\AjaxHandler\RecommendFactory', + 'VuFind\AjaxHandler\RelaisAvailability' => + 'VuFind\AjaxHandler\AbstractRelaisActionFactory', + 'VuFind\AjaxHandler\RelaisInfo' => + 'VuFind\AjaxHandler\AbstractRelaisActionFactory', + 'VuFind\AjaxHandler\RelaisOrder' => + 'VuFind\AjaxHandler\AbstractRelaisActionFactory', + 'VuFind\AjaxHandler\SystemStatus' => + 'VuFind\AjaxHandler\SystemStatusFactory', + 'VuFind\AjaxHandler\TagRecord' => 'VuFind\AjaxHandler\TagRecordFactory', + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\AjaxHandler\AjaxHandlerInterface'; + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/Recommend.php b/module/VuFind/src/VuFind/AjaxHandler/Recommend.php new file mode 100644 index 0000000000000000000000000000000000000000..df4105b89e0519ba7d360221f22861f678ae0449 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/Recommend.php @@ -0,0 +1,109 @@ +<?php +/** + * Load a recommendation module via AJAX. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Recommend\PluginManager as RecommendManager; +use VuFind\Search\Solr\Results; +use VuFind\Session\Settings as SessionSettings; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Stdlib\Parameters; +use Zend\View\Renderer\RendererInterface; + +/** + * Load a recommendation module via AJAX. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Recommend extends AbstractBase +{ + /** + * Recommendation plugin manager + * + * @var RecommendManager + */ + protected $pluginManager; + + /** + * Solr search results object + * + * @var Results + */ + protected $results; + + /** + * View renderer + * + * @var RendererInterface + */ + protected $renderer; + + /** + * Constructor + * + * @param SessionSettings $ss Session settings + * @param RecommendManager $pm Recommendation plugin manager + * @param Results $results Solr results object + * @param RendererInterface $renderer View renderer + */ + public function __construct(SessionSettings $ss, RecommendManager $pm, + Results $results, RendererInterface $renderer + ) { + $this->sessionSettings = $ss; + $this->pluginManager = $pm; + $this->results = $results; + $this->renderer = $renderer; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + // Process recommendations -- for now, we assume Solr-based search objects, + // since deferred recommendations work best for modules that don't care about + // the details of the search objects anyway: + $module = $this->pluginManager->get($params->fromQuery('mod')); + $module->setConfig($params->fromQuery('params')); + $paramsObj = $this->results->getParams(); + $module->init($paramsObj, new Parameters($params->fromQuery())); + $module->process($this->results); + + // Render recommendations: + $recommend = $this->renderer->plugin('recommend'); + return $this->formatResponse($recommend($module)); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php b/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b445a47cee8434a767042663f793a9f97a837118 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for Recommend AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Recommend AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RecommendFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Session\Settings'), + $container->get('VuFind\Recommend\PluginManager'), + $container->get('VuFind\Search\Results\PluginManager')->get('Solr'), + $container->get('ViewRenderer') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/RelaisAvailability.php b/module/VuFind/src/VuFind/AjaxHandler/RelaisAvailability.php new file mode 100644 index 0000000000000000000000000000000000000000..17299a7f714a481127e939c319a70467538f9a47 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/RelaisAvailability.php @@ -0,0 +1,75 @@ +<?php +/** + * Relais: Check item availability using a generic patron ID + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * Relais: Check item availability using a generic patron ID + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RelaisAvailability extends AbstractRelaisAction +{ + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $oclcNumber = $params->fromQuery('oclcNumber'); + + // Authenticate + $authorizationId = $this->relais->authenticatePatron(); + if ($authorizationId === null) { + return $this->formatResponse( + $this->translate('Failed'), self::STATUS_HTTP_FORBIDDEN + ); + } + + // Search + $responseText = $this->relais->search($oclcNumber, $authorizationId); + if (strpos($responseText, 'error') !== false + || strpos($responseText, 'ErrorMessage') !== false + || strpos($responseText, 'false') !== false + ) { + $result = 'no'; + } else { + $result = 'ok'; + } + return $this->formatResponse(compact('result')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/RelaisInfo.php b/module/VuFind/src/VuFind/AjaxHandler/RelaisInfo.php new file mode 100644 index 0000000000000000000000000000000000000000..c4ca1c5fa5c98d6d6bda8f2a4899f9caae566827 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/RelaisInfo.php @@ -0,0 +1,75 @@ +<?php +/** + * Relais: Check if logged-in patron can order an item. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * Relais: Check if logged-in patron can order an item. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RelaisInfo extends AbstractRelaisAction +{ + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $oclcNumber = $params->fromQuery('oclcNumber'); + $lin = $this->user['cat_username'] ?? null; + + // Authenticate + $authResponse = $this->relais->authenticatePatron($lin, true); + $authorizationId = $authResponse->AuthorizationId ?? null; + if ($authorizationId === null) { + return $this->formatResponse( + $this->translate('Failed'), self::STATUS_HTTP_FORBIDDEN + ); + } + + $allowLoan = $authResponse->AllowLoanAddRequest ?? false; + if ($allowLoan == false) { + return $this->formatResponse( + 'AllowLoan was false', self::STATUS_HTTP_ERROR + ); + } + + $result = $this->relais->search($oclcNumber, $authorizationId, $lin); + return $this->formatResponse(compact('result')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/RelaisOrder.php b/module/VuFind/src/VuFind/AjaxHandler/RelaisOrder.php new file mode 100644 index 0000000000000000000000000000000000000000..689d8edcf1d48d45cf18e59e5151652a03cf1baf --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/RelaisOrder.php @@ -0,0 +1,73 @@ +<?php +/** + * Relais: Order an item. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Zend\Mvc\Controller\Plugin\Params; + +/** + * Relais: Order an item. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RelaisOrder extends AbstractRelaisAction +{ + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + $this->disableSessionWrites(); // avoid session write timing bug + $oclcNumber = $params->fromQuery('oclcNumber'); + + $lin = $this->user['cat_username'] ?? null; + + // Authenticate + $authorizationId = $this->relais->authenticatePatron($lin); + if ($authorizationId === null) { + return $this->formatResponse( + $this->translate('Failed'), self::STATUS_HTTP_FORBIDDEN + ); + } + + // Place order + $result = $this->relais + ->placeRequest($oclcNumber, $authorizationId, $lin); + if (strpos($result, 'error') !== false) { + return $this->formatResponse($result, self::STATUS_HTTP_ERROR); + } + return $this->formatResponse(compact('result')); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/SystemStatus.php b/module/VuFind/src/VuFind/AjaxHandler/SystemStatus.php new file mode 100644 index 0000000000000000000000000000000000000000..771014dd86a95cf6d0133f06b86e37ff02ba96a7 --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/SystemStatus.php @@ -0,0 +1,141 @@ +<?php +/** + * "Keep Alive" AJAX handler + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Db\Table\Session; +use VuFind\Search\Results\PluginManager as ResultsManager; +use Zend\Config\Config; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\Session\SessionManager; + +/** + * "Keep Alive" AJAX handler + * + * This is responsible for keeping the session alive whenever called + * (via JavaScript) + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SystemStatus extends AbstractBase +{ + /** + * Session Manager + * + * @var SessionManager + */ + protected $sessionManager; + + /** + * Session database table + * + * @var Session + */ + protected $sessionTable; + + /** + * Results manager + * + * @var ResultsManager + */ + protected $resultsManager; + + /** + * Top-level VuFind configuration (config.ini) + * + * @var Config + */ + protected $config; + + /** + * Constructor + * + * @param SessionManager $sm Session manager + * @param ResultsManager $rm Results manager + * @param Config $config Top-level VuFind configuration (config.ini) + * @param Session $table Session database table + */ + public function __construct(SessionManager $sm, ResultsManager $rm, + Config $config, Session $table + ) { + $this->sessionManager = $sm; + $this->resultsManager = $rm; + $this->config = $config; + $this->sessionTable = $table; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function handleRequest(Params $params) + { + // Check system status + if (!empty($this->config->System->healthCheckFile) + && file_exists($this->config->System->healthCheckFile) + ) { + return $this->formatResponse( + 'Health check file exists', self::STATUS_HTTP_UNAVAILABLE + ); + } + + // Test search index + try { + $results = $this->resultsManager->get('Solr'); + $paramsObj = $results->getParams(); + $paramsObj->setQueryIDs(['healthcheck']); + $results->performAndProcessSearch(); + } catch (\Exception $e) { + return $this->formatResponse( + 'Search index error: ' . $e->getMessage(), self::STATUS_HTTP_ERROR + ); + } + + // Test database connection + try { + $this->sessionTable->getBySessionId('healthcheck', false); + } catch (\Exception $e) { + return $this->formatResponse( + 'Database error: ' . $e->getMessage(), self::STATUS_HTTP_ERROR + ); + } + + // This may be called frequently, don't leave sessions dangling + $this->sessionManager->destroy(); + + return $this->formatResponse(''); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php b/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d8f08e00f3155ab6880ed948dc7aed3c9bf281fe --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for SystemStatus AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for SystemStatus AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SystemStatusFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + return new $requestedName( + $container->get('Zend\Session\SessionManager'), + $container->get('VuFind\Search\Results\PluginManager'), + $container->get('VuFind\Config\PluginManager')->get('config'), + $tablePluginManager->get('VuFind\Db\Table\Session') + ); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/TagRecord.php b/module/VuFind/src/VuFind/AjaxHandler/TagRecord.php new file mode 100644 index 0000000000000000000000000000000000000000..d4a68dcb49a90cee2e9586e2a4fa8ce9e24c4ccd --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/TagRecord.php @@ -0,0 +1,113 @@ +<?php +/** + * AJAX handler to tag/untag a record. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use VuFind\Db\Row\User; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Record\Loader; +use VuFind\Tags; +use Zend\Mvc\Controller\Plugin\Params; + +/** + * AJAX handler to tag/untag a record. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TagRecord extends AbstractBase implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Record loader + * + * @var Loader + */ + protected $loader; + + /** + * Tag parser + * + * @var Tags + */ + protected $tagParser; + + /** + * Logged in user (or false) + * + * @var User|bool + */ + protected $user; + + /** + * Constructor + * + * @param Loader $loader Record loader + * @param Tags $parser Tag parser + * @param User|bool $user Logged in user (or false) + */ + public function __construct(Loader $loader, Tags $parser, $user) + { + $this->loader = $loader; + $this->tagParser = $parser; + $this->user = $user; + } + + /** + * Handle a request. + * + * @param Params $params Parameter helper from controller + * + * @return array [response data, HTTP status code] + */ + public function handleRequest(Params $params) + { + if (!$this->user) { + return $this->formatResponse( + $this->translate('You must be logged in first'), + self::STATUS_HTTP_NEED_AUTH + ); + } + + $id = $params->fromPost('id'); + $source = $params->fromPost('source', DEFAULT_SEARCH_BACKEND); + $tag = $params->fromPost('tag', ''); + + if (strlen($tag) > 0) { // don't add empty tags + $driver = $this->loader->load($id, $source); + ('false' === $params->fromPost('remove', 'false')) + ? $driver->addTags($this->user, $this->tagParser->parse($tag)) + : $driver->deleteTags($this->user, $this->tagParser->parse($tag)); + } + + return $this->formatResponse(''); + } +} diff --git a/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php b/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..97a1e947396bf39d7ad532ed490a56cf2a699d9a --- /dev/null +++ b/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for TagRecord AJAX handler. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\AjaxHandler; + +use Interop\Container\ContainerInterface; + +/** + * Factory for TagRecord AJAX handler. + * + * @category VuFind + * @package AJAX + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TagRecordFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Record\Loader'), + $container->get('VuFind\Tags'), + $container->get('VuFind\Auth\Manager')->isLoggedIn() + ); + } +} diff --git a/module/VuFind/src/VuFind/Auth/AbstractBase.php b/module/VuFind/src/VuFind/Auth/AbstractBase.php index 95c0d82ba00db2d7965313cb5d8920645c774cca..b4eb40ab255afc8abf5342de15bd3e69c1136f71 100644 --- a/module/VuFind/src/VuFind/Auth/AbstractBase.php +++ b/module/VuFind/src/VuFind/Auth/AbstractBase.php @@ -2,7 +2,7 @@ /** * Abstract authentication base class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Auth; -use VuFind\Db\Row\User, VuFind\Exception\Auth as AuthException; + +use VuFind\Db\Row\User; +use VuFind\Exception\Auth as AuthException; /** * Abstract authentication base class @@ -312,7 +314,7 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface, $policy['hint'] = $config->Authentication->password_hint; } else { $policy['hint'] = $this->getCannedPasswordPolicyHint( - isset($policy['pattern']) ? $policy['pattern'] : null + $policy['pattern'] ?? null ); } return $policy; diff --git a/module/VuFind/src/VuFind/Auth/AlmaDatabase.php b/module/VuFind/src/VuFind/Auth/AlmaDatabase.php new file mode 100644 index 0000000000000000000000000000000000000000..d4e17a253b0cd2a957a8102d6ffdc6d03513a983 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/AlmaDatabase.php @@ -0,0 +1,142 @@ +<?php +/** + * Alma Database authentication class + * + * PHP version 5 + * + * Copyright (C) AK Bibliothek Wien für Sozialwissenschaften 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Michael Birkner <michael.birkner@akwien.at> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki + */ +namespace VuFind\Auth; + +use VuFind\Exception\Auth as AuthException; + +/** + * Authentication class for Alma. The VuFind database and the Alma API are + * combined for authentication by this classe. + * + * @category VuFind + * @package Authentication + * @author Michael Birkner <michael.birkner@akwien.at> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki + */ +class AlmaDatabase extends Database +{ + /** + * ILS Authenticator + * + * @var \VuFind\Auth\ILSAuthenticator + */ + protected $authenticator; + + /** + * Catalog connection + * + * @var \VuFind\ILS\Connection + */ + protected $catalog = null; + + /** + * Alma driver + * + * @var \VuFind\ILS\Driver\Alma + */ + protected $almaDriver = null; + + /** + * Alma config + * + * @var array + */ + protected $almaConfig = null; + + /** + * Constructor + * + * @param \VuFind\ILS\Connection $connection The ILS connection + * @param \VuFind\Auth\ILSAuthenticator $authenticator The ILS authenticator + */ + public function __construct( + \VuFind\ILS\Connection $connection, + \VuFind\Auth\ILSAuthenticator $authenticator + ) { + $this->catalog = $connection; + $this->authenticator = $authenticator; + $this->almaDriver = $connection->getDriver(); + $this->almaConfig = $connection->getDriverConfig(); + } + + /** + * Create a new user account in Alma AND in the VuFind Database. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object containing + * new account details. + * + * @return NULL|\VuFind\Db\Row\User New user row. + */ + public function create($request) + { + // When in privacy mode, don't create an Alma account and delegate + // further code execution to the parent. + if ($this->getConfig()->Authentication->privacy) { + return parent::create($request); + } + + // User variable + $user = null; + + // Collect POST parameters from request + $params = $this->collectParamsFromRequest($request); + + // Validate username and password + $this->validateUsernameAndPassword($params); + + // Get the user table + $userTable = $this->getUserTable(); + + // Make sure parameters are correct + $this->validateParams($params, $userTable); + + // Create user account in Alma + $almaAnswer = $this->almaDriver->createAlmaUser($params); + + // Create user account in VuFind user table if Alma gave us an answer + if ($almaAnswer !== null) { + // If we got this far, we're ready to create the account: + $user = $this->createUserFromParams($params, $userTable); + + // Add the Alma primary ID as cat_id to the VuFind user table + $user->cat_id = $almaAnswer->primary_id ?? null; + + // Save the new user to the user table + $user->save(); + + // Save the credentials to cat_username and cat_password to bypass + // the ILS login screen from VuFind + $user->saveCredentials($params['username'], $params['password']); + } else { + throw new AuthException($this->translate('ils_account_create_error')); + } + + return $user; + } +} diff --git a/module/VuFind/src/VuFind/Auth/CAS.php b/module/VuFind/src/VuFind/Auth/CAS.php index f50f61bd565f895f500f1a639332f06900e23a31..7194eab6a1b2dd0ffb4f47643b356f36f0a1a08b 100644 --- a/module/VuFind/src/VuFind/Auth/CAS.php +++ b/module/VuFind/src/VuFind/Auth/CAS.php @@ -2,7 +2,7 @@ /** * CAS authentication module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; /** @@ -250,8 +251,7 @@ class CAS extends AbstractBase foreach ($cas as $key => $value) { if (preg_match("/userattribute_[0-9]{1,}/", $key)) { $valueKey = 'userattribute_value_' . substr($key, 14); - $sortedUserAttributes[$value] = isset($cas->$valueKey) - ? $cas->$valueKey : null; + $sortedUserAttributes[$value] = $cas->$valueKey ?? null; // Throw an exception if attributes are missing/empty. if (empty($sortedUserAttributes[$value])) { @@ -264,6 +264,7 @@ class CAS extends AbstractBase return $sortedUserAttributes; } + /** * Establishes phpCAS Configuration and Enables the phpCAS Client * diff --git a/module/VuFind/src/VuFind/Auth/ChoiceAuth.php b/module/VuFind/src/VuFind/Auth/ChoiceAuth.php index 7c37b91a081b86be452ed21c46dc8ae9f3dba7f5..f7aa7521e775453c8b8eb2574863a3bffc865274 100644 --- a/module/VuFind/src/VuFind/Auth/ChoiceAuth.php +++ b/module/VuFind/src/VuFind/Auth/ChoiceAuth.php @@ -2,7 +2,7 @@ /** * MultiAuth Authentication plugin * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki */ namespace VuFind\Auth; -use VuFind\Db\Row\User, VuFind\Exception\Auth as AuthException; + +use VuFind\Db\Row\User; +use VuFind\Exception\Auth as AuthException; use Zend\Http\PhpEnvironment\Request; /** diff --git a/module/VuFind/src/VuFind/Auth/Database.php b/module/VuFind/src/VuFind/Auth/Database.php index bf546b4f3785f543a89db462b55515c29badc768..68882265812c239517c157b58f628d1ccd1639b9 100644 --- a/module/VuFind/src/VuFind/Auth/Database.php +++ b/module/VuFind/src/VuFind/Auth/Database.php @@ -2,7 +2,7 @@ /** * Database authentication class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,7 +28,11 @@ * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki */ namespace VuFind\Auth; -use VuFind\Exception\Auth as AuthException, Zend\Crypt\Password\Bcrypt; + +use VuFind\Db\Table\User as UserTable; +use VuFind\Exception\Auth as AuthException; +use Zend\Crypt\Password\Bcrypt; +use Zend\Http\PhpEnvironment\Request; /** * Database authentication class @@ -60,8 +64,7 @@ class Database extends AbstractBase /** * Attempt to authenticate the current user. Throws exception if login fails. * - * @param \Zend\Http\PhpEnvironment\Request $request Request object containing - * account credentials. + * @param Request $request Request object containing account credentials. * * @throws AuthException * @return \VuFind\Db\Row\User Object representing logged-in user. @@ -100,66 +103,36 @@ class Database extends AbstractBase /** * Create a new user account from the request. * - * @param \Zend\Http\PhpEnvironment\Request $request Request object containing - * new account details. + * @param Request $request Request object containing new account details. * * @throws AuthException * @return \VuFind\Db\Row\User New user row. */ public function create($request) { - // Ensure that all expected parameters are populated to avoid notices - // in the code below. - $params = [ - 'firstname' => '', 'lastname' => '', 'username' => '', - 'password' => '', 'password2' => '', 'email' => '' - ]; - foreach ($params as $param => $default) { - $params[$param] = $request->getPost()->get($param, $default); - } + // Collect POST parameters from request + $params = $this->collectParamsFromRequest($request); - // Validate Input + // Validate username and password $this->validateUsernameAndPassword($params); - // Invalid Email Check - $validator = new \Zend\Validator\EmailAddress(); - if (!$validator->isValid($params['email'])) { - throw new AuthException('Email address is invalid'); - } - if (!$this->emailAllowed($params['email'])) { - throw new AuthException('authentication_error_creation_blocked'); - } + // Get the user table + $userTable = $this->getUserTable(); - // Make sure we have a unique username - $table = $this->getUserTable(); - if ($table->getByUsername($params['username'], false)) { - throw new AuthException('That username is already taken'); - } - // Make sure we have a unique email - if ($table->getByEmail($params['email'])) { - throw new AuthException('That email address is already used'); - } + // Make sure parameters are correct + $this->validateParams($params, $userTable); // If we got this far, we're ready to create the account: - $user = $table->createRowForUsername($params['username']); - $user->firstname = $params['firstname']; - $user->lastname = $params['lastname']; - $user->email = $params['email']; - if ($this->passwordHashingEnabled()) { - $bcrypt = new Bcrypt(); - $user->pass_hash = $bcrypt->create($params['password']); - } else { - $user->password = $params['password']; - } + $user = $this->createUserFromParams($params, $userTable); $user->save(); + return $user; } /** * Update a user's password from the request. * - * @param \Zend\Http\PhpEnvironment\Request $request Request object containing - * new account details. + * @param Request $request Request object containing new account details. * * @throws AuthException * @return \VuFind\Db\Row\User New user row. @@ -221,7 +194,7 @@ class Database extends AbstractBase * Check that the user's password matches the provided value. * * @param string $password Password to check. - * @param object $userRow The user row. We pass this instead of the password + * @param object $userRow The user row. We pass this instead of the password * because we may need to check different values depending on the password * hashing configuration. * @@ -322,4 +295,86 @@ class Database extends AbstractBase } return $policy; } + + /** + * Collect parameters from request and populate them. + * + * @param Request $request Request object containing new account details. + * + * @return string[] + */ + protected function collectParamsFromRequest($request) + { + // Ensure that all expected parameters are populated to avoid notices + // in the code below. + $params = [ + 'firstname' => '', 'lastname' => '', 'username' => '', + 'password' => '', 'password2' => '', 'email' => '' + ]; + foreach ($params as $param => $default) { + $params[$param] = $request->getPost()->get($param, $default); + } + + return $params; + } + + /** + * Validate parameters. + * + * @param string[] $params Parameters returned from collectParamsFromRequest() + * @param UserTable $table The VuFind user table + * + * @throws AuthException + * + * @return void + */ + protected function validateParams($params, $table) + { + // Invalid Email Check + $validator = new \Zend\Validator\EmailAddress(); + if (!$validator->isValid($params['email'])) { + throw new AuthException('Email address is invalid'); + } + + // Check if Email is on whitelist (if applicable) + if (!$this->emailAllowed($params['email'])) { + throw new AuthException('authentication_error_creation_blocked'); + } + + // Make sure we have a unique username + if ($table->getByUsername($params['username'], false)) { + throw new AuthException('That username is already taken'); + } + + // Make sure we have a unique email + if ($table->getByEmail($params['email'])) { + throw new AuthException('That email address is already used'); + } + } + + /** + * Create a user row object from given parametes. + * + * @param string[] $params Parameters returned from collectParamsFromRequest() + * @param UserTable $table The VuFind user table + * + * @return \VuFind\Db\Row\User A user row object + */ + protected function createUserFromParams($params, $table) + { + $user = $table->createRowForUsername($params['username']); + $user->firstname = $params['firstname']; + $user->lastname = $params['lastname']; + $user->email = $params['email']; + if ($this->passwordHashingEnabled()) { + $bcrypt = new Bcrypt(); + $user->pass_hash = $bcrypt->create($params['password']); + } else { + $user->password = $params['password']; + } + + return $user; + } } +?> + diff --git a/module/VuFind/src/VuFind/Auth/Facebook.php b/module/VuFind/src/VuFind/Auth/Facebook.php index bf58a9c3d61615b605f7f6a0b2a5bc3874727bd2..2ca46ee2410629d553ef70b464900018d3596e7e 100644 --- a/module/VuFind/src/VuFind/Auth/Facebook.php +++ b/module/VuFind/src/VuFind/Auth/Facebook.php @@ -2,7 +2,7 @@ /** * Facebook authentication module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; /** @@ -168,7 +169,7 @@ class Facebook extends AbstractBase implements $response = $this->httpService->get($requestUrl); $parts = explode('&', $response->getBody(), 2); $parts = explode('=', $parts[0], 2); - return isset($parts[1]) ? $parts[1] : null; + return $parts[1] ?? null; } /** diff --git a/module/VuFind/src/VuFind/Auth/Factory.php b/module/VuFind/src/VuFind/Auth/Factory.php index 28acc1ceb60ea6dabfc9db8044020ba317a7b712..e7c773b7ca76fb057a9d6bdbdf819961c8346003 100644 --- a/module/VuFind/src/VuFind/Auth/Factory.php +++ b/module/VuFind/src/VuFind/Auth/Factory.php @@ -2,7 +2,7 @@ /** * Factory for authentication services. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Auth; + use Zend\ServiceManager\ServiceManager; /** @@ -51,9 +52,11 @@ class Factory public static function getChoiceAuth(ServiceManager $sm) { $container = new \Zend\Session\Container( - 'ChoiceAuth', $sm->getServiceLocator()->get('VuFind\SessionManager') + 'ChoiceAuth', $sm->get('Zend\Session\SessionManager') ); - return new ChoiceAuth($container); + $auth = new ChoiceAuth($container); + $auth->setPluginManager($sm->get('VuFind\Auth\PluginManager')); + return $auth; } /** @@ -66,7 +69,7 @@ class Factory public static function getFacebook(ServiceManager $sm) { $container = new \Zend\Session\Container( - 'Facebook', $sm->getServiceLocator()->get('VuFind\SessionManager') + 'Facebook', $sm->get('Zend\Session\SessionManager') ); return new Facebook($container); } @@ -81,76 +84,23 @@ class Factory public static function getILS(ServiceManager $sm) { return new ILS( - $sm->getServiceLocator()->get('VuFind\ILSConnection'), - $sm->getServiceLocator()->get('VuFind\ILSAuthenticator') + $sm->get('VuFind\ILS\Connection'), + $sm->get('VuFind\Auth\ILSAuthenticator') ); } /** - * Construct the ILS authenticator. + * Construct the MultiAuth plugin. * * @param ServiceManager $sm Service manager. * - * @return ILSAuthenticator + * @return MultiAuth */ - public static function getILSAuthenticator(ServiceManager $sm) + public static function getMultiAuth(ServiceManager $sm) { - // Construct the ILS authenticator as a lazy loading value holder so that - // the object is not instantiated until it is called. This helps break a - // potential circular dependency with the MultiBackend driver as well as - // saving on initialization costs in cases where the authenticator is not - // actually utilized. - $callback = function (& $wrapped, $proxy) use ($sm) { - // Generate wrapped object: - $auth = $sm->get('VuFind\AuthManager'); - $catalog = $sm->get('VuFind\ILSConnection'); - $wrapped = new ILSAuthenticator($auth, $catalog); - - // Indicate that initialization is complete to avoid reinitialization: - $proxy->setProxyInitializer(null); - }; - $cfg = $sm->get('VuFind\ProxyConfig'); - $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($cfg); - return $factory->createProxy('VuFind\Auth\ILSAuthenticator', $callback); - } - - /** - * Construct the authentication manager. - * - * @param ServiceManager $sm Service manager. - * - * @return Manager - */ - public static function getManager(ServiceManager $sm) - { - // Set up configuration: - $config = $sm->get('VuFind\Config')->get('config'); - try { - // Check if the catalog wants to hide the login link, and override - // the configuration if necessary. - $catalog = $sm->get('VuFind\ILSConnection'); - if ($catalog->loginIsHidden()) { - $config = new \Zend\Config\Config($config->toArray(), true); - $config->Authentication->hideLogin = true; - $config->setReadOnly(); - } - } catch (\Exception $e) { - // Ignore exceptions; if the catalog is broken, throwing an exception - // here may interfere with UI rendering. If we ignore it now, it will - // still get handled appropriately later in processing. - error_log($e->getMessage()); - } - - // Load remaining dependencies: - $userTable = $sm->get('VuFind\DbTablePluginManager')->get('user'); - $sessionManager = $sm->get('VuFind\SessionManager'); - $pm = $sm->get('VuFind\AuthPluginManager'); - $cookies = $sm->get('VuFind\CookieManager'); - - // Build the object and make sure account credentials haven't expired: - $manager = new Manager($config, $userTable, $sessionManager, $pm, $cookies); - $manager->checkForExpiredCredentials(); - return $manager; + $auth = new MultiAuth(); + $auth->setPluginManager($sm->get('VuFind\Auth\PluginManager')); + return $auth; } /** @@ -163,8 +113,8 @@ class Factory public static function getMultiILS(ServiceManager $sm) { return new MultiILS( - $sm->getServiceLocator()->get('VuFind\ILSConnection'), - $sm->getServiceLocator()->get('VuFind\ILSAuthenticator') + $sm->get('VuFind\ILS\Connection'), + $sm->get('VuFind\Auth\ILSAuthenticator') ); } @@ -178,7 +128,22 @@ class Factory public static function getShibboleth(ServiceManager $sm) { return new Shibboleth( - $sm->getServiceLocator()->get('VuFind\SessionManager') + $sm->get('Zend\Session\SessionManager') + ); + } + + /** + * Construct the AlmaDatabase plugin. + * + * @param ServiceManager $sm Service manager. + * + * @return AlmaDatabase + */ + public static function getAlmaDatabase(ServiceManager $sm) + { + return new AlmaDatabase( + $sm->get('VuFind\ILS\Connection'), + $sm->get('VuFind\Auth\ILSAuthenticator') ); } } diff --git a/module/VuFind/src/VuFind/Auth/ILS.php b/module/VuFind/src/VuFind/Auth/ILS.php index 95671c9ce8d7ec20d83fcfe07af89575263e7341..c39b3aeb6624b5a4a00ac7452e3a4fca14341a61 100644 --- a/module/VuFind/src/VuFind/Auth/ILS.php +++ b/module/VuFind/src/VuFind/Auth/ILS.php @@ -2,7 +2,7 @@ /** * ILS authentication module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFind\Auth; -use VuFind\Exception\Auth as AuthException, - VuFind\Exception\ILS as ILSException; +use VuFind\Exception\Auth as AuthException; +use VuFind\Exception\ILS as ILSException; /** * ILS authentication module. @@ -243,13 +243,13 @@ class ILS extends AbstractBase // Update user information based on ILS data: $fields = ['firstname', 'lastname', 'email', 'major', 'college']; foreach ($fields as $field) { - $user->$field = isset($info[$field]) ? $info[$field] : ' '; + $user->$field = $info[$field] ?? ' '; } // Update the user in the database, then return it to the caller: $user->saveCredentials( - isset($info['cat_username']) ? $info['cat_username'] : ' ', - isset($info['cat_password']) ? $info['cat_password'] : ' ' + $info['cat_username'] ?? ' ', + $info['cat_password'] ?? ' ' ); return $user; diff --git a/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php b/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php index caedb859a5ef5d36cd91c5568c4614fd9214c238..60473282fd58789a1065214ebc5cbd761f341815 100644 --- a/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php +++ b/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php @@ -2,7 +2,7 @@ /** * Class for managing ILS-specific authentication. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Auth; + use VuFind\ILS\Connection as ILSConnection; /** diff --git a/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php b/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..68c7f1faa355b99c5fbede7cea2f9c64cb57cdd0 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php @@ -0,0 +1,82 @@ +<?php +/** + * ILS Authenticator factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ILS Authenticator factory. + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ILSAuthenticatorFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + // Construct the ILS authenticator as a lazy loading value holder so that + // the object is not instantiated until it is called. This helps break a + // potential circular dependency with the MultiBackend driver as well as + // saving on initialization costs in cases where the authenticator is not + // actually utilized. + $callback = function (& $wrapped, $proxy) use ($container, $requestedName) { + // Generate wrapped object: + $auth = $container->get('VuFind\Auth\Manager'); + $catalog = $container->get('VuFind\ILS\Connection'); + $wrapped = new $requestedName($auth, $catalog); + + // Indicate that initialization is complete to avoid reinitialization: + $proxy->setProxyInitializer(null); + }; + $cfg = $container->get('ProxyManager\Configuration'); + $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($cfg); + return $factory->createProxy($requestedName, $callback); + } +} diff --git a/module/VuFind/src/VuFind/Auth/InvalidArgumentException.php b/module/VuFind/src/VuFind/Auth/InvalidArgumentException.php index 7f72239bdc2869a2a4ba58256c64151b4d462bfa..814a17d60f8b98c7dab8714382a0a5ee5de9c950 100644 --- a/module/VuFind/src/VuFind/Auth/InvalidArgumentException.php +++ b/module/VuFind/src/VuFind/Auth/InvalidArgumentException.php @@ -2,7 +2,7 @@ /** * Invalid Authentication Argument Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Auth/LDAP.php b/module/VuFind/src/VuFind/Auth/LDAP.php index b1fdf5644cd56a0468f680a802b3c5e188293a37..5927eb61b37e0d2435719e159f5befa771951bc9 100644 --- a/module/VuFind/src/VuFind/Auth/LDAP.php +++ b/module/VuFind/src/VuFind/Auth/LDAP.php @@ -2,7 +2,7 @@ /** * LDAP authentication class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; /** @@ -291,7 +292,7 @@ class LDAP extends AbstractBase } else { $value = $value[0]; } - + if ($field != "cat_password") { $user->$field = ($value === null) ? '' : $value; } else { diff --git a/module/VuFind/src/VuFind/Auth/Manager.php b/module/VuFind/src/VuFind/Auth/Manager.php index 69c93ae22d8d21ffb0e4c21e060be94bbbd72758..67f64d0090ffc2cc40b0bc40d9f50b5745016c58 100644 --- a/module/VuFind/src/VuFind/Auth/Manager.php +++ b/module/VuFind/src/VuFind/Auth/Manager.php @@ -2,7 +2,7 @@ /** * Wrapper class for handling logged-in user in session. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,10 +26,14 @@ * @link https://vufind.org Main Page */ namespace VuFind\Auth; -use VuFind\Cookie\CookieManager, - VuFind\Db\Row\User as UserRow, VuFind\Db\Table\User as UserTable, - VuFind\Exception\Auth as AuthException, - Zend\Config\Config, Zend\Session\SessionManager, Zend\Validator\Csrf; + +use VuFind\Cookie\CookieManager; +use VuFind\Db\Row\User as UserRow; +use VuFind\Db\Table\User as UserTable; +use VuFind\Exception\Auth as AuthException; +use VuFind\Validator\Csrf; +use Zend\Config\Config; +use Zend\Session\SessionManager; /** * Wrapper class for handling logged-in user in session. @@ -120,10 +124,11 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface * @param SessionManager $sessionManager Session manager * @param PluginManager $pm Authentication plugin manager * @param CookieManager $cookieManager Cookie manager + * @param Csrf $csrf CSRF validator */ public function __construct(Config $config, UserTable $userTable, SessionManager $sessionManager, PluginManager $pm, - CookieManager $cookieManager + CookieManager $cookieManager, Csrf $csrf ) { // Store dependencies: $this->config = $config; @@ -131,19 +136,11 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface $this->sessionManager = $sessionManager; $this->pluginManager = $pm; $this->cookieManager = $cookieManager; + $this->csrf = $csrf; // Set up session: $this->session = new \Zend\Session\Container('Account', $sessionManager); - // Set up CSRF: - $this->csrf = new Csrf( - [ - 'session' => new \Zend\Session\Container('csrf', $sessionManager), - 'salt' => isset($this->config->Security->HMACkey) - ? $this->config->Security->HMACkey : 'VuFindCsrfSalt', - ] - ); - // Initialize active authentication setting (defaulting to Database // if no setting passed in): $method = isset($config->Authentication->method) @@ -426,7 +423,7 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface ->select(['id' => $this->session->userId]); $this->currentUser = count($results) < 1 ? false : $results->current(); - } else if (isset($this->session->userDetails)) { + } elseif (isset($this->session->userDetails)) { // privacy mode $results = $this->userTable->createRow(); $results->exchangeArray($this->session->userDetails); @@ -445,11 +442,15 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface * If no CSRF token currently exists, or should be regenerated, generates one. * * @param bool $regenerate Should we regenerate token? (default false) + * @param int $maxTokens The maximum number of tokens to store in the + * session. * * @return string */ - public function getCsrfHash($regenerate = false) + public function getCsrfHash($regenerate = false, $maxTokens = 5) { + // Reset token store if we've overflowed the limit: + $this->csrf->trimTokenList($maxTokens); return $this->csrf->getHash($regenerate); } @@ -518,6 +519,7 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface public function create($request) { $user = $this->getAuth()->create($request); + $this->updateUser($user); $this->updateSession($user); return $user; } @@ -555,11 +557,14 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface $this->getAuth()->preLoginCheck($request); // Validate CSRF for form-based authentication methods: - if (!$this->getAuth()->getSessionInitiator(null) - && !$this->csrf->isValid($request->getPost()->get('csrf')) - ) { - $this->getAuth()->resetState(); - throw new AuthException('authentication_error_technical'); + if (!$this->getAuth()->getSessionInitiator(null)) { + if (!$this->csrf->isValid($request->getPost()->get('csrf'))) { + $this->getAuth()->resetState(); + throw new AuthException('authentication_error_technical'); + } else { + // After successful token verification, clear list to shrink session: + $this->csrf->trimTokenList(0); + } } // Perform authentication: @@ -581,6 +586,9 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface throw new AuthException('authentication_error_technical'); } + // Update user object + $this->updateUser($user); + // Store the user in the session and send it back to the caller: $this->updateSession($user); return $user; @@ -614,6 +622,7 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface ); } } + /** * Validate the credentials in the provided request, but do not change the state * of the current logged-in user. Return true for valid credentials, false @@ -629,4 +638,23 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface { return $this->getAuth()->validateCredentials($request); } + + /** + * Update common user attributes on login + * + * @param \VuFind\Db\Row\User $user User object + * + * @return void + */ + protected function updateUser($user) + { + if ($this->getAuth() instanceof ChoiceAuth) { + $method = $this->getAuth()->getSelectedAuthOption(); + } else { + $method = $this->activeAuth; + } + $user->auth_method = strtolower($method); + $user->last_login = date('Y-m-d H:i:s'); + $user->save(); + } } diff --git a/module/VuFind/src/VuFind/Auth/ManagerFactory.php b/module/VuFind/src/VuFind/Auth/ManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..9b1436cb86e60f086a3cdf162faa24740e1d6f2f --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/ManagerFactory.php @@ -0,0 +1,96 @@ +<?php +/** + * Authentication Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Authentication Manager factory. + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + // Set up configuration: + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + try { + // Check if the catalog wants to hide the login link, and override + // the configuration if necessary. + $catalog = $container->get('VuFind\ILS\Connection'); + if ($catalog->loginIsHidden()) { + $config = new \Zend\Config\Config($config->toArray(), true); + $config->Authentication->hideLogin = true; + $config->setReadOnly(); + } + } catch (\Exception $e) { + // Ignore exceptions; if the catalog is broken, throwing an exception + // here may interfere with UI rendering. If we ignore it now, it will + // still get handled appropriately later in processing. + error_log($e->getMessage()); + } + + // Load remaining dependencies: + $userTable = $container->get('VuFind\Db\Table\PluginManager')->get('user'); + $sessionManager = $container->get('Zend\Session\SessionManager'); + $pm = $container->get('VuFind\Auth\PluginManager'); + $cookies = $container->get('VuFind\Cookie\CookieManager'); + $csrf = $container->get('VuFind\Validator\Csrf'); + + // Build the object and make sure account credentials haven't expired: + $manager = new $requestedName( + $config, $userTable, $sessionManager, $pm, $cookies, $csrf + ); + $manager->checkForExpiredCredentials(); + return $manager; + } +} diff --git a/module/VuFind/src/VuFind/Auth/MultiAuth.php b/module/VuFind/src/VuFind/Auth/MultiAuth.php index b643e3240d7a56269174c5b5e0269edb457fccd7..3d4eb19cc4cd0892d3dcaa662e4bd53f6ba79044 100644 --- a/module/VuFind/src/VuFind/Auth/MultiAuth.php +++ b/module/VuFind/src/VuFind/Auth/MultiAuth.php @@ -2,7 +2,7 @@ /** * MultiAuth Authentication plugin * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; /** diff --git a/module/VuFind/src/VuFind/Auth/MultiILS.php b/module/VuFind/src/VuFind/Auth/MultiILS.php index 8084e63b39e3dc2dd7eacac10c4e7fc217e21ef4..e4cd1822bfc4937051f5062311ccdbbec27d02ca 100644 --- a/module/VuFind/src/VuFind/Auth/MultiILS.php +++ b/module/VuFind/src/VuFind/Auth/MultiILS.php @@ -2,7 +2,7 @@ /** * Multiple ILS authentication module that works with MultiBackend driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2013. @@ -29,6 +29,7 @@ * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; use VuFind\ILS\Driver\MultiBackend; diff --git a/module/VuFind/src/VuFind/Auth/PluginFactory.php b/module/VuFind/src/VuFind/Auth/PluginFactory.php index 602c66e8c9b6d642d577d2f7da395d9241a46392..15513c98374a35f7c6efd5250b6c3a9bd84a05b9 100644 --- a/module/VuFind/src/VuFind/Auth/PluginFactory.php +++ b/module/VuFind/src/VuFind/Auth/PluginFactory.php @@ -2,7 +2,7 @@ /** * Auth handler plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Auth/PluginManager.php b/module/VuFind/src/VuFind/Auth/PluginManager.php index d4a4cc01d9b4d5b212cc386d752e1a89ab3a3bf8..f2ba6ecc9ebae9959623d3fa096215181b7e15c0 100644 --- a/module/VuFind/src/VuFind/Auth/PluginManager.php +++ b/module/VuFind/src/VuFind/Auth/PluginManager.php @@ -2,7 +2,7 @@ /** * Auth handler plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,63 @@ namespace VuFind\Auth; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'almadatabase' => 'VuFind\Auth\AlmaDatabase', + 'cas' => 'VuFind\Auth\CAS', + 'choiceauth' => 'VuFind\Auth\ChoiceAuth', + 'database' => 'VuFind\Auth\Database', + 'facebook' => 'VuFind\Auth\Facebook', + 'ils' => 'VuFind\Auth\ILS', + 'ldap' => 'VuFind\Auth\LDAP', + 'multiauth' => 'VuFind\Auth\MultiAuth', + 'multiils' => 'VuFind\Auth\MultiILS', + 'shibboleth' => 'VuFind\Auth\Shibboleth', + 'sip2' => 'VuFind\Auth\SIP2', + // for legacy 1.x compatibility + 'db' => 'VuFind\Auth\Database', + 'sip' => 'VuFind\Auth\SIP2', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Auth\AlmaDatabase' => 'VuFind\Auth\Factory::getAlmaDatabase', + 'VuFind\Auth\CAS' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Auth\ChoiceAuth' => 'VuFind\Auth\Factory::getChoiceAuth', + 'VuFind\Auth\Database' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Auth\Facebook' => 'VuFind\Auth\Factory::getFacebook', + 'VuFind\Auth\ILS' => 'VuFind\Auth\Factory::getILS', + 'VuFind\Auth\LDAP' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Auth\MultiAuth' => 'VuFind\Auth\Factory::getMultiAuth', + 'VuFind\Auth\MultiILS' => 'VuFind\Auth\Factory::getMultiILS', + 'VuFind\Auth\Shibboleth' => 'VuFind\Auth\Factory::getShibboleth', + 'VuFind\Auth\SIP2' => 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Auth\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Auth/SIP2.php b/module/VuFind/src/VuFind/Auth/SIP2.php index 542fce8a65ebe76ba1ac0fd43606faa328da50bb..ec6fd0b6bfcffb809c557b4713607304723fe2ee 100644 --- a/module/VuFind/src/VuFind/Auth/SIP2.php +++ b/module/VuFind/src/VuFind/Auth/SIP2.php @@ -2,7 +2,7 @@ /** * SIP2 authentication module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; /** diff --git a/module/VuFind/src/VuFind/Auth/Shibboleth.php b/module/VuFind/src/VuFind/Auth/Shibboleth.php index e9e7a3bb8cff5dd01c30873d5422dbe800ae50d9..2e490709de48d59fe127d9b6a25e226ebed556a2 100644 --- a/module/VuFind/src/VuFind/Auth/Shibboleth.php +++ b/module/VuFind/src/VuFind/Auth/Shibboleth.php @@ -2,7 +2,7 @@ /** * Shibboleth authentication module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * Copyright (C) The National Library of Finland 2016. @@ -31,6 +31,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Auth; + use VuFind\Exception\Auth as AuthException; /** @@ -204,13 +205,11 @@ class Shibboleth extends AbstractBase $shibTarget = $target; } $append = (strpos($shibTarget, '?') !== false) ? '&' : '?'; + // Adding the auth_method parameter makes it possible to handle logins when + // using an auth method that proxies others. $sessionInitiator = $config->Shibboleth->login . '?target=' . urlencode($shibTarget) . urlencode($append . 'auth_method=Shibboleth'); - // makes it possible to - // handle logins when using - // an auth method that - // proxies others if (isset($config->Shibboleth->provider_id)) { $sessionInitiator = $sessionInitiator . '&entityID=' . @@ -281,8 +280,7 @@ class Shibboleth extends AbstractBase foreach ($shib as $key => $value) { if (preg_match("/userattribute_[0-9]{1,}/", $key)) { $valueKey = 'userattribute_value_' . substr($key, 14); - $sortedUserAttributes[$value] = isset($shib->$valueKey) - ? $shib->$valueKey : null; + $sortedUserAttributes[$value] = $shib->$valueKey ?? null; // Throw an exception if attributes are missing/empty. if (empty($sortedUserAttributes[$value])) { diff --git a/module/VuFind/src/VuFind/Autocomplete/AutocompleteInterface.php b/module/VuFind/src/VuFind/Autocomplete/AutocompleteInterface.php index 247a2108bc3bd273d17753440f5201650c7af3ed..4e2a84044c490bde9d4539cb454461ad646b305f 100644 --- a/module/VuFind/src/VuFind/Autocomplete/AutocompleteInterface.php +++ b/module/VuFind/src/VuFind/Autocomplete/AutocompleteInterface.php @@ -2,7 +2,7 @@ /** * Autocomplete Plug-In Interface * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/Eds.php b/module/VuFind/src/VuFind/Autocomplete/Eds.php new file mode 100644 index 0000000000000000000000000000000000000000..2865685c2c5c43bae2f3207a6458e4f548a8d21e --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/Eds.php @@ -0,0 +1,109 @@ +<?php +/** + * EDS Autocomplete Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <challber@villanova.edu> + * @author Jochen Lienhard <jochen.lienhard@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +namespace VuFind\Autocomplete; + +/** + * EDS Autocomplete Module + * + * This class provides popular terms provided by EDS. + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <jochen.lienhard@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +class Eds implements AutocompleteInterface +{ + /** + * Eds domain + * + * @var string + */ + protected $domain = 'rawqueries'; + + /** + * Search object family to use + * + * @var string + */ + protected $searchClassId = 'EDS'; + + /** + * Results plugin manager + * + * @var \VuFindSearch\Backend\EDS\Backend + */ + protected $backend; + + /** + * Constructor + * + * @param \VuFindSearch\Backend\EDS\Backend $backend Results plugin manager + */ + public function __construct(\VuFindSearch\Backend\EDS\Backend $backend) + { + $this->backend = $backend; + } + + /** + * This method returns an array of strings matching the user's query for + * display in the autocomplete box. + * + * @param string $query The user query + * + * @return array The suggestions for the provided query + */ + public function getSuggestions($query) + { + try { + // Perform the autocomplete search: + $results = $this->backend->autocomplete($query, $this->domain); + } catch (\Exception $e) { + // Ignore errors -- just return empty results if we must. + } + return is_array($results ?? null) ? array_unique($results) : []; + } + + /** + * Set parameters that affect the behavior of the autocomplete handler. + * These values normally come from the EDS configuration file. + * + * @param string $params Parameters to set + * + * @return void + */ + public function setConfig($params) + { + // Only change the value if it is not empty: + $this->domain = !empty($params) ? $params : $this->domain; + } +} diff --git a/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php b/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1dbe0ef91e05212fe46217ee73b17e19d7b72dbc --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for EDS-driven autocomplete plugins. Works for \VuFind\Autocomplete\Eds + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <jochen.lienhard@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Autocomplete; + +use Interop\Container\ContainerInterface; + +/** + * Factory for EDS-driven autocomplete plugins. Works for \VuFind\Autocomplete\Eds + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <jochen.lienhard@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class EdsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + return new $requestedName( + $container->get('VuFind\Search\BackendManager')->get('EDS') + ); + } +} diff --git a/module/VuFind/src/VuFind/Autocomplete/Factory.php b/module/VuFind/src/VuFind/Autocomplete/Factory.php deleted file mode 100644 index 0d73fc2ee4b15da2bdfbd0baabb42ed5a666e445..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Autocomplete/Factory.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Factory for autocomplete plugins. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Autocomplete - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Autocomplete; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for autocomplete plugins. - * - * @category VuFind - * @package Autocomplete - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the Solr plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Solr - */ - public static function getSolr(ServiceManager $sm) - { - return new Solr( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') - ); - } - - /** - * Construct the SolrAuth plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrAuth - */ - public static function getSolrAuth(ServiceManager $sm) - { - return new SolrAuth( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') - ); - } - - /** - * Construct the SolrCN (call number) plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrCN - */ - public static function getSolrCN(ServiceManager $sm) - { - return new SolrCN( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') - ); - } - - /** - * Construct the SolrReserves plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrReserves - */ - public static function getSolrReserves(ServiceManager $sm) - { - return new SolrReserves( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') - ); - } -} diff --git a/module/VuFind/src/VuFind/Autocomplete/None.php b/module/VuFind/src/VuFind/Autocomplete/None.php index 1be913e29597b591cb147f82be820b5905db7dc0..aaecff85528b8646c0074503daf06747489e1523 100644 --- a/module/VuFind/src/VuFind/Autocomplete/None.php +++ b/module/VuFind/src/VuFind/Autocomplete/None.php @@ -2,7 +2,7 @@ /** * No Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/OCLCIdentities.php b/module/VuFind/src/VuFind/Autocomplete/OCLCIdentities.php index 36a8c5ee5e14685f5dd437ced52a192a6b8077de..9cb6364d5fe37c8dbda94cabd0d0f73ecbc28b35 100644 --- a/module/VuFind/src/VuFind/Autocomplete/OCLCIdentities.php +++ b/module/VuFind/src/VuFind/Autocomplete/OCLCIdentities.php @@ -2,7 +2,7 @@ /** * OCLC Identities Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/PluginFactory.php b/module/VuFind/src/VuFind/Autocomplete/PluginFactory.php index 1c0a1c5a030f0a4b33bb82d2b612d1cda2b54679..d01d450129be8b6d59f5d2d04769577025c0c1b2 100644 --- a/module/VuFind/src/VuFind/Autocomplete/PluginFactory.php +++ b/module/VuFind/src/VuFind/Autocomplete/PluginFactory.php @@ -2,7 +2,7 @@ /** * Autocomplete handler plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/PluginManager.php b/module/VuFind/src/VuFind/Autocomplete/PluginManager.php index ceffd0844121e342fc6ef92e290483e23887f452..748c4ba3d56051717ca3ae3d0a99e77f0ecb275e 100644 --- a/module/VuFind/src/VuFind/Autocomplete/PluginManager.php +++ b/module/VuFind/src/VuFind/Autocomplete/PluginManager.php @@ -2,7 +2,7 @@ /** * Autocomplete handler plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -39,76 +39,74 @@ namespace VuFind\Autocomplete; class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { /** - * Return the name of the base class or interface that plug-ins must conform - * to. + * Default plugin aliases. * - * @return string + * @var array */ - protected function getExpectedInterface() - { - return 'VuFind\Autocomplete\AutocompleteInterface'; - } + protected $aliases = [ + 'none' => 'VuFind\Autocomplete\None', + 'eds' => 'VuFind\Autocomplete\Eds', + 'oclcidentities' => 'VuFind\Autocomplete\OCLCIdentities', + 'search2' => 'VuFind\Autocomplete\Search2', + 'search2cn' => 'VuFind\Autocomplete\Search2CN', + 'solr' => 'VuFind\Autocomplete\Solr', + 'solrauth' => 'VuFind\Autocomplete\SolrAuth', + 'solrcn' => 'VuFind\Autocomplete\SolrCN', + 'solrreserves' => 'VuFind\Autocomplete\SolrReserves', + 'tag' => 'VuFind\Autocomplete\Tag', + // for legacy 1.x compatibility + 'noautocomplete' => 'None', + 'oclcidentitiesautocomplete' => 'OCLCIdentities', + 'solrautocomplete' => 'Solr', + 'solrauthautocomplete' => 'SolrAuth', + 'solrcnautocomplete' => 'SolrCN', + 'solrreservesautocomplete' => 'SolrReserves', + 'tagautocomplete' => 'Tag', + ]; /** - * This returns an array of suggestions based on current request parameters. - * This logic is present in the factory class so that it can be easily shared - * by multiple AJAX handlers. + * Default plugin factories. * - * @param \Zend\Stdlib\Parameters $request The user request - * @param string $typeParam Request parameter containing search - * type - * @param string $queryParam Request parameter containing query - * string - * - * @return array + * @var array */ - public function getSuggestions($request, $typeParam = 'type', $queryParam = 'q') - { - // Process incoming parameters: - $type = $request->get($typeParam, ''); - $query = $request->get($queryParam, ''); - $searcher = $request->get('searcher', 'Solr'); - $hiddenFilters = $request->get('hiddenFilters', []); - - // If we're using a combined search box, we need to override the searcher - // and type settings. - if (substr($type, 0, 7) == 'VuFind:') { - list(, $tmp) = explode(':', $type, 2); - list($searcher, $type) = explode('|', $tmp, 2); - } - - // get Autocomplete_Type config - $options = $this->getServiceLocator() - ->get('VuFind\SearchOptionsPluginManager')->get($searcher); - $config = $this->getServiceLocator()->get('VuFind\Config') - ->get($options->getSearchIni()); - $types = isset($config->Autocomplete_Types) ? - $config->Autocomplete_Types->toArray() : []; + protected $factories = [ + 'VuFind\Autocomplete\None' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Autocomplete\Eds' => 'VuFind\Autocomplete\EdsFactory', + 'VuFind\Autocomplete\OCLCIdentities' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Autocomplete\Search2' => 'VuFind\Autocomplete\SolrFactory', + 'VuFind\Autocomplete\Search2CN' => 'VuFind\Autocomplete\SolrFactory', + 'VuFind\Autocomplete\Solr' => 'VuFind\Autocomplete\SolrFactory', + 'VuFind\Autocomplete\SolrAuth' => 'VuFind\Autocomplete\SolrFactory', + 'VuFind\Autocomplete\SolrCN' => 'VuFind\Autocomplete\SolrFactory', + 'VuFind\Autocomplete\SolrReserves' => 'VuFind\Autocomplete\SolrFactory', + 'VuFind\Autocomplete\Tag' => 'Zend\ServiceManager\Factory\InvokableFactory', + ]; - // Figure out which handler to use: - if (!empty($type) && isset($types[$type])) { - $module = $types[$type]; - } else if (isset($config->Autocomplete->default_handler)) { - $module = $config->Autocomplete->default_handler; - } else { - $module = false; - } - - // Get suggestions: - if ($module) { - if (strpos($module, ':') === false) { - $module .= ':'; // force colon to avoid warning in explode below - } - list($name, $params) = explode(':', $module, 2); - $handler = $this->get($name); - $handler->setConfig($params); - } - - if (is_callable([$handler, 'addFilters'])) { - $handler->addFilters($hiddenFilters); - } + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Autocomplete\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } - return (isset($handler) && is_object($handler)) - ? array_values($handler->getSuggestions($query)) : []; + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\Autocomplete\AutocompleteInterface'; } } diff --git a/module/VuFind/src/VuFind/Autocomplete/Search2.php b/module/VuFind/src/VuFind/Autocomplete/Search2.php new file mode 100644 index 0000000000000000000000000000000000000000..cdad6dbe5f95e899469cab34a5aa3b23d1bde3a6 --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/Search2.php @@ -0,0 +1,50 @@ +<?php +/** + * Search2 Autocomplete Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +namespace VuFind\Autocomplete; + +/** + * Search2 Autocomplete Module + * + * This class provides suggestions by using the secondary Solr index. + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +class Search2 extends Solr +{ + /** + * Search object family to use + * + * @var string + */ + protected $searchClassId = 'Search2'; +} diff --git a/module/VuFind/src/VuFind/Autocomplete/Search2CN.php b/module/VuFind/src/VuFind/Autocomplete/Search2CN.php new file mode 100644 index 0000000000000000000000000000000000000000..2c3b60fd0664e62c865d4c313cb910508fb477bc --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/Search2CN.php @@ -0,0 +1,48 @@ +<?php +/** + * Search2 Call Number Autocomplete Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +namespace VuFind\Autocomplete; + +/** + * Search2 Call Number Autocomplete Module + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +class Search2CN extends SolrCN +{ + /** + * Search object family to use + * + * @var string + */ + protected $searchClassId = 'Search2'; +} diff --git a/module/VuFind/src/VuFind/Autocomplete/Solr.php b/module/VuFind/src/VuFind/Autocomplete/Solr.php index 402fde785dd431c009ce57813cd3e20eb1b30f7d..577047797f8adcfc7cb3de60df04b392e3b46a12 100644 --- a/module/VuFind/src/VuFind/Autocomplete/Solr.php +++ b/module/VuFind/src/VuFind/Autocomplete/Solr.php @@ -2,7 +2,7 @@ /** * Solr Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/SolrAuth.php b/module/VuFind/src/VuFind/Autocomplete/SolrAuth.php index 9d7e86a1be1d2b58cd420c66fa4aa7e3ec7f9eb6..ee09e689ba2a3733e39ad87117bfc137fe9ba89b 100644 --- a/module/VuFind/src/VuFind/Autocomplete/SolrAuth.php +++ b/module/VuFind/src/VuFind/Autocomplete/SolrAuth.php @@ -2,7 +2,7 @@ /** * Solr Authority Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/SolrCN.php b/module/VuFind/src/VuFind/Autocomplete/SolrCN.php index 580337dc83e026d008290e41ea0f197c4b02be1d..1fcb1ea3a812ec3767c748975a37d4dbe40187ba 100644 --- a/module/VuFind/src/VuFind/Autocomplete/SolrCN.php +++ b/module/VuFind/src/VuFind/Autocomplete/SolrCN.php @@ -2,7 +2,7 @@ /** * Solr Call Number Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php b/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..cb6d44c1b762d45ca788cde06b8d7a2d9f58e677 --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for Solr-driven autocomplete plugins. Works for \VuFind\Autocomplete\Solr + * and all of its subclasses. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Autocomplete; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Solr-driven autocomplete plugins. Works for \VuFind\Autocomplete\Solr + * and all of its subclasses. + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SolrFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + return new $requestedName( + $container->get('VuFind\Search\Results\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/Autocomplete/SolrReserves.php b/module/VuFind/src/VuFind/Autocomplete/SolrReserves.php index b047c11b2d33fc0e232ecc6fb64bdc896d933dd2..863d5c49a8868a2733f9878d17eedf2d13e41b69 100644 --- a/module/VuFind/src/VuFind/Autocomplete/SolrReserves.php +++ b/module/VuFind/src/VuFind/Autocomplete/SolrReserves.php @@ -2,7 +2,7 @@ /** * Solr Reserves Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Autocomplete/Suggester.php b/module/VuFind/src/VuFind/Autocomplete/Suggester.php new file mode 100644 index 0000000000000000000000000000000000000000..eb3ea0dbff2f8c23b6eb0223d999e145269ae5c2 --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/Suggester.php @@ -0,0 +1,147 @@ +<?php +/** + * Autocomplete handler plugin manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +namespace VuFind\Autocomplete; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\Search\Options\PluginManager as OptionsManager; + +/** + * Autocomplete handler plugin manager + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:autosuggesters Wiki + */ +class Suggester +{ + /** + * Autocomplete plugin manager. + * + * @var PluginManager + */ + protected $pluginManager = null; + + /** + * Search options plugin manager. + * + * @var OptionsManager + */ + protected $optionsManager = null; + + /** + * Configuration manager. + * + * @var ConfigManager + */ + protected $configManager = null; + + /** + * Constructor + * + * @param PluginManager $pm Autocomplete plugin manager + * @param ConfigManager $cm Config manager + * @param OptionsManager $om Options manager + */ + public function __construct(PluginManager $pm, ConfigManager $cm, + OptionsManager $om + ) { + $this->pluginManager = $pm; + $this->configManager = $cm; + $this->optionsManager = $om; + } + + /** + * This returns an array of suggestions based on current request parameters. + * This logic is present in the factory class so that it can be easily shared + * by multiple AJAX handlers. + * + * @param \Zend\Stdlib\Parameters $request The user request + * @param string $typeParam Request parameter containing search + * type + * @param string $queryParam Request parameter containing query + * string + * + * @return array + */ + public function getSuggestions($request, $typeParam = 'type', $queryParam = 'q') + { + // Process incoming parameters: + $type = $request->get($typeParam, ''); + $query = $request->get($queryParam, ''); + $searcher = $request->get('searcher', 'Solr'); + $hiddenFilters = $request->get('hiddenFilters', []); + + // If we're using a combined search box, we need to override the searcher + // and type settings. + if (substr($type, 0, 7) == 'VuFind:') { + list(, $tmp) = explode(':', $type, 2); + list($searcher, $type) = explode('|', $tmp, 2); + } + + // get Autocomplete_Type config + $options = $this->optionsManager->get($searcher); + $config = $this->configManager->get($options->getSearchIni()); + $types = isset($config->Autocomplete_Types) ? + $config->Autocomplete_Types->toArray() : []; + + // Figure out which handler to use: + if (!empty($type) && isset($types[$type])) { + $module = $types[$type]; + } elseif (isset($config->Autocomplete->default_handler)) { + $module = $config->Autocomplete->default_handler; + } else { + $module = false; + } + + // Get suggestions: + if ($module) { + if (strpos($module, ':') === false) { + $module .= ':'; // force colon to avoid warning in explode below + } + list($name, $params) = explode(':', $module, 2); + $handler = $this->pluginManager->get($name); + $handler->setConfig($params); + } else { + $handler = null; + } + + if (is_callable([$handler, 'addFilters'])) { + $handler->addFilters($hiddenFilters); + } + + // if the handler needs the complete request, pass it on + if (is_callable([$handler, 'setRequest'])) { + $handler->setRequest($request); + } + + return is_object($handler) + ? array_values($handler->getSuggestions($query)) : []; + } +} diff --git a/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php b/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..98e85ed0c852d97a3b07c998d2cc4b30f901e88f --- /dev/null +++ b/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for autocomplete suggester. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Autocomplete; + +use Interop\Container\ContainerInterface; + +/** + * Factory for autocomplete suggester. + * + * @category VuFind + * @package Autocomplete + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SuggesterFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + return new $requestedName( + $container->get('VuFind\Autocomplete\PluginManager'), + $container->get('VuFind\Config\PluginManager'), + $container->get('VuFind\Search\Options\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/Autocomplete/Tag.php b/module/VuFind/src/VuFind/Autocomplete/Tag.php index fde1921715b5f02f907cb0de1e9194f8b5c95091..2f96e4103ad81a05b94840599716a52c9cf31f5f 100644 --- a/module/VuFind/src/VuFind/Autocomplete/Tag.php +++ b/module/VuFind/src/VuFind/Autocomplete/Tag.php @@ -2,7 +2,7 @@ /** * Tag Autocomplete Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Bootstrapper.php b/module/VuFind/src/VuFind/Bootstrapper.php index 23f49552046c6721d328c7b4460901ce2514f807..32b15d513ffaedd5aca8b675a4d8cc4ef30ad124 100644 --- a/module/VuFind/src/VuFind/Bootstrapper.php +++ b/module/VuFind/src/VuFind/Bootstrapper.php @@ -2,7 +2,7 @@ /** * VuFind Bootstrapper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,10 @@ * @link https://vufind.org Main Site */ namespace VuFind; -use Zend\Console\Console, Zend\Mvc\MvcEvent, Zend\Mvc\Router\Http\RouteMatch; + +use Zend\Console\Console; +use Zend\Mvc\MvcEvent; +use Zend\Router\Http\RouteMatch; /** * VuFind Bootstrapper @@ -96,8 +99,8 @@ class Bootstrapper { // Create the configuration manager: $app = $this->event->getApplication(); - $serviceManager = $app->getServiceManager(); - $this->config = $serviceManager->get('VuFind\Config')->get('config'); + $sm = $app->getServiceManager(); + $this->config = $sm->get('VuFind\Config\PluginManager')->get('config'); } /** @@ -118,7 +121,7 @@ class Bootstrapper if ($debugOverride) { $auth = $sm->get('ZfcRbac\Service\AuthorizationService'); if ($auth->isGranted('access.DebugMode')) { - $logger = $sm->get('VuFind\Logger'); + $logger = $sm->get('VuFind\Log\Logger'); $logger->addDebugWriter($debugOverride); } } @@ -176,17 +179,20 @@ class Bootstrapper { $callback = function ($event) { $serviceManager = $event->getApplication()->getServiceManager(); - $viewModel = $serviceManager->get('viewmanager')->getViewModel(); - - // Grab the template name from the first child -- we can use this to - // figure out the current template context. - $children = $viewModel->getChildren(); - if (!empty($children)) { - $parts = explode('/', $children[0]->getTemplate()); - $viewModel->setVariable('templateDir', $parts[0]); - $viewModel->setVariable( - 'templateName', isset($parts[1]) ? $parts[1] : null - ); + if (!Console::isConsole()) { + $viewModel = $serviceManager->get('ViewManager')->getViewModel(); + + // Grab the template name from the first child -- we can use this to + // figure out the current template context. + $children = $viewModel->getChildren(); + if (!empty($children)) { + $parts = explode('/', $children[0]->getTemplate()); + $viewModel->setVariable('templateDir', $parts[0]); + $viewModel->setVariable( + 'templateName', + $parts[1] ?? null + ); + } } }; $this->events->attach('dispatch', $callback); @@ -315,7 +321,7 @@ class Bootstrapper if (($language = $request->getPost()->get('mylang', false)) || ($language = $request->getQuery()->get('lng', false)) ) { - $cookieManager = $sm->get('VuFind\CookieManager'); + $cookieManager = $sm->get('VuFind\Cookie\CookieManager'); $cookieManager->set('language', $language); } elseif (!empty($request->getCookie()->language)) { $language = $request->getCookie()->language; @@ -329,7 +335,7 @@ class Bootstrapper $language = $config->Site->language; } try { - $translator = $sm->get('VuFind\Translator'); + $translator = $sm->get('Zend\Mvc\I18n\Translator'); $translator->setLocale($language) ->addTranslationFile('ExtendedIni', null, 'default', $language); foreach ($this->getTextDomains() as $domain) { @@ -340,7 +346,7 @@ class Bootstrapper 'ExtendedIni', $domain, $domain, $language ); } - } catch (\Zend\Mvc\Exception\BadMethodCallException $e) { + } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $e) { if (!extension_loaded('intl')) { throw new \Exception( 'Translation broken due to missing PHP intl extension.' @@ -349,7 +355,7 @@ class Bootstrapper } } // Send key values to view: - $viewModel = $sm->get('viewmanager')->getViewModel(); + $viewModel = $sm->get('ViewManager')->getViewModel(); $viewModel->setVariable('userLang', $language); $viewModel->setVariable('allLangs', $config->Languages); $rtlLangs = isset($config->LanguageSettings->rtl_langs) @@ -438,8 +444,8 @@ class Bootstrapper { $callback = function ($event) { $sm = $event->getApplication()->getServiceManager(); - if ($sm->has('VuFind\Logger')) { - $log = $sm->get('VuFind\Logger'); + if ($sm->has('VuFind\Log\Logger')) { + $log = $sm->get('VuFind\Log\Logger'); if (is_callable([$log, 'logException'])) { $exception = $event->getParam('exception'); // Console request does not include server, @@ -470,7 +476,7 @@ class Bootstrapper // a user-friendly message instead of a fatal error. $callback = function ($event) { $serviceManager = $event->getApplication()->getServiceManager(); - $viewModel = $serviceManager->get('viewmanager')->getViewModel(); + $viewModel = $serviceManager->get('ViewManager')->getViewModel(); $viewModel->renderingError = true; }; $this->events->attach('render.error', $callback, 10000); diff --git a/module/VuFind/src/VuFind/Cache/KeyGeneratorTrait.php b/module/VuFind/src/VuFind/Cache/KeyGeneratorTrait.php index 490c886e16aa0ccbd8d804e7e7c22f739d3f0621..14c070b3a038eb66463a6c26aef5629d17bbda41 100644 --- a/module/VuFind/src/VuFind/Cache/KeyGeneratorTrait.php +++ b/module/VuFind/src/VuFind/Cache/KeyGeneratorTrait.php @@ -2,7 +2,7 @@ /** * VuFind Cache Key Generator Trait * - * PHP version 5 + * PHP version 7 * * Copyright (C) Leipzig University Library 2016. * diff --git a/module/VuFind/src/VuFind/Cache/Manager.php b/module/VuFind/src/VuFind/Cache/Manager.php index 835c43fa543faeaaecea7354b6fd9901dbcdb4e9..8ced9ece90a15358cf494ec6314e2ed3fb799620 100644 --- a/module/VuFind/src/VuFind/Cache/Manager.php +++ b/module/VuFind/src/VuFind/Cache/Manager.php @@ -2,9 +2,10 @@ /** * VuFind Cache Manager * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2007. + * Copyright (C) Villanova University 2007, + * 2018 Leipzig University Library <info@ub.uni-leipzig.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -22,11 +23,15 @@ * @category VuFind * @package Cache * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ namespace VuFind\Cache; -use Zend\Cache\StorageFactory, Zend\Config\Config; + +use Zend\Cache\Storage\StorageInterface; +use Zend\Cache\StorageFactory; +use Zend\Config\Config; /** * VuFind Cache Manager @@ -36,6 +41,7 @@ use Zend\Cache\StorageFactory, Zend\Config\Config; * @category VuFind * @package Cache * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ @@ -65,7 +71,7 @@ class Manager /** * Actual cache objects generated from settings. * - * @var array + * @var StorageInterface[] */ protected $caches = []; @@ -115,26 +121,33 @@ class Manager /** * Retrieve the specified cache object. * - * @param string $key Key identifying the requested cache. + * @param string $name Name of the requested cache. + * @param string|null $namespace Optional namespace to use. Defaults to the + * value of {@see $name}. * - * @return object + * @return StorageInterface + * @throws \Exception */ - public function getCache($key) + public function getCache($name, $namespace = null) { + $namespace = $namespace ?? $name; + $key = "$name:$namespace"; + if (!isset($this->caches[$key])) { - if (!isset($this->cacheSettings[$key])) { - throw new \Exception('Requested unknown cache: ' . $key); + if (!isset($this->cacheSettings[$name])) { + throw new \Exception('Requested unknown cache: ' . $name); } // Special case for "no-cache" caches: - if ($this->cacheSettings[$key] === false) { + if ($this->cacheSettings[$name] === false) { $this->caches[$key] = new \VuFind\Cache\Storage\Adapter\NoCacheAdapter(); } else { - $this->caches[$key] = StorageFactory::factory( - $this->cacheSettings[$key] - ); + $settings = $this->cacheSettings[$name]; + $settings['adapter']['options']['namespace'] = $namespace; + $this->caches[$key] = StorageFactory::factory($settings); } } + return $this->caches[$key]; } @@ -159,7 +172,7 @@ class Manager if (strlen(LOCAL_CACHE_DIR) > 0) { $dir = LOCAL_CACHE_DIR . '/'; - } else if (strlen(LOCAL_OVERRIDE_DIR) > 0) { + } elseif (strlen(LOCAL_OVERRIDE_DIR) > 0) { $dir = LOCAL_OVERRIDE_DIR . '/cache/'; } else { $dir = APPLICATION_PATH . '/data/cache/'; diff --git a/module/VuFind/src/VuFind/Cache/ManagerFactory.php b/module/VuFind/src/VuFind/Cache/ManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..317ca426c20bbdfeb3105e6ecdc524b136694010 --- /dev/null +++ b/module/VuFind/src/VuFind/Cache/ManagerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Cache Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cache + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Cache; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cache Manager factory. + * + * @category VuFind + * @package Cache + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('config'), + $container->get('VuFind\Config\PluginManager')->get('searches') + ); + } +} diff --git a/module/VuFind/src/VuFind/Cache/Storage/Adapter/NoCacheAdapter.php b/module/VuFind/src/VuFind/Cache/Storage/Adapter/NoCacheAdapter.php index 5c7ea1245b47693ed2985862c4bfc1520c39d4a6..ab5143e696136192884b5df34a18cd3ef746d213 100644 --- a/module/VuFind/src/VuFind/Cache/Storage/Adapter/NoCacheAdapter.php +++ b/module/VuFind/src/VuFind/Cache/Storage/Adapter/NoCacheAdapter.php @@ -3,7 +3,7 @@ /** * VuFind NoCacheAdapter. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/Cart.php b/module/VuFind/src/VuFind/Cart.php index 2f592f1e8c00f53680c7a88e186f1d148395abb4..1fed7c18ffb7cf84e4f54a7afc6d39e1b3d13f79 100644 --- a/module/VuFind/src/VuFind/Cart.php +++ b/module/VuFind/src/VuFind/Cart.php @@ -2,7 +2,7 @@ /** * Cart Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind; + use VuFind\Cookie\CookieManager; /** @@ -215,7 +216,7 @@ class Cart */ public function isFull() { - return (count($this->items) >= $this->maxSize); + return count($this->items) >= $this->maxSize; } /** diff --git a/module/VuFind/src/VuFind/CartFactory.php b/module/VuFind/src/VuFind/CartFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a4c76e307ae4ab33a108e35b34395f828295e311 --- /dev/null +++ b/module/VuFind/src/VuFind/CartFactory.php @@ -0,0 +1,77 @@ +<?php +/** + * Cart factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cart + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cart factory. + * + * @category VuFind + * @package Cart + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CartFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $active = isset($config->Site->showBookBag) + ? (bool)$config->Site->showBookBag : false; + $size = isset($config->Site->bookBagMaxSize) + ? $config->Site->bookBagMaxSize : 100; + $activeInSearch = isset($config->Site->bookbagTogglesInSearch) + ? $config->Site->bookbagTogglesInSearch : true; + return new $requestedName( + $container->get('VuFind\Record\Loader'), + $container->get('VuFind\Cookie\CookieManager'), + $size, $active, $activeInSearch + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/AbstractChannelProvider.php b/module/VuFind/src/VuFind/ChannelProvider/AbstractChannelProvider.php index 2f89e9bf2182e9d8af318fbb5f3facb248b25e50..3b2931c3ff9acc64a77b258843b0e7734ea62f06 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/AbstractChannelProvider.php +++ b/module/VuFind/src/VuFind/ChannelProvider/AbstractChannelProvider.php @@ -1,8 +1,8 @@ <?php /** - * Facet-driven channel provider. + * Abstract base class for channel providers. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,11 +26,13 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; + use VuFind\Cover\Router as CoverRouter; +use VuFind\Record\Router as RecordRouter; use VuFind\Search\Base\Params; /** - * Facet-driven channel provider. + * Abstract base class for channel providers. * * @category VuFind * @package Channels @@ -54,6 +56,13 @@ abstract class AbstractChannelProvider implements ChannelProviderInterface */ protected $providerId = ''; + /** + * Record router + * + * @var RecordRouter + */ + protected $recordRouter = null; + /** * Hook to configure search parameters before executing search. * @@ -80,6 +89,18 @@ abstract class AbstractChannelProvider implements ChannelProviderInterface $this->coverRouter = $coverRouter; } + /** + * Inject record router + * + * @param RecordRouter $recordRouter Record router. + * + * @return void + */ + public function setRecordRouter(RecordRouter $recordRouter) + { + $this->recordRouter = $recordRouter; + } + /** * Set an identifier that will be injected as the 'providerId' key of all * channels created by this provider. @@ -124,6 +145,9 @@ abstract class AbstractChannelProvider implements ChannelProviderInterface 'thumbnail' => $this->coverRouter ? $this->coverRouter->getUrl($current, 'medium') : false, + 'routeDetails' => $this->recordRouter + ? $this->recordRouter->getTabRouteDetails($current) + : false, 'id' => $current->getUniqueId(), ]; } diff --git a/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProvider.php b/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..efa272996683ed5e6a8fda8a77aa0e9c51b50078 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProvider.php @@ -0,0 +1,184 @@ +<?php +/** + * Abstract base class for channel providers relying on the ILS. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\RecordDriver\AbstractBase as RecordDriver; +use VuFind\Search\Base\Results; + +/** + * Abstract base class for channel providers relying on the ILS. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractILSChannelProvider extends AbstractChannelProvider + implements TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Number of results to include in each channel. + * + * @var int + */ + protected $channelSize; + + /** + * Channel title (will be run through translator). + * + * @var string + */ + protected $channelTitle = 'Please set $channelTitle property!'; + + /** + * Maximum age (in days) of results to retrieve. + * + * @var int + */ + protected $maxAge; + + /** + * ILS connection + * + * @var \VuFind\ILS\Connection + */ + protected $ils; + + /** + * Search service + * + * @var \VuFindSearch\Service + */ + protected $searchService; + + /** + * Constructor + * + * @param \VuFindSearch\Service $search Search service + * @param \VuFind\ILS\Connection $ils ILS connection + * @param array $options Settings (optional) + */ + public function __construct(\VuFindSearch\Service $search, + \VuFind\ILS\Connection $ils, array $options = [] + ) { + $this->searchService = $search; + $this->ils = $ils; + $this->setOptions($options); + } + + /** + * Set the options for the provider. + * + * @param array $options Options + * + * @return void + */ + public function setOptions(array $options) + { + $this->channelSize = $options['channelSize'] ?? 20; + $this->maxAge = $options['maxAge'] ?? 30; + } + + /** + * Return channel information derived from a record driver object. + * + * @param RecordDriver $driver Record driver + * @param string $channelToken Token identifying a single specific channel + * to load (if omitted, all channels will be loaded) -- not used in this provider + * + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFromRecord(RecordDriver $driver, $channelToken = null) + { + return $this->getChannel(); + } + + /** + * Return channel information derived from a search results object. + * + * @param Results $results Search results + * @param string $channelToken Token identifying a single specific channel + * to load (if omitted, all channels will be loaded) -- not used in this provider + * + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFromSearch(Results $results, $channelToken = null) + { + return $this->getChannel(); + } + + /** + * Retrieve data from the ILS. + * + * @return array + */ + abstract protected function getIlsResponse(); + + /** + * Given one element from the ILS function's response array, extract the + * ID value. + * + * @param array $response Response array + * + * @return string + */ + abstract protected function extractIdsFromResponse($response); + + /** + * Recently returned channel contents are always the same; this does not + * care about specific records or search parameters. + * + * @return array + */ + protected function getChannel() + { + // Use a callback to extract IDs from the arrays in the ILS return value: + $ids = array_map([$this, 'extractIdsFromResponse'], $this->getIlsResponse()); + // No IDs means no response! + if (empty($ids)) { + return []; + } + // Look up the record drivers for the recently returned IDs: + $records = $this->searchService->retrieveBatch('Solr', $ids)->getRecords(); + // Build the return value: + $retVal = [ + 'title' => $this->translate($this->channelTitle), + 'providerId' => $this->providerId, + 'contents' => $this->summarizeRecordDrivers($records), + ]; + return (count($retVal['contents']) > 0) ? [$retVal] : []; + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php b/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b3d942f941d6842aab0ea352edf96ee079414733 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for channel providers relying on the ILS. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for channel providers relying on the ILS. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractILSChannelProviderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get('VuFindSearch\Service'), + $container->get('VuFind\ILS\Connection') + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowse.php b/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowse.php index 980aa4b145a1b563b870c0df9a0b7952ce6ec151..0527d8d6bd1d2e4f7b3c6d031708ceeb9a257f0d 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowse.php +++ b/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowse.php @@ -2,7 +2,7 @@ /** * Alphabrowse channel provider. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,10 +26,12 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; -use VuFind\RecordDriver\AbstractBase as RecordDriver; -use VuFind\Record\Router as RecordRouter; -use VuFind\Search\Base\Params, VuFind\Search\Base\Results; + use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Record\Router as RecordRouter; +use VuFind\RecordDriver\AbstractBase as RecordDriver; +use VuFind\Search\Base\Params; +use VuFind\Search\Base\Results; use VuFindSearch\Backend\Solr\Backend; use VuFindSearch\ParamBag; use Zend\Mvc\Controller\Plugin\Url; @@ -139,16 +141,11 @@ class AlphaBrowse extends AbstractChannelProvider */ public function setOptions(array $options) { - $this->channelSize = isset($options['channelSize']) - ? $options['channelSize'] : 20; - $this->maxRecordsToExamine = isset($options['maxRecordsToExamine']) - ? $options['maxRecordsToExamine'] : 2; - $this->browseIndex = isset($options['browseIndex']) ? - $options['browseIndex'] : 'lcc'; - $this->solrField = isset($options['solrField']) ? - $options['solrField'] : 'callnumber-raw'; - $this->rowsBefore = isset($options['rows_before']) ? - $options['rows_before'] : 10; + $this->channelSize = $options['channelSize'] ?? 20; + $this->maxRecordsToExamine = $options['maxRecordsToExamine'] ?? 2; + $this->browseIndex = $options['browseIndex'] ?? 'lcc'; + $this->solrField = $options['solrField'] ?? 'callnumber-raw'; + $this->rowsBefore = $options['rows_before'] ?? 10; } /** diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php new file mode 100644 index 0000000000000000000000000000000000000000..df9f225d300ac995b8552edef0a96e2f0dc46d0d --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php @@ -0,0 +1,291 @@ +<?php +/** + * Channel loader + * + * PHP version 7 + * + * Copyright (C) Villanova University 2016. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use VuFind\Cache\Manager as CacheManager; +use VuFind\ChannelProvider\PluginManager as ChannelManager; +use VuFind\Record\Loader as RecordLoader; +use VuFind\Search\Base\Results; +use VuFind\Search\SearchRunner; +use Zend\Config\Config; + +/** + * Channel loader + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ChannelLoader +{ + /** + * Cache manager + * + * @var CacheManager + */ + protected $cacheManager; + + /** + * Channel manager + * + * @var ChannelManager + */ + protected $channelManager; + + /** + * Channel configuration + * + * @var Config + */ + protected $config; + + /** + * Record loader + * + * @var RecordLoader + */ + protected $recordLoader; + + /** + * Search runner + * + * @var SearchRunner + */ + protected $searchRunner; + + /** + * Constructor + * + * @param Config $config Channels configuration + * @param CacheManager $cache Cache manager + * @param ChannelManager $cm Channel manager + * @param SearchRunner $runner Search runner + * @param RecordLoader $loader Record loader + */ + public function __construct(Config $config, CacheManager $cache, + ChannelManager $cm, SearchRunner $runner, RecordLoader $loader + ) { + $this->config = $config; + $this->cacheManager = $cache; + $this->channelManager = $cm; + $this->searchRunner = $runner; + $this->recordLoader = $loader; + } + + /** + * Get a search results object configured by channel providers. + * + * @param array $searchRequest Search request parameters + * @param array $providers Array of channel providers + * @param string $source Backend to use + * + * @return Results + */ + protected function performChannelSearch($searchRequest, $providers, $source) + { + // Perform search and configure providers: + $callback = function ($runner, $params) use ($providers) { + foreach ($providers as $provider) { + $provider->configureSearchParams($params); + } + }; + return $this->searchRunner->run($searchRequest, $source, $callback); + } + + /** + * Get channel details using an array of providers and a populated search + * results object. + * + * @param array $providers Array of channel providers + * @param Results $results Search results object from performChannelSearch + * @param string $token Optional channel token + * + * @return array + */ + protected function getChannelsFromResults($providers, Results $results, $token) + { + // Collect details: + $channels = []; + foreach ($providers as $provider) { + $channels = array_merge( + $channels, $provider->getFromSearch($results, $token) + ); + } + return $channels; + } + + /** + * Get an array of channel providers matching the provided IDs (or just one, + * if the channelProvider GET parameter is set). + * + * @param string $source Search backend ID + * @param array $configSection Configuration section to load ID list from + * @param string $activeId Currently selected channel ID (if any; used + * when making an AJAX request for a single additional channel) + * + * @return array + */ + protected function getChannelProviders($source, $configSection, $activeId = null) + { + $providerIds = isset($this->config->{"source.$source"}->$configSection) + ? $this->config->{"source.$source"}->$configSection->toArray() : []; + $finalIds = (!empty($activeId) && in_array($activeId, $providerIds)) + ? [$activeId] : $providerIds; + return array_map([$this, 'getChannelProvider'], $finalIds); + } + + /** + * Convenience method to retrieve a channel provider. + * + * @param string $providerId Channel provider name and optional config + * (colon-delimited) + * + * @return ChannelProviderInterface + */ + protected function getChannelProvider($providerId) + { + // The provider ID consists of a service name and an optional config + // section -- break out the relevant parts: + list($serviceName, $configSection) = explode(':', $providerId . ':'); + + // Load configuration, using default value if necessary: + if (empty($configSection)) { + $configSection = "provider.$serviceName"; + } + $options = isset($this->config->{$configSection}) + ? $this->config->{$configSection}->toArray() : []; + + // Load the service, and configure appropriately: + $provider = $this->channelManager->get($serviceName); + $provider->setProviderId($providerId); + $provider->setOptions($options); + return $provider; + } + + /** + * Generates static front page of channels. + * + * @param string $token Channel token (optional, used for AJAX fetching) + * @param string $activeChannel Channel being requested (optional, used w/ token) + * @param string $activeSource Search backend to use (null to use configured + * default). + * + * @return array + */ + public function getHomeContext($token = null, $activeChannel = null, + $activeSource = null + ) { + // Load appropriate channel objects: + $defaultSource = $this->config->General->default_home_source + ?? DEFAULT_SEARCH_BACKEND; + $source = $activeSource ?? $defaultSource; + $providers = $this->getChannelProviders($source, 'home', $activeChannel); + + // Set up the cache, if appropriate: + if ($this->config->General->cache_home_channels ?? false) { + $providerIds = array_map('get_class', $providers); + $parts = [implode(',', $providerIds), $source, $token]; + $cacheKey = md5(implode('-', $parts)); + $cache = $this->cacheManager->getCache('object', 'homeChannels'); + } else { + $cacheKey = false; + } + + // Fetch channel data from cache, or populate cache if necessary: + if (!($channels = $cacheKey ? $cache->getItem($cacheKey) : false)) { + $results = $this->performChannelSearch([], $providers, $source); + $channels = $this->getChannelsFromResults($providers, $results, $token); + if ($cacheKey) { + $cache->setItem($cacheKey, $channels); + } + } + + // Return context array: + return compact('token', 'channels'); + } + + /** + * Generates channels for a record. + * + * @param string $recordId Record ID to load + * @param string $token Channel token (optional, used for AJAX fetching) + * @param string $activeChannel Channel being requested (optional, used w/ token) + * @param string $source Search backend to use + * + * @return array + */ + public function getRecordContext($recordId, $token = null, + $activeChannel = null, $source = DEFAULT_SEARCH_BACKEND + ) { + // Load record: + $driver = $this->recordLoader->load($recordId, $source); + + // Load appropriate channel objects: + $providers = $this->getChannelProviders($source, 'record', $activeChannel); + + // Collect details: + $channels = []; + foreach ($providers as $provider) { + $channels = array_merge( + $channels, $provider->getFromRecord($driver, $token) + ); + } + + // Return context array: + return compact('driver', 'channels', 'token'); + } + + /** + * Generates channels for a search. + * + * @param array $searchRequest Request parameters + * @param string $token Channel token (optional, used for AJAX fetching) + * @param string $activeChannel Channel being requested (optional, used w/ token) + * @param string $source Search backend to use + * + * @return array + */ + public function getSearchContext($searchRequest = [], $token = null, + $activeChannel = null, $source = DEFAULT_SEARCH_BACKEND + ) { + // Load appropriate channel objects: + $providers = $this->getChannelProviders($source, 'search', $activeChannel); + + // Perform search: + $results = $this->performChannelSearch($searchRequest, $providers, $source); + + // Collect details: + $lookfor = $searchRequest['lookfor'] ?? null; + $channels = $this->getChannelsFromResults($providers, $results, $token); + + // Return context array: + return compact('results', 'lookfor', 'channels', 'token'); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b969c763b8bff487e2b5844af4bada7d3efa6741 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for channel loader. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for channel loader. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ChannelLoaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('channels'), + $container->get('VuFind\Cache\Manager'), + $container->get('VuFind\ChannelProvider\PluginManager'), + $container->get('VuFind\Search\SearchRunner'), + $container->get('VuFind\Record\Loader') + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelProviderInterface.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelProviderInterface.php index bfbdec0b21edbde6e38ce5e21ff0c156eb0c82ef..78f6aec11d3345c7b547a553334b75fa65223dbb 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/ChannelProviderInterface.php +++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelProviderInterface.php @@ -2,7 +2,7 @@ /** * Channel provider interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; + use VuFind\RecordDriver\AbstractBase as RecordDriver; -use VuFind\Search\Base\Params, VuFind\Search\Base\Results; +use VuFind\Search\Base\Params; +use VuFind\Search\Base\Results; /** * Channel provider interface. diff --git a/module/VuFind/src/VuFind/ChannelProvider/Facets.php b/module/VuFind/src/VuFind/ChannelProvider/Facets.php index ba027d7e947c8eb7571a717b70e70cdfedbdba8e..30301132877b5a3340c6ac130e417a88937809dd 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/Facets.php +++ b/module/VuFind/src/VuFind/ChannelProvider/Facets.php @@ -2,7 +2,7 @@ /** * Facet-driven channel provider. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,9 +26,11 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; + use VuFind\I18n\Translator\TranslatorAwareInterface; use VuFind\RecordDriver\AbstractBase as RecordDriver; -use VuFind\Search\Base\Params, VuFind\Search\Base\Results; +use VuFind\Search\Base\Params; +use VuFind\Search\Base\Results; use VuFind\Search\Results\PluginManager as ResultsManager; use Zend\Mvc\Controller\Plugin\Url; @@ -103,14 +105,11 @@ class Facets extends AbstractChannelProvider */ public function setOptions(array $options) { - $this->fields = isset($options['fields']) - ? $options['fields'] - : ['topic_facet' => 'Topic', 'author_facet' => 'Author']; - $this->maxFieldsToSuggest = isset($options['maxFieldsToSuggest']) - ? $options['maxFieldsToSuggest'] : 2; + $this->fields = $options['fields'] + ?? ['topic_facet' => 'Topic', 'author_facet' => 'Author']; + $this->maxFieldsToSuggest = $options['maxFieldsToSuggest'] ?? 2; $this->maxValuesToSuggestPerField - = isset($options['maxValuesToSuggestPerField']) - ? $options['maxValuesToSuggestPerField'] : 2; + = $options['maxValuesToSuggestPerField'] ?? 2; } /** @@ -253,7 +252,7 @@ class Facets extends AbstractChannelProvider return $retVal; } - $newResults = clone($results); + $newResults = clone $results; $params = $newResults->getParams(); // Determine the filter for the current channel, and add it: diff --git a/module/VuFind/src/VuFind/ChannelProvider/Factory.php b/module/VuFind/src/VuFind/ChannelProvider/Factory.php index 25a1b96172b158a006e64828ad2506054e04a025..b4014736630a7df8472ece79032bf70944ceb2ac 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/Factory.php +++ b/module/VuFind/src/VuFind/ChannelProvider/Factory.php @@ -2,7 +2,7 @@ /** * Factory for ChannelProvider plugins. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; + use Zend\ServiceManager\ServiceManager; /** @@ -50,17 +51,13 @@ class Factory */ public static function getAlphaBrowse(ServiceManager $sm) { - $helper = new AlphaBrowse( - $sm->getServiceLocator()->get('VuFind\Search'), - $sm->getServiceLocator()->get('VuFind\Search\BackendManager') + return new AlphaBrowse( + $sm->get('VuFindSearch\Service'), + $sm->get('VuFind\Search\BackendManager') ->get('Solr'), - $sm->getServiceLocator()->get('ControllerPluginManager')->get('url'), - $sm->getServiceLocator()->get('VuFind\RecordRouter') - ); - $helper->setCoverRouter( - $sm->getServiceLocator()->get('VuFind\Cover\Router') + $sm->get('ControllerPluginManager')->get('url'), + $sm->get('VuFind\Record\Router') ); - return $helper; } /** @@ -72,14 +69,10 @@ class Factory */ public static function getFacets(ServiceManager $sm) { - $helper = new Facets( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager'), - $sm->getServiceLocator()->get('ControllerPluginManager')->get('url') - ); - $helper->setCoverRouter( - $sm->getServiceLocator()->get('VuFind\Cover\Router') + return new Facets( + $sm->get('VuFind\Search\Results\PluginManager'), + $sm->get('ControllerPluginManager')->get('url') ); - return $helper; } /** @@ -91,16 +84,11 @@ class Factory */ public static function getListItems(ServiceManager $sm) { - $helper = new ListItems( - $sm->getServiceLocator()->get('VuFind\DbTablePluginManager') - ->get('UserList'), - $sm->getServiceLocator()->get('ControllerPluginManager')->get('url'), - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + return new ListItems( + $sm->get('VuFind\Db\Table\PluginManager')->get('UserList'), + $sm->get('ControllerPluginManager')->get('url'), + $sm->get('VuFind\Search\Results\PluginManager') ); - $helper->setCoverRouter( - $sm->getServiceLocator()->get('VuFind\Cover\Router') - ); - return $helper; } /** @@ -112,14 +100,10 @@ class Factory */ public static function getRandom(ServiceManager $sm) { - $helper = new Random( - $sm->getServiceLocator()->get('VuFind\Search'), - $sm->getServiceLocator()->get('VuFind\SearchParamsPluginManager') - ); - $helper->setCoverRouter( - $sm->getServiceLocator()->get('VuFind\Cover\Router') + return new Random( + $sm->get('VuFindSearch\Service'), + $sm->get('VuFind\Search\Params\PluginManager') ); - return $helper; } /** @@ -131,14 +115,10 @@ class Factory */ public static function getSimilarItems(ServiceManager $sm) { - $helper = new SimilarItems( - $sm->getServiceLocator()->get('VuFind\Search'), - $sm->getServiceLocator()->get('ControllerPluginManager')->get('url'), - $sm->getServiceLocator()->get('VuFind\RecordRouter') - ); - $helper->setCoverRouter( - $sm->getServiceLocator()->get('VuFind\Cover\Router') + return new SimilarItems( + $sm->get('VuFindSearch\Service'), + $sm->get('ControllerPluginManager')->get('url'), + $sm->get('VuFind\Record\Router') ); - return $helper; } } diff --git a/module/VuFind/src/VuFind/ChannelProvider/ListItems.php b/module/VuFind/src/VuFind/ChannelProvider/ListItems.php index d872e2f2786691f1ac031a9bd88477a46e72bd6c..576690a5da06a700c273a2f545330e6845c38fc3 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/ListItems.php +++ b/module/VuFind/src/VuFind/ChannelProvider/ListItems.php @@ -2,7 +2,7 @@ /** * "List items" channel provider. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; + use VuFind\RecordDriver\AbstractBase as RecordDriver; -use VuFind\Search\Base\Params, VuFind\Search\Base\Results; +use VuFind\Search\Base\Params; +use VuFind\Search\Base\Results; use Zend\Mvc\Controller\Plugin\Url; use Zend\Stdlib\Parameters; @@ -113,11 +115,10 @@ class ListItems extends AbstractChannelProvider */ public function setOptions(array $options) { - $this->ids = isset($options['ids']) ? $options['ids'] : []; + $this->ids = $options['ids'] ?? []; $this->displayPublicLists = isset($options['displayPublicLists']) ? (bool)$options['displayPublicLists'] : true; - $this->initialListsToDisplay = isset($options['initialListsToDisplay']) - ? $options['initialListsToDisplay'] : 2; + $this->initialListsToDisplay = $options['initialListsToDisplay'] ?? 2; } /** @@ -163,7 +164,9 @@ class ListItems extends AbstractChannelProvider protected function buildListChannels($channelToken) { $channels = []; - foreach ($this->getLists() as $list) { + $lists = $channelToken + ? $this->getListsById([$channelToken]) : $this->getLists(); + foreach ($lists as $list) { $tokenOnly = (count($channels) >= $this->initialListsToDisplay); $channel = $this->getChannelFromList($list, $tokenOnly); if ($tokenOnly || count($channel['contents']) > 0) { @@ -174,19 +177,35 @@ class ListItems extends AbstractChannelProvider } /** - * Get a list of public lists to display: + * Get a list of lists, identified by ID; filter to public lists only. + * + * @param array $ids IDs to retrieve * * @return array */ - protected function getLists() + protected function getListsById($ids) { $lists = []; - foreach ($this->ids as $id) { + foreach ($ids as $id) { $list = $this->userList->getExisting($id); if ($list->public) { $lists[] = $list; } } + return $lists; + } + + /** + * Get a list of public lists to display: + * + * @return array + */ + protected function getLists() + { + // First fetch hard-coded IDs: + $lists = $this->getListsById($this->ids); + + // Next add public lists if necessary: if ($this->displayPublicLists) { $ids = $this->ids; $callback = function ($select) use ($ids) { @@ -199,6 +218,7 @@ class ListItems extends AbstractChannelProvider $lists[] = $list; } } + return $lists; } diff --git a/module/VuFind/src/VuFind/ChannelProvider/NewILSItems.php b/module/VuFind/src/VuFind/ChannelProvider/NewILSItems.php new file mode 100644 index 0000000000000000000000000000000000000000..36f733bc6d6005da53063283c7ee3f1bf614cb3f --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/NewILSItems.php @@ -0,0 +1,75 @@ +<?php +/** + * "New ILS items" channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +/** + * "New ILS items" channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class NewILSItems extends AbstractILSChannelProvider +{ + /** + * Channel title (will be run through translator). + * + * @var string + */ + protected $channelTitle = 'New Items'; + + /** + * Retrieve data from the ILS. + * + * @return array + */ + protected function getIlsResponse() + { + if ($this->ils->checkCapability('getNewItems')) { + $response = $this->ils + ->getNewItems(1, $this->channelSize, $this->maxAge); + return $response['results'] ?? []; + } + return []; + } + + /** + * Given one element from the ILS function's response array, extract the + * ID value. + * + * @param array $response Response array + * + * @return string + */ + protected function extractIdsFromResponse($response) + { + return $response['id']; + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/PluginFactory.php b/module/VuFind/src/VuFind/ChannelProvider/PluginFactory.php index 0c684bcb42ce2a170be1d99bc41df2b2144e79da..5ba5cdf97c4de0923fc02da6120d535f24294449 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/PluginFactory.php +++ b/module/VuFind/src/VuFind/ChannelProvider/PluginFactory.php @@ -2,7 +2,7 @@ /** * Channel provider plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php b/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php index 4ebf605792a6490998cee3048bc71ed7af65400f..603b7c1254d6e0c9459302c4c9c55244867951b1 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php +++ b/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php @@ -2,7 +2,7 @@ /** * Channel provider plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -38,6 +38,62 @@ namespace VuFind\ChannelProvider; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'alphabrowse' => 'VuFind\ChannelProvider\AlphaBrowse', + 'facets' => 'VuFind\ChannelProvider\Facets', + 'listitems' => 'VuFind\ChannelProvider\ListItems', + 'newilsitems' => 'VuFind\ChannelProvider\NewILSItems', + 'random' => 'VuFind\ChannelProvider\Random', + 'recentlyreturned' => 'VuFind\ChannelProvider\RecentlyReturned', + 'similaritems' => 'VuFind\ChannelProvider\SimilarItems', + 'trendingilsitems' => 'VuFind\ChannelProvider\TrendingILSItems', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\ChannelProvider\AlphaBrowse' => + 'VuFind\ChannelProvider\Factory::getAlphaBrowse', + 'VuFind\ChannelProvider\Facets' => + 'VuFind\ChannelProvider\Factory::getFacets', + 'VuFind\ChannelProvider\ListItems' => + 'VuFind\ChannelProvider\Factory::getListItems', + 'VuFind\ChannelProvider\NewILSItems' => + 'VuFind\ChannelProvider\AbstractILSChannelProviderFactory', + 'VuFind\ChannelProvider\Random' => + 'VuFind\ChannelProvider\Factory::getRandom', + 'VuFind\ChannelProvider\RecentlyReturned' => + 'VuFind\ChannelProvider\AbstractILSChannelProviderFactory', + 'VuFind\ChannelProvider\SimilarItems' => + 'VuFind\ChannelProvider\Factory::getSimilarItems', + 'VuFind\ChannelProvider\TrendingILSItems' => + 'VuFind\ChannelProvider\AbstractILSChannelProviderFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addInitializer('VuFind\ChannelProvider\RouterInitializer'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/ChannelProvider/Random.php b/module/VuFind/src/VuFind/ChannelProvider/Random.php index 51f6b40f55f2b122e6e631cd424ce40748322937..3d3552d0890cb2f83c851c4f92f2cd47b292385f 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/Random.php +++ b/module/VuFind/src/VuFind/ChannelProvider/Random.php @@ -2,7 +2,7 @@ /** * "Random items" channel provider. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,9 +26,11 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; -use VuFind\RecordDriver\AbstractBase as RecordDriver; -use VuFind\Search\Base\Params, VuFind\Search\Base\Results; + use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\RecordDriver\AbstractBase as RecordDriver; +use VuFind\Search\Base\Params; +use VuFind\Search\Base\Results; /** * "Random items" channel provider. @@ -96,9 +98,8 @@ class Random extends AbstractChannelProvider */ public function setOptions(array $options) { - $this->channelSize = isset($options['channelSize']) - ? $options['channelSize'] : 20; - $this->mode = isset($options['mode']) ? $options['mode'] : 'retain'; + $this->channelSize = $options['channelSize'] ?? 20; + $this->mode = $options['mode'] ?? 'retain'; } /** diff --git a/module/VuFind/src/VuFind/ChannelProvider/RecentlyReturned.php b/module/VuFind/src/VuFind/ChannelProvider/RecentlyReturned.php new file mode 100644 index 0000000000000000000000000000000000000000..f9510843763895b74275ab3ced6b2e8d4e0fdb94 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/RecentlyReturned.php @@ -0,0 +1,72 @@ +<?php +/** + * "Recently returned" channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +/** + * "Recently returned" channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RecentlyReturned extends AbstractILSChannelProvider +{ + /** + * Channel title (will be run through translator). + * + * @var string + */ + protected $channelTitle = 'recently_returned_channel_title'; + + /** + * Retrieve data from the ILS. + * + * @return array + */ + protected function getIlsResponse() + { + return $this->ils->checkCapability('getRecentlyReturnedBibs') + ? $this->ils->getRecentlyReturnedBibs($this->channelSize, $this->maxAge) + : []; + } + + /** + * Given one element from the ILS function's response array, extract the + * ID value. + * + * @param array $response Response array + * + * @return string + */ + protected function extractIdsFromResponse($response) + { + return $response['id']; + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php b/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php new file mode 100644 index 0000000000000000000000000000000000000000..264a9e059b554ffcd1e9150ac9052521d1cfc2b1 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php @@ -0,0 +1,60 @@ +<?php +/** + * Channel Provider Router Initializer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Initializer\InitializerInterface; + +/** + * Channel Provider Router Initializer + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RouterInitializer implements InitializerInterface +{ + /** + * Given an instance and a Service Manager, initialize the instance. + * + * @param ContainerInterface $container Service manager + * @param object $instance Instance to initialize + * + * @return object + */ + public function __invoke(ContainerInterface $container, $instance) + { + if ($instance instanceof AbstractChannelProvider) { + $instance->setCoverRouter($container->get('VuFind\Cover\Router')); + $instance->setRecordRouter($container->get('VuFind\Record\Router')); + } + return $instance; + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/SimilarItems.php b/module/VuFind/src/VuFind/ChannelProvider/SimilarItems.php index 6e369b5083446636af4fe8e90cf73d74cf41314c..9c01d2ca71993d7e9eeab63d7c2a323e03284c73 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/SimilarItems.php +++ b/module/VuFind/src/VuFind/ChannelProvider/SimilarItems.php @@ -2,7 +2,7 @@ /** * "Similar items" channel provider. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,10 +26,12 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ChannelProvider; -use VuFind\RecordDriver\AbstractBase as RecordDriver; -use VuFind\Record\Router as RecordRouter; -use VuFind\Search\Base\Params, VuFind\Search\Base\Results; + use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Record\Router as RecordRouter; +use VuFind\RecordDriver\AbstractBase as RecordDriver; +use VuFind\Search\Base\Params; +use VuFind\Search\Base\Results; use Zend\Mvc\Controller\Plugin\Url; /** @@ -107,10 +109,8 @@ class SimilarItems extends AbstractChannelProvider */ public function setOptions(array $options) { - $this->channelSize = isset($options['channelSize']) - ? $options['channelSize'] : 20; - $this->maxRecordsToExamine = isset($options['maxRecordsToExamine']) - ? $options['maxRecordsToExamine'] : 2; + $this->channelSize = $options['channelSize'] ?? 20; + $this->maxRecordsToExamine = $options['maxRecordsToExamine'] ?? 2; } /** @@ -162,7 +162,10 @@ class SimilarItems extends AbstractChannelProvider } // If the search results did not include the object we were looking for, // we need to fetch it from the search service: - if (empty($channels) && is_object($driver) && $channelToken !== null) { + if (empty($channels) + && is_object($driver ?? null) + && $channelToken !== null + ) { $driver = $this->searchService->retrieve( $driver->getSourceIdentifier(), $channelToken )->first(); diff --git a/module/VuFind/src/VuFind/ChannelProvider/TrendingILSItems.php b/module/VuFind/src/VuFind/ChannelProvider/TrendingILSItems.php new file mode 100644 index 0000000000000000000000000000000000000000..78b840fa99ba0a5ea7645001f156cd1d25cd43ae --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/TrendingILSItems.php @@ -0,0 +1,88 @@ +<?php +/** + * "Trending ILS items" channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +/** + * "Trending ILS items" channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TrendingILSItems extends AbstractILSChannelProvider +{ + /** + * Channel title (will be run through translator). + * + * @var string + */ + protected $channelTitle = 'trending_items_channel_title'; + + /** + * Set the options for the provider. + * + * @param array $options Options + * + * @return void + */ + public function setOptions(array $options) + { + // Use higher default age for trending. + if (!isset($options['maxAge'])) { + $options['maxAge'] = 90; + } + return parent::setOptions($options); + } + + /** + * Retrieve data from the ILS. + * + * @return array + */ + protected function getIlsResponse() + { + return $this->ils->checkCapability('getTrendingBibs') + ? $this->ils->getTrendingBibs($this->channelSize, $this->maxAge) + : []; + } + + /** + * Given one element from the ILS function's response array, extract the + * ID value. + * + * @param array $response Response array + * + * @return string + */ + protected function extractIdsFromResponse($response) + { + return $response['id']; + } +} diff --git a/module/VuFind/src/VuFind/Config/AccountCapabilities.php b/module/VuFind/src/VuFind/Config/AccountCapabilities.php index ffa222cc738c4e510397bccb89d28044978768a7..f1b066d4fc49d7c0671736ce3840deac721f859c 100644 --- a/module/VuFind/src/VuFind/Config/AccountCapabilities.php +++ b/module/VuFind/src/VuFind/Config/AccountCapabilities.php @@ -3,7 +3,7 @@ * Class to determine which account capabilities are available, based on * configuration and other factors. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -21,12 +21,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Controller + * @package Config * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site */ namespace VuFind\Config; + use VuFind\Auth\Manager as AuthManager; use Zend\Config\Config; @@ -35,7 +36,7 @@ use Zend\Config\Config; * configuration and other factors. * * @category VuFind - * @package Controller + * @package Config * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site diff --git a/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php b/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..85c82ba345e39a3841ffade4d7483cca822c6583 --- /dev/null +++ b/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Account capabilities factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Config + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Config; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Account capabilities factory. + * + * @category VuFind + * @package Config + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AccountCapabilitiesFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('config'), + $container->get('VuFind\Auth\Manager') + ); + } +} diff --git a/module/VuFind/src/VuFind/Config/Locator.php b/module/VuFind/src/VuFind/Config/Locator.php index 1f92f623de354dc448d7ea74977137b09a3df3ba..b6eb3f6a1c02708d03b32ce329ced03331c08917 100644 --- a/module/VuFind/src/VuFind/Config/Locator.php +++ b/module/VuFind/src/VuFind/Config/Locator.php @@ -2,7 +2,7 @@ /** * VF Configuration Locator * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -52,7 +52,7 @@ class Locator public static function getLocalConfigPath($filename, $path = null, $force = false ) { - if (is_null($path)) { + if (null === $path) { $path = 'config/vufind'; } if (defined('LOCAL_OVERRIDE_DIR') && strlen(trim(LOCAL_OVERRIDE_DIR)) > 0) { diff --git a/module/VuFind/src/VuFind/Config/PluginFactory.php b/module/VuFind/src/VuFind/Config/PluginFactory.php index 5fbd16913bb3a133cece1813230244e129a4c77c..347173e5f7d1842f0149f7fbaece54d3de0a83c1 100644 --- a/module/VuFind/src/VuFind/Config/PluginFactory.php +++ b/module/VuFind/src/VuFind/Config/PluginFactory.php @@ -2,7 +2,7 @@ /** * VuFind Config Plugin Factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,11 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Config; -use Zend\Config\Config, Zend\Config\Reader\Ini as IniReader, - Zend\ServiceManager\AbstractFactoryInterface, - Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; +use Zend\Config\Config; +use Zend\Config\Reader\Ini as IniReader; +use Zend\ServiceManager\Factory\AbstractFactoryInterface; /** * VuFind Config Plugin Factory @@ -104,7 +106,7 @@ class PluginFactory implements AbstractFactoryInterface // Now we'll pull all the children down one at a time and override settings // as appropriate: - while (!is_null($child = array_pop($configs))) { + while (null !== ($child = array_pop($configs))) { $overrideSections = isset($child->Parent_Config->override_full_sections) ? explode( ',', str_replace( @@ -158,17 +160,15 @@ class PluginFactory implements AbstractFactoryInterface /** * Can we create a service for the specified name? * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service * * @return bool * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName - ) { + public function canCreate(ContainerInterface $container, $requestedName) + { // Assume that configurations exist: return true; } @@ -176,16 +176,16 @@ class PluginFactory implements AbstractFactoryInterface /** * Create a service for the specified name. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service + * @param array $options Options (unused) * * @return object * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null ) { return $this->loadConfigFile($requestedName . '.ini'); } diff --git a/module/VuFind/src/VuFind/Config/PluginManager.php b/module/VuFind/src/VuFind/Config/PluginManager.php index f486f2c7ddfce81afce3f9a7f2bb4b29c40927ad..e91f268a4813022ddf055732917b1c3f283548b9 100644 --- a/module/VuFind/src/VuFind/Config/PluginManager.php +++ b/module/VuFind/src/VuFind/Config/PluginManager.php @@ -2,7 +2,7 @@ /** * VuFind Config Manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Config; + use Zend\ServiceManager\AbstractPluginManager as Base; /** @@ -39,6 +40,22 @@ use Zend\ServiceManager\AbstractPluginManager as Base; */ class PluginManager extends Base { + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Config\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Validate the plugin * @@ -52,7 +69,7 @@ class PluginManager extends Base * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validatePlugin($plugin) + public function validate($plugin) { // Assume everything is okay. } diff --git a/module/VuFind/src/VuFind/Config/PluginManagerFactory.php b/module/VuFind/src/VuFind/Config/PluginManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8f7bb858017a248ca1a8d5ebf8ea740a9fb87ef1 --- /dev/null +++ b/module/VuFind/src/VuFind/Config/PluginManagerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Plugin Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ServiceManager + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Config; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Plugin Manager factory. + * + * @category VuFind + * @package ServiceManager + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PluginManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('Config'); + return new $requestedName( + $container, $config['vufind']['config_reader'] + ); + } +} diff --git a/module/VuFind/src/VuFind/Config/Reader/CacheDecorator.php b/module/VuFind/src/VuFind/Config/Reader/CacheDecorator.php index 3f5243e3899e31319291234137b7ba18df76f53c..943e819a50b7188ad5f626cd0dce77bc97f68830 100644 --- a/module/VuFind/src/VuFind/Config/Reader/CacheDecorator.php +++ b/module/VuFind/src/VuFind/Config/Reader/CacheDecorator.php @@ -3,7 +3,7 @@ /** * Cache decorator. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFind\Config\Reader; -use Zend\Config\Reader\ReaderInterface; use Zend\Cache\Storage\StorageInterface; +use Zend\Config\Reader\ReaderInterface; /** * This class decorates a configuration file reader with caching support. @@ -121,5 +121,4 @@ class CacheDecorator implements ReaderInterface { return md5($string); } - } diff --git a/module/VuFind/src/VuFind/Config/SearchSpecsReader.php b/module/VuFind/src/VuFind/Config/SearchSpecsReader.php index e49deb4c3eea0efedbbf1d42892bf03bbec1c373..6e1ebabf5b0b94b56d4755a0144b527b7af82c45 100644 --- a/module/VuFind/src/VuFind/Config/SearchSpecsReader.php +++ b/module/VuFind/src/VuFind/Config/SearchSpecsReader.php @@ -2,7 +2,7 @@ /** * VuFind SearchSpecs Configuration Reader * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Config/Upgrade.php b/module/VuFind/src/VuFind/Config/Upgrade.php index f1ebe2ed79f3e58389c7926b162270b3b5f51a81..d8545d9f03249c51fba35ebfd2da7bef5ab280c4 100644 --- a/module/VuFind/src/VuFind/Config/Upgrade.php +++ b/module/VuFind/src/VuFind/Config/Upgrade.php @@ -2,7 +2,7 @@ /** * VF Configuration Upgrade Tool * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Config; -use VuFind\Config\Writer as ConfigWriter, - VuFind\Exception\FileAccess as FileAccessException; + +use VuFind\Config\Writer as ConfigWriter; +use VuFind\Exception\FileAccess as FileAccessException; /** * Class to upgrade previous VuFind configurations to the current version @@ -292,8 +293,8 @@ class Upgrade // Configuration files to load. Note that config.ini must always be loaded // first so that getOldConfigPath can work properly! $configs = [ - 'config.ini', 'authority.ini', 'facets.ini', 'reserves.ini', - 'searches.ini', 'Summon.ini', 'WorldCat.ini', 'sms.ini', + 'config.ini', 'authority.ini', 'facets.ini', 'geofeatures.ini', + 'reserves.ini', 'searches.ini', 'Summon.ini', 'WorldCat.ini', 'sms.ini', 'permissions.ini', 'Collection.ini', 'Primo.ini' ]; foreach ($configs as $config) { @@ -476,13 +477,13 @@ class Upgrade $from = (float)$this->from; if ($from >= 2.4) { $default = 'MARC:MARCXML:EndNote:EndNoteWeb:RefWorks:BibTeX:RIS'; - } else if ($from >= 2.0) { + } elseif ($from >= 2.0) { $default = 'MARC:MARCXML:EndNote:EndNoteWeb:RefWorks:BibTeX'; - } else if ($from >= 1.4) { + } elseif ($from >= 1.4) { $default = 'MARC:MARCXML:EndNote:RefWorks:BibTeX'; - } else if ($from >= 1.3) { + } elseif ($from >= 1.3) { $default = 'MARC:EndNote:RefWorks:BibTeX'; - } else if ($from >= 1.2) { + } elseif ($from >= 1.2) { $default = 'MARC:EndNote:BibTeX'; } else { $default = 'MARC:EndNote'; @@ -1022,7 +1023,7 @@ class Upgrade $cfg = & $this->newConfigs['Summon.ini']['Advanced_Facet_Settings']; if (!isset($cfg['special_facets']) || empty($cfg['special_facets'])) { $cfg['special_facets'] = 'checkboxes:Summon'; - } else if (false === strpos('checkboxes', $cfg['special_facets'])) { + } elseif (false === strpos('checkboxes', $cfg['special_facets'])) { $cfg['special_facets'] .= ',checkboxes:Summon'; } } @@ -1315,9 +1316,9 @@ class Upgrade ? $this->newConfigs['config.ini']['Catalog']['driver'] : ''; if (empty($driver)) { $this->addWarning("WARNING: Could not find ILS driver setting."); - } else if ('Sample' == $driver) { + } elseif ('Sample' == $driver) { // No configuration file for Sample driver - } else if (!file_exists($this->oldDir . '/' . $driver . '.ini')) { + } elseif (!file_exists($this->oldDir . '/' . $driver . '.ini')) { $this->addWarning( "WARNING: Could not find {$driver}.ini file; " . "check your ILS driver configuration." @@ -1430,7 +1431,7 @@ class Upgrade // string. Note that we treat blank lines as comments. if (substr($trimmed, 0, 1) == ';' || empty($trimmed)) { $comments .= $line; - } else if (substr($trimmed, 0, 1) == '[' + } elseif (substr($trimmed, 0, 1) == '[' && ($closeBracket = strpos($trimmed, ']')) > 1 ) { // Is the current line the start of a section? If so, create the @@ -1449,7 +1450,7 @@ class Upgrade 'settings' => []]; $comments = ''; } - } else if (($equals = strpos($trimmed, '=')) !== false) { + } elseif (($equals = strpos($trimmed, '=')) !== false) { // Is the current line a setting? If so, add to the return value: $set = trim(substr($trimmed, 0, $equals)); $set = trim(str_replace('[]', '', $set)); diff --git a/module/VuFind/src/VuFind/Config/Version.php b/module/VuFind/src/VuFind/Config/Version.php index 58d62ca1a4908d1d1d805d5e8fa25604e58696c9..d70d0641024cdbd59d5aa0e4c3646084bcef56ba 100644 --- a/module/VuFind/src/VuFind/Config/Version.php +++ b/module/VuFind/src/VuFind/Config/Version.php @@ -2,7 +2,7 @@ /** * Version check utility * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015. diff --git a/module/VuFind/src/VuFind/Config/Writer.php b/module/VuFind/src/VuFind/Config/Writer.php index dc56420cf43ba1ff3dda51e543e59a3c167f0e0b..2bdfbfdc6fc19e9a9e6baf7175fa0ecbf7fbbc9f 100644 --- a/module/VuFind/src/VuFind/Config/Writer.php +++ b/module/VuFind/src/VuFind/Config/Writer.php @@ -2,7 +2,7 @@ /** * VF Configuration Writer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -72,7 +72,7 @@ class Writer if (false === $this->content) { throw new \Exception('Could not read ' . $filename); } - } else if (is_array($content)) { + } elseif (is_array($content)) { $this->content = $this->buildContent($content, $comments); } else { $this->content = $content; @@ -103,7 +103,7 @@ class Writer // Separate comments from content: $parts = explode(';', trim($line), 2); $content = trim($parts[0]); - $comment = isset($parts[1]) ? $parts[1] : ''; + $comment = $parts[1] ?? ''; // Is this a section heading? if (preg_match('/^\[(.+)\]$/', trim($content), $matches)) { @@ -117,7 +117,7 @@ class Writer $settingSet = true; } $currentSection = $matches[1]; - } else if (strstr($content, '=')) { + } elseif (strstr($content, '=')) { $contentParts = explode('=', $content, 2); $key = trim($contentParts[0]); // If the key we are trying to set is already present as an array, @@ -212,9 +212,9 @@ class Writer { if ($e === true) { return 'true'; - } else if ($e === false) { + } elseif ($e === false) { return 'false'; - } else if ($e == "") { + } elseif ($e == "") { return ''; } else { return '"' . str_replace('"', '\"', $e) . '"'; diff --git a/module/VuFind/src/VuFind/Config/YamlReader.php b/module/VuFind/src/VuFind/Config/YamlReader.php index 2e14bb4f7e1e1e63f7d6d45bbaa126419ace27b3..9b75189a2e97f03190c5ba8e7826e5b8ec8be383 100644 --- a/module/VuFind/src/VuFind/Config/YamlReader.php +++ b/module/VuFind/src/VuFind/Config/YamlReader.php @@ -2,7 +2,7 @@ /** * VuFind YAML Configuration Reader * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Config; + use Symfony\Component\Yaml\Yaml; /** @@ -143,7 +144,14 @@ class YamlReader // Override default parent with explicitly-defined parent, if present: if (isset($results['@parent_yaml'])) { - $defaultParent = $results['@parent_yaml']; + // First try parent as absolute path, then as relative: + $defaultParent = file_exists($results['@parent_yaml']) + ? $results['@parent_yaml'] + : dirname($file) . '/' . $results['@parent_yaml']; + if (!file_exists($defaultParent)) { + $defaultParent = null; + error_log('Cannot find parent file: ' . $results['@parent_yaml']); + } // Swallow the directive after processing it: unset($results['@parent_yaml']); } diff --git a/module/VuFind/src/VuFind/Config/YamlReaderFactory.php b/module/VuFind/src/VuFind/Config/YamlReaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..aa7c306e533dbf50370abba9402feb92f1073dd1 --- /dev/null +++ b/module/VuFind/src/VuFind/Config/YamlReaderFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for YamlReader (and subclasses). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Config + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Config; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for YamlReader (and subclasses). + * + * @category VuFind + * @package Config + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class YamlReaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Cache\Manager')); + } +} diff --git a/module/VuFind/src/VuFind/Connection/OpenLibrary.php b/module/VuFind/src/VuFind/Connection/OpenLibrary.php index e3e371ae9b364689c19aab750ad5ae3c47fbb4dd..65c93a577847c129bfb1b184df29abfbd944beb9 100644 --- a/module/VuFind/src/VuFind/Connection/OpenLibrary.php +++ b/module/VuFind/src/VuFind/Connection/OpenLibrary.php @@ -2,7 +2,7 @@ /** * Open Library Utilities * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Connection/Oracle.php b/module/VuFind/src/VuFind/Connection/Oracle.php index aa73b4015ad8447c607ec750f29d8cfd12a27654..d40c6a1f7747783e6bcd8664e19d1551b978e2c2 100644 --- a/module/VuFind/src/VuFind/Connection/Oracle.php +++ b/module/VuFind/src/VuFind/Connection/Oracle.php @@ -2,7 +2,7 @@ /** * Oracle support code for VTLS Virtua Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) University of Southern Queensland 2008. * @@ -412,7 +412,7 @@ class Oracle // For building the sql $columns[] = $column; // Dates are special - if (count($tmp) > 0 && !is_null($datum)) { + if (count($tmp) > 0 && null !== $datum) { $values[] = "TO_DATE(:$column, '" . join(":", $tmp) . "')"; } else { $values[] = ":$column"; diff --git a/module/VuFind/src/VuFind/Connection/Relais.php b/module/VuFind/src/VuFind/Connection/Relais.php new file mode 100644 index 0000000000000000000000000000000000000000..7e11291f9ee223babd7a80cef84adb9438b05c3a --- /dev/null +++ b/module/VuFind/src/VuFind/Connection/Relais.php @@ -0,0 +1,194 @@ +<?php +/** + * Relais connection class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Relais + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Connection; + +use Zend\Config\Config; +use Zend\Http\Client; + +/** + * Relais connection class. + * + * @category VuFind + * @package Relais + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Relais implements \Zend\Log\LoggerAwareInterface +{ + use \VuFind\Log\LoggerAwareTrait; + + /** + * HTTP client + * + * @var Client + */ + protected $client; + + /** + * Relais configuration + * + * @var Config + */ + protected $config; + + /** + * Constructor + * + * @param Client $client HTTP client + * @param Config $config Relais configuration + */ + public function __construct(Client $client, Config $config) + { + $this->client = $client; + $this->config = $config; + } + + /** + * Get default data to send to API. + * + * @return array + */ + protected function getDefaultData() + { + return [ + 'ApiKey' => $this->config->apikey ?? null, + 'UserGroup' => 'PATRON', + 'PartnershipId' => $this->config->group ?? null, + 'LibrarySymbol' => $this->config->symbol ?? null, + ]; + } + + /** + * Format the parameters needed to look up an OCLC number in the API. + * + * @param string $oclc OCLC number to look up + * @param string $patron Patron ID (null to use default from config) + * + * @return type + */ + protected function getOclcRequestData($oclc, $patron) + { + return [ + 'PickupLocation' => $this->config->pickupLocation ?? null, + 'Notes' => 'This request was made through the VuFind Catalog interface', + 'PatronId' => $patron ?? $this->config->patronForLookup ?? null, + 'ExactSearch' => [ + [ + 'Type' => 'OCLC', + 'Value' => $oclc, + ] + ] + ]; + } + + /** + * Make an API request + * + * @param string $uri Endpoint to request from + * @param array $data Data to send with request + * + * @return string + */ + protected function request($uri, $data) + { + $this->client->resetParameters() + ->setUri($uri) + ->setMethod('POST'); + $requestBody = json_encode($data + $this->getDefaultData()); + $this->debug('Posting ' . $requestBody . ' to ' . $uri); + $this->client->setRawBody($requestBody, 'application/json'); + $this->client->getRequest()->getHeaders() + ->addHeaderLine('Content-Type: application/json'); + $response = $this->client->send(); + $body = $response->getBody(); + $this->debug('Status: ' . $response->getStatusCode() . ', body: ' . $body); + return $body; + } + + /** + * Authenticate a patron + * + * @param string $patron Patron ID (null to use default from config) + * @param bool $returnFullObject True to return the full API response object; + * false to return only the authorization ID. + * + * @return mixed + * @throws \Exception + */ + public function authenticatePatron($patron = null, $returnFullObject = false) + { + $uri = $this->config->authenticateurl ?? null; + if (empty($uri)) { + throw new \Exception('authenticateurl not configured!'); + } + $data = ['PatronId' => $patron ?? $this->config->patronForLookup ?? null]; + $result = json_decode($this->request($uri, $data)); + return $returnFullObject ? $result : ($result->AuthorizationId ?? null); + } + + /** + * Place a request + * + * @param string $oclc OCLC number to look up + * @param string $auth Authentication ID from authenticatePatron() + * @param string $patron Patron ID (null to use default from config) + * + * @return string + * @throws \Exception + */ + public function placeRequest($oclc, $auth, $patron = null) + { + $uri = $this->config->addurl ?? null; + if (empty($uri)) { + throw new \Exception('addurl not configured!'); + } + $data = $this->getOclcRequestData($oclc, $patron); + return $this->request($uri . '?aid=' . urlencode($auth), $data); + } + + /** + * Perform a search + * + * @param string $oclc OCLC number to look up + * @param string $auth Authentication ID from authenticatePatron() + * @param string $patron Patron ID (null to use default from config) + * + * @return string + * @throws \Exception + */ + public function search($oclc, $auth, $patron = null) + { + $uri = $this->config->availableurl ?? null; + if (empty($uri)) { + throw new \Exception('availableurl not configured!'); + } + $data = $this->getOclcRequestData($oclc, $patron); + return $this->request($uri . '?aid=' . urlencode($auth), $data); + } +} diff --git a/module/VuFind/src/VuFind/Connection/RelaisFactory.php b/module/VuFind/src/VuFind/Connection/RelaisFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..13101eac9598d354078810475f3e380ccc307e68 --- /dev/null +++ b/module/VuFind/src/VuFind/Connection/RelaisFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Relais factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Relais + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Connection; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Relais factory. + * + * @category VuFind + * @package Relais + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RelaisFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $url = $config->Relais->authenticateurl ?? null; + $client = $container->get('VuFindHttp\HttpService')->createClient($url); + $client->setOptions(['timeout' => $config->Relais->timeout ?? 500]); + return new $requestedName($client, $config->Relais ?? null); + } +} diff --git a/module/VuFind/src/VuFind/Connection/Wikipedia.php b/module/VuFind/src/VuFind/Connection/Wikipedia.php index cb1c13eb611710ad724af3d66eef3734009e3fc5..88b2f5e8d747db02648e62a52d051e352705d6a2 100644 --- a/module/VuFind/src/VuFind/Connection/Wikipedia.php +++ b/module/VuFind/src/VuFind/Connection/Wikipedia.php @@ -2,7 +2,7 @@ /** * Wikipedia connection class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Connection; + use VuFind\I18n\Translator\TranslatorAwareInterface; /** @@ -454,9 +455,7 @@ class Wikipedia implements TranslatorAwareInterface $imageUrl = $this->getWikipediaImageURL($imageName); if ($imageUrl != false) { $info['image'] = $imageUrl; - $info['altimage'] = isset($imageCaption) - ? $imageCaption - : $name; + $info['altimage'] = $imageCaption ?? $name; } } @@ -505,6 +504,6 @@ class Wikipedia implements TranslatorAwareInterface } } - return isset($imageUrl) ? $imageUrl : false; + return $imageUrl ?? false; } } diff --git a/module/VuFind/src/VuFind/Connection/WorldCatUtils.php b/module/VuFind/src/VuFind/Connection/WorldCatUtils.php index de76a864932faa1b1911088574dd50b87f775669..a5390a20707fb9b48b72f724def8a25d3f1925fa 100644 --- a/module/VuFind/src/VuFind/Connection/WorldCatUtils.php +++ b/module/VuFind/src/VuFind/Connection/WorldCatUtils.php @@ -2,7 +2,7 @@ /** * World Cat Utilities * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Connection; -use File_MARCXML, VuFind\XSLT\Processor as XSLTProcessor, Zend\Config\Config; + +use Zend\Config\Config; /** * World Cat Utilities @@ -166,7 +167,7 @@ class WorldCatUtils implements \Zend\Log\LoggerAwareInterface // Is the first name empty? If so, save this there. if (empty($first)) { $first = $current; - } else if (strlen($current) > 2 || empty($last)) { + } elseif (strlen($current) > 2 || empty($last)) { // If this isn't the first name, we always want to save it as the // last name UNLESS it's an initial, in which case we'll only // save it if we don't already have something better! @@ -179,7 +180,7 @@ class WorldCatUtils implements \Zend\Log\LoggerAwareInterface // based on whether we found a first name only or both first and last names: if (empty($first) && empty($last)) { return false; - } else if (empty($last)) { + } elseif (empty($last)) { return "local.PersonalName=\"{$first}\""; } else { return "local.PersonalName=\"{$last}\" " @@ -206,7 +207,7 @@ class WorldCatUtils implements \Zend\Log\LoggerAwareInterface // Collect subjects for current name: $retVal = []; - if (!is_null($subjects) && count($subjects) > 0) { + if (null !== $subjects && count($subjects) > 0) { foreach ($subjects as $currentSubject) { if ($currentSubject['tag'] == '650') { $text = (string)$currentSubject; diff --git a/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php b/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b38f8f6a435f6c8ddebfef72f45bf544eafa25c0 --- /dev/null +++ b/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * WorldCat utils factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package WorldCat + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Connection; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * WorldCat utils factory. + * + * @category VuFind + * @package WorldCat + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class WorldCatUtilsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $client = $container->get('VuFindHttp\HttpService')->createClient(); + $ip = $container->get('Request')->getServer()->get('SERVER_ADDR'); + return new $requestedName( + isset($config->WorldCat) ? $config->WorldCat : null, + $client, true, $ip + ); + } +} diff --git a/module/VuFind/src/VuFind/Content/AbstractAmazon.php b/module/VuFind/src/VuFind/Content/AbstractAmazon.php index 4cb9b28daca4d2d9625096fc0118e664ff937cff..25d02f0a19f4d671f6b8e0b109ee21e10ac6c3e9 100644 --- a/module/VuFind/src/VuFind/Content/AbstractAmazon.php +++ b/module/VuFind/src/VuFind/Content/AbstractAmazon.php @@ -2,7 +2,7 @@ /** * Generic Amazon content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php b/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b094fba62777723cce02c13539eae969838b870e --- /dev/null +++ b/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Generic Amazon content plugin factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Content; + +use Interop\Container\ContainerInterface; + +/** + * Generic Amazon content plugin factory. + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractAmazonFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $associate = isset($config->Content->amazonassociate) + ? $config->Content->amazonassociate : null; + $secret = isset($config->Content->amazonsecret) + ? $config->Content->amazonsecret : null; + $label = $container->get('Zend\Mvc\I18n\Translator')->translate( + 'Supplied by Amazon' + ); + return new $requestedName($associate, $secret, $label); + } +} diff --git a/module/VuFind/src/VuFind/Content/AbstractBase.php b/module/VuFind/src/VuFind/Content/AbstractBase.php index 78dcb20bd44f4d9593ba7ff490145821ffada3f6..cc2fb4de81c2766bf348b71ea71968f68273066a 100644 --- a/module/VuFind/src/VuFind/Content/AbstractBase.php +++ b/module/VuFind/src/VuFind/Content/AbstractBase.php @@ -2,7 +2,7 @@ /** * Abstract base for content loader plug-ins. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content; + use VuFindCode\ISBN; /** diff --git a/module/VuFind/src/VuFind/Content/AbstractCover.php b/module/VuFind/src/VuFind/Content/AbstractCover.php index fd9ee46af21f53ec503c2de8576dd87f7b2b912b..39a6e7b26b714ce0b1b057698e635f6928c2ceda 100644 --- a/module/VuFind/src/VuFind/Content/AbstractCover.php +++ b/module/VuFind/src/VuFind/Content/AbstractCover.php @@ -2,7 +2,7 @@ /** * Abstract base for cover loader plug-ins. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/AbstractSyndetics.php b/module/VuFind/src/VuFind/Content/AbstractSyndetics.php index 6da5c441838da66bc22e8326f466ac124ee79bd7..522459e3324e7be09d6306e0088766cc25f7e3d6 100644 --- a/module/VuFind/src/VuFind/Content/AbstractSyndetics.php +++ b/module/VuFind/src/VuFind/Content/AbstractSyndetics.php @@ -2,7 +2,7 @@ /** * Abstract base for Syndetics content loader plug-ins. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content; + use DOMDocument; /** @@ -82,7 +83,7 @@ abstract class AbstractSyndetics extends AbstractBase * @return \Zend\Http\Client * @throws \Exception */ - protected function getHttpClient($url) + protected function getHttpClient($url = null) { $client = parent::getHttpClient($url); $client->setOptions(['timeout' => $this->timeout]); diff --git a/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php b/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3abb04f083cf4629994f8d4a53accf9540179a9a --- /dev/null +++ b/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php @@ -0,0 +1,79 @@ +<?php +/** + * Generic Syndetics content plugin factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Content; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic Syndetics content plugin factory. + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractSyndeticsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + + // Special case: if the class name ends in Plus, we need to strip off + // the "Plus" and instead configure the base Syndetics class into "plus" + // mode. + $plus = substr($requestedName, -4) === 'Plus'; + $className = $plus + ? substr($requestedName, 0, strlen($requestedName) - 4) : $requestedName; + + return new $className( + isset($config->Syndetics->use_ssl) && $config->Syndetics->use_ssl, + $plus, + isset($config->Syndetics->timeout) ? $config->Syndetics->timeout : 10 + ); + } +} diff --git a/module/VuFind/src/VuFind/Content/AuthorNotes/Demo.php b/module/VuFind/src/VuFind/Content/AuthorNotes/Demo.php new file mode 100644 index 0000000000000000000000000000000000000000..dc01d324a886bdc0c74e93af1bc0dc3392105acd --- /dev/null +++ b/module/VuFind/src/VuFind/Content/AuthorNotes/Demo.php @@ -0,0 +1,59 @@ +<?php +/** + * Demo (fake data) author notes content loader. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\AuthorNotes; + +/** + * Demo (fake data) author notes content loader. + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Demo extends \VuFind\Content\AbstractBase +{ + /** + * This method is responsible for generating fake author note data for testing + * purposes. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with table of contents data. + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + return [ + ['Content' => 'Demo author note key: ' . $key], + ['Content' => 'Demo author note ISBN: ' . $isbnObj->get13()], + ]; + } +} diff --git a/module/VuFind/src/VuFind/Content/AuthorNotes/Factory.php b/module/VuFind/src/VuFind/Content/AuthorNotes/Factory.php deleted file mode 100644 index 599459bce8e5906b705d89b17f7d79102d0898ca..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Content/AuthorNotes/Factory.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Factory for instantiating content loaders - * - * PHP version 5 - * - * Copyright (C) Villanova University 2009. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Content\AuthorNotes; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for instantiating content loaders - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Create either a Syndetics or SyndeticsPlus loader - * - * @param ServiceManager $sm Service manager - * @param bool $plus Instantiate in Syndetics Plus mode? - * - * @return mixed - */ - public static function getAbstractSyndetics(ServiceManager $sm, $plus) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Syndetics( - isset($config->Syndetics->use_ssl) && $config->Syndetics->use_ssl, - $plus, - isset($config->Syndetics->timeout) ? $config->Syndetics->timeout : 10 - ); - } - - /** - * Create Syndetics loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndetics(ServiceManager $sm) - { - return static::getAbstractSyndetics($sm, false); - } - - /** - * Create SyndeticsPlus loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndeticsPlus(ServiceManager $sm) - { - return static::getAbstractSyndetics($sm, true); - } -} diff --git a/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php b/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php index 67c52c12434869fc6d5f004ee040e151b086f767..75bee998b92f0a648559c2d24b6692a9686f370e 100644 --- a/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php @@ -2,7 +2,7 @@ /** * Author notes content loader plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,31 @@ namespace VuFind\Content\AuthorNotes; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'demo' => 'VuFind\Content\AuthorNotes\Demo', + 'syndetics' => 'VuFind\Content\AuthorNotes\Syndetics', + 'syndeticsplus' => 'VuFind\Content\AuthorNotes\SyndeticsPlus', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Content\AuthorNotes\Demo' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\AuthorNotes\Syndetics' => + 'VuFind\Content\AbstractSyndeticsFactory', + 'VuFind\Content\AuthorNotes\SyndeticsPlus' => + 'VuFind\Content\AbstractSyndeticsFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Content/AuthorNotes/Syndetics.php b/module/VuFind/src/VuFind/Content/AuthorNotes/Syndetics.php index 521427ec21c4130dc4a04c9ac0f5ae34ff95d10d..ec89bf37771966d80d7773b933523aaaa8d286b0 100644 --- a/module/VuFind/src/VuFind/Content/AuthorNotes/Syndetics.php +++ b/module/VuFind/src/VuFind/Content/AuthorNotes/Syndetics.php @@ -2,7 +2,7 @@ /** * Syndetics author notes content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/Amazon.php b/module/VuFind/src/VuFind/Content/Covers/Amazon.php index 1896efda924aa423e7320c273e0ff6bb4fda7f44..e85d1cafa4d7c7b06bc7e81c95b51de19a2095b7 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Amazon.php +++ b/module/VuFind/src/VuFind/Content/Covers/Amazon.php @@ -2,7 +2,7 @@ /** * Amazon cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content\Covers; + use ZendService\Amazon\Amazon as AmazonService; /** diff --git a/module/VuFind/src/VuFind/Content/Covers/Booksite.php b/module/VuFind/src/VuFind/Content/Covers/Booksite.php index 787e4fe217a7159ae71dd8f018a50ccc5d08aafe..9a6cbadbb99a57915bdeba90ec16d6fcd37603d2 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Booksite.php +++ b/module/VuFind/src/VuFind/Content/Covers/Booksite.php @@ -2,7 +2,7 @@ /** * Booksite cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/Buchhandel.php b/module/VuFind/src/VuFind/Content/Covers/Buchhandel.php index d75ab2bd2863d95cb7547c07da4207ed388f3523..4453bffc6587e3f71fb9ab9397f1d2c3190abf73 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Buchhandel.php +++ b/module/VuFind/src/VuFind/Content/Covers/Buchhandel.php @@ -2,7 +2,7 @@ /** * Buchhandel cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php b/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php index 7a39eff5c4bb20b6d1ba800c408b676fa1fa7da1..276e492013f2ae0addb027a7322be77bd2acb8fb 100644 --- a/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php +++ b/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php @@ -2,7 +2,7 @@ /** * ContentCafe cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/Factory.php b/module/VuFind/src/VuFind/Content/Covers/Factory.php index 9d62e7fa63c4d34b030fa9f763da49c516ffd135..780403779c9eed39ef0705104371392c5e7cd29e 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Factory.php +++ b/module/VuFind/src/VuFind/Content/Covers/Factory.php @@ -2,7 +2,7 @@ /** * Factory for instantiating content loaders * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content\Covers; + use Zend\ServiceManager\ServiceManager; /** @@ -50,7 +51,7 @@ class Factory */ public static function getAmazon(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $associate = isset($config->Content->amazonassociate) ? $config->Content->amazonassociate : null; $secret = isset($config->Content->amazonsecret) @@ -67,7 +68,7 @@ class Factory */ public static function getBooksite(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $url = isset($config->Booksite->url) ? $config->Booksite->url : 'https://api.booksite.com'; if (!isset($config->Booksite->key)) { @@ -85,7 +86,7 @@ class Factory */ public static function getBuchhandel(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $url = isset($config->Buchhandel->url) ? trim($config->Buchhandel->url, '/') . '/' : 'https://api.vlb.de/api/v1/cover/'; @@ -104,7 +105,7 @@ class Factory */ public static function getContentCafe(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $finalConfig = isset($config->Contentcafe) ? $config->Contentcafe : new \Zend\Config\Config([]); return new ContentCafe($finalConfig); @@ -119,7 +120,7 @@ class Factory */ public static function getSyndetics(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); return new Syndetics( isset($config->Syndetics->use_ssl) && $config->Syndetics->use_ssl ); diff --git a/module/VuFind/src/VuFind/Content/Covers/Google.php b/module/VuFind/src/VuFind/Content/Covers/Google.php index 3242303bba65a705a964e56a8a924de4ed635a94..2267f00000ddd7d63a50af440363504287ab641f 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Google.php +++ b/module/VuFind/src/VuFind/Content/Covers/Google.php @@ -2,7 +2,7 @@ /** * Google cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -56,7 +56,7 @@ class Google extends \VuFind\Content\AbstractCover * * @return \Zend\Http\Client */ - protected function getHttpClient($url) + protected function getHttpClient($url = null) { if (null === $this->httpService) { throw new \Exception('HTTP service missing.'); diff --git a/module/VuFind/src/VuFind/Content/Covers/LibraryThing.php b/module/VuFind/src/VuFind/Content/Covers/LibraryThing.php index 2ec6d2f3a81f87cf58ff090f3113c1c2119a1575..9e71d1ea0db5a9cddd9ba81477a9d474c309874e 100644 --- a/module/VuFind/src/VuFind/Content/Covers/LibraryThing.php +++ b/module/VuFind/src/VuFind/Content/Covers/LibraryThing.php @@ -2,7 +2,7 @@ /** * LibraryThing cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/LocalFile.php b/module/VuFind/src/VuFind/Content/Covers/LocalFile.php index 94d5c30e5b89bc16a4b0b7f890f555fd73cb2de5..7ebc916bcf8455cf9e5efcf2287fb3a7da0a6813 100644 --- a/module/VuFind/src/VuFind/Content/Covers/LocalFile.php +++ b/module/VuFind/src/VuFind/Content/Covers/LocalFile.php @@ -2,7 +2,7 @@ /** * LocalFile cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -144,7 +144,7 @@ class LocalFile extends \VuFind\Content\AbstractCover foreach ([$val, strtoupper($val), ucwords($val)] as $finalVal) { $checkFile = str_replace('%anyimage%', $finalVal, $fileName); if (file_exists($checkFile)) { - return $checkFile; + return $checkFile; } } } diff --git a/module/VuFind/src/VuFind/Content/Covers/OpenLibrary.php b/module/VuFind/src/VuFind/Content/Covers/OpenLibrary.php index d15098b8e009e57663e62402ec3f2221ba3003c4..42982b6b772fd6affe645d00b8592043c54ea939 100644 --- a/module/VuFind/src/VuFind/Content/Covers/OpenLibrary.php +++ b/module/VuFind/src/VuFind/Content/Covers/OpenLibrary.php @@ -2,7 +2,7 @@ /** * OpenLibrary cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/PluginManager.php b/module/VuFind/src/VuFind/Content/Covers/PluginManager.php index d09f6e0d1031f3dbfca731e30188eeb8c1bc4976..0b26f0d95f07479d1c5ba8a3ef275d1b838a92f5 100644 --- a/module/VuFind/src/VuFind/Content/Covers/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Covers/PluginManager.php @@ -2,7 +2,7 @@ /** * Covers content loader plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,51 @@ namespace VuFind\Content\Covers; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'amazon' => 'VuFind\Content\Covers\Amazon', + 'booksite' => 'VuFind\Content\Covers\Booksite', + 'buchhandel' => 'VuFind\Content\Covers\Buchhandel', + 'contentcafe' => 'VuFind\Content\Covers\ContentCafe', + 'google' => 'VuFind\Content\Covers\Google', + 'librarything' => 'VuFind\Content\Covers\LibraryThing', + 'localfile' => 'VuFind\Content\Covers\LocalFile', + 'openlibrary' => 'VuFind\Content\Covers\OpenLibrary', + 'summon' => 'VuFind\Content\Covers\Summon', + 'syndetics' => 'VuFind\Content\Covers\Syndetics', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Content\Covers\Amazon' => 'VuFind\Content\Covers\Factory::getAmazon', + 'VuFind\Content\Covers\Booksite' => + 'VuFind\Content\Covers\Factory::getBooksite', + 'VuFind\Content\Covers\Buchhandel' => + 'VuFind\Content\Covers\Factory::getBuchhandel', + 'VuFind\Content\Covers\ContentCafe' => + 'VuFind\Content\Covers\Factory::getContentCafe', + 'VuFind\Content\Covers\Google' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Covers\LibraryThing' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Covers\LocalFile' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Covers\OpenLibrary' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Covers\Summon' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Covers\Syndetics' => + 'VuFind\Content\Covers\Factory::getSyndetics', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Content/Covers/Summon.php b/module/VuFind/src/VuFind/Content/Covers/Summon.php index a08ae44ce3fa0885dfd194999e99521a978d7998..c5b6e8d94e9e0e29133398318ae92169c7b2eb47 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Summon.php +++ b/module/VuFind/src/VuFind/Content/Covers/Summon.php @@ -2,7 +2,7 @@ /** * Summon cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Covers/Syndetics.php b/module/VuFind/src/VuFind/Content/Covers/Syndetics.php index 5f7095ba4d712954bc52d6db5ba0cf7493e5e2f0..fcc24e54ed9cc42831ea6874ef3b4f1e5fb5a611 100644 --- a/module/VuFind/src/VuFind/Content/Covers/Syndetics.php +++ b/module/VuFind/src/VuFind/Content/Covers/Syndetics.php @@ -2,7 +2,7 @@ /** * Syndetics cover content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Excerpts/Demo.php b/module/VuFind/src/VuFind/Content/Excerpts/Demo.php new file mode 100644 index 0000000000000000000000000000000000000000..0f2b1247977914a73a2f43dad1a42ac20ba8006f --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Excerpts/Demo.php @@ -0,0 +1,59 @@ +<?php +/** + * Demo (fake data) excerpts content loader. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\Excerpts; + +/** + * Demo (fake data) excerpts content loader. + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Demo extends \VuFind\Content\AbstractBase +{ + /** + * This method is responsible for generating fake author note data for testing + * purposes. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with table of contents data. + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + return [ + ['Content' => 'Demo excerpt key: ' . $key], + ['Content' => 'Demo excerpt ISBN: ' . $isbnObj->get13()], + ]; + } +} diff --git a/module/VuFind/src/VuFind/Content/Excerpts/Factory.php b/module/VuFind/src/VuFind/Content/Excerpts/Factory.php deleted file mode 100644 index cc34ec49ae602f1624c900ec1eba4dbe62916970..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Content/Excerpts/Factory.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Factory for instantiating content loaders - * - * PHP version 5 - * - * Copyright (C) Villanova University 2009. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Content\Excerpts; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for instantiating content loaders - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Create either a Syndetics or SyndeticsPlus loader - * - * @param ServiceManager $sm Service manager - * @param bool $plus Instantiate in Syndetics Plus mode? - * - * @return mixed - */ - public static function getAbstractSyndetics(ServiceManager $sm, $plus) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Syndetics( - isset($config->Syndetics->use_ssl) && $config->Syndetics->use_ssl, - $plus, - isset($config->Syndetics->timeout) ? $config->Syndetics->timeout : 10 - ); - } - - /** - * Create Syndetics loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndetics(ServiceManager $sm) - { - return static::getAbstractSyndetics($sm, false); - } - - /** - * Create SyndeticsPlus loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndeticsPlus(ServiceManager $sm) - { - return static::getAbstractSyndetics($sm, true); - } -} diff --git a/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php b/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php index 10e0be3e3a1ffcd9823bc1f5c44b0e99fd2f5557..fd5b3da8e6818d22095c654021ccfe68a8437e62 100644 --- a/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php @@ -2,7 +2,7 @@ /** * Excerpts content loader plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,31 @@ namespace VuFind\Content\Excerpts; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'demo' => 'VuFind\Content\Excerpts\Demo', + 'syndetics' => 'VuFind\Content\Excerpts\Syndetics', + 'syndeticsplus' => 'VuFind\Content\Excerpts\SyndeticsPlus', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Content\Excerpts\Demo' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Excerpts\Syndetics' => + 'VuFind\Content\AbstractSyndeticsFactory', + 'VuFind\Content\Excerpts\SyndeticsPlus' => + 'VuFind\Content\AbstractSyndeticsFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Content/Excerpts/Syndetics.php b/module/VuFind/src/VuFind/Content/Excerpts/Syndetics.php index 07ffb6564ce4af45fa7402b8a48c64f5ae24cc0d..3b98839470b442364c8d975e000c83fbcafa714b 100644 --- a/module/VuFind/src/VuFind/Content/Excerpts/Syndetics.php +++ b/module/VuFind/src/VuFind/Content/Excerpts/Syndetics.php @@ -2,7 +2,7 @@ /** * Syndetics excerpt content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Factory.php b/module/VuFind/src/VuFind/Content/Factory.php index 63e874de7bd1d9b4cbe78881a3a9b23aec2f62b4..8e2bc7b44027fe27c339104431170cd6b8779313 100644 --- a/module/VuFind/src/VuFind/Content/Factory.php +++ b/module/VuFind/src/VuFind/Content/Factory.php @@ -2,7 +2,7 @@ /** * Factory for instantiating content loaders * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content; + use Zend\ServiceManager\ServiceManager; /** @@ -50,9 +51,8 @@ class Factory */ public static function getAuthorNotes(ServiceManager $sm) { - $loader = $sm->getServiceLocator() - ->get('VuFind\ContentAuthorNotesPluginManager'); - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $loader = $sm->get('VuFind\Content\AuthorNotes\PluginManager'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $providers = isset($config->Content->authorNotes) ? $config->Content->authorNotes : ''; return new Loader($loader, $providers); @@ -67,9 +67,8 @@ class Factory */ public static function getExcerpts(ServiceManager $sm) { - $loader = $sm->getServiceLocator() - ->get('VuFind\ContentExcerptsPluginManager'); - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $loader = $sm->get('VuFind\Content\Excerpts\PluginManager'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $providers = isset($config->Content->excerpts) ? $config->Content->excerpts : ''; return new Loader($loader, $providers); @@ -84,11 +83,42 @@ class Factory */ public static function getReviews(ServiceManager $sm) { - $loader = $sm->getServiceLocator() - ->get('VuFind\ContentReviewsPluginManager'); - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $loader = $sm->get('VuFind\Content\Reviews\PluginManager'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $providers = isset($config->Content->reviews) ? $config->Content->reviews : ''; return new Loader($loader, $providers); } + + /** + * Create Summaries loader + * + * @param ServiceManager $sm Service manager + * + * @return mixed + */ + public static function getSummaries(ServiceManager $sm) + { + $loader = $sm->get('VuFind\Content\Summaries\PluginManager'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); + $providers = isset($config->Content->summaries) + ? $config->Content->summaries : ''; + return new Loader($loader, $providers); + } + + /** + * Create TOC loader + * + * @param ServiceManager $sm Service manager + * + * @return mixed + */ + public static function getTOC(ServiceManager $sm) + { + $loader = $sm->get('VuFind\Content\TOC\PluginManager'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); + $providers = isset($config->Content->toc) + ? $config->Content->toc : ''; + return new Loader($loader, $providers); + } } diff --git a/module/VuFind/src/VuFind/Content/Loader.php b/module/VuFind/src/VuFind/Content/Loader.php index 4ccc2b63396c246cec5fdcfa7aa847104836aea2..747cf0cbd471945791bb2f65822f36cbf817bf9e 100644 --- a/module/VuFind/src/VuFind/Content/Loader.php +++ b/module/VuFind/src/VuFind/Content/Loader.php @@ -2,7 +2,7 @@ /** * Third-party content loader * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content; + use VuFind\ServiceManager\AbstractPluginManager; /** @@ -99,7 +100,7 @@ class Loader $parts = explode(':', trim($provider)); $provider = $parts[0]; if (!empty($provider)) { - $key = isset($parts[1]) ? $parts[1] : ''; + $key = $parts[1] ?? ''; try { $plugin = $this->loader->get($provider); $results[$provider] = $plugin->loadByIsbn($key, $isbnObj); diff --git a/module/VuFind/src/VuFind/Content/PluginManager.php b/module/VuFind/src/VuFind/Content/PluginManager.php index 3277fd31118a3738f7c479d006fb71afc616e9bd..94a2e457ee053640ed46a2c2f0e8226b82590f92 100644 --- a/module/VuFind/src/VuFind/Content/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/PluginManager.php @@ -2,7 +2,7 @@ /** * Content loader plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,19 @@ namespace VuFind\Content; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'authornotes' => 'VuFind\Content\Factory::getAuthorNotes', + 'excerpts' => 'VuFind\Content\Factory::getExcerpts', + 'reviews' => 'VuFind\Content\Factory::getReviews', + 'summaries' => 'VuFind\Content\Factory::getSummaries', + 'toc' => 'VuFind\Content\Factory::getTOC', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Content/Reviews/Amazon.php b/module/VuFind/src/VuFind/Content/Reviews/Amazon.php index 40d746f76517cbf91ced442cfe0d9788f88c4f00..fc73673bbeb32644085bca572db2d808820fa0d5 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/Amazon.php +++ b/module/VuFind/src/VuFind/Content/Reviews/Amazon.php @@ -2,7 +2,7 @@ /** * Amazon review content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Reviews/AmazonEditorial.php b/module/VuFind/src/VuFind/Content/Reviews/AmazonEditorial.php index d9fca065a6d02c39d77e6266ac0040ee36077a61..16720deed7ccf3a36e27e96bd052d9b5452fbe97 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/AmazonEditorial.php +++ b/module/VuFind/src/VuFind/Content/Reviews/AmazonEditorial.php @@ -2,7 +2,7 @@ /** * Amazon review content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Content\Reviews; + use ZendService\Amazon\Amazon; /** diff --git a/module/VuFind/src/VuFind/Content/Reviews/Booksite.php b/module/VuFind/src/VuFind/Content/Reviews/Booksite.php index e494071220109648bced42936097a0b0a76e1bb2..81c25f434f8abc74c5a129da09f6a92819d07200 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/Booksite.php +++ b/module/VuFind/src/VuFind/Content/Reviews/Booksite.php @@ -2,7 +2,7 @@ /** * Booksite review content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -101,8 +101,8 @@ class BookSite extends \VuFind\Content\AbstractBase $i = 0; $json = json_decode($response->getBody()); foreach ($json as $source => $values) { - $reviews[$i]['Source' ] = $source; - $reviews[$i]['Date' ] = (string)$values->reviewDate; + $reviews[$i]['Source'] = $source; + $reviews[$i]['Date'] = (string)$values->reviewDate; $reviews[$i]['Content'] = (string)$values->reviewText; $i++; } diff --git a/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php b/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c2363e1846be0f1c42578eaf293e4094e12892c7 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Booksite review plugin factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Content\Reviews; + +use Interop\Container\ContainerInterface; + +/** + * Booksite review plugin factory. + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class BooksiteFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $url = isset($config->Booksite->url) + ? $config->Booksite->url : 'https://api.booksite.com'; + if (!isset($config->Booksite->key)) { + throw new \Exception("Booksite 'key' not set in VuFind config"); + } + return new $requestedName($url, $config->Booksite->key); + } +} diff --git a/module/VuFind/src/VuFind/Content/Reviews/Demo.php b/module/VuFind/src/VuFind/Content/Reviews/Demo.php new file mode 100644 index 0000000000000000000000000000000000000000..e59314fb23e4b5da3c1cb5e79ee1ba55708a48b6 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Reviews/Demo.php @@ -0,0 +1,59 @@ +<?php +/** + * Demo (fake data) reviews content loader. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\Reviews; + +/** + * Demo (fake data) reviews content loader. + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Demo extends \VuFind\Content\AbstractBase +{ + /** + * This method is responsible for generating fake author note data for testing + * purposes. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with table of contents data. + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + return [ + ['Content' => 'Demo review key: ' . $key], + ['Content' => 'Demo review ISBN: ' . $isbnObj->get13()], + ]; + } +} diff --git a/module/VuFind/src/VuFind/Content/Reviews/Factory.php b/module/VuFind/src/VuFind/Content/Reviews/Factory.php deleted file mode 100644 index 17f516442b2dc882700e759943e1fbc0ed8be013..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Content/Reviews/Factory.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php -/** - * Factory for instantiating content loaders - * - * PHP version 5 - * - * Copyright (C) Villanova University 2009. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Content\Reviews; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for instantiating content loaders - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Create either a Syndetics or SyndeticsPlus loader - * - * @param ServiceManager $sm Service manager - * @param bool $plus Instantiate in Syndetics Plus mode? - * - * @return mixed - */ - public static function getAbstractSyndetics(ServiceManager $sm, $plus) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Syndetics( - isset($config->Syndetics->use_ssl) && $config->Syndetics->use_ssl, - $plus, - isset($config->Syndetics->timeout) ? $config->Syndetics->timeout : 10 - ); - } - - /** - * Create Amazon loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getAmazon(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $associate = isset($config->Content->amazonassociate) - ? $config->Content->amazonassociate : null; - $secret = isset($config->Content->amazonsecret) - ? $config->Content->amazonsecret : null; - $label = $sm->getServiceLocator()->get('VuFind\Translator')->translate( - 'Supplied by Amazon' - ); - return new Amazon($associate, $secret, $label); - } - - /** - * Create AmazonEditorial loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getAmazonEditorial(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $associate = isset($config->Content->amazonassociate) - ? $config->Content->amazonassociate : null; - $secret = isset($config->Content->amazonsecret) - ? $config->Content->amazonsecret : null; - $label = $sm->getServiceLocator()->get('VuFind\Translator')->translate( - 'Supplied by Amazon' - ); - return new AmazonEditorial($associate, $secret, $label); - } - - /** - * Create Booksite loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getBooksite(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $url = isset($config->Booksite->url) - ? $config->Booksite->url : 'https://api.booksite.com'; - if (!isset($config->Booksite->key)) { - throw new \Exception("Booksite 'key' not set in VuFind config"); - } - return new Booksite($url, $config->Booksite->key); - } - - /** - * Create Syndetics loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndetics(ServiceManager $sm) - { - return static::getAbstractSyndetics($sm, false); - } - - /** - * Create SyndeticsPlus loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndeticsPlus(ServiceManager $sm) - { - return static::getAbstractSyndetics($sm, true); - } -} diff --git a/module/VuFind/src/VuFind/Content/Reviews/Guardian.php b/module/VuFind/src/VuFind/Content/Reviews/Guardian.php index c0afc13ee1e20129b59abbf6302d1c943fd58897..04893aa22c6b5350bba2db5b0df6744af14b0971 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/Guardian.php +++ b/module/VuFind/src/VuFind/Content/Reviews/Guardian.php @@ -2,7 +2,7 @@ /** * Guardian review content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php b/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php index c21ebdab35573ab2cee3c3d1b9376ce2600d2aba..614f8e66abe210fd7b2cbc966ade9b94cc8b83de 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php @@ -2,7 +2,7 @@ /** * Reviews content loader plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,42 @@ namespace VuFind\Content\Reviews; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'amazon' => 'VuFind\Content\Reviews\Amazon', + 'amazoneditorial' => 'VuFind\Content\Reviews\AmazonEditorial', + 'booksite' => 'VuFind\Content\Reviews\Booksite', + 'demo' => 'VuFind\Content\Reviews\Demo', + 'guardian' => 'VuFind\Content\Reviews\Guardian', + 'syndetics' => 'VuFind\Content\Reviews\Syndetics', + 'syndeticsplus' => 'VuFind\Content\Reviews\SyndeticsPlus', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Content\Reviews\Amazon' => 'VuFind\Content\AbstractAmazonFactory', + 'VuFind\Content\Reviews\AmazonEditorial' => + 'VuFind\Content\AbstractAmazonFactory', + 'VuFind\Content\Reviews\Booksite' => + 'VuFind\Content\Reviews\BooksiteFactory', + 'VuFind\Content\Reviews\Demo' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Reviews\Guardian' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Reviews\Syndetics' => + 'VuFind\Content\AbstractSyndeticsFactory', + 'VuFind\Content\Reviews\SyndeticsPlus' => + 'VuFind\Content\AbstractSyndeticsFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Content/Reviews/Syndetics.php b/module/VuFind/src/VuFind/Content/Reviews/Syndetics.php index a65e2c03faa50d9248f14a7131af48f57d8fe8c9..765f5f9ea0f2af5880f4a883f084ca89dc843028 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/Syndetics.php +++ b/module/VuFind/src/VuFind/Content/Reviews/Syndetics.php @@ -2,7 +2,7 @@ /** * Syndetics review content loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Content/Summaries/Demo.php b/module/VuFind/src/VuFind/Content/Summaries/Demo.php new file mode 100644 index 0000000000000000000000000000000000000000..3d309654e0870b48646db8c18849a588b83c2534 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Summaries/Demo.php @@ -0,0 +1,59 @@ +<?php +/** + * Demo (fake data) summaries content loader. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\Summaries; + +/** + * Demo (fake data) summaries content loader. + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Demo extends \VuFind\Content\AbstractBase +{ + /** + * This method is responsible for generating fake summary data for testing + * purposes. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with table of contents data. + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + return [ + 'Demo summary key: ' . $key, + 'Demo summary ISBN: ' . $isbnObj->get13() + ]; + } +} diff --git a/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php b/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..8a1a8ec4414ca3ca8c679480f29747c2397bd6b2 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php @@ -0,0 +1,76 @@ +<?php +/** + * Summaries content loader plugin manager + * + * PHP version 7 + * + * Copyright (C) The University of Chicago 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace VuFind\Content\Summaries; + +/** + * Summaries content loader plugin manager + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'demo' => 'VuFind\Content\Summaries\Demo', + 'syndetics' => 'VuFind\Content\Summaries\Syndetics', + 'syndeticsplus' => 'VuFind\Content\Summaries\SyndeticsPlus', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Content\Summaries\Demo' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\Summaries\Syndetics' => + 'VuFind\Content\AbstractSyndeticsFactory', + 'VuFind\Content\Summaries\SyndeticsPlus' => + 'VuFind\Content\AbstractSyndeticsFactory', + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\Content\AbstractBase'; + } +} diff --git a/module/VuFind/src/VuFind/Content/Summaries/Syndetics.php b/module/VuFind/src/VuFind/Content/Summaries/Syndetics.php new file mode 100644 index 0000000000000000000000000000000000000000..e518f3a0c7e4a82b41da3274d5f23d18f35e3b10 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Summaries/Syndetics.php @@ -0,0 +1,127 @@ +<?php +/** + * Syndetics Summaries content loader. + * + * PHP version 7 + * + * Copyright (C) The University of Chicago 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\Summaries; + +/** + * Syndetics Summaries content loader. + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Syndetics extends \VuFind\Content\AbstractSyndetics +{ + /** + * List of data sources for author notes. + * + * @var array + */ + protected $sourceList = [ + 'AVSUMMARY' => [ + 'title' => 'Summaries', + 'file' => 'AVSUMMARY.XML', + 'div' => '<div id="syn_avsummary"></div>' + ], + 'SUMMARY' => [ + 'title' => 'Summaries', + 'file' => 'SUMMARY.XML', + 'div' => '<div id="syn_summary"></div>' + ], + ]; + + /** + * This method is responsible for connecting to Syndetics for summaries. + * + * It first queries the master url for the ISBN entry seeking a summary URL. + * If a summary URL is found, the script will then use HTTP request to + * retrieve summaries. + * Configuration: Sources are processed in order - refer to $sourceList above. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with summary data. + * @author John Jung <jej@uchicago.edu> + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + $summaries = []; + + // Find out if there are any summaries + $isbn = $this->getIsbn10($isbnObj); + $url = $this->getIsbnUrl($isbn, $key); + $result = $this->getHttpClient($url)->send(); + if (!$result->isSuccess()) { + return $summaries; + } + + // Test XML Response + if (!($xmldoc = $this->xmlToDOMDocument($result->getBody()))) { + throw new \Exception('Invalid XML'); + } + + foreach ($this->sourceList as $source => $sourceInfo) { + $nodes = $xmldoc->getElementsByTagName($source); + if ($nodes->length) { + // Load summary + $url = $this->getIsbnUrl($isbn, $key, $sourceInfo['file']); + $result2 = $this->getHttpClient($url)->send(); + if (!$result2->isSuccess()) { + continue; + } + + // Test XML Response + $xmldoc2 = $this->xmlToDOMDocument($result2->getBody()); + if (!$xmldoc2) { + throw new \Exception('Invalid XML'); + } + + // If we have syndetics plus, we don't actually want the content + // we'll just stick in the relevant div + if ($this->usePlus) { + $summaries[] = $sourceInfo['div']; + } else { + // Get the marc field for summaries. (520) + $nodes = $xmldoc2->GetElementsbyTagName("Fld520"); + foreach ($nodes as $node) { + $summaries[] = preg_replace( + '/<a>|<a [^>]*>|<\/a>/', '', + html_entity_decode($node->nodeValue) + ); + } + } + } + } + + return $summaries; + } +} diff --git a/module/VuFind/src/VuFind/Content/TOC/Demo.php b/module/VuFind/src/VuFind/Content/TOC/Demo.php new file mode 100644 index 0000000000000000000000000000000000000000..c848aea5482a909b7381b928ea2502cdafcbb3d5 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/TOC/Demo.php @@ -0,0 +1,59 @@ +<?php +/** + * Demo (fake data) TOC content loader. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\TOC; + +/** + * Demo (fake data) TOC content loader. + * + * @category VuFind2 + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Demo extends \VuFind\Content\AbstractBase +{ + /** + * This method is responsible for generating fake TOC data for testing + * purposes. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with table of contents data. + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + return [ + 'Demo TOC key: ' . $key, + 'Demo TOC ISBN: ' . $isbnObj->get13() + ]; + } +} diff --git a/module/VuFind/src/VuFind/Content/TOC/PluginManager.php b/module/VuFind/src/VuFind/Content/TOC/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..233a79e5080c88c757eb02bf08cb534870fbb5aa --- /dev/null +++ b/module/VuFind/src/VuFind/Content/TOC/PluginManager.php @@ -0,0 +1,74 @@ +<?php +/** + * TOC content loader plugin manager + * + * PHP version 7 + * + * Copyright (C) The University of Chicago 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace VuFind\Content\TOC; + +/** + * TOC content loader plugin manager + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'demo' => 'VuFind\Content\TOC\Demo', + 'syndetics' => 'VuFind\Content\TOC\Syndetics', + 'syndeticsplus' => 'VuFind\Content\TOC\SyndeticsPlus', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Content\TOC\Demo' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Content\TOC\Syndetics' => 'VuFind\Content\AbstractSyndeticsFactory', + 'VuFind\Content\TOC\SyndeticsPlus' => + 'VuFind\Content\AbstractSyndeticsFactory', + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\Content\AbstractBase'; + } +} diff --git a/module/VuFind/src/VuFind/Content/TOC/Syndetics.php b/module/VuFind/src/VuFind/Content/TOC/Syndetics.php new file mode 100644 index 0000000000000000000000000000000000000000..0d67d97a2e65cd05a7941b23752bf26c011cce66 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/TOC/Syndetics.php @@ -0,0 +1,142 @@ +<?php +/** + * Syndetics TOC content loader. + * + * PHP version 7 + * + * Copyright (C) The University of Chicago 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace VuFind\Content\TOC; + +/** + * Syndetics TOC content loader. + * + * @category VuFind2 + * @package Content + * @author John Jung <jej@uchicago.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Syndetics extends \VuFind\Content\AbstractSyndetics +{ + /** + * List of data sources for author notes. + * + * @var array + */ + protected $sourceList = [ + 'TOC' => [ + 'title' => 'TOC', + 'file' => 'TOC.XML', + 'div' => '<div id="syn_toc"></div>' + ] + ]; + + /** + * This method is responsible for connecting to Syndetics for tables + * of contents. + * + * It first queries the master url for the ISBN entry seeking an excerpt URL. + * If an excerpt URL is found, the script will then use HTTP request to + * retrieve the script. The script will then parse the excerpt according to + * US MARC (I believe). It will provide a link to the URL master HTML page + * for more information. + * Configuration: Sources are processed in order - refer to $sourceList above. + * + * @param string $key API key + * @param \VuFindCode\ISBN $isbnObj ISBN object + * + * @throws \Exception + * @return array Returns array with table of contents data. + * @author John Jung <jej@uchicago.edu> + */ + public function loadByIsbn($key, \VuFindCode\ISBN $isbnObj) + { + // Initialize return value: + $toc = []; + + // Find out if there are any tables of contents + $isbn = $this->getIsbn10($isbnObj); + $url = $this->getIsbnUrl($isbn, $key); + $result = $this->getHttpClient($url)->send(); + if (!$result->isSuccess()) { + return $toc; + } + + // Test XML Response + if (!($xmldoc = $this->xmlToDOMDocument($result->getBody()))) { + throw new \Exception('Invalid XML'); + } + + $i = 0; + foreach ($this->sourceList as $source => $sourceInfo) { + $nodes = $xmldoc->getElementsByTagName($source); + if ($nodes->length) { + // Load toc + $url = $this->getIsbnUrl($isbn, $key, $sourceInfo['file']); + $result2 = $this->getHttpClient($url)->send(); + if (!$result2->isSuccess()) { + continue; + } + + // Test XML Response + $xmldoc2 = $this->xmlToDOMDocument($result2->getBody()); + if (!$xmldoc2) { + throw new \Exception('Invalid XML'); + } + + // If we have syndetics plus, we don't actually want the content + // we'll just stick in the relevant div + if ($this->usePlus) { + $toc = $sourceInfo['div']; + } else { + // Get the marc field for toc (970) + $nodes = $xmldoc2->GetElementsbyTagName("Fld970"); + + foreach ($nodes as $node) { + $li = ''; + + // Chapter labels. + $nodeList = $node->getElementsByTagName('l'); + if ($nodeList->length > 0) { + $li .= sprintf("%s. ", $nodeList->item(0)->nodeValue); + } + + // Chapter title. + $nodeList = $node->getElementsByTagName('t'); + if ($nodeList->length > 0) { + $li .= $nodeList->item(0)->nodeValue; + } + + $toc[] = preg_replace( + '/<a>|<a [^>]*>|<\/a>/', '', + html_entity_decode($li) + ); + } + } + $i++; + } + } + + return $toc; + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/AbstractBase.php b/module/VuFind/src/VuFind/ContentBlock/AbstractBase.php new file mode 100644 index 0000000000000000000000000000000000000000..baa930a78de129c75a11020340716b27e2813e3b --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/AbstractBase.php @@ -0,0 +1,70 @@ +<?php +/** + * Abstract base content block. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +/** + * Abstract base content block. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class AbstractBase implements ContentBlockInterface +{ + /** + * Configuration + * + * @var string + */ + protected $config = ''; + + /** + * Store the configuration of the content block. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + $this->config = $settings; + } + + /** + * Return context variables used for rendering the block's template. + * + * @return array + */ + public function getContext() + { + // Expose the block object directly by default. + return ['block' => $this]; + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/BlockLoader.php b/module/VuFind/src/VuFind/ContentBlock/BlockLoader.php new file mode 100644 index 0000000000000000000000000000000000000000..2491d91e3e5b328b9f74954a778cba857af70d41 --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/BlockLoader.php @@ -0,0 +1,147 @@ +<?php +/** + * Content block loader + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\ContentBlock\PluginManager as BlockManager; +use VuFind\Search\Base\Options; +use VuFind\Search\Options\PluginManager as OptionsManager; +use Zend\Config\Config; + +/** + * Content block plugin manager + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class BlockLoader +{ + /** + * Options manager. + * + * @var OptionsManager + */ + protected $optionsManager; + + /** + * Config manager. + * + * @var ConfigManager + */ + protected $configManager; + + /** + * Block manager. + * + * @var BlockManager + */ + protected $blockManager; + + /** + * Constructor + * + * @param OptionsManager $om Options manager + * @param ConfigManager $cm Config manager + * @param BlockManager $bm Block manager + */ + public function __construct(OptionsManager $om, ConfigManager $cm, + BlockManager $bm + ) { + $this->optionsManager = $om; + $this->configManager = $cm; + $this->blockManager = $bm; + } + + /** + * Fetch blocks using a search class ID. + * + * @param string $searchClassId Search class ID + * + * @return array + */ + public function getFromSearchClassId($searchClassId) + { + $options = $this->optionsManager->get($searchClassId); + return $this->getFromOptions($options); + } + + /** + * Fetch blocks using an Options object. + * + * @param Options $options Options object + * + * @return array + */ + public function getFromOptions(Options $options) + { + return $this->getFromConfig($options->getSearchIni()); + } + + /** + * Fetch blocks using a configuration name + * + * @param string $name Configuration name + * @param string $section Section to load from object + * @param string $setting Setting to load from section + * + * @return array + */ + public function getFromConfig($name, $section = 'HomePage', + $setting = 'content' + ) { + $config = $this->configManager->get($name); + return $this->getFromConfigObject($config, $section, $setting); + } + + /** + * Fetch blocks using Config object. + * + * @param Config $config Configuration object + * @param string $section Section to load from object + * @param string $setting Setting to load from section + * + * @return array + */ + public function getFromConfigObject(Config $config, $section = 'HomePage', + $setting = 'content' + ) { + $blocks = []; + if (isset($config->$section->$setting)) { + foreach ($config->$section->$setting as $current) { + $parts = explode(':', $current, 2); + $block = $this->blockManager->get($parts[0]); + $block->setConfig($parts[1] ?? null); + $blocks[] = $block; + } + } + return $blocks; + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php b/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4ad84522ef75833254d1a8a9bc713d7201740568 --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * BlockLoader factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * BlockLoader factory. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class BlockLoaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Search\Options\PluginManager'), + $container->get('VuFind\Config\PluginManager'), + $container->get('VuFind\ContentBlock\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/Channels.php b/module/VuFind/src/VuFind/ContentBlock/Channels.php new file mode 100644 index 0000000000000000000000000000000000000000..b258db76d3030f5775df2e4436a477537e06cd8b --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/Channels.php @@ -0,0 +1,102 @@ +<?php +/** + * Channels content block. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +use VuFind\ChannelProvider\ChannelLoader; +use Zend\Http\PhpEnvironment\Request; + +/** + * Channels content block. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class Channels implements ContentBlockInterface +{ + /** + * Request object + * + * @var Request + */ + protected $request; + + /** + * Channel loader + * + * @var ChannelLoader + */ + protected $loader; + + /** + * Data source (null to use default found in channels.ini) + * + * @var string + */ + protected $source = null; + + /** + * Constructor + * + * @param Request $request Request object + * @param ChannelLoader $loader Channel loader + */ + public function __construct(Request $request, ChannelLoader $loader) + { + $this->request = $request; + $this->loader = $loader; + } + + /** + * Store the configuration of the content block. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + if (!empty($settings)) { + $this->source = $settings; + } + } + + /** + * Return context variables used for rendering the block's template. + * + * @return array + */ + public function getContext() + { + $activeChannel = $this->request->getQuery()->get('channelProvider'); + $token = $this->request->getQuery()->get('channelToken'); + return $this->loader->getHomeContext($token, $activeChannel, $this->source); + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php b/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..39cb267cc2daf1f8c91df892b829ecaf7bceb93c --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Channels factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Channels factory. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class ChannelsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('Request'), + $container->get('VuFind\ChannelProvider\ChannelLoader') + ); + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/ContentBlockInterface.php b/module/VuFind/src/VuFind/ContentBlock/ContentBlockInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..8ed451f42f359a51508477f1534fc0755a2e6ab5 --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/ContentBlockInterface.php @@ -0,0 +1,56 @@ +<?php +/** + * Content block interface + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +/** + * Content block interface + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +interface ContentBlockInterface +{ + /** + * Store the configuration of the content block. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings); + + /** + * Return context variables used for rendering the block's template. + * + * @return array + */ + public function getContext(); +} diff --git a/module/VuFind/src/VuFind/ContentBlock/FacetList.php b/module/VuFind/src/VuFind/ContentBlock/FacetList.php new file mode 100644 index 0000000000000000000000000000000000000000..f8673e001623cb4e467d392d8975f9aee690eddd --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/FacetList.php @@ -0,0 +1,148 @@ +<?php +/** + * FacetList content block. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\Search\FacetCache\PluginManager as FacetCacheManager; +use Zend\Config\Config; + +/** + * FacetList content block. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class FacetList implements ContentBlockInterface +{ + /** + * Number of values to put in each column of results. + * + * @var int + */ + protected $columnSize = 10; + + /** + * Search class ID to use for retrieving facets. + * + * @var string + */ + protected $searchClassId = 'Solr'; + + /** + * Configuration manager + * + * @var ConfigManager + */ + protected $configManager; + + /** + * Facet cache plugin manager + * + * @var FacetCacheManager + */ + protected $facetCacheManager; + + /** + * Constructor + * + * @param FacetCacheManager $fcm Facet cache plugin manager + * @param ConfigManager $cm Configuration manager + */ + public function __construct(FacetCacheManager $fcm, ConfigManager $cm) + { + $this->facetCacheManager = $fcm; + $this->configManager = $cm; + } + + /** + * Get an array of hierarchical facets + * + * @param Config $facetConfig Facet configuration object. + * + * @return array Facets + */ + protected function getHierarchicalFacets($facetConfig) + { + return isset($facetConfig->SpecialFacets->hierarchical) + ? $facetConfig->SpecialFacets->hierarchical->toArray() + : []; + } + + /** + * Get hierarchical facet sort settings + * + * @param Config $facetConfig Facet configuration object. + * + * @return array Array of sort settings keyed by facet + */ + protected function getHierarchicalFacetSortSettings($facetConfig) + { + return isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions) + ? $facetConfig->SpecialFacets->hierarchicalFacetSortOptions->toArray() + : []; + } + + /** + * Store the configuration of the content block. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + $parts = explode(':', $settings); + $this->searchClassId = empty($parts[0]) ? $this->searchClassId : $parts[0]; + $this->columnSize = $parts[1] ?? $this->columnSize; + } + + /** + * Return context variables used for rendering the block's template. + * + * @return array + */ + public function getContext() + { + $facetCache = $this->facetCacheManager->get($this->searchClassId); + $results = $facetCache->getResults(); + $facetConfig = $this->configManager + ->get($results->getOptions()->getFacetsIni()); + return [ + 'searchClassId' => $this->searchClassId, + 'columnSize' => $this->columnSize, + 'facetList' => $facetCache->getList('HomePage'), + 'hierarchicalFacets' => $this->getHierarchicalFacets($facetConfig), + 'hierarchicalFacetSortOptions' => + $this->getHierarchicalFacetSortSettings($facetConfig), + 'results' => $results, + ]; + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php b/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..573fd4dc9f65cba4053ce39d254c24e731cbd02f --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * FacetList content block factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * FacetList content block factory. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class FacetListFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $fcpm = $container->get('VuFind\Search\FacetCache\PluginManager'); + $cm = $container->get('VuFind\Config\PluginManager'); + return new $requestedName($fcpm, $cm); + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/IlsStatusMonitor.php b/module/VuFind/src/VuFind/ContentBlock/IlsStatusMonitor.php new file mode 100644 index 0000000000000000000000000000000000000000..02a6b190756c5d0df456f7de862d581363a87103 --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/IlsStatusMonitor.php @@ -0,0 +1,70 @@ +<?php +/** + * ILS status monitor content block. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +/** + * Abstract base content block. + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class IlsStatusMonitor implements ContentBlockInterface +{ + /** + * Target selector for status message. + * + * @var string + */ + protected $target = '.searchHomeContent'; + + /** + * Store the configuration of the content block. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + $this->target = empty($settings) ? $this->target : $settings; + } + + /** + * Return context variables used for rendering the block's template. + * + * @return array + */ + public function getContext() + { + // Expose the block object directly by default. + return ['target' => $this->target]; + } +} diff --git a/module/VuFind/src/VuFind/ContentBlock/PluginManager.php b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..8a99e18eecf1b2bcb637ed2d74a67ac78f4217e0 --- /dev/null +++ b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php @@ -0,0 +1,93 @@ +<?php +/** + * Content block plugin manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\ContentBlock; + +/** + * Content block plugin manager + * + * @category VuFind + * @package ContentBlock + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'channels' => 'VuFind\ContentBlock\Channels', + 'facetlist' => 'VuFind\ContentBlock\FacetList', + 'ilsstatusmonitor' => 'VuFind\ContentBlock\IlsStatusMonitor', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\ContentBlock\Channels' => 'VuFind\ContentBlock\ChannelsFactory', + 'VuFind\ContentBlock\FacetList' => 'VuFind\ContentBlock\FacetListFactory', + 'VuFind\ContentBlock\IlsStatusMonitor' => + 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + // These objects are not meant to be shared -- every time we retrieve one, + // we are building a brand new object. + $this->sharedByDefault = false; + + parent::__construct($configOrContainerInstance, $v3config); + } + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\ContentBlock\ContentBlockInterface'; + } +} diff --git a/module/VuFind/src/VuFind/Controller/AbstractBase.php b/module/VuFind/src/VuFind/Controller/AbstractBase.php index 1236a31c84ca66ddb3b71becab7967dcf7418041..033d9f56ff8c2800c94e62c4e483334728633531 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractBase.php +++ b/module/VuFind/src/VuFind/Controller/AbstractBase.php @@ -3,7 +3,7 @@ * VuFind controller base class (defines some methods that can be shared by other * controllers). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,14 +28,12 @@ */ namespace VuFind\Controller; -use VuFind\Exception\Forbidden as ForbiddenException, - VuFind\Exception\ILS as ILSException, - Zend\Mvc\Controller\AbstractActionController, - Zend\Mvc\MvcEvent, - Zend\ServiceManager\ServiceLocatorInterface, - Zend\View\Model\ViewModel, - ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; +use VuFind\Exception\ILS as ILSException; +use Zend\Mvc\Controller\AbstractActionController; +use Zend\Mvc\MvcEvent; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\View\Model\ViewModel; +use ZfcRbac\Service\AuthorizationServiceAwareInterface; /** * VuFind controller base class (defines some methods that can be shared by other @@ -69,6 +67,13 @@ class AbstractBase extends AbstractActionController */ protected $accessDeniedBehavior = null; + /** + * Service manager + * + * @var ServiceLocatorInterface + */ + protected $serviceLocator; + /** * Constructor * @@ -76,7 +81,7 @@ class AbstractBase extends AbstractActionController */ public function __construct(ServiceLocatorInterface $sm) { - $this->setServiceLocator($sm); + $this->serviceLocator = $sm; } /** @@ -149,7 +154,7 @@ class AbstractBase extends AbstractActionController $view = $this->createViewModel($params); // Load configuration and current user for convenience: - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); + $config = $this->getConfig(); $view->disableFrom = (isset($config->Mail->disable_from) && $config->Mail->disable_from); $view->editableSubject = isset($config->Mail->user_editable_subjects) @@ -183,7 +188,7 @@ class AbstractBase extends AbstractActionController ) { $view->userEmailInFrom = true; $view->from = $user->email; - } else if (isset($config->Mail->default_from) + } elseif (isset($config->Mail->default_from) && $config->Mail->default_from ) { $view->from = $config->Mail->default_from; @@ -213,7 +218,7 @@ class AbstractBase extends AbstractActionController */ protected function getAuthManager() { - return $this->serviceLocator->get('VuFind\AuthManager'); + return $this->serviceLocator->get('VuFind\Auth\Manager'); } /** @@ -236,7 +241,7 @@ class AbstractBase extends AbstractActionController */ protected function getILSAuthenticator() { - return $this->serviceLocator->get('VuFind\ILSAuthenticator'); + return $this->serviceLocator->get('VuFind\Auth\ILSAuthenticator'); } /** @@ -362,7 +367,7 @@ class AbstractBase extends AbstractActionController */ public function getConfig($id = 'config') { - return $this->serviceLocator->get('VuFind\Config')->get($id); + return $this->serviceLocator->get('VuFind\Config\PluginManager')->get($id); } /** @@ -372,7 +377,7 @@ class AbstractBase extends AbstractActionController */ public function getILS() { - return $this->serviceLocator->get('VuFind\ILSConnection'); + return $this->serviceLocator->get('VuFind\ILS\Connection'); } /** @@ -382,7 +387,7 @@ class AbstractBase extends AbstractActionController */ public function getRecordLoader() { - return $this->serviceLocator->get('VuFind\RecordLoader'); + return $this->serviceLocator->get('VuFind\Record\Loader'); } /** @@ -392,7 +397,7 @@ class AbstractBase extends AbstractActionController */ public function getRecordCache() { - return $this->serviceLocator->get('VuFind\RecordCache'); + return $this->serviceLocator->get('VuFind\Record\Cache'); } /** @@ -402,7 +407,7 @@ class AbstractBase extends AbstractActionController */ public function getRecordRouter() { - return $this->serviceLocator->get('VuFind\RecordRouter'); + return $this->serviceLocator->get('VuFind\Record\Router'); } /** @@ -414,7 +419,7 @@ class AbstractBase extends AbstractActionController */ public function getTable($table) { - return $this->serviceLocator->get('VuFind\DbTablePluginManager') + return $this->serviceLocator->get('VuFind\Db\Table\PluginManager') ->get($table); } @@ -545,7 +550,7 @@ class AbstractBase extends AbstractActionController */ protected function commentsEnabled() { - $check = $this->serviceLocator->get('VuFind\AccountCapabilities'); + $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); return $check->getCommentSetting() !== 'disabled'; } @@ -556,7 +561,7 @@ class AbstractBase extends AbstractActionController */ protected function listsEnabled() { - $check = $this->serviceLocator->get('VuFind\AccountCapabilities'); + $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); return $check->getListSetting() !== 'disabled'; } @@ -567,7 +572,7 @@ class AbstractBase extends AbstractActionController */ protected function tagsEnabled() { - $check = $this->serviceLocator->get('VuFind\AccountCapabilities'); + $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); return $check->getTagSetting() !== 'disabled'; } diff --git a/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php b/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..6f3d621cc8a42f64a02a5cc252718a906dde3aa6 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Generic controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractBaseFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container); + } +} diff --git a/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php b/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3da3fd398e51bf0d217d12fcbd5eeb0d1632e46f --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Generic controller factory (with config injection). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic controller factory (with config injection). + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractBaseWithConfigFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($container, $config); + } +} diff --git a/module/VuFind/src/VuFind/Controller/AbstractRecord.php b/module/VuFind/src/VuFind/Controller/AbstractRecord.php index 652a05d55b40138bb3200575d5b7e0c4c6410905..80fe11cd20a725bcf2baf85fc203b51a101d6c38 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractRecord.php +++ b/module/VuFind/src/VuFind/Controller/AbstractRecord.php @@ -2,7 +2,7 @@ /** * VuFind Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,10 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFind\Controller; -use VuFind\Exception\Forbidden as ForbiddenException, - VuFind\Exception\Mail as MailException, - VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; + +use VuFind\Exception\Forbidden as ForbiddenException; +use VuFind\Exception\Mail as MailException; +use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; /** * VuFind Record Controller @@ -167,7 +168,7 @@ class AbstractRecord extends AbstractBase } $id = $this->params()->fromQuery('delete'); $table = $this->getTable('Comments'); - if (!is_null($id) && $table->deleteIfOwnedByUser($id, $user)) { + if (null !== $id && $table->deleteIfOwnedByUser($id, $user)) { $this->flashMessenger()->addMessage('delete_comment_success', 'success'); } else { $this->flashMessenger()->addMessage('delete_comment_failure', 'error'); @@ -288,7 +289,7 @@ class AbstractRecord extends AbstractBase $post = $this->getRequest()->getPost()->toArray(); $tagParser = $this->serviceLocator->get('VuFind\Tags'); $post['mytags'] - = $tagParser->parse(isset($post['mytags']) ? $post['mytags'] : ''); + = $tagParser->parse($post['mytags'] ?? ''); $favorites = $this->serviceLocator ->get('VuFind\Favorites\FavoritesService'); $results = $favorites->save($post, $user, $driver); @@ -414,7 +415,7 @@ class AbstractRecord extends AbstractBase $driver = $this->loadRecord(); // Create view - $mailer = $this->serviceLocator->get('VuFind\Mailer'); + $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); $view = $this->createEmailViewModel( null, $mailer->getDefaultRecordSubject($driver) ); @@ -451,7 +452,7 @@ class AbstractRecord extends AbstractBase */ protected function smsEnabled() { - $check = $this->serviceLocator->get('VuFind\AccountCapabilities'); + $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); return $check->getSmsSetting() !== 'disabled'; } @@ -471,7 +472,7 @@ class AbstractRecord extends AbstractBase $driver = $this->loadRecord(); // Load the SMS carrier list: - $sms = $this->serviceLocator->get('VuFind\SMS'); + $sms = $this->serviceLocator->get('VuFind\SMS\SMSInterface'); $view = $this->createViewModel(); $view->carriers = $sms->getCarriers(); $view->validation = $sms->getValidationType(); @@ -549,12 +550,33 @@ class AbstractRecord extends AbstractBase ->toUrl($export->getRedirectUrl($format, $callback)); } + $recordHelper = $this->getViewRenderer()->plugin('record'); + + $exportType = $export->getBulkExportType($format); + if ('post' === $exportType) { + $params = [ + 'exportType' => 'post', + 'postField' => $export->getPostField($format), + 'postData' => $recordHelper($driver)->getExport($format), + 'targetWindow' => $export->getTargetWindow($format), + 'url' => $export->getRedirectUrl($format, ''), + 'format' => $format + ]; + $msg = [ + 'translate' => false, 'html' => true, + 'msg' => $this->getViewRenderer()->render( + 'cart/export-success.phtml', $params + ) + ]; + $this->flashMessenger()->addSuccessMessage($msg); + return $this->redirectToRecord(); + } + // Send appropriate HTTP headers for requested format: $response = $this->getResponse(); $response->getHeaders()->addHeaders($export->getHeaders($format)); // Actually export the record - $recordHelper = $this->getViewRenderer()->plugin('record'); $response->setContent($recordHelper($driver)->getExport($format)); return $response; } @@ -628,7 +650,7 @@ class AbstractRecord extends AbstractBase } /** - * Support method to load tab information from the RecordTabPluginManager. + * Support method to load tab information from the RecordTab PluginManager. * * @return void */ @@ -636,7 +658,7 @@ class AbstractRecord extends AbstractBase { $driver = $this->loadRecord(); $request = $this->getRequest(); - $rtpm = $this->serviceLocator->get('VuFind\RecordTabPluginManager'); + $rtpm = $this->serviceLocator->get('VuFind\RecordTab\PluginManager'); $details = $rtpm->getTabDetailsForRecord( $driver, $this->getRecordTabConfig(), $request, $this->fallbackDefaultTab @@ -716,7 +738,7 @@ class AbstractRecord extends AbstractBase && !$this->getUser() ) { return $this->forceLogin(null); - } else if ($this->params()->fromQuery('catalogLogin', 'false') == 'true' + } elseif ($this->params()->fromQuery('catalogLogin', 'false') == 'true' && !is_array($patron = $this->catalogLogin()) ) { return $patron; @@ -731,7 +753,7 @@ class AbstractRecord extends AbstractBase $view->backgroundTabs = $this->getBackgroundTabs(); $view->loadInitialTabWithAjax = isset($config->Site->loadInitialTabWithAjax) - ? (bool) $config->Site->loadInitialTabWithAjax : false; + ? (bool)$config->Site->loadInitialTabWithAjax : false; // Set up next/previous record links (if appropriate) if ($this->resultScrollerActive()) { diff --git a/module/VuFind/src/VuFind/Controller/AbstractSearch.php b/module/VuFind/src/VuFind/Controller/AbstractSearch.php index 0d88e634b10ecaf377758a982506392b0c0e030d..1127317d33f71e71ba709bc5ad44cd3fd65123ab 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractSearch.php +++ b/module/VuFind/src/VuFind/Controller/AbstractSearch.php @@ -2,7 +2,7 @@ /** * VuFind Search Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller; -use VuFind\Search\RecommendListener, VuFind\Solr\Utils as SolrUtils; + +use VuFind\Search\RecommendListener; +use VuFind\Solr\Utils as SolrUtils; use Zend\Stdlib\Parameters; /** @@ -84,7 +86,7 @@ class AbstractSearch extends AbstractBase { $view = $this->createViewModel(); $view->options = $this->serviceLocator - ->get('VuFind\SearchOptionsPluginManager')->get($this->searchClassId); + ->get('VuFind\Search\Options\PluginManager')->get($this->searchClassId); if ($view->options->getAdvancedSearchAction() === false) { throw new \Exception('Advanced search not supported.'); } @@ -100,7 +102,7 @@ class AbstractSearch extends AbstractBase // to properly populate special controls on the advanced screen. if (!$view->saved && count($view->options->getDefaultFilters()) > 0) { $view->saved = $this->serviceLocator - ->get('VuFind\SearchResultsPluginManager') + ->get('VuFind\Search\Results\PluginManager') ->get($this->searchClassId); $view->saved->getParams()->initFromRequest( new \Zend\StdLib\Parameters([]) @@ -189,7 +191,7 @@ class AbstractSearch extends AbstractBase || $noRecommend === 'true' || $noRecommend === true ) { return []; - } else if ($noRecommend === 0 || $noRecommend === '0' + } elseif ($noRecommend === 0 || $noRecommend === '0' || $noRecommend === 'false' || $noRecommend === false ) { return $all; @@ -212,7 +214,7 @@ class AbstractSearch extends AbstractBase return null; } - $rManager = $this->serviceLocator->get('VuFind\RecommendPluginManager'); + $rManager = $this->serviceLocator->get('VuFind\Recommend\PluginManager'); // Special case: override recommend settings through parameter (used by // combined search) @@ -240,6 +242,18 @@ class AbstractSearch extends AbstractBase }; } + /** + * Home action + * + * @return mixed + */ + public function homeAction() + { + $blocks = $this->serviceLocator->get('VuFind\ContentBlock\BlockLoader') + ->getFromSearchClassId($this->searchClassId); + return $this->createViewModel(compact('blocks')); + } + /** * Send search results to results view * @@ -255,7 +269,7 @@ class AbstractSearch extends AbstractBase return $this->redirectToSavedSearch($savedId); } - $runner = $this->serviceLocator->get('VuFind\SearchRunner'); + $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); // Send both GET and POST variables to search class: $request = $this->getRequest()->getQuery()->toArray() @@ -305,7 +319,8 @@ class AbstractSearch extends AbstractBase } // Search toolbar - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); $view->showBulkOptions = isset($config->Site->showBulkOptions) && $config->Site->showBulkOptions; @@ -324,7 +339,8 @@ class AbstractSearch extends AbstractBase { // Jump to only result, if configured $default = null; - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); if (isset($config->Record->jump_to_single_search_result) && $config->Record->jump_to_single_search_result && $results->getResultTotal() == 1 @@ -361,7 +377,7 @@ class AbstractSearch extends AbstractBase protected function retrieveSearchSecurely($searchId) { $searchTable = $this->getTable('Search'); - $sessId = $this->serviceLocator->get('VuFind\SessionManager')->getId(); + $sessId = $this->serviceLocator->get('Zend\Session\SessionManager')->getId(); $user = $this->getUser(); $userId = $user ? $user->id : null; return $searchTable->getOwnedRowById($searchId, $sessId, $userId); @@ -377,7 +393,7 @@ class AbstractSearch extends AbstractBase protected function saveSearchToHistory($results) { $user = $this->getUser(); - $sessId = $this->serviceLocator->get('VuFind\SessionManager')->getId(); + $sessId = $this->serviceLocator->get('Zend\Session\SessionManager')->getId(); $history = $this->getTable('Search'); $history->saveSearch( $this->getResultsManager(), $results, $sessId, @@ -431,7 +447,7 @@ class AbstractSearch extends AbstractBase */ protected function getResultsManager() { - return $this->serviceLocator->get('VuFind\SearchResultsPluginManager'); + return $this->serviceLocator->get('VuFind\Search\Results\PluginManager'); } /** @@ -491,7 +507,8 @@ class AbstractSearch extends AbstractBase */ protected function getRangeFieldList($config, $section, $filter) { - $config = $this->serviceLocator->get('VuFind\Config')->get($config); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get($config); $fields = isset($config->SpecialFacets->$section) ? $config->SpecialFacets->$section->toArray() : []; @@ -640,11 +657,12 @@ class AbstractSearch extends AbstractBase protected function processAdvancedCheckboxes($params, $savedSearch = false) { // Set defaults for missing parameters: - $config = isset($params[0]) ? $params[0] : 'facets'; - $section = isset($params[1]) ? $params[1] : 'CheckboxFacets'; + $config = $params[0] ?? 'facets'; + $section = $params[1] ?? 'CheckboxFacets'; // Load config file: - $config = $this->serviceLocator->get('VuFind\Config')->get($config); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get($config); // Process checkbox settings in config: if (substr($section, 0, 1) == '~') { // reverse flag @@ -693,7 +711,7 @@ class AbstractSearch extends AbstractBase $params->initFromRequest($this->getRequest()->getQuery()); // Get parameters $facet = $this->params()->fromQuery('facet'); - $page = (int) $this->params()->fromQuery('facetpage', 1); + $page = (int)$this->params()->fromQuery('facetpage', 1); $options = $results->getOptions(); $facetSortOptions = $options->getFacetSortOptions(); $sort = $this->params()->fromQuery('facetsort', null); @@ -702,7 +720,7 @@ class AbstractSearch extends AbstractBase ? 'count' : current(array_keys($facetSortOptions)); } - $config = $this->serviceLocator->get('VuFind\Config') + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') ->get($options->getFacetsIni()); $limit = isset($config->Results_Settings->lightboxLimit) ? $config->Results_Settings->lightboxLimit @@ -712,7 +730,7 @@ class AbstractSearch extends AbstractBase [$facet], false, $limit, $sort, $page, $this->params()->fromQuery('facetop', 'AND') == 'OR' ); - $list = $facets[$facet]['data']['list']; + $list = $facets[$facet]['data']['list'] ?? []; $params->activateAllFacets(); $facetLabel = $params->getFacetLabel($facet); @@ -725,9 +743,10 @@ class AbstractSearch extends AbstractBase 'operator' => $this->params()->fromQuery('facetop', 'AND'), 'page' => $page, 'results' => $results, - 'anotherPage' => $facets[$facet]['more'], + 'anotherPage' => $facets[$facet]['more'] ?? '', 'sort' => $sort, - 'sortOptions' => $facetSortOptions + 'sortOptions' => $facetSortOptions, + 'baseUriExtra' => $this->params()->fromQuery('baseUriExtra'), ] ); $view->setTemplate('search/facet-list'); diff --git a/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php b/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php new file mode 100644 index 0000000000000000000000000000000000000000..9aa07b0910b37299dd2a6b14df35aabb0bdf0e15 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php @@ -0,0 +1,184 @@ +<?php +/** + * AbstractSearch with Solr-specific features added. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Controller; + +/** + * AbstractSearch with Solr-specific features added. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class AbstractSolrSearch extends AbstractSearch +{ + /** + * Handle an advanced search + * + * @return mixed + */ + public function advancedAction() + { + // Standard setup from base class: + $view = parent::advancedAction(); + + // Set up facet information: + $facets = $this->serviceLocator + ->get('VuFind\Search\FacetCache\PluginManager') + ->get($this->searchClassId) + ->getList('Advanced'); + $view->hierarchicalFacets + = $this->getHierarchicalFacets($view->options->getFacetsIni()); + $view->facetList = $this->processAdvancedFacets( + $facets, $view->saved, $view->hierarchicalFacets + ); + $specialFacets = $this->parseSpecialFacetsSetting( + $view->options->getSpecialAdvancedFacets() + ); + if (isset($specialFacets['illustrated'])) { + $view->illustratedLimit + = $this->getIllustrationSettings($view->saved); + } + if (isset($specialFacets['checkboxes'])) { + $view->checkboxFacets = $this->processAdvancedCheckboxes( + $specialFacets['checkboxes'], $view->saved + ); + } + $view->ranges = $this->getAllRangeSettings($specialFacets, $view->saved); + + return $view; + } + + /** + * Get the possible legal values for the illustration limit radio buttons. + * + * @param object $savedSearch Saved search object (false if none) + * + * @return array Legal options, with selected value flagged. + */ + protected function getIllustrationSettings($savedSearch = false) + { + $illYes = [ + 'text' => 'Has Illustrations', 'value' => 1, 'selected' => false + ]; + $illNo = [ + 'text' => 'Not Illustrated', 'value' => 0, 'selected' => false + ]; + $illAny = [ + 'text' => 'No Preference', 'value' => -1, 'selected' => false + ]; + + // Find the selected value by analyzing facets -- if we find match, remove + // the offending facet to avoid inappropriate items appearing in the + // "applied filters" sidebar! + if ($savedSearch + && $savedSearch->getParams()->hasFilter('illustrated:Illustrated') + ) { + $illYes['selected'] = true; + $savedSearch->getParams()->removeFilter('illustrated:Illustrated'); + } elseif ($savedSearch + && $savedSearch->getParams()->hasFilter('illustrated:"Not Illustrated"') + ) { + $illNo['selected'] = true; + $savedSearch->getParams()->removeFilter('illustrated:"Not Illustrated"'); + } else { + $illAny['selected'] = true; + } + return [$illYes, $illNo, $illAny]; + } + + /** + * Process the facets to be used as limits on the Advanced Search screen. + * + * @param array $facetList The advanced facet values + * @param object $searchObject Saved search object (false if none) + * @param array $hierarchicalFacets Hierarchical facet list (if any) + * + * @return array Sorted facets, with selected values flagged. + */ + protected function processAdvancedFacets($facetList, $searchObject = false, + $hierarchicalFacets = [] + ) { + // Process the facets + $facetHelper = null; + if (!empty($hierarchicalFacets)) { + $facetHelper = $this->serviceLocator + ->get('VuFind\Search\Solr\HierarchicalFacetHelper'); + } + foreach ($facetList as $facet => &$list) { + // Hierarchical facets: format display texts and sort facets + // to a flat array according to the hierarchy + if (in_array($facet, $hierarchicalFacets)) { + $tmpList = $list['list']; + $facetHelper->sortFacetList($tmpList, true); + $tmpList = $facetHelper->buildFacetArray( + $facet, + $tmpList + ); + $list['list'] = $facetHelper->flattenFacetHierarchy($tmpList); + } + + foreach ($list['list'] as $key => $value) { + // Build the filter string for the URL: + $fullFilter = ($value['operator'] == 'OR' ? '~' : '') + . $facet . ':"' . $value['value'] . '"'; + + // If we haven't already found a selected facet and the current + // facet has been applied to the search, we should store it as + // the selected facet for the current control. + if ($searchObject + && $searchObject->getParams()->hasFilter($fullFilter) + ) { + $list['list'][$key]['selected'] = true; + // Remove the filter from the search object -- we don't want + // it to show up in the "applied filters" sidebar since it + // will already be accounted for by being selected in the + // filter select list! + $searchObject->getParams()->removeFilter($fullFilter); + } + } + } + return $facetList; + } + + /** + * Get an array of hierarchical facets + * + * @param string $config Name of facet configuration file to load. + * + * @return array Facets + */ + protected function getHierarchicalFacets($config) + { + $facetConfig = $this->getConfig($config); + return isset($facetConfig->SpecialFacets->hierarchical) + ? $facetConfig->SpecialFacets->hierarchical->toArray() + : []; + } +} diff --git a/module/VuFind/src/VuFind/Controller/AjaxController.php b/module/VuFind/src/VuFind/Controller/AjaxController.php index cae11df5b6666a8748ff1d4078eb53aff22467bb..a664271db62cccf9b8a9e81227f330e02802a2ef 100644 --- a/module/VuFind/src/VuFind/Controller/AjaxController.php +++ b/module/VuFind/src/VuFind/Controller/AjaxController.php @@ -2,7 +2,7 @@ /** * Ajax Controller Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFind\Controller; -use VuFind\Exception\Auth as AuthException; -use Zend\ServiceManager\ServiceLocatorInterface; + +use VuFind\AjaxHandler\PluginManager; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use Zend\Mvc\Controller\AbstractActionController; /** * This controller handles global AJAX functionality @@ -38,69 +40,32 @@ use Zend\ServiceManager\ServiceLocatorInterface; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ -class AjaxController extends AbstractBase +class AjaxController extends AbstractActionController + implements TranslatorAwareInterface { - // define some status constants - const STATUS_OK = 'OK'; // good - const STATUS_ERROR = 'ERROR'; // bad - const STATUS_NEED_AUTH = 'NEED_AUTH'; // must login first - - /** - * Type of output to use - * - * @var string - */ - protected $outputMode; - - /** - * Array of PHP errors captured during execution - * - * @var array - */ - protected static $php_errors = []; + use AjaxResponseTrait; + use \VuFind\I18n\Translator\TranslatorAwareTrait; /** * Constructor * - * @param ServiceLocatorInterface $sm Service locator + * @param PluginManager $am AJAX Handler Plugin Manager */ - public function __construct(ServiceLocatorInterface $sm) + public function __construct(PluginManager $am) { // Add notices to a key in the output - set_error_handler(['VuFind\Controller\AjaxController', "storeError"]); - parent::__construct($sm); + set_error_handler([static::class, 'storeError']); + $this->ajaxManager = $am; } /** - * Handles passing data to the class + * Make an AJAX call with a JSON-formatted response. * - * @return mixed + * @return \Zend\Http\Response */ public function jsonAction() { - // Set the output mode to JSON: - $this->outputMode = 'json'; - - // Call the method specified by the 'method' parameter; append Ajax to - // the end to avoid access to arbitrary inappropriate methods. - $callback = [$this, $this->params()->fromQuery('method') . 'Ajax']; - if (is_callable($callback)) { - try { - return call_user_func($callback); - } catch (\Exception $e) { - $debugMsg = ('development' == APPLICATION_ENV) - ? ': ' . $e->getMessage() : ''; - return $this->output( - $this->translate('An error has occurred') . $debugMsg, - self::STATUS_ERROR, - 500 - ); - } - } else { - return $this->output( - $this->translate('Invalid Method'), self::STATUS_ERROR, 400 - ); - } + return $this->callAjaxMethod($this->params()->fromQuery('method')); } /** @@ -110,1293 +75,7 @@ class AjaxController extends AbstractBase */ public function recommendAction() { - $this->disableSessionWrites(); // avoid session write timing bug - // Process recommendations -- for now, we assume Solr-based search objects, - // since deferred recommendations work best for modules that don't care about - // the details of the search objects anyway: - $rm = $this->serviceLocator->get('VuFind\RecommendPluginManager'); - $module = $rm->get($this->params()->fromQuery('mod')); - $module->setConfig($this->params()->fromQuery('params')); - $results = $this->getResultsManager()->get('Solr'); - $params = $results->getParams(); - $module->init($params, $this->getRequest()->getQuery()); - $module->process($results); - - // Set headers: - $response = $this->getResponse(); - $headers = $response->getHeaders(); - $headers->addHeaderLine('Content-type', 'text/html'); - $headers->addHeaderLine('Cache-Control', 'no-cache, must-revalidate'); - $headers->addHeaderLine('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT'); - - // Render recommendations: - $recommend = $this->getViewRenderer()->plugin('recommend'); - $response->setContent($recommend($module)); - return $response; - } - - /** - * Support method for getItemStatuses() -- filter suppressed locations from the - * array of item information for a particular bib record. - * - * @param array $record Information on items linked to a single bib record - * - * @return array Filtered version of $record - */ - protected function filterSuppressedLocations($record) - { - static $hideHoldings = false; - if ($hideHoldings === false) { - $logic = $this->serviceLocator->get('VuFind\ILSHoldLogic'); - $hideHoldings = $logic->getSuppressedLocations(); - } - - $filtered = []; - foreach ($record as $current) { - if (!in_array($current['location'], $hideHoldings)) { - $filtered[] = $current; - } - } - return $filtered; - } - - /** - * Get Item Statuses - * - * This is responsible for printing the holdings information for a - * collection of records in JSON format. - * - * @return \Zend\Http\Response - * @author Chris Delis <cedelis@uillinois.edu> - * @author Tuan Nguyen <tuan@yorku.ca> - */ - protected function getItemStatusesAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - $catalog = $this->getILS(); - $ids = $this->params()->fromPost('id', $this->params()->fromQuery('id')); - $results = $catalog->getStatuses($ids); - - if (!is_array($results)) { - // If getStatuses returned garbage, let's turn it into an empty array - // to avoid triggering a notice in the foreach loop below. - $results = []; - } - - // In order to detect IDs missing from the status response, create an - // array with a key for every requested ID. We will clear keys as we - // encounter IDs in the response -- anything left will be problems that - // need special handling. - $missingIds = array_flip($ids); - - // Get access to PHP template renderer for partials: - $renderer = $this->getViewRenderer(); - - // Load messages for response: - $messages = [ - 'available' => $renderer->render('ajax/status-available.phtml'), - 'unavailable' => $renderer->render('ajax/status-unavailable.phtml'), - 'unknown' => $renderer->render('ajax/status-unknown.phtml') - ]; - - // Load callnumber and location settings: - $config = $this->getConfig(); - $callnumberSetting = isset($config->Item_Status->multiple_call_nos) - ? $config->Item_Status->multiple_call_nos : 'msg'; - $locationSetting = isset($config->Item_Status->multiple_locations) - ? $config->Item_Status->multiple_locations : 'msg'; - $showFullStatus = isset($config->Item_Status->show_full_status) - ? $config->Item_Status->show_full_status : false; - - // Loop through all the status information that came back - $statuses = []; - foreach ($results as $recordNumber => $record) { - // Filter out suppressed locations: - $record = $this->filterSuppressedLocations($record); - - // Skip empty records: - if (count($record)) { - if ($locationSetting == "group") { - $current = $this->getItemStatusGroup( - $record, $messages, $callnumberSetting - ); - } else { - $current = $this->getItemStatus( - $record, $messages, $locationSetting, $callnumberSetting - ); - } - // If a full status display has been requested, append the HTML: - if ($showFullStatus) { - $current['full_status'] = $renderer->render( - 'ajax/status-full.phtml', [ - 'statusItems' => $record, - 'callnumberHandler' => $this->getCallnumberHandler() - ] - ); - } - $current['record_number'] = array_search($current['id'], $ids); - $statuses[] = $current; - - // The current ID is not missing -- remove it from the missing list. - unset($missingIds[$current['id']]); - } - } - - // If any IDs were missing, send back appropriate dummy data - foreach ($missingIds as $missingId => $recordNumber) { - $statuses[] = [ - 'id' => $missingId, - 'availability' => 'false', - 'availability_message' => $messages['unavailable'], - 'location' => $this->translate('Unknown'), - 'locationList' => false, - 'reserve' => 'false', - 'reserve_message' => $this->translate('Not On Reserve'), - 'callnumber' => '', - 'missing_data' => true, - 'record_number' => $recordNumber - ]; - } - - // Done - return $this->output($statuses, self::STATUS_OK); - } - - /** - * Support method for getItemStatuses() -- when presented with multiple values, - * pick which one(s) to send back via AJAX. - * - * @param array $list Array of values to choose from. - * @param string $mode config.ini setting -- first, all or msg - * @param string $msg Message to display if $mode == "msg" - * @param string $transPrefix Translator prefix to apply to values (false to - * omit translation of values) - * - * @return string - */ - protected function pickValue($list, $mode, $msg, $transPrefix = false) - { - // Make sure array contains only unique values: - $list = array_unique($list); - - // If there is only one value in the list, or if we're in "first" mode, - // send back the first list value: - if ($mode == 'first' || count($list) == 1) { - if (!$transPrefix) { - return $list[0]; - } else { - return $this->translate($transPrefix . $list[0], [], $list[0]); - } - } else if (count($list) == 0) { - // Empty list? Return a blank string: - return ''; - } else if ($mode == 'all') { - // Translate values if necessary: - if ($transPrefix) { - $transList = []; - foreach ($list as $current) { - $transList[] = $this->translate( - $transPrefix . $current, [], $current - ); - } - $list = $transList; - } - // All values mode? Return comma-separated values: - return implode(",\t", $list); - } else { - // Message mode? Return the specified message, translated to the - // appropriate language. - return $this->translate($msg); - } - } - - /** - * Based on settings and the number of callnumbers, return callnumber handler - * Use callnumbers before pickValue is run. - * - * @param array $list Array of callnumbers. - * @param string $displaySetting config.ini setting -- first, all or msg - * - * @return string - */ - protected function getCallnumberHandler($list = null, $displaySetting = null) - { - if ($displaySetting == 'msg' && count($list) > 1) { - return false; - } - $config = $this->getConfig(); - return isset($config->Item_Status->callnumber_handler) - ? $config->Item_Status->callnumber_handler - : false; - } - - /** - * Reduce an array of service names to a human-readable string. - * - * @param array $services Names of available services. - * - * @return string - */ - protected function reduceServices(array $services) - { - // Normalize, dedup and sort available services - $normalize = function ($in) { - return strtolower(preg_replace('/[^A-Za-z]/', '', $in)); - }; - $services = array_map($normalize, array_unique($services)); - sort($services); - - // Do we need to deal with a preferred service? - $config = $this->getConfig(); - $preferred = isset($config->Item_Status->preferred_service) - ? $normalize($config->Item_Status->preferred_service) : false; - if (false !== $preferred && in_array($preferred, $services)) { - $services = [$preferred]; - } - - return $this->getViewRenderer()->render( - 'ajax/status-available-services.phtml', - ['services' => $services] - ); - } - - /** - * Support method for getItemStatuses() -- process a single bibliographic record - * for location settings other than "group". - * - * @param array $record Information on items linked to a single bib - * record - * @param array $messages Custom status HTML - * (keys = available/unavailable) - * @param string $locationSetting The location mode setting used for - * pickValue() - * @param string $callnumberSetting The callnumber mode setting used for - * pickValue() - * - * @return array Summarized availability information - */ - protected function getItemStatus($record, $messages, $locationSetting, - $callnumberSetting - ) { - // Summarize call number, location and availability info across all items: - $callNumbers = $locations = []; - $use_unknown_status = $available = false; - $services = []; - - foreach ($record as $info) { - // Find an available copy - if ($info['availability']) { - $available = true; - } - // Check for a use_unknown_message flag - if (isset($info['use_unknown_message']) - && $info['use_unknown_message'] == true - ) { - $use_unknown_status = true; - } - // Store call number/location info: - $callNumbers[] = $info['callnumber']; - $locations[] = $info['location']; - // Store all available services - if (isset($info['services'])) { - $services = array_merge($services, $info['services']); - } - } - - $callnumberHandler = $this->getCallnumberHandler( - $callNumbers, $callnumberSetting - ); - - // Determine call number string based on findings: - $callNumber = $this->pickValue( - $callNumbers, $callnumberSetting, 'Multiple Call Numbers' - ); - - // Determine location string based on findings: - $location = $this->pickValue( - $locations, $locationSetting, 'Multiple Locations', 'location_' - ); - - if (!empty($services)) { - $availability_message = $this->reduceServices($services); - } else { - $availability_message = $use_unknown_status - ? $messages['unknown'] - : $messages[$available ? 'available' : 'unavailable']; - } - - // Send back the collected details: - return [ - 'id' => $record[0]['id'], - 'availability' => ($available ? 'true' : 'false'), - 'availability_message' => $availability_message, - 'location' => htmlentities($location, ENT_COMPAT, 'UTF-8'), - 'locationList' => false, - 'reserve' => - ($record[0]['reserve'] == 'Y' ? 'true' : 'false'), - 'reserve_message' => $record[0]['reserve'] == 'Y' - ? $this->translate('on_reserve') - : $this->translate('Not On Reserve'), - 'callnumber' => htmlentities($callNumber, ENT_COMPAT, 'UTF-8'), - 'callnumber_handler' => $callnumberHandler - ]; - } - - /** - * Support method for getItemStatuses() -- process a single bibliographic record - * for "group" location setting. - * - * @param array $record Information on items linked to a single - * bib record - * @param array $messages Custom status HTML - * (keys = available/unavailable) - * @param string $callnumberSetting The callnumber mode setting used for - * pickValue() - * - * @return array Summarized availability information - */ - protected function getItemStatusGroup($record, $messages, $callnumberSetting) - { - // Summarize call number, location and availability info across all items: - $locations = []; - $use_unknown_status = $available = false; - foreach ($record as $info) { - // Find an available copy - if ($info['availability']) { - $available = $locations[$info['location']]['available'] = true; - } - // Check for a use_unknown_message flag - if (isset($info['use_unknown_message']) - && $info['use_unknown_message'] == true - ) { - $use_unknown_status = true; - $locations[$info['location']]['status_unknown'] = true; - } - // Store call number/location info: - $locations[$info['location']]['callnumbers'][] = $info['callnumber']; - } - - // Build list split out by location: - $locationList = false; - foreach ($locations as $location => $details) { - $locationCallnumbers = array_unique($details['callnumbers']); - // Determine call number string based on findings: - $callnumberHandler = $this->getCallnumberHandler( - $locationCallnumbers, $callnumberSetting - ); - $locationCallnumbers = $this->pickValue( - $locationCallnumbers, $callnumberSetting, 'Multiple Call Numbers' - ); - $locationInfo = [ - 'availability' => - isset($details['available']) ? $details['available'] : false, - 'location' => htmlentities( - $this->translate('location_' . $location, [], $location), - ENT_COMPAT, 'UTF-8' - ), - 'callnumbers' => - htmlentities($locationCallnumbers, ENT_COMPAT, 'UTF-8'), - 'status_unknown' => isset($details['status_unknown']) - ? $details['status_unknown'] : false, - 'callnumber_handler' => $callnumberHandler - ]; - $locationList[] = $locationInfo; - } - - $availability_message = $use_unknown_status - ? $messages['unknown'] - : $messages[$available ? 'available' : 'unavailable']; - - // Send back the collected details: - return [ - 'id' => $record[0]['id'], - 'availability' => ($available ? 'true' : 'false'), - 'availability_message' => $availability_message, - 'location' => false, - 'locationList' => $locationList, - 'reserve' => - ($record[0]['reserve'] == 'Y' ? 'true' : 'false'), - 'reserve_message' => $record[0]['reserve'] == 'Y' - ? $this->translate('on_reserve') - : $this->translate('Not On Reserve'), - 'callnumber' => false - ]; - } - - /** - * Check one or more records to see if they are saved in one of the user's list. - * - * @return \Zend\Http\Response - */ - protected function getSaveStatusesAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - // check if user is logged in - $user = $this->getUser(); - if (!$user) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - - // loop through each ID check if it is saved to any of the user's lists - $ids = $this->params()->fromPost('id', $this->params()->fromQuery('id', [])); - $sources = $this->params()->fromPost( - 'source', $this->params()->fromQuery('source', []) - ); - if (!is_array($ids) || !is_array($sources)) { - return $this->output( - $this->translate('Argument must be array.'), - self::STATUS_ERROR, - 400 - ); - } - $result = $checked = []; - foreach ($ids as $i => $id) { - $source = isset($sources[$i]) ? $sources[$i] : DEFAULT_SEARCH_BACKEND; - $selector = $source . '|' . $id; - - // We don't want to bother checking the same ID more than once, so - // use the $checked flag array to avoid duplicates: - if (isset($checked[$selector])) { - continue; - } - $checked[$selector] = true; - - $data = $user->getSavedData($id, null, $source); - $result[$selector] = []; - if ($data && count($data) > 0) { - // if this item was saved, add it to the list of saved items. - foreach ($data as $list) { - $result[$selector][] = [ - 'list_url' => $this->url()->fromRoute( - 'userList', - ['id' => $list->list_id] - ), - 'list_title' => $list->list_title - ]; - } - } - } - return $this->output($result, self::STATUS_OK); - } - - /** - * Send output data and exit. - * - * @param mixed $data The response data - * @param string $status Status of the request - * @param int $httpCode A custom HTTP Status Code - * - * @return \Zend\Http\Response - * @throws \Exception - */ - protected function output($data, $status, $httpCode = null) - { - $response = $this->getResponse(); - $headers = $response->getHeaders(); - $headers->addHeaderLine('Cache-Control', 'no-cache, must-revalidate'); - $headers->addHeaderLine('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT'); - if ($httpCode !== null) { - $response->setStatusCode($httpCode); - } - if ($this->outputMode == 'json') { - $headers->addHeaderLine('Content-type', 'application/javascript'); - $output = ['data' => $data, 'status' => $status]; - if ('development' == APPLICATION_ENV && count(self::$php_errors) > 0) { - $output['php_errors'] = self::$php_errors; - } - $response->setContent(json_encode($output)); - return $response; - } else if ($this->outputMode == 'plaintext') { - $headers->addHeaderLine('Content-type', 'text/plain'); - $response->setContent($data ? $status . " $data" : $status); - return $response; - } else { - throw new \Exception('Unsupported output mode: ' . $this->outputMode); - } - } - - /** - * Store the errors for later, to be added to the output - * - * @param string $errno Error code number - * @param string $errstr Error message - * @param string $errfile File where error occurred - * @param string $errline Line number of error - * - * @return bool Always true to cancel default error handling - */ - public static function storeError($errno, $errstr, $errfile, $errline) - { - self::$php_errors[] = "ERROR [$errno] - " . $errstr . "<br />\n" - . " Occurred in " . $errfile . " on line " . $errline . "."; - return true; - } - - /** - * Generate the "salt" used in the salt'ed login request. - * - * @return string - */ - protected function generateSalt() - { - return str_replace( - '.', '', $this->getRequest()->getServer()->get('REMOTE_ADDR') - ); - } - - /** - * Send the "salt" to be used in the salt'ed login request. - * - * @return \Zend\Http\Response - */ - protected function getSaltAjax() - { - return $this->output($this->generateSalt(), self::STATUS_OK); - } - - /** - * Login with post'ed username and encrypted password. - * - * @return \Zend\Http\Response - */ - protected function loginAjax() - { - // Fetch Salt - $salt = $this->generateSalt(); - - // HexDecode Password - $password = pack('H*', $this->params()->fromPost('password')); - - // Decrypt Password - $password = base64_decode(\VuFind\Crypt\RC4::encrypt($salt, $password)); - - // Update the request with the decrypted password: - $this->getRequest()->getPost()->set('password', $password); - - // Authenticate the user: - try { - $this->getAuthManager()->login($this->getRequest()); - } catch (AuthException $e) { - return $this->output( - $this->translate($e->getMessage()), - self::STATUS_ERROR, - 401 - ); - } - - return $this->output(true, self::STATUS_OK); - } - - /** - * Tag a record. - * - * @return \Zend\Http\Response - */ - protected function tagRecordAjax() - { - $user = $this->getUser(); - if ($user === false) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - // empty tag - try { - $driver = $this->getRecordLoader()->load( - $this->params()->fromPost('id'), - $this->params()->fromPost('source', DEFAULT_SEARCH_BACKEND) - ); - $tag = $this->params()->fromPost('tag', ''); - $tagParser = $this->serviceLocator->get('VuFind\Tags'); - if (strlen($tag) > 0) { // don't add empty tags - if ('false' === $this->params()->fromPost('remove', 'false')) { - $driver->addTags($user, $tagParser->parse($tag)); - } else { - $driver->deleteTags($user, $tagParser->parse($tag)); - } - } - } catch (\Exception $e) { - return $this->output( - ('development' == APPLICATION_ENV) ? $e->getMessage() : 'Failed', - self::STATUS_ERROR, - 500 - ); - } - - return $this->output($this->translate('Done'), self::STATUS_OK); - } - - /** - * Get all tags for a record. - * - * @return \Zend\Http\Response - */ - protected function getRecordTagsAjax() - { - $user = $this->getUser(); - $is_me_id = null === $user ? null : $user->id; - // Retrieve from database: - $tagTable = $this->getTable('Tags'); - $tags = $tagTable->getForResource( - $this->params()->fromQuery('id'), - $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND), - 0, null, null, 'count', $is_me_id - ); - - // Build data structure for return: - $tagList = []; - foreach ($tags as $tag) { - $tagList[] = [ - 'tag' => $tag->tag, - 'cnt' => $tag->cnt, - 'is_me' => !empty($tag->is_me) - ]; - } - - // Set layout to render content only: - $this->layout()->setTemplate('layout/lightbox'); - $view = $this->createViewModel( - [ - 'tagList' => $tagList, - 'loggedin' => null !== $user - ] - ); - $view->setTemplate('record/taglist'); - return $view; - } - - /** - * Get record for integrated list view. - * - * @return \Zend\Http\Response - */ - protected function getRecordDetailsAjax() - { - $driver = $this->getRecordLoader()->load( - $this->params()->fromQuery('id'), - $this->params()->fromQuery('source') - ); - $viewtype = preg_replace( - '/\W/', '', - trim(strtolower($this->params()->fromQuery('type'))) - ); - $request = $this->getRequest(); - $config = $this->serviceLocator->get('Config'); - - $recordTabPlugin = $this->serviceLocator - ->get('VuFind\RecordTabPluginManager'); - $details = $recordTabPlugin - ->getTabDetailsForRecord( - $driver, - $config['vufind']['recorddriver_tabs'], - $request, - 'Information' - ); - - $rtpm = $this->serviceLocator->get('VuFind\RecordTabPluginManager'); - $html = $this->getViewRenderer() - ->render( - "record/ajaxview-" . $viewtype . ".phtml", - [ - 'defaultTab' => $details['default'], - 'driver' => $driver, - 'tabs' => $details['tabs'], - 'backgroundTabs' => $rtpm->getBackgroundTabNames( - $driver, $this->getRecordTabConfig() - ) - ] - ); - return $this->output($html, self::STATUS_OK); - } - - /** - * AJAX for timeline feature (PubDateVisAjax) - * - * @param array $fields Solr fields to retrieve data from - * - * @author Chris Hallberg <crhallberg@gmail.com> - * @author Till Kinstler <kinstler@gbv.de> - * - * @return \Zend\Http\Response - */ - protected function getVisDataAjax($fields = ['publishDate']) - { - $this->disableSessionWrites(); // avoid session write timing bug - $results = $this->getResultsManager()->get('Solr'); - $params = $results->getParams(); - $params->initFromRequest($this->getRequest()->getQuery()); - foreach ($this->params()->fromQuery('hf', []) as $hf) { - $params->addHiddenFilter($hf); - } - $params->getOptions()->disableHighlighting(); - $params->getOptions()->spellcheckEnabled(false); - $filters = $params->getFilters(); - $dateFacets = $this->params()->fromQuery('facetFields'); - $dateFacets = empty($dateFacets) ? [] : explode(':', $dateFacets); - $fields = $this->processDateFacets($filters, $dateFacets, $results); - $facets = $this->processFacetValues($fields, $results); - foreach ($fields as $field => $val) { - $facets[$field]['min'] = $val[0] > 0 ? $val[0] : 0; - $facets[$field]['max'] = $val[1] > 0 ? $val[1] : 0; - $facets[$field]['removalURL'] - = $results->getUrlQuery()->removeFacet( - $field, - isset($filters[$field][0]) ? $filters[$field][0] : null - )->getParams(false); - } - return $this->output($facets, self::STATUS_OK); - } - - /** - * Support method for getVisData() -- extract details from applied filters. - * - * @param array $filters Current filter list - * @param array $dateFacets Objects containing the date - * ranges - * @param \VuFind\Search\Solr\Results $results Search results object - * - * @return array - */ - protected function processDateFacets($filters, $dateFacets, $results) - { - $result = []; - foreach ($dateFacets as $current) { - $from = $to = ''; - if (isset($filters[$current])) { - foreach ($filters[$current] as $filter) { - if (preg_match('/\[[\d\*]+ TO [\d\*]+\]/', $filter)) { - $range = explode(' TO ', trim($filter, '[]')); - $from = $range[0] == '*' ? '' : $range[0]; - $to = $range[1] == '*' ? '' : $range[1]; - break; - } - } - } - $result[$current] = [$from, $to]; - $result[$current]['label'] - = $results->getParams()->getFacetLabel($current); - } - return $result; - } - - /** - * Support method for getVisData() -- filter bad values from facet lists. - * - * @param array $fields Processed date information from - * processDateFacets - * @param \VuFind\Search\Solr\Results $results Search results object - * - * @return array - */ - protected function processFacetValues($fields, $results) - { - $facets = $results->getFullFieldFacets(array_keys($fields)); - $retVal = []; - foreach ($facets as $field => $values) { - $newValues = ['data' => []]; - foreach ($values['data']['list'] as $current) { - // Only retain numeric values! - if (preg_match("/^[0-9]+$/", $current['value'])) { - $newValues['data'][] - = [$current['value'], $current['count']]; - } - } - $retVal[$field] = $newValues; - } - return $retVal; - } - - /** - * Get Autocomplete suggestions. - * - * @return \Zend\Http\Response - */ - protected function getACSuggestionsAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - $query = $this->getRequest()->getQuery(); - $autocompleteManager = $this->serviceLocator - ->get('VuFind\AutocompletePluginManager'); - return $this->output( - $autocompleteManager->getSuggestions($query), self::STATUS_OK - ); - } - - /** - * Check Request is Valid - * - * @return \Zend\Http\Response - */ - protected function checkRequestIsValidAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - $id = $this->params()->fromQuery('id'); - $data = $this->params()->fromQuery('data'); - $requestType = $this->params()->fromQuery('requestType'); - if (empty($id) || empty($data)) { - return $this->output( - $this->translate('bulk_error_missing'), - self::STATUS_ERROR, - 400 - ); - } - // check if user is logged in - $user = $this->getUser(); - if (!$user) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - - try { - $catalog = $this->getILS(); - $patron = $this->getILSAuthenticator()->storedCatalogLogin(); - if ($patron) { - switch ($requestType) { - case 'ILLRequest': - $results = $catalog->checkILLRequestIsValid($id, $data, $patron); - if (is_array($results)) { - $msg = $results['status']; - $results = $results['valid']; - } else { - $msg = $results - ? 'ill_request_place_text' : 'ill_request_error_blocked'; - } - break; - case 'StorageRetrievalRequest': - $results = $catalog->checkStorageRetrievalRequestIsValid( - $id, $data, $patron - ); - if (is_array($results)) { - $msg = $results['status']; - $results = $results['valid']; - } else { - $msg = $results ? 'storage_retrieval_request_place_text' - : 'storage_retrieval_request_error_blocked'; - } - break; - default: - $results = $catalog->checkRequestIsValid($id, $data, $patron); - if (is_array($results)) { - $msg = $results['status']; - $results = $results['valid']; - } else { - $msg = $results ? 'request_place_text' - : 'hold_error_blocked'; - break; - } - } - return $this->output( - ['status' => $results, 'msg' => $this->translate($msg)], - self::STATUS_OK - ); - } - } catch (\Exception $e) { - // Do nothing -- just fail through to the error message below. - } - - return $this->output( - $this->translate('An error has occurred'), self::STATUS_ERROR, 500 - ); - } - - /** - * Comment on a record. - * - * @return \Zend\Http\Response - */ - protected function commentRecordAjax() - { - // Make sure comments are enabled: - if (!$this->commentsEnabled()) { - return $this->output( - $this->translate('Comments disabled'), - self::STATUS_ERROR, - 403 - ); - } - - $user = $this->getUser(); - if ($user === false) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - - $id = $this->params()->fromPost('id'); - $comment = $this->params()->fromPost('comment'); - if (empty($id) || empty($comment)) { - return $this->output( - $this->translate('bulk_error_missing'), - self::STATUS_ERROR, - 400 - ); - } - - $useCaptcha = $this->recaptcha()->active('userComments'); - $this->recaptcha()->setErrorMode('none'); - if (!$this->formWasSubmitted('comment', $useCaptcha)) { - return $this->output( - $this->translate('recaptcha_not_passed'), - self::STATUS_ERROR, - 403 - ); - } - - $table = $this->getTable('Resource'); - $resource = $table->findResource( - $id, $this->params()->fromPost('source', DEFAULT_SEARCH_BACKEND) - ); - $id = $resource->addComment($comment, $user); - - return $this->output($id, self::STATUS_OK); - } - - /** - * Delete a comment on a record. - * - * @return \Zend\Http\Response - */ - protected function deleteRecordCommentAjax() - { - // Make sure comments are enabled: - if (!$this->commentsEnabled()) { - return $this->output( - $this->translate('Comments disabled'), - self::STATUS_ERROR, - 403 - ); - } - - $user = $this->getUser(); - if ($user === false) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - - $id = $this->params()->fromQuery('id'); - if (empty($id)) { - return $this->output( - $this->translate('bulk_error_missing'), - self::STATUS_ERROR, - 400 - ); - } - $table = $this->getTable('Comments'); - if (!$table->deleteIfOwnedByUser($id, $user)) { - return $this->output( - $this->translate('edit_list_fail'), - self::STATUS_ERROR, - 403 - ); - } - - return $this->output($this->translate('Done'), self::STATUS_OK); - } - - /** - * Get list of comments for a record as HTML. - * - * @return \Zend\Http\Response - */ - protected function getRecordCommentsAsHTMLAjax() - { - $driver = $this->getRecordLoader()->load( - $this->params()->fromQuery('id'), - $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND) - ); - $html = $this->getViewRenderer() - ->render('record/comments-list.phtml', ['driver' => $driver]); - return $this->output($html, self::STATUS_OK); - } - - /** - * Process an export request - * - * @return \Zend\Http\Response - */ - protected function exportFavoritesAjax() - { - $format = $this->params()->fromPost('format'); - $export = $this->serviceLocator->get('VuFind\Export'); - $url = $export->getBulkUrl( - $this->getViewRenderer(), $format, - $this->params()->fromPost('ids', []) - ); - $html = $this->getViewRenderer()->render( - 'ajax/export-favorites.phtml', - ['url' => $url, 'format' => $format] - ); - return $this->output( - [ - 'result' => $this->translate('Done'), - 'result_additional' => $html, - 'needs_redirect' => $export->needsRedirect($format), - 'export_type' => $export->getBulkExportType($format), - 'result_url' => $url - ], self::STATUS_OK - ); - } - - /** - * Fetch Links from resolver given an OpenURL and format as HTML - * and output the HTML content in JSON object. - * - * @return \Zend\Http\Response - * @author Graham Seaman <Graham.Seaman@rhul.ac.uk> - */ - protected function getResolverLinksAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - $openUrl = $this->params()->fromQuery('openurl', ''); - $searchClassId = $this->params()->fromQuery('searchClassId', ''); - - $config = $this->getConfig(); - $resolverType = isset($config->OpenURL->resolver) - ? $config->OpenURL->resolver : 'other'; - $pluginManager = $this->serviceLocator - ->get('VuFind\ResolverDriverPluginManager'); - if (!$pluginManager->has($resolverType)) { - return $this->output( - $this->translate("Could not load driver for $resolverType"), - self::STATUS_ERROR, - 500 - ); - } - $resolver = new \VuFind\Resolver\Connection( - $pluginManager->get($resolverType) - ); - if (isset($config->OpenURL->resolver_cache)) { - $resolver->enableCache($config->OpenURL->resolver_cache); - } - $result = $resolver->fetchLinks($openUrl); - - // Sort the returned links into categories based on service type: - $electronic = $print = $services = []; - foreach ($result as $link) { - switch (isset($link['service_type']) ? $link['service_type'] : '') { - case 'getHolding': - $print[] = $link; - break; - case 'getWebService': - $services[] = $link; - break; - case 'getDOI': - // Special case -- modify DOI text for special display: - $link['title'] = $this->translate('Get full text'); - $link['coverage'] = ''; - case 'getFullTxt': - default: - $electronic[] = $link; - break; - } - } - - // Get the OpenURL base: - if (isset($config->OpenURL) && isset($config->OpenURL->url)) { - // Trim off any parameters (for legacy compatibility -- default config - // used to include extraneous parameters): - list($base) = explode('?', $config->OpenURL->url); - } else { - $base = false; - } - - $moreOptionsLink = $resolver->supportsMoreOptionsLink() - ? $resolver->getResolverUrl($openUrl) : ''; - - // Render the links using the view: - $view = [ - 'openUrlBase' => $base, 'openUrl' => $openUrl, 'print' => $print, - 'electronic' => $electronic, 'services' => $services, - 'searchClassId' => $searchClassId, - 'moreOptionsLink' => $moreOptionsLink - ]; - $html = $this->getViewRenderer()->render('ajax/resolverLinks.phtml', $view); - - // output HTML encoded in JSON object - return $this->output($html, self::STATUS_OK); - } - - /** - * Keep Alive - * - * This is responsible for keeping the session alive whenever called - * (via JavaScript) - * - * @return \Zend\Http\Response - */ - protected function keepAliveAjax() - { - // Request ID from session to mark it active - $this->serviceLocator->get('VuFind\SessionManager')->getId(); - return $this->output(true, self::STATUS_OK); - } - - /** - * Get pick up locations for a library - * - * @return \Zend\Http\Response - */ - protected function getLibraryPickupLocationsAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - $id = $this->params()->fromQuery('id'); - $pickupLib = $this->params()->fromQuery('pickupLib'); - if (null === $id || null === $pickupLib) { - return $this->output( - $this->translate('bulk_error_missing'), - self::STATUS_ERROR, - 400 - ); - } - // check if user is logged in - $user = $this->getUser(); - if (!$user) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - - try { - $catalog = $this->getILS(); - $patron = $this->getILSAuthenticator()->storedCatalogLogin(); - if ($patron) { - $results = $catalog->getILLPickupLocations($id, $pickupLib, $patron); - foreach ($results as &$result) { - if (isset($result['name'])) { - $result['name'] = $this->translate( - 'location_' . $result['name'], - [], - $result['name'] - ); - } - } - return $this->output(['locations' => $results], self::STATUS_OK); - } - } catch (\Exception $e) { - // Do nothing -- just fail through to the error message below. - } - - return $this->output( - $this->translate('An error has occurred'), self::STATUS_ERROR, 500 - ); - } - - /** - * Get pick up locations for a request group - * - * @return \Zend\Http\Response - */ - protected function getRequestGroupPickupLocationsAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - $id = $this->params()->fromQuery('id'); - $requestGroupId = $this->params()->fromQuery('requestGroupId'); - if (null === $id || null === $requestGroupId) { - return $this->output( - $this->translate('bulk_error_missing'), - self::STATUS_ERROR, - 400 - ); - } - // check if user is logged in - $user = $this->getUser(); - if (!$user) { - return $this->output( - $this->translate('You must be logged in first'), - self::STATUS_NEED_AUTH, - 401 - ); - } - - try { - $catalog = $this->getILS(); - $patron = $this->getILSAuthenticator()->storedCatalogLogin(); - if ($patron) { - $details = [ - 'id' => $id, - 'requestGroupId' => $requestGroupId - ]; - $results = $catalog->getPickupLocations($patron, $details); - foreach ($results as &$result) { - if (isset($result['locationDisplay'])) { - $result['locationDisplay'] = $this->translate( - 'location_' . $result['locationDisplay'], - [], - $result['locationDisplay'] - ); - } - } - return $this->output(['locations' => $results], self::STATUS_OK); - } - } catch (\Exception $e) { - // Do nothing -- just fail through to the error message below. - } - - return $this->output( - $this->translate('An error has occurred'), self::STATUS_ERROR, 500 - ); - } - - /** - * Get hierarchical facet data for jsTree - * - * Parameters: - * facetName The facet to retrieve - * facetSort By default all facets are sorted by count. Two values are available - * for alternative sorting: - * top = sort the top level alphabetically, rest by count - * all = sort all levels alphabetically - * - * @return \Zend\Http\Response - */ - protected function getFacetDataAjax() - { - $this->disableSessionWrites(); // avoid session write timing bug - - $facet = $this->params()->fromQuery('facetName'); - $sort = $this->params()->fromQuery('facetSort'); - $operator = $this->params()->fromQuery('facetOperator'); - - $results = $this->getResultsManager()->get('Solr'); - $params = $results->getParams(); - $params->addFacet($facet, null, $operator === 'OR'); - $params->initFromRequest($this->getRequest()->getQuery()); - - $facets = $results->getFullFieldFacets([$facet], false, -1, 'count'); - if (empty($facets[$facet]['data']['list'])) { - return $this->output([], self::STATUS_OK); - } - - $facetList = $facets[$facet]['data']['list']; - - $facetHelper = $this->serviceLocator - ->get('VuFind\HierarchicalFacetHelper'); - if (!empty($sort)) { - $facetHelper->sortFacetList($facetList, $sort == 'top'); - } - - return $this->output( - $facetHelper->buildFacetArray( - $facet, $facetList, $results->getUrlQuery() - ), - self::STATUS_OK - ); + return $this->callAjaxMethod('recommend', 'text/html'); } /** @@ -1406,83 +85,8 @@ class AjaxController extends AbstractBase * * @return \Zend\Http\Response */ - protected function systemStatusAction() - { - $this->outputMode = 'plaintext'; - - // Check system status - $config = $this->getConfig(); - if (!empty($config->System->healthCheckFile) - && file_exists($config->System->healthCheckFile) - ) { - return $this->output( - 'Health check file exists', self::STATUS_ERROR, 503 - ); - } - - // Test search index - try { - $results = $this->getResultsManager()->get('Solr'); - $params = $results->getParams(); - $params->setQueryIDs(['healthcheck']); - $results->performAndProcessSearch(); - } catch (\Exception $e) { - return $this->output( - 'Search index error: ' . $e->getMessage(), self::STATUS_ERROR, 500 - ); - } - - // Test database connection - try { - $sessionTable = $this->getTable('Session'); - $sessionTable->getBySessionId('healthcheck', false); - } catch (\Exception $e) { - return $this->output( - 'Database error: ' . $e->getMessage(), self::STATUS_ERROR, 500 - ); - } - - // This may be called frequently, don't leave sessions dangling - $this->serviceLocator->get('VuFind\SessionManager')->destroy(); - - return $this->output('', self::STATUS_OK); - } - - /** - * Convenience method for accessing results - * - * @return \VuFind\Search\Results\PluginManager - */ - protected function getResultsManager() - { - return $this->serviceLocator->get('VuFind\SearchResultsPluginManager'); - } - - /** - * Get Ils Status - * - * This will check the ILS for being online and will return the ils-offline - * template upon failure. - * - * @return \Zend\Http\Response - * @author André Lahmann <lahmann@ub.uni-leipzig.de> - */ - protected function getIlsStatusAjax() + public function systemStatusAction() { - $this->disableSessionWrites(); // avoid session write timing bug - if ($this->getILS()->getOfflineMode(true) == 'ils-offline') { - $offlineModeMsg = $this->params()->fromPost( - 'offlineModeMsg', - $this->params()->fromQuery('offlineModeMsg') - ); - return $this->output( - $this->getViewRenderer()->render( - 'Helpers/ils-offline.phtml', - compact('offlineModeMsg') - ), - self::STATUS_OK - ); - } - return $this->output('', self::STATUS_OK); + return $this->callAjaxMethod('systemStatus', 'text/plain'); } } diff --git a/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php b/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4055fa902ac2e800832ecab8763571e7dacb05ab --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Ajax controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Ajax controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AjaxControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $pm = $container->get('VuFind\AjaxHandler\PluginManager'); + return new $requestedName($pm); + } +} diff --git a/module/VuFind/src/VuFind/Controller/AjaxResponseTrait.php b/module/VuFind/src/VuFind/Controller/AjaxResponseTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..e7d2ef4431ad68a46e298738c389e20b812748e2 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/AjaxResponseTrait.php @@ -0,0 +1,185 @@ +<?php +/** + * Trait to allow AJAX response generation. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +namespace VuFind\Controller; + +use VuFind\AjaxHandler\AjaxHandlerInterface as Ajax; +use VuFind\AjaxHandler\PluginManager; + +/** + * Trait to allow AJAX response generation. + * + * Dependencies: + * - \VuFind\I18n\Translator\TranslatorAwareTrait + * - Injection of $this->ajaxManager (for some functionality) + * + * @category VuFind + * @package Controller + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +trait AjaxResponseTrait +{ + /** + * Array of PHP errors captured during execution. Add this code to your + * constructor in order to populate the array: + * set_error_handler([static::class, 'storeError']); + * + * @var array + */ + protected static $php_errors = []; + + /** + * AJAX Handler plugin manager + * + * @var PluginManager; + */ + protected $ajaxManager = null; + + /** + * Format the content of the AJAX response based on the response type. + * + * @param string $type Content-type of output + * @param mixed $data The response data + * @param int $httpCode A custom HTTP Status Code + * + * @return string + * @throws \Exception + */ + protected function formatContent($type, $data, $httpCode) + { + switch ($type) { + case 'application/javascript': + $output = ['data' => $data]; + if ('development' == APPLICATION_ENV && count(self::$php_errors) > 0) { + $output['php_errors'] = self::$php_errors; + } + return json_encode($output); + case 'text/plain': + return ((null !== $httpCode && $httpCode >= 400) ? 'ERROR ' : 'OK ') + . $data; + case 'text/html': + return $data ?: ''; + default: + throw new \Exception("Unsupported content type: $type"); + } + } + + /** + * Send output data and exit. + * + * @param string $type Content type to output + * @param mixed $data The response data + * @param int $httpCode A custom HTTP Status Code + * + * @return \Zend\Http\Response + * @throws \Exception + */ + protected function getAjaxResponse($type, $data, $httpCode = null) + { + $response = $this->getResponse(); + $headers = $response->getHeaders(); + $headers->addHeaderLine('Content-type', $type); + $headers->addHeaderLine('Cache-Control', 'no-cache, must-revalidate'); + $headers->addHeaderLine('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT'); + if ($httpCode !== null) { + $response->setStatusCode($httpCode); + } + $response->setContent($this->formatContent($type, $data, $httpCode)); + return $response; + } + + /** + * Turn an exception into error response. + * + * @param string $type Content type to output + * @param \Exception $e Exception to output. + * + * @return \Zend\Http\Response + */ + protected function getExceptionResponse($type, \Exception $e) + { + $debugMsg = ('development' == APPLICATION_ENV) + ? ': ' . $e->getMessage() : ''; + return $this->getAjaxResponse( + $type, + $this->translate('An error has occurred') . $debugMsg, + Ajax::STATUS_HTTP_ERROR + ); + } + + /** + * Call an AJAX method and turn the result into a response. + * + * @param string $method AJAX method to call + * @param string $type Content type to output + * + * @return \Zend\Http\Response + */ + protected function callAjaxMethod($method, $type = 'application/javascript') + { + // Check the AJAX handler plugin manager for the method. + if (!$this->ajaxManager) { + throw new \Exception('AJAX Handler Plugin Manager missing.'); + } + if ($this->ajaxManager->has($method)) { + try { + $handler = $this->ajaxManager->get($method); + return $this->getAjaxResponse( + $type, ...$handler->handleRequest($this->params()) + ); + } catch (\Exception $e) { + return $this->getExceptionResponse($type, $e); + } + } + + // If we got this far, we can't handle the requested method: + return $this->getAjaxResponse( + $type, + $this->translate('Invalid Method'), + Ajax::STATUS_HTTP_BAD_REQUEST + ); + } + + /** + * Store the errors for later, to be added to the output + * + * @param string $errno Error code number + * @param string $errstr Error message + * @param string $errfile File where error occurred + * @param string $errline Line number of error + * + * @return bool Always true to cancel default error handling + */ + public static function storeError($errno, $errstr, $errfile, $errline) + { + self::$php_errors[] = "ERROR [$errno] - " . $errstr . "<br />\n" + . " Occurred in " . $errfile . " on line " . $errline . "."; + return true; + } +} diff --git a/module/VuFind/src/VuFind/Controller/AlmaController.php b/module/VuFind/src/VuFind/Controller/AlmaController.php new file mode 100644 index 0000000000000000000000000000000000000000..d2ef5671e71127aea822ce1262495348475b0f49 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/AlmaController.php @@ -0,0 +1,500 @@ +<?php +/** + * Alma controller + * + * PHP version 5 + * + * Copyright (C) AK Bibliothek Wien für Sozialwissenschaften 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Michael Birkner <michael.birkner@akwien.at> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +namespace VuFind\Controller; + +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Alma controller, mainly for webhooks. + * + * @category VuFind + * @package Controller + * @author Michael Birkner <michael.birkner@akwien.at> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +class AlmaController extends AbstractBase +{ + /** + * Http service + * + * @var \VuFindHttp\HttpService + */ + protected $httpService; + + /** + * Http response + * + * @var \Zend\Http\PhpEnvironment\Response + */ + protected $httpResponse; + + /** + * Http headers + * + * @var \Zend\Http\Headers + */ + protected $httpHeaders; + + /** + * Configuration from config.ini + * + * @var \Zend\Config\Config + */ + protected $config; + + /** + * Alma.ini config + * + * @var \Zend\Config\Config + */ + protected $configAlma; + + /** + * User table + * + * @var \VuFind\Db\Table\User + */ + protected $userTable; + + /** + * Alma Controler constructor. + * + * @param ServiceLocatorInterface $sm The ServiceLocatorInterface + */ + public function __construct(ServiceLocatorInterface $sm) + { + parent::__construct($sm); + $this->httpResponse = $this->getResponse(); + $this->httpHeaders = $this->httpResponse->getHeaders(); + $this->config = $this->getConfig('config'); + $this->configAlma = $this->getConfig('Alma'); + $this->userTable = $this->getTable('user'); + } + + /** + * Action that is executed when the webhook page is called. + * + * @return \Zend\Http\Response|NULL + */ + public function webhookAction() + { + // Request from external + $request = $this->getRequest(); + + // Get request method (GET, POST, ...) + $requestMethod = $request->getMethod(); + + // Get request body if method is POST and is not empty + $requestBodyJson = null; + if ($request->getContent() != null + && !empty($request->getContent()) + && $requestMethod == 'POST' + ) { + try { + $this->checkMessageSignature($request); + } catch (\VuFind\Exception\Forbidden $ex) { + return $this->createJsonResponse( + 'Access to Alma Webhook is forbidden. ' . + 'The message signature is not correct.', 403 + ); + } + $requestBodyJson = json_decode($request->getContent()); + } + + // Get webhook action + $webhookAction = $requestBodyJson->action ?? null; + + // Perform webhook action + switch ($webhookAction) { + + case 'USER': + $accessPermission = 'access.alma.webhook.user'; + try { + $this->checkPermission($accessPermission); + } catch (\VuFind\Exception\Forbidden $ex) { + return $this->createJsonResponse( + 'Access to Alma Webhook \'' . $webhookAction . '\' forbidden. ' . + 'Set permission \'' . $accessPermission . + '\' in \'permissions.ini\'.', 403 + ); + } + + return $this->webhookUser($requestBodyJson); + break; + case 'JOB_END': + case 'NOTIFICATION': + case 'LOAN': + case 'REQUEST': + case 'BIB': + case 'ITEM': + return $this->webhookNotImplemented($webhookAction); + break; + default: + $accessPermission = 'access.alma.webhook.challenge'; + try { + $this->checkPermission($accessPermission); + } catch (\VuFind\Exception\Forbidden $ex) { + return $this->createJsonResponse( + 'Access to Alma Webhook challenge forbidden. Set permission \'' . + $accessPermission . '\' in \'permissions.ini\'.', 403 + ); + } + return $this->webhookChallenge(); + break; + } + } + + /** + * Webhook actions related to a newly created, updated or deleted user in Alma. + * + * @param mixed $requestBodyJson A JSON string decode with json_decode() + * + * @return NULL|\Zend\Http\Response + */ + protected function webhookUser($requestBodyJson) + { + + // Initialize user variable that should hold the user table row + $user = null; + + // Initialize response variable + $jsonResponse = null; + + // Get method from webhook (e. g. "create" for "new user") + $method = $requestBodyJson->webhook_user->method ?? null; + + // Get primary ID + $primaryId = $requestBodyJson->webhook_user->user->primary_id ?? null; + + if ($method == 'CREATE' || $method == 'UPDATE') { + // Get username (could e. g. be the barcode) + $username = null; + $userIdentifiers = $requestBodyJson->webhook_user->user->user_identifier + ?? null; + $idTypeConfig = $this->configAlma->NewUser->idType ?? null; + foreach ($userIdentifiers as $userIdentifier) { + $idTypeHook = $userIdentifier->id_type->value ?? null; + if ($idTypeHook != null + && $idTypeHook == $idTypeConfig + && $username == null + ) { + $username = $userIdentifier->value ?? null; + } + } + + // Use primary ID as username as a fallback if no other + // username ID is available + $username = ($username == null) ? $primaryId : $username; + + // Get user details from Alma Webhook message + $firstname = $requestBodyJson->webhook_user->user->first_name ?? null; + $lastname = $requestBodyJson->webhook_user->user->last_name ?? null; + + $allEmails = $requestBodyJson->webhook_user->user->contact_info->email + ?? null; + $email = null; + foreach ($allEmails as $currentEmail) { + $preferred = $currentEmail->preferred ?? false; + if ($preferred && $email == null) { + $email = $currentEmail->email_address ?? null; + } + } + + if ($method == 'CREATE') { + $user = $this->userTable->getByUsername($username, true); + } + + if ($method == 'UPDATE') { + $user = $this->userTable->getByCatalogId($primaryId); + } + + if ($user) { + $user->username = $username; + $user->firstname = $firstname; + $user->lastname = $lastname; + $user->email = $email; + $user->cat_id = $primaryId; + $user->cat_username = $username; + + try { + $user->save(); + if ($method == 'CREATE') { + $this->sendSetPasswordEmail($user, $this->config); + } + $jsonResponse = $this->createJsonResponse( + 'Successfully ' . strtolower($method) . + 'd user with primary ID \'' . $primaryId . + '\' | username \'' . $username . '\'.', 200 + ); + } catch (\Exception $ex) { + $jsonResponse = $this->createJsonResponse( + 'Error when saving new user with primary ID \'' . + $primaryId . '\' | username \'' . $username . + '\' to VuFind database and sending the welcome email: ' . + $ex->getMessage() . '. ', + 400 + ); + } + } else { + $jsonResponse = $this->createJsonResponse( + 'User with primary ID \'' . $primaryId . '\' | username \'' . + $username . '\' was not found in VuFind database and ' . + 'therefore could not be ' . strtolower($method) . 'd.', + 404 + ); + } + } elseif ($method == 'DELETE') { + $user = $this->userTable->getByCatalogId($primaryId); + if ($user) { + $rowsAffected = $user->delete(); + if ($rowsAffected == 1) { + $jsonResponse = $this->createJsonResponse( + 'Successfully deleted use with primary ID \'' . $primaryId . + '\' in VuFind.', 200 + ); + } else { + $jsonResponse = $this->createJsonResponse( + 'Problem when deleting user with \'' . $primaryId . + '\' in VuFind. It is expected that only 1 row of the ' . + 'VuFind user table is affected by the deletion. But ' . + $rowsAffected . ' were affected. Please check the status ' . + 'of the user in the VuFind database.', 400 + ); + } + } else { + $jsonResponse = $this->createJsonResponse( + 'User with primary ID \'' . $primaryId . '\' was not found in ' . + 'VuFind database and therefore could not be deleted.', 404 + ); + } + } + + return $jsonResponse; + } + + /** + * The webhook challenge. This is used to activate the webhook in Alma. Without + * activating it, Alma will not send its webhook messages to VuFind. + * + * @return \Zend\Http\Response + */ + protected function webhookChallenge() + { + // Get challenge string from the get parameter that Alma sends us. We need to + // return this string in the return message. + $secret = $this->params()->fromQuery('challenge'); + + // Create the return array + $returnArray = []; + + if (isset($secret) && !empty(trim($secret))) { + $returnArray['challenge'] = $secret; + $this->httpResponse->setStatusCode(200); + } else { + $returnArray['error'] = 'GET parameter \'challenge\' is empty, not ' . + 'set or not available when receiving webhook challenge from Alma.'; + $this->httpResponse->setStatusCode(500); + } + + // Remove null from array + $returnArray = array_filter($returnArray); + + // Create return JSON value and set it to the response + $returnJson = json_encode($returnArray, JSON_PRETTY_PRINT); + $this->httpHeaders->addHeaderLine('Content-type', 'application/json'); + $this->httpResponse->setContent($returnJson); + + return $this->httpResponse; + } + + /** + * Send the "set password email" to a new user that was created in Alma and sent + * to VuFind via webhook. + * + * @param \VuFind\Db\Row\User $user A user row object from the VuFind + * user table. + * @param \Zend\Config\Config $config A config object of config.ini + * + * @return void + */ + protected function sendSetPasswordEmail($user, $config) + { + // If we can't find a user + if (null == $user) { + error_log( + 'Could not send the email to new user for setting the ' . + 'password because the user object was not found.' + ); + } else { + // Attempt to send the email + try { + // Create a fresh hash + $user->updateHash(); + $config = $this->getConfig(); + $renderer = $this->getViewRenderer(); + $method = $this->getAuthManager()->getAuthMethod(); + + // Custom template for emails (text-only) + $message = $renderer->render( + 'Email/new-user-welcome.phtml', [ + 'library' => $config->Site->title, + 'firstname' => $user->firstname, + 'lastname' => $user->lastname, + 'username' => $user->username, + 'url' => $this->getServerUrl('myresearch-verify') . '?hash=' . + $user->verify_hash . '&auth_method=' . $method + ] + ); + // Send the email + $this->serviceLocator->get('VuFind\Mailer\Mailer')->send( + $user->email, $config->Site->email, + $this->translate( + 'new_user_welcome_subject', + ['%%library%%' => $config->Site->title] + ), + $message + ); + } catch (\VuFind\Exception\Mail $e) { + error_log( + 'Could not send the \'set-password-email\' to user with ' . + 'primary ID \'' . $user->cat_id . '\' | username \'' . + $user->username . '\': ' . $e->getMessage() + ); + } + } + } + + /** + * Create a HTTP response with JSON content and HTTP status codes that Alma takes + * as "answer" to its webhook calls. + * + * @param string $text The text that should be sent back to Alma + * @param int $httpStatusCode The HTTP status code that should be sent back + * to Alma + * + * @return \Zend\Http\Response + */ + protected function createJsonResponse($text, $httpStatusCode) + { + $returnArray = []; + $returnArray[] = $text; + $returnJson = json_encode($returnArray, JSON_PRETTY_PRINT); + $this->httpHeaders->addHeaderLine('Content-type', 'application/json'); + $this->httpResponse->setStatusCode($httpStatusCode); + $this->httpResponse->setContent($returnJson); + return $this->httpResponse; + } + + /** + * A default message to be sent back to Alma if an action for a certain webhook + * type is not implemented (yet). + * + * @param string $webhookType The type of the webhook + * + * @return \Zend\Http\Response + */ + protected function webhookNotImplemented($webhookType) + { + return $this->createJsonResponse( + $webhookType . ' Alma Webhook is not (yet) implemented in VuFind.', 400 + ); + } + + /** + * Helper function to check access permissions defined in permissions.ini. + * The function validateAccessPermission() will throw an exception that can be + * catched when the permission is denied. + * + * @param string $accessPermission The permission name from permissions.ini that + * should be checked. + * + * @return void + */ + protected function checkPermission($accessPermission) + { + $this->accessPermission = $accessPermission; + $this->accessDeniedBehavior = 'exception'; + $this->validateAccessPermission($this->getEvent()); + } + + /** + * Signing and hashing the body content of the Alma POST request with the + * webhook secret in Alma.ini. The calculated hash value must be the same as + * the 'X-Exl-Signature' in the request header. This is a security measure to + * be sure that the request comes from Alma. + * + * @param \Zend\Stdlib\RequestInterface $request The request from Alma. + * + * @throws \VuFind\Exception\Forbidden Throws forbidden exception + * if hash values are not the + * same. + * + * @return void + */ + protected function checkMessageSignature(\Zend\Stdlib\RequestInterface $request) + { + // Get request content + $requestBodyString = $request->getContent(); + + // Get hashed message signature from request header of Alma webhook request + $almaSignature = ($request->getHeaders()->get('X-Exl-Signature')) + ? $request->getHeaders()->get('X-Exl-Signature')->getFieldValue() + : null; + + // Get the webhook secret defined in Alma.ini + $secretConfig = $this->configAlma->Webhook->secret ?? null; + + // Calculate hmac-sha256 hash from request body we get from Alma webhook and + // sign it with the Alma webhook secret from Alma.ini + $calculatedHash = base64_encode( + hash_hmac( + 'sha256', + $requestBodyString, + $secretConfig, + true + ) + ); + + // Check for correct signature + if ($almaSignature != $calculatedHash) { + error_log( + '[Alma] Unauthorized: Signature value not correct! ' . + 'Hash from Alma: "' . $almaSignature . '". ' . + 'Calculated hash: "' . $calculatedHash . '". ' . + 'Body content for calculating the hash was: ' . + '"' . json_encode( + json_decode($requestBodyString), + JSON_UNESCAPED_UNICODE | + JSON_UNESCAPED_SLASHES + ) . '"' + ); + throw new \VuFind\Exception\Forbidden; + } + } +} diff --git a/module/VuFind/src/VuFind/Controller/AlphabrowseController.php b/module/VuFind/src/VuFind/Controller/AlphabrowseController.php index de24e2cbb74720a25c5d38e7fe2a8813a4c01ef0..4d9229d256d65d2a01433e4c7e35afd3e34782b8 100644 --- a/module/VuFind/src/VuFind/Controller/AlphabrowseController.php +++ b/module/VuFind/src/VuFind/Controller/AlphabrowseController.php @@ -2,7 +2,7 @@ /** * AlphaBrowse Module Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -29,6 +29,7 @@ namespace VuFind\Controller; use VuFindSearch\ParamBag; + /** * AlphabrowseController Class * @@ -86,12 +87,12 @@ class AlphabrowseController extends AbstractBase // Load remaining config parameters $rows_before = isset($config->AlphaBrowse->rows_before) && is_numeric($config->AlphaBrowse->rows_before) - ? (int) $config->AlphaBrowse->rows_before : 0; + ? (int)$config->AlphaBrowse->rows_before : 0; $highlighting = isset($config->AlphaBrowse->highlighting) ? $config->AlphaBrowse->highlighting : false; $limit = isset($config->AlphaBrowse->page_size) && is_numeric($config->AlphaBrowse->page_size) - ? (int) $config->AlphaBrowse->page_size : 20; + ? (int)$config->AlphaBrowse->page_size : 20; // Connect to Solr: $db = $this->serviceLocator->get('VuFind\Search\BackendManager') diff --git a/module/VuFind/src/VuFind/Controller/AuthorController.php b/module/VuFind/src/VuFind/Controller/AuthorController.php index 9166c1858ff13ccb8ceadd64d77f04a3df1167ef..e659071771ec71ebbf0c504b2ebb5b17decf373c 100644 --- a/module/VuFind/src/VuFind/Controller/AuthorController.php +++ b/module/VuFind/src/VuFind/Controller/AuthorController.php @@ -2,7 +2,7 @@ /** * Author Search Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -95,10 +95,8 @@ class AuthorController extends AbstractSearch // If an author was requested, forward to the results page; otherwise, // display the search form: $author = $this->params()->fromQuery('author'); - if (!empty($author)) { - return $this->forwardTo('Author', 'Results'); - } - return $this->createViewModel(); + return !empty($author) + ? $this->forwardTo('Author', 'Results') : parent::homeAction(); } /** @@ -108,8 +106,9 @@ class AuthorController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Controller/AuthorityController.php b/module/VuFind/src/VuFind/Controller/AuthorityController.php index e7102924d355f51daaf5923ca60c0204e76143a5..d560c24ce3e5817c82aa0fa54a5d738131ca579f 100644 --- a/module/VuFind/src/VuFind/Controller/AuthorityController.php +++ b/module/VuFind/src/VuFind/Controller/AuthorityController.php @@ -2,7 +2,7 @@ /** * Authority Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -63,8 +64,8 @@ class AuthorityController extends AbstractSearch return $this->forwardTo('Authority', 'Record'); } - // Do nothing -- just display template - return $this->createViewModel(); + // Default behavior: + return parent::homeAction(); } /** @@ -77,11 +78,11 @@ class AuthorityController extends AbstractSearch $id = $this->params()->fromQuery('id'); $cfg = $this->serviceLocator->get('Config'); $tabConfig = $cfg['vufind']['recorddriver_tabs']; - $driver = $this->serviceLocator->get('VuFind\RecordLoader') + $driver = $this->serviceLocator->get('VuFind\Record\Loader') ->load($id, 'SolrAuth'); $request = $this->getRequest(); $tabs = $this->serviceLocator - ->get('VuFind\RecordTabPluginManager') + ->get('VuFind\RecordTab\PluginManager') ->getTabsForRecord($driver, $tabConfig, $request); return $this->createViewModel(['driver' => $driver, 'tabs' => $tabs]); } diff --git a/module/VuFind/src/VuFind/Controller/BrowZineController.php b/module/VuFind/src/VuFind/Controller/BrowZineController.php index 8c6cd8255959f0675104c61705055987fc16911f..0bfe9da11cf16d5d445891c1b2b7942ec4a180b4 100644 --- a/module/VuFind/src/VuFind/Controller/BrowZineController.php +++ b/module/VuFind/src/VuFind/Controller/BrowZineController.php @@ -2,7 +2,7 @@ /** * BrowZine Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -50,16 +51,6 @@ class BrowZineController extends AbstractSearch parent::__construct($sm); } - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - return $this->createViewModel(); - } - /** * Search action -- call standard results action * diff --git a/module/VuFind/src/VuFind/Controller/BrowseController.php b/module/VuFind/src/VuFind/Controller/BrowseController.php index c6af723d6840f7ff101000d97e5037cb54a69948..09a1bb2f14f65175b4bb24ecdca16bf2dd791175 100644 --- a/module/VuFind/src/VuFind/Controller/BrowseController.php +++ b/module/VuFind/src/VuFind/Controller/BrowseController.php @@ -2,7 +2,7 @@ /** * Browse Module Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFind\Controller; + use VuFind\Exception\Forbidden as ForbiddenException; use Zend\Config\Config; use Zend\ServiceManager\ServiceLocatorInterface; @@ -170,7 +171,7 @@ class BrowseController extends AbstractBase 'Author', 'Topic', 'Genre', 'Region', 'Era' ]; foreach ($remainingOptions as $current) { - $option = strToLower($current); + $option = strtolower($current); if (!isset($this->config->Browse->$option) || $this->config->Browse->$option == true ) { @@ -258,7 +259,7 @@ class BrowseController extends AbstractBase ? 'filter[]=' . $this->params()->fromQuery('query_field') . ':' . urlencode($this->params()->fromQuery('query')) . '&' : ''; - switch($this->getCurrentAction()) { + switch ($this->getCurrentAction()) { case 'LCC': $view->paramTitle .= 'filter[]=callnumber-subject:'; break; @@ -538,7 +539,7 @@ class BrowseController extends AbstractBase protected function getSecondaryList($facet) { $category = $this->getCategory(); - switch($facet) { + switch ($facet) { case 'alphabetical': return ['', $this->getAlphabetList()]; case 'dewey': @@ -595,7 +596,7 @@ class BrowseController extends AbstractBase $sort = 'count', $query = '[* TO *]' ) { $results = $this->serviceLocator - ->get('VuFind\SearchResultsPluginManager')->get('Solr'); + ->get('VuFind\Search\Results\PluginManager')->get('Solr'); $params = $results->getParams(); $params->addFacet($facet); if ($category != null) { @@ -659,7 +660,7 @@ class BrowseController extends AbstractBase if ($action == null) { $action = $this->getCurrentAction(); } - switch(strToLower($action)) { + switch (strtolower($action)) { case 'alphabetical': return $this->getCategory(); case 'dewey': diff --git a/module/VuFind/src/VuFind/Controller/CartController.php b/module/VuFind/src/VuFind/Controller/CartController.php index 1ce57449b217f5898172e1c98106c6fba0ab38cc..db97500faf4ddea1ab633ab33428f2d2b6dea984 100644 --- a/module/VuFind/src/VuFind/Controller/CartController.php +++ b/module/VuFind/src/VuFind/Controller/CartController.php @@ -2,7 +2,7 @@ /** * Book Bag / Bulk Action Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,11 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; -use VuFind\Exception\Forbidden as ForbiddenException, - VuFind\Exception\Mail as MailException, - Zend\ServiceManager\ServiceLocatorInterface, - Zend\Session\Container; + +use VuFind\Exception\Forbidden as ForbiddenException; +use VuFind\Exception\Mail as MailException; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Session\Container; /** * Book Bag / Bulk Action Controller @@ -82,11 +83,11 @@ class CartController extends AbstractBase { if (strlen($this->params()->fromPost('email', '')) > 0) { return 'Email'; - } else if (strlen($this->params()->fromPost('print', '')) > 0) { + } elseif (strlen($this->params()->fromPost('print', '')) > 0) { return 'PrintCart'; - } else if (strlen($this->params()->fromPost('saveCart', '')) > 0) { + } elseif (strlen($this->params()->fromPost('saveCart', '')) > 0) { return 'Save'; - } else if (strlen($this->params()->fromPost('export', '')) > 0) { + } elseif (strlen($this->params()->fromPost('export', '')) > 0) { return 'Export'; } // Check if the user is in the midst of a login process; if not, @@ -148,20 +149,20 @@ class CartController extends AbstractBase $this->followup()->retrieveAndClear('cartAction'); $this->followup()->retrieveAndClear('cartIds'); - $ids = is_null($this->params()->fromPost('selectAll')) + $ids = null === $this->params()->fromPost('selectAll') ? $this->params()->fromPost('ids') : $this->params()->fromPost('idsAll'); // Add items if necessary: if (strlen($this->params()->fromPost('empty', '')) > 0) { $this->getCart()->emptyCart(); - } else if (strlen($this->params()->fromPost('delete', '')) > 0) { + } elseif (strlen($this->params()->fromPost('delete', '')) > 0) { if (empty($ids)) { return $this->redirectToSource('error', 'bulk_noitems_advice'); } else { $this->getCart()->removeItems($ids); } - } else if (strlen($this->params()->fromPost('add', '')) > 0) { + } elseif (strlen($this->params()->fromPost('add', '')) > 0) { if (empty($ids)) { return $this->redirectToSource('error', 'bulk_noitems_advice'); } else { @@ -202,17 +203,20 @@ class CartController extends AbstractBase $controller = 'Cart'; // assume Cart unless overridden below. if (strlen($this->params()->fromPost('email', '')) > 0) { $action = 'Email'; - } else if (strlen($this->params()->fromPost('print', '')) > 0) { + } elseif (strlen($this->params()->fromPost('print', '')) > 0) { $action = 'PrintCart'; - } else if (strlen($this->params()->fromPost('delete', '')) > 0) { + } elseif (strlen($this->params()->fromPost('delete', '')) > 0) { $controller = 'MyResearch'; $action = 'Delete'; - } else if (strlen($this->params()->fromPost('add', '')) > 0) { + } elseif (strlen($this->params()->fromPost('add', '')) > 0) { $action = 'Home'; - } else if (strlen($this->params()->fromPost('export', '')) > 0) { + } elseif (strlen($this->params()->fromPost('export', '')) > 0) { $action = 'Export'; } else { - throw new \Exception('Unrecognized bulk action.'); + $action = $this->followup()->retrieveAndClear('cartAction', null); + if (empty($action)) { + throw new \Exception('Unrecognized bulk action.'); + } } return $this->forwardTo($controller, $action); } @@ -225,7 +229,7 @@ class CartController extends AbstractBase public function emailAction() { // Retrieve ID list: - $ids = is_null($this->params()->fromPost('selectAll')) + $ids = null === $this->params()->fromPost('selectAll') ? $this->params()->fromPost('ids') : $this->params()->fromPost('idsAll'); @@ -266,7 +270,7 @@ class CartController extends AbstractBase // Attempt to send the email and show an appropriate flash message: try { // If we got this far, we're ready to send the email: - $mailer = $this->serviceLocator->get('VuFind\Mailer'); + $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); $mailer->setMaxRecipients($view->maxRecipients); $cc = $this->params()->fromPost('ccself') && $view->from != $view->to ? $view->from : null; @@ -290,7 +294,7 @@ class CartController extends AbstractBase */ public function printcartAction() { - $ids = is_null($this->params()->fromPost('selectAll')) + $ids = null === $this->params()->fromPost('selectAll') ? $this->params()->fromPost('ids') : $this->params()->fromPost('idsAll'); if (!is_array($ids) || empty($ids)) { @@ -322,7 +326,7 @@ class CartController extends AbstractBase public function exportAction() { // Get the desired ID list: - $ids = is_null($this->params()->fromPost('selectAll')) + $ids = null === $this->params()->fromPost('selectAll') ? $this->params()->fromPost('ids') : $this->params()->fromPost('idsAll'); if (!is_array($ids) || empty($ids)) { @@ -339,13 +343,30 @@ class CartController extends AbstractBase if ($export->needsRedirect($format)) { return $this->redirect()->toUrl($url); } + $exportType = $export->getBulkExportType($format); + $params = [ + 'exportType' => $exportType, + 'format' => $format + ]; + if ('post' === $exportType) { + $records = $this->getRecordLoader()->loadBatch($ids); + $recordHelper = $this->getViewRenderer()->plugin('record'); + $parts = []; + foreach ($records as $record) { + $parts[] = $recordHelper($record)->getExport($format); + } + + $params['postField'] = $export->getPostField($format); + $params['postData'] = $export->processGroup($format, $parts); + $params['targetWindow'] = $export->getTargetWindow($format); + $params['url'] = $export->getRedirectUrl($format, ''); + } else { + $params['url'] = $url; + } $msg = [ 'translate' => false, 'html' => true, 'msg' => $this->getViewRenderer()->render( - 'cart/export-success.phtml', [ - 'url' => $url, - 'exportType' => $export->getBulkExportType($format) - ] + 'cart/export-success.phtml', $params ) ]; return $this->redirectToSource('success', $msg); @@ -415,7 +436,7 @@ class CartController extends AbstractBase // Load record information first (no need to prompt for login if we just // need to display a "no records" error message): - $ids = is_null($this->params()->fromPost('selectAll')) + $ids = null === $this->params()->fromPost('selectAll') ? $this->params()->fromPost('ids', $this->params()->fromQuery('ids')) : $this->params()->fromPost('idsAll'); if (!is_array($ids) || empty($ids)) { diff --git a/module/VuFind/src/VuFind/Controller/CartControllerFactory.php b/module/VuFind/src/VuFind/Controller/CartControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..776eaea2ec19b072a9232a5a7968dc5172338e8a --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/CartControllerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Cart controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cart controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CartControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $session = new \Zend\Session\Container( + 'cart_followup', $container->get('Zend\Session\SessionManager') + ); + return new $requestedName($container, $session); + } +} diff --git a/module/VuFind/src/VuFind/Controller/ChannelsController.php b/module/VuFind/src/VuFind/Controller/ChannelsController.php index 94f96a04066d91b573143f3212fecad662b8ab3c..d60077c81cd7ed8bcaf58c52bc58be9bf6900a32 100644 --- a/module/VuFind/src/VuFind/Controller/ChannelsController.php +++ b/module/VuFind/src/VuFind/Controller/ChannelsController.php @@ -2,7 +2,7 @@ /** * Channels Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/indexing:alphabetical_heading_browse Wiki */ namespace VuFind\Controller; -use Zend\Config\Config; + +use VuFind\ChannelProvider\ChannelLoader; /** * Channels Class @@ -42,31 +43,20 @@ use Zend\Config\Config; class ChannelsController extends AbstractBase { /** - * Retrieve channel information for the Channels/Home page. + * Channel loader * - * @param array $providers Array of channel providers - * @param string $searchClassId Search class ID - * @param string $token Channel token + * @var ChannelLoader + */ + protected $loader; + + /** + * Constructor * - * @return array + * @param ChannelLoader $loader Channel loader */ - protected function getHomeChannels($providers, $searchClassId, $token) + public function __construct(ChannelLoader $loader) { - $callback = function ($runner, $params, $searchClassId) use ($providers) { - foreach ($providers as $provider) { - $provider->configureSearchParams($params); - } - }; - $runner = $this->serviceLocator->get('VuFind\SearchRunner'); - $results = $runner->run([], $searchClassId, $callback); - - $channels = []; - foreach ($providers as $provider) { - $channels = array_merge( - $channels, $provider->getFromSearch($results, $token) - ); - } - return $channels; + $this->loader = $loader; } /** @@ -76,33 +66,11 @@ class ChannelsController extends AbstractBase */ public function homeAction() { - $config = $this->getConfig('channels'); - $defaultSearchClassId = isset($config->General->default_home_source) - ? $config->General->default_home_source : DEFAULT_SEARCH_BACKEND; - $searchClassId = $this->params()->fromQuery('source', $defaultSearchClassId); - $providerIds = isset($config->{"source.$searchClassId"}->home) - ? $config->{"source.$searchClassId"}->home->toArray() : []; - $providers = $this->getChannelProviderArray($providerIds, $config); - + $source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND); + $activeChannel = $this->params()->fromQuery('channelProvider'); $token = $this->params()->fromQuery('channelToken'); - if (isset($config->General->cache_home_channels) - && $config->General->cache_home_channels - ) { - $parts = [implode(',', $providerIds), $searchClassId, $token]; - $cacheKey = 'homeChannels-' . md5(implode('-', $parts)); - $cache = $this->serviceLocator->get('VuFind\CacheManager') - ->getCache('object'); - } else { - $cacheKey = false; - } - $channels = $cacheKey ? $cache->getItem($cacheKey) : false; - if (!$channels) { - $channels = $this->getHomeChannels($providers, $searchClassId, $token); - if ($cacheKey) { - $cache->setItem($cacheKey, $channels); - } - } - return $this->createViewModel(compact('token', 'channels')); + $context = $this->loader->getHomeContext($token, $activeChannel, $source); + return $this->createViewModel($context); } /** @@ -112,25 +80,13 @@ class ChannelsController extends AbstractBase */ public function recordAction() { - $view = $this->createViewModel(); - - $loader = $this->getRecordLoader(); + $recordId = $this->params()->fromQuery('id'); $source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND); - $view->driver = $loader->load($this->params()->fromQuery('id'), $source); - - $config = $this->getConfig('channels'); - $providerIds = isset($config->{"source.$source"}->record) - ? $config->{"source.$source"}->record->toArray() : []; - $view->channels = []; - $view->token = $this->params()->fromQuery('channelToken'); - $providers = $this->getChannelProviderArray($providerIds, $config); - foreach ($providers as $provider) { - $view->channels = array_merge( - $view->channels, - $provider->getFromRecord($view->driver, $view->token) - ); - } - return $view; + $activeChannel = $this->params()->fromQuery('channelProvider'); + $token = $this->params()->fromQuery('channelToken'); + $context = $this->loader + ->getRecordContext($recordId, $token, $activeChannel, $source); + return $this->createViewModel($context); } /** @@ -140,89 +96,14 @@ class ChannelsController extends AbstractBase */ public function searchAction() { - $view = $this->createViewModel(); - - $runner = $this->serviceLocator->get('VuFind\SearchRunner'); - // Send both GET and POST variables to search class: $request = $this->getRequest()->getQuery()->toArray() + $this->getRequest()->getPost()->toArray(); - $searchClassId = $this->params() - ->fromQuery('source', DEFAULT_SEARCH_BACKEND); - - $config = $this->getConfig('channels'); - $providerIds = isset($config->{"source.$searchClassId"}->search) - ? $config->{"source.$searchClassId"}->search->toArray() : []; - $providers = $this->getChannelProviderArray($providerIds, $config); - - $callback = function ($runner, $params, $searchClassId) use ($providers) { - foreach ($providers as $provider) { - $provider->configureSearchParams($params); - } - }; - $view->results = $runner->run($request, $searchClassId, $callback); - - $view->channels = []; - $view->lookfor = $this->params()->fromQuery('lookfor'); - $view->token = $this->params()->fromQuery('channelToken'); - foreach ($providers as $provider) { - $view->channels = array_merge( - $view->channels, - $provider->getFromSearch($view->results, $view->token) - ); - } - return $view; - } - - /** - * Get an array of channel providers matching the provided IDs (or just one, - * if the channelProvider GET parameter is set). - * - * @param array $providerIds Array of IDs to load - * @param Config $config Channel configuration - * - * @return array - */ - protected function getChannelProviderArray($providerIds, $config) - { - $id = $this->params()->fromQuery('channelProvider'); - if (!empty($id) && in_array($id, $providerIds)) { - return [$this->getChannelProvider($id, $config)]; - } - $results = []; - foreach ($providerIds as $id) { - $results[] = $this->getChannelProvider($id, $config); - } - return $results; - } - - /** - * Convenience method to retrieve a channel provider. - * - * @param string $providerId Channel provider name and optional config - * (colon-delimited) - * @param Config $config Channel configuration - * - * @return \VuFind\ChannelProvider\ChannelProviderInterface - */ - protected function getChannelProvider($providerId, Config $config) - { - // The provider ID consists of a service name and an optional config - // section -- break out the relevant parts: - list($serviceName, $configSection) = explode(':', $providerId . ':'); - - // Load configuration, using default value if necessary: - if (empty($configSection)) { - $configSection = "provider.$serviceName"; - } - $options = isset($config->{$configSection}) - ? $config->{$configSection}->toArray() : []; - - // Load the service, and configure appropriately: - $provider = $this->serviceLocator - ->get('VuFind\ChannelProviderPluginManager')->get($serviceName); - $provider->setProviderId($providerId); - $provider->setOptions($options); - return $provider; + $source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND); + $activeChannel = $this->params()->fromQuery('channelProvider'); + $token = $this->params()->fromQuery('channelToken'); + $context = $this->loader + ->getSearchContext($request, $token, $activeChannel, $source); + return $this->createViewModel($context); } } diff --git a/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..72fb8015b4701bf014c80c70c84d119377424c8b --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Channels controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Channels controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ChannelsControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $loader = $container->get('VuFind\ChannelProvider\ChannelLoader'); + return new $requestedName($loader); + } +} diff --git a/module/VuFind/src/VuFind/Controller/CollectionController.php b/module/VuFind/src/VuFind/Controller/CollectionController.php index 758f67b5ee0ac989b7c547e9bac66330be3e7f22..e2848e13e149b44275319528167a5e35a5bb2523 100644 --- a/module/VuFind/src/VuFind/Controller/CollectionController.php +++ b/module/VuFind/src/VuFind/Controller/CollectionController.php @@ -2,7 +2,7 @@ /** * Collection Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\Config\Config; use Zend\ServiceManager\ServiceLocatorInterface; @@ -78,6 +79,12 @@ class CollectionController extends AbstractRecord */ protected function showTab($tab, $ajax = false) { + // Check that collections are enabled and redirect if necessary + $config = $this->getConfig(); + if (empty($config->Collections->collections)) { + return $this->redirectToRecord(); + } + $result = parent::showTab($tab, $ajax); if (!$ajax && $result instanceof \Zend\View\Model\ViewModel) { $result->setTemplate('collection/view'); @@ -92,8 +99,9 @@ class CollectionController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Controller/CollectionsController.php b/module/VuFind/src/VuFind/Controller/CollectionsController.php index 503ebffe5fef224e488e97bee8a8595d7b921daf..7055802642a230abc93b781594cd1e314b066543 100644 --- a/module/VuFind/src/VuFind/Controller/CollectionsController.php +++ b/module/VuFind/src/VuFind/Controller/CollectionsController.php @@ -2,7 +2,7 @@ /** * Collections Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use VuFindSearch\Query\Query; use Zend\Config\Config; use Zend\ServiceManager\ServiceLocatorInterface; @@ -173,7 +174,7 @@ class CollectionsController extends AbstractBase $browseField = "hierarchy_browse"; $searchObject = $this->serviceLocator - ->get('VuFind\SearchResultsPluginManager')->get('Solr'); + ->get('VuFind\Search\Results\PluginManager')->get('Solr'); foreach ($appliedFilters as $filter) { $searchObject->getParams()->addFilter($filter); } @@ -182,8 +183,7 @@ class CollectionsController extends AbstractBase $result = $searchObject->getFullFieldFacets( [$browseField], false, 150000, 'index' ); - $result = isset($result[$browseField]['data']['list']) - ? $result[$browseField]['data']['list'] : []; + $result = $result[$browseField]['data']['list'] ?? []; $delimiter = $this->getBrowseDelimiter(); foreach ($result as $rkey => $collection) { @@ -265,7 +265,7 @@ class CollectionsController extends AbstractBase } $result = $sorted; - return isset($key) ? $key : 0; + return $key ?? 0; } /** @@ -337,7 +337,7 @@ class CollectionsController extends AbstractBase { $title = addcslashes($title, '"'); $query = new Query("is_hierarchy_title:\"$title\"", 'AllFields'); - $searchService = $this->serviceLocator->get('VuFind\Search'); + $searchService = $this->serviceLocator->get('VuFindSearch\Service'); $result = $searchService->search('Solr', $query, 0, $this->getBrowseLimit()); return $result->getRecords(); } diff --git a/module/VuFind/src/VuFind/Controller/CombinedController.php b/module/VuFind/src/VuFind/Controller/CombinedController.php index 7f59203591a433ad23708d00d62d50a541e40ea9..6c8351d5fc94de5c4bae110fdab817c858b95fa6 100644 --- a/module/VuFind/src/VuFind/Controller/CombinedController.php +++ b/module/VuFind/src/VuFind/Controller/CombinedController.php @@ -2,7 +2,7 @@ /** * Combined Search Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -39,6 +40,8 @@ use Zend\ServiceManager\ServiceLocatorInterface; */ class CombinedController extends AbstractSearch { + use AjaxResponseTrait; + /** * Constructor * @@ -57,7 +60,11 @@ class CombinedController extends AbstractSearch */ public function homeAction() { - return $this->createViewModel(); + // We need to load blocks differently in this controller since it + // doesn't follow the usual configuration pattern. + $blocks = $this->serviceLocator->get('VuFind\ContentBlock\BlockLoader') + ->getFromConfig('combined'); + return $this->createViewModel(compact('blocks')); } /** @@ -74,8 +81,8 @@ class CombinedController extends AbstractSearch // Validate configuration: $sectionId = $this->params()->fromQuery('id'); - $config = $this->serviceLocator->get('VuFind\Config')->get('combined') - ->toArray(); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('combined')->toArray(); $tabConfig = $this->getTabConfig($config); if (!isset($tabConfig[$sectionId])) { throw new \Exception('Illegal ID'); @@ -84,7 +91,7 @@ class CombinedController extends AbstractSearch // Retrieve results: $options = $this->serviceLocator - ->get('VuFind\SearchOptionsPluginManager'); + ->get('VuFind\Search\Options\PluginManager'); $currentOptions = $options->get($searchClassId); list($controller, $action) = explode('-', $currentOptions->getSearchAction()); @@ -93,13 +100,6 @@ class CombinedController extends AbstractSearch $this->adjustQueryForSettings($settings); $settings['view'] = $this->forwardTo($controller, $action); - // Send response: - $response = $this->getResponse(); - $headers = $response->getHeaders(); - $headers->addHeaderLine('Content-type', 'text/html'); - $headers->addHeaderLine('Cache-Control', 'no-cache, must-revalidate'); - $headers->addHeaderLine('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT'); - // Should we suppress content due to emptiness? if (isset($settings['hide_if_empty']) && $settings['hide_if_empty'] && $settings['view']->results->getResultTotal() == 0 @@ -107,7 +107,7 @@ class CombinedController extends AbstractSearch $html = ''; } else { $cart = $this->serviceLocator->get('VuFind\Cart'); - $general = $this->serviceLocator->get('VuFind\Config') + $general = $this->serviceLocator->get('VuFind\Config\PluginManager') ->get('config'); $viewParams = [ 'searchClassId' => $searchClassId, @@ -126,8 +126,7 @@ class CombinedController extends AbstractSearch $viewParams ); } - $response->setContent($html); - return $response; + return $this->getAjaxResponse('text/html', $html); } /** @@ -140,7 +139,7 @@ class CombinedController extends AbstractSearch // Set up current request context: $request = $this->getRequest()->getQuery()->toArray() + $this->getRequest()->getPost()->toArray(); - $results = $this->serviceLocator->get('VuFind\SearchRunner')->run( + $results = $this->serviceLocator->get('VuFind\Search\SearchRunner')->run( $request, 'Combined', $this->getSearchSetupCallback() ); @@ -152,9 +151,9 @@ class CombinedController extends AbstractSearch // Gather combined results: $combinedResults = []; $options = $this->serviceLocator - ->get('VuFind\SearchOptionsPluginManager'); - $config = $this->serviceLocator->get('VuFind\Config')->get('combined') - ->toArray(); + ->get('VuFind\Search\Options\PluginManager'); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('combined')->toArray(); $supportsCart = false; $supportsCartOptions = []; foreach ($this->getTabConfig($config) as $current => $settings) { @@ -193,15 +192,15 @@ class CombinedController extends AbstractSearch && intval($config['Layout']['columns']) <= count($combinedResults) ? intval($config['Layout']['columns']) : count($combinedResults); - $placement = isset($config['Layout']['stack_placement']) - ? $config['Layout']['stack_placement'] - : 'distributed'; + $placement = $config['Layout']['stack_placement'] + ?? 'distributed'; if (!in_array($placement, ['distributed', 'left', 'right'])) { $placement = 'distributed'; } // Get default config for showBulkOptions - $settings = $this->serviceLocator->get('VuFind\Config')->get('config'); + $settings = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); // Build view model: return $this->createViewModel( @@ -214,8 +213,7 @@ class CombinedController extends AbstractSearch 'results' => $results, 'supportsCart' => $supportsCart, 'supportsCartOptions' => $supportsCartOptions, - 'showBulkOptions' => isset($settings->Site->showBulkOptions) - && $settings->Site->showBulkOptions + 'showBulkOptions' => $settings->Site->showBulkOptions ?? false ] ); } @@ -242,7 +240,7 @@ class CombinedController extends AbstractSearch unset($params['activeSearchClassId']); // don't need to pass this forward $route = $this->serviceLocator - ->get('VuFind\SearchOptionsPluginManager') + ->get('VuFind\Search\Options\PluginManager') ->get($searchClassId)->getSearchAction(); $base = $this->url()->fromRoute($route); return $this->redirect()->toUrl($base . '?' . http_build_query($params)); @@ -277,7 +275,7 @@ class CombinedController extends AbstractSearch { // Apply limit setting, if any: $query = $this->getRequest()->getQuery(); - $query->limit = isset($settings['limit']) ? $settings['limit'] : null; + $query->limit = $settings['limit'] ?? null; // Apply filters, if any: $query->filter = isset($settings['filter']) @@ -294,6 +292,10 @@ class CombinedController extends AbstractSearch // Reset override to avoid bleed-over from one section to the next! $query->recommendOverride = false; + // Always disable 'jumpto' setting, as it does not make sense to + // load a record view in the context of combined search. + $query->jumpto = false; + // Always leave noresults active (useful for 0-hit searches) and // side inactive (no room to display) but display or hide top based // on include_recommendations setting. @@ -320,6 +322,7 @@ class CombinedController extends AbstractSearch protected function getTabConfig($config) { // Strip out non-tab sections of the configuration: + unset($config['HomePage']); unset($config['Layout']); unset($config['RecommendationModules']); diff --git a/module/VuFind/src/VuFind/Controller/ConfirmController.php b/module/VuFind/src/VuFind/Controller/ConfirmController.php index 7bf76a42fafe228bd007ccd3dc5fc53dae1e5716..3bacfcea283aad84914e5d86587359c7661d9a1d 100644 --- a/module/VuFind/src/VuFind/Controller/ConfirmController.php +++ b/module/VuFind/src/VuFind/Controller/ConfirmController.php @@ -2,7 +2,7 @@ /** * Confirm Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Controller/ContentController.php b/module/VuFind/src/VuFind/Controller/ContentController.php index e2cd7763bba98c26792bf9b084356c0ef5878a30..85b9de46d4355b1c853f9bb62ad44267dcb780f3 100644 --- a/module/VuFind/src/VuFind/Controller/ContentController.php +++ b/module/VuFind/src/VuFind/Controller/ContentController.php @@ -2,7 +2,7 @@ /** * Content Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * Copyright (C) The National Library of Finland 2014-2016. @@ -51,7 +51,7 @@ class ContentController extends AbstractBase { $page = $this->params()->fromRoute('page'); $themeInfo = $this->serviceLocator->get('VuFindTheme\ThemeInfo'); - $language = $this->serviceLocator->get('VuFind\Translator') + $language = $this->serviceLocator->get('Zend\Mvc\I18n\Translator') ->getLocale(); $defaultLanguage = $this->getConfig()->Site->language; diff --git a/module/VuFind/src/VuFind/Controller/CoverController.php b/module/VuFind/src/VuFind/Controller/CoverController.php index c1c455f50510063291a54016b8b8620753a2cb16..c4d3b6770689de0de2db8989079236d5e41b6a2d 100644 --- a/module/VuFind/src/VuFind/Controller/CoverController.php +++ b/module/VuFind/src/VuFind/Controller/CoverController.php @@ -2,7 +2,7 @@ /** * Cover Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,7 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller; -use VuFind\Cover\CachingProxy, VuFind\Cover\Loader; + +use VuFind\Cover\CachingProxy; +use VuFind\Cover\Loader; +use VuFind\Session\Settings as SessionSettings; /** * Generates covers for book entries @@ -37,73 +40,42 @@ use VuFind\Cover\CachingProxy, VuFind\Cover\Loader; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ -class CoverController extends AbstractBase +class CoverController extends \Zend\Mvc\Controller\AbstractActionController { /** * Cover loader * * @var Loader */ - protected $loader = false; + protected $loader; /** - * Caching proxy + * Proxy loader * * @var CachingProxy */ - protected $proxy = false; - - /** - * Get the cover cache directory - * - * @return string - */ - protected function getCacheDir() - { - return $this->serviceLocator->get('VuFind\CacheManager') - ->getCache('cover')->getOptions()->getCacheDir(); - } + protected $proxy; /** - * Get the cover loader object + * Session settings * - * @return Loader + * @var SessionSettings */ - protected function getLoader() - { - // Construct object for loading cover images if it does not already exist: - if (!$this->loader) { - $cacheDir = $this->getCacheDir(); - $this->loader = new Loader( - $this->getConfig(), - $this->serviceLocator->get('VuFind\ContentCoversPluginManager'), - $this->serviceLocator->get('VuFindTheme\ThemeInfo'), - $this->serviceLocator->get('VuFind\Http')->createClient(), - $cacheDir - ); - \VuFind\ServiceManager\Initializer::initInstance( - $this->loader, $this->serviceLocator - ); - } - return $this->loader; - } + protected $sessionSettings = null; /** - * Get the caching proxy object + * Constructor * - * @return CachingProxy + * @param Loader $loader Cover loader + * @param CachingProxy $proxy Proxy loader + * @param SessionSettings $ss Session settings */ - protected function getProxy() - { - if (!$this->proxy) { - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); - $cacheDir = $this->getCacheDir() . '/proxy'; - $config = $this->getConfig()->toArray(); - $whitelist = isset($config['Content']['coverproxyCache']) - ? (array)$config['Content']['coverproxyCache'] : []; - $this->proxy = new CachingProxy($client, $cacheDir, $whitelist); - } - return $this->proxy; + public function __construct(Loader $loader, CachingProxy $proxy, + SessionSettings $ss + ) { + $this->loader = $loader; + $this->proxy = $proxy; + $this->sessionSettings = $ss; } /** @@ -137,15 +109,15 @@ class CoverController extends AbstractBase */ public function showAction() { - $this->disableSessionWrites(); // avoid session write timing bug + $this->sessionSettings->disableWrite(); // avoid session write timing bug // Special case: proxy a full URL: - $proxy = $this->params()->fromQuery('proxy'); - if (!empty($proxy)) { + $url = $this->params()->fromQuery('proxy'); + if (!empty($url)) { try { - $image = $this->getProxy()->fetch($proxy); + $image = $this->proxy->fetch($url); return $this->displayImage( - $image->getHeaders()->get('contenttype')->getFieldValue(), + $image->getHeaders()->get('content-type')->getFieldValue(), $image->getContent() ); } catch (\Exception $e) { @@ -155,7 +127,7 @@ class CoverController extends AbstractBase } // Default case -- use image loader: - $this->getLoader()->loadImage($this->getImageParams()); + $this->loader->loadImage($this->getImageParams()); return $this->displayImage(); } @@ -166,8 +138,8 @@ class CoverController extends AbstractBase */ public function unavailableAction() { - $this->disableSessionWrites(); // avoid session write timing bug - $this->getLoader()->loadUnavailable(); + $this->sessionSettings->disableWrite(); // avoid session write timing bug + $this->loader->loadUnavailable(); return $this->displayImage(); } @@ -185,7 +157,7 @@ class CoverController extends AbstractBase $response = $this->getResponse(); $headers = $response->getHeaders(); $headers->addHeaderLine( - 'Content-type', $type ?: $this->getLoader()->getContentType() + 'Content-type', $type ?: $this->loader->getContentType() ); // Send proper caching headers so that the user's browser @@ -203,7 +175,7 @@ class CoverController extends AbstractBase 'Expires', gmdate('D, d M Y H:i:s', time() + $coverImageTtl) . ' GMT' ); - $response->setContent($image ?: $this->getLoader()->getImage()); + $response->setContent($image ?: $this->loader->getImage()); return $response; } } diff --git a/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php b/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..26de632cd2a86d138aa8747a1ccb5e1438d643bd --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Cover controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cover controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CoverControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Cover\Loader'), + $container->get('VuFind\Cover\CachingProxy'), + $container->get('VuFind\Session\Settings') + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/EITController.php b/module/VuFind/src/VuFind/Controller/EITController.php index 79bd63712c3cc47a65a64ff3c0e128448ae9b76c..b38f4dfd7e6ca4418727cbc3185e0943fce94113 100644 --- a/module/VuFind/src/VuFind/Controller/EITController.php +++ b/module/VuFind/src/VuFind/Controller/EITController.php @@ -2,7 +2,7 @@ /** * EIT Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -58,20 +59,10 @@ class EITController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('EIT'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); - } - - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - // Set up default parameters: - return $this->createViewModel(); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('EIT'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } /** diff --git a/module/VuFind/src/VuFind/Controller/EITrecordController.php b/module/VuFind/src/VuFind/Controller/EITrecordController.php index d16202bcddeae61bf0b878ad5b579d971931f650..5bab533561128c0f64aa829b0d6d693b8fc6677f 100644 --- a/module/VuFind/src/VuFind/Controller/EITrecordController.php +++ b/module/VuFind/src/VuFind/Controller/EITrecordController.php @@ -2,7 +2,7 @@ /** * EIT Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -65,9 +66,9 @@ class EITrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('EIT'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('EIT'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } - } diff --git a/module/VuFind/src/VuFind/Controller/EdsController.php b/module/VuFind/src/VuFind/Controller/EdsController.php index 24b8f5434d0b4767a74a21f612bf3462546fcd6d..cb49ea2d27e28768e43fae6ec0a2f2746fd70604 100644 --- a/module/VuFind/src/VuFind/Controller/EdsController.php +++ b/module/VuFind/src/VuFind/Controller/EdsController.php @@ -2,7 +2,7 @@ /** * Eds Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use VuFind\Solr\Utils as SolrUtils; use Zend\ServiceManager\ServiceLocatorInterface; @@ -58,9 +59,10 @@ class EdsController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('EDS'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('EDS'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } /** @@ -90,7 +92,7 @@ class EdsController extends AbstractSearch public function homeAction() { $this->setUp(); - return $this->createViewModel(); + return parent::homeAction(); } /** @@ -287,7 +289,6 @@ class EdsController extends AbstractSearch // Explicitly execute search within controller -- this allows us to // catch exceptions more reliably: $results->performAndProcessSearch(); - } catch (\VuFindSearch\Backend\Exception\BackendException $e) { if ($e->hasTag('VuFind\Search\ParserError')) { // If it's a parse error or the user specified an invalid field, we diff --git a/module/VuFind/src/VuFind/Controller/EdsrecordController.php b/module/VuFind/src/VuFind/Controller/EdsrecordController.php index e409e99ada79653e70ab650f145a7d700b0dcbe0..526588c36ffe92ee2a8640cc080e91d1d3863da0 100644 --- a/module/VuFind/src/VuFind/Controller/EdsrecordController.php +++ b/module/VuFind/src/VuFind/Controller/EdsrecordController.php @@ -2,7 +2,7 @@ /** * EDS Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use VuFind\Exception\Forbidden as ForbiddenException; use Zend\ServiceManager\ServiceLocatorInterface; @@ -81,8 +82,9 @@ class EdsrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('EDS'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('EDS'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Controller/ErrorController.php b/module/VuFind/src/VuFind/Controller/ErrorController.php index 2255beed4bda996b8281d9606c2e095b2451450b..fcd92d1c508badae99a235051168b4315439a4af 100644 --- a/module/VuFind/src/VuFind/Controller/ErrorController.php +++ b/module/VuFind/src/VuFind/Controller/ErrorController.php @@ -2,7 +2,7 @@ /** * Error Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -63,5 +63,4 @@ class ErrorController extends AbstractActionController ['msg' => $this->params()->fromQuery('msg')] ); } - } diff --git a/module/VuFind/src/VuFind/Controller/ExternalAuthController.php b/module/VuFind/src/VuFind/Controller/ExternalAuthController.php index 1964d2105011551be592cedc43f801ed5847614f..65a646c199eda9e6463e9023c5eedd075cac97cf 100644 --- a/module/VuFind/src/VuFind/Controller/ExternalAuthController.php +++ b/module/VuFind/src/VuFind/Controller/ExternalAuthController.php @@ -2,7 +2,7 @@ /** * External Authentication/Authorization Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2016. * @@ -27,6 +27,8 @@ */ namespace VuFind\Controller; +use Zend\Log\LoggerAwareInterface; + /** * External Authentication/Authorization Controller * @@ -38,8 +40,10 @@ namespace VuFind\Controller; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ -class ExternalAuthController extends AbstractBase +class ExternalAuthController extends AbstractBase implements LoggerAwareInterface { + use \VuFind\Log\LoggerAwareTrait; + /** * Permission from permissions.ini required for EZProxy authorization. * @@ -62,24 +66,40 @@ class ExternalAuthController extends AbstractBase } $user = $this->getUser(); - if ($user) { - // Logged in, check for authorization - $authService = $this->serviceLocator - ->get('ZfcRbac\Service\AuthorizationService'); - if (!$authService->isGranted($this->ezproxyRequiredPermission)) { - $view = $this->createViewModel(); - $view->unauthorized = true; - $this->flashMessenger()->addErrorMessage( - 'external_auth_unauthorized' + + $authService = $this->serviceLocator + ->get('ZfcRbac\Service\AuthorizationService'); + if ($authService->isGranted($this->ezproxyRequiredPermission)) { + // Access granted, redirect to EZproxy + if (empty($config->EZproxy->disable_ticket_auth_logging)) { + $logger = $this->serviceLocator->get('VuFind\Logger'); + $logger->log( + \Zend\Log\Logger::INFO, + "EZproxy login to '" . $config->EZproxy->host + . "' for '" . ($user ? $user->username : 'anonymous') + . "' from IP address " + . $this->request->getServer()->get('REMOTE_ADDR') ); - return $view; } $url = $this->params()->fromPost( 'url', $this->params()->fromQuery('url') ); + $username = !empty($config->EZproxy->anonymous_ticket) || !$user + ? 'anonymous' : $user->username; return $this->redirect()->toUrl( - $this->createEzproxyTicketUrl($user->username, $url) + $this->createEzproxyTicketUrl($username, $url) + ); + } + + if ($user) { + // User already logged in, inform that the current login does not + // allow access. + $view = $this->createViewModel(); + $view->unauthorized = true; + $this->flashMessenger()->addErrorMessage( + 'external_auth_unauthorized' ); + return $view; } return $this->forceLogin('external_auth_login_message'); } diff --git a/module/VuFind/src/VuFind/Controller/Factory.php b/module/VuFind/src/VuFind/Controller/Factory.php deleted file mode 100644 index 79fcd32f993c79db2f570997fc2f76d557553447..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Controller/Factory.php +++ /dev/null @@ -1,191 +0,0 @@ -<?php -/** - * Factory for controllers. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Controller; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for controllers. - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct a generic controller. - * - * @param string $name Name of table to construct (fully qualified - * class name, or else a class name within the current namespace) - * @param ServiceManager $sm Service manager - * - * @return object - */ - public static function getGenericController($name, ServiceManager $sm) - { - // Prepend the current namespace unless we receive a FQCN: - $class = (strpos($name, '\\') === false) - ? __NAMESPACE__ . '\\' . $name : $name; - if (!class_exists($class)) { - throw new \Exception('Cannot construct ' . $class); - } - return new $class($sm->getServiceLocator()); - } - - /** - * Construct a generic controller. - * - * @param string $name Method name being called - * @param array $args Method arguments - * - * @return object - */ - public static function __callStatic($name, $args) - { - // Strip "get" from method name to get name of class; pass first argument - // on assumption that it should be the ServiceManager object. - return static::getGenericController( - substr($name, 3), isset($args[0]) ? $args[0] : null - ); - } - - /** - * Construct the BrowseController. - * - * @param ServiceManager $sm Service manager. - * - * @return BrowseController - */ - public static function getBrowseController(ServiceManager $sm) - { - return new BrowseController( - $sm->getServiceLocator(), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the CartController. - * - * @param ServiceManager $sm Service manager. - * - * @return BrowseController - */ - public static function getCartController(ServiceManager $sm) - { - return new CartController( - $sm->getServiceLocator(), - new \Zend\Session\Container( - 'cart_followup', - $sm->getServiceLocator()->get('VuFind\SessionManager') - ) - ); - } - - /** - * Construct the CollectionController. - * - * @param ServiceManager $sm Service manager. - * - * @return CollectionController - */ - public static function getCollectionController(ServiceManager $sm) - { - return new CollectionController( - $sm->getServiceLocator(), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the CollectionsController. - * - * @param ServiceManager $sm Service manager. - * - * @return CollectionsController - */ - public static function getCollectionsController(ServiceManager $sm) - { - return new CollectionsController( - $sm->getServiceLocator(), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the IndexController. - * - * @param ServiceManager $sm Service manager. - * - * @return RecordController - */ - public static function getIndexController(ServiceManager $sm) - { - return new IndexController( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - $sm->getServiceLocator()->get('VuFind\AuthManager') - ); - } - - /** - * Construct the RecordController. - * - * @param ServiceManager $sm Service manager. - * - * @return RecordController - */ - public static function getRecordController(ServiceManager $sm) - { - return new RecordController( - $sm->getServiceLocator(), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the UpgradeController. - * - * @param ServiceManager $sm Service manager. - * - * @return UpgradeController - */ - public static function getUpgradeController(ServiceManager $sm) - { - return new UpgradeController( - $sm->getServiceLocator(), - $sm->getServiceLocator()->get('VuFind\CookieManager'), - new \Zend\Session\Container( - 'upgrade', $sm->getServiceLocator()->get('VuFind\SessionManager') - ) - ); - } -} diff --git a/module/VuFind/src/VuFind/Controller/FeedbackController.php b/module/VuFind/src/VuFind/Controller/FeedbackController.php index 13f750ae6269e09f44d112ccebe71e1e804e5147..211bbe1d34a04d5accfbba7ba464e00c8c17bee0 100644 --- a/module/VuFind/src/VuFind/Controller/FeedbackController.php +++ b/module/VuFind/src/VuFind/Controller/FeedbackController.php @@ -2,7 +2,7 @@ /** * Feedback Controller * - * PHP version 5 + * PHP version 7 * * @category VuFind * @package Controller @@ -11,6 +11,8 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + +use VuFind\Exception\Mail as MailException; use Zend\Mail\Address; /** @@ -52,14 +54,13 @@ class FeedbackController extends AbstractBase // Process form submission: if ($this->formWasSubmitted('submit', $view->useRecaptcha)) { - if (empty($view->email) || empty($view->comments)) { $this->flashMessenger()->addMessage('bulk_error_missing', 'error'); return; } // These settings are set in the feedback settion of your config.ini - $config = $this->serviceLocator->get('VuFind\Config') + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') ->get('config'); $feedback = isset($config->Feedback) ? $config->Feedback : null; $recipient_email = isset($feedback->recipient_email) @@ -85,7 +86,7 @@ class FeedbackController extends AbstractBase // This sets up the email to be sent // Attempt to send the email and show an appropriate flash message: try { - $mailer = $this->serviceLocator->get('VuFind\Mailer'); + $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); $mailer->send( new Address($recipient_email, $recipient_name), new Address($sender_email, $sender_name), diff --git a/module/VuFind/src/VuFind/Controller/HelpController.php b/module/VuFind/src/VuFind/Controller/HelpController.php index 320dee1df69fe87b17dfea38069b5e7c56207ec6..4d85bfa75539e05c3ff635d53c9c505ecce2ffa7 100644 --- a/module/VuFind/src/VuFind/Controller/HelpController.php +++ b/module/VuFind/src/VuFind/Controller/HelpController.php @@ -2,7 +2,7 @@ /** * Home action for Help module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/Controller/HierarchyController.php b/module/VuFind/src/VuFind/Controller/HierarchyController.php index 3ed8b0dd7fbebaeae08d60bc02bea73f93ba46d9..c99b5edce387fa58ba689d059061436a2233c7ee 100644 --- a/module/VuFind/src/VuFind/Controller/HierarchyController.php +++ b/module/VuFind/src/VuFind/Controller/HierarchyController.php @@ -2,7 +2,7 @@ /** * Hierarchy Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -90,7 +90,7 @@ class HierarchyController extends AbstractBase $searchType = $this->params()->fromQuery('type', 'AllFields'); $results = $this->serviceLocator - ->get('VuFind\SearchResultsPluginManager')->get('Solr'); + ->get('VuFind\Search\Results\PluginManager')->get('Solr'); $results->getParams()->setBasicSearch($lookfor, $searchType); $results->getParams()->addFilter('hierarchy_top_id:' . $hierarchyID); $facets = $results->getFullFieldFacets(['id'], false, $limit + 1); @@ -120,7 +120,7 @@ class HierarchyController extends AbstractBase $this->disableSessionWrites(); // avoid session write timing bug // Retrieve the record from the index $id = $this->params()->fromQuery('id'); - $loader = $this->serviceLocator->get('VuFind\RecordLoader'); + $loader = $this->serviceLocator->get('VuFind\Record\Loader'); try { if ($recordDriver = $loader->load($id)) { $results = $recordDriver->getHierarchyDriver()->render( @@ -153,7 +153,7 @@ class HierarchyController extends AbstractBase $this->disableSessionWrites(); // avoid session write timing bug // Retrieve the record from the index $id = $this->params()->fromQuery('id'); - $loader = $this->serviceLocator->get('VuFind\RecordLoader'); + $loader = $this->serviceLocator->get('VuFind\Record\Loader'); try { if ($recordDriver = $loader->load($id)) { $results = $recordDriver->getHierarchyDriver() @@ -183,7 +183,7 @@ class HierarchyController extends AbstractBase public function getrecordAction() { $id = $this->params()->fromQuery('id'); - $loader = $this->serviceLocator->get('VuFind\RecordLoader'); + $loader = $this->serviceLocator->get('VuFind\Record\Loader'); try { $record = $loader->load($id); $result = $this->getViewRenderer()->record($record) diff --git a/module/VuFind/src/VuFind/Controller/HoldsTrait.php b/module/VuFind/src/VuFind/Controller/HoldsTrait.php index 0028faea2a0b4e5748de306af433c5bfdf50c69d..e1081430708b15c899510f195233c72dd1b5ed24 100644 --- a/module/VuFind/src/VuFind/Controller/HoldsTrait.php +++ b/module/VuFind/src/VuFind/Controller/HoldsTrait.php @@ -2,7 +2,7 @@ /** * Holds trait (for subclasses of AbstractRecord) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -165,7 +165,7 @@ trait HoldsTrait $defaultRequired = $this->holds()->getDefaultRequiredDate( $checkHolds, $catalog, $patron, $gatheredDetails ); - $defaultRequired = $this->serviceLocator->get('VuFind\DateConverter') + $defaultRequired = $this->serviceLocator->get('VuFind\Date\Converter') ->convertToDisplayDate("U", $defaultRequired); try { $defaultPickup @@ -192,8 +192,7 @@ trait HoldsTrait 'requestGroups' => $requestGroups, 'defaultRequestGroup' => $defaultRequestGroup, 'requestGroupNeeded' => $requestGroupNeeded, - 'helpText' => isset($checkHolds['helpText']) - ? $checkHolds['helpText'] : null + 'helpText' => $checkHolds['helpText'] ?? null ] ); $view->setTemplate('record/hold'); diff --git a/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php b/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php index 24d3ba082caa3ac405ecea8d21a2e9ebf90cd4b1..b06d691ea1de1bf9e2f68cf52a7ebe8a75f401d9 100644 --- a/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php +++ b/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php @@ -2,7 +2,7 @@ /** * ILL trait (for subclasses of AbstractRecord) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -92,7 +92,7 @@ trait ILLRequestsTrait ? explode(":", $checkRequests['extraFields']) : []; // Process form submissions if necessary: - if (!is_null($this->params()->fromPost('placeILLRequest'))) { + if (null !== $this->params()->fromPost('placeILLRequest')) { // If we made it this far, we're ready to place the hold; // if successful, we will redirect and can stop here. @@ -132,7 +132,7 @@ trait ILLRequestsTrait // Find and format the default required date: $defaultRequired = $this->ILLRequests() ->getDefaultRequiredDate($checkRequests); - $defaultRequired = $this->serviceLocator->get('VuFind\DateConverter') + $defaultRequired = $this->serviceLocator->get('VuFind\Date\Converter') ->convertToDisplayDate("U", $defaultRequired); // Get pickup libraries @@ -153,8 +153,7 @@ trait ILLRequestsTrait 'homeLibrary' => $this->getUser()->home_library, 'extraFields' => $extraFields, 'defaultRequiredDate' => $defaultRequired, - 'helpText' => isset($checkRequests['helpText']) - ? $checkRequests['helpText'] : null + 'helpText' => $checkRequests['helpText'] ?? null ] ); $view->setTemplate('record/illrequest'); diff --git a/module/VuFind/src/VuFind/Controller/IndexController.php b/module/VuFind/src/VuFind/Controller/IndexController.php index 3abd92f9ee6c9e972605ed803bae24c1333c07d9..2b5dfaa0fff102273e2d8eae884eee6f18b79780 100644 --- a/module/VuFind/src/VuFind/Controller/IndexController.php +++ b/module/VuFind/src/VuFind/Controller/IndexController.php @@ -2,7 +2,7 @@ /** * Default Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; -use Zend\Config\Config; + use VuFind\Auth\Manager as AuthManager; +use Zend\Config\Config; /** * Redirects the user to the appropriate default VuFind action. @@ -74,12 +75,20 @@ class IndexController extends \Zend\Mvc\Controller\AbstractActionController */ public function homeAction() { - $loggedInModule = isset($this->config->Site->defaultLoggedInModule) - ? $this->config->Site->defaultLoggedInModule : 'MyResearch'; - $loggedOutModule = isset($this->config->Site->defaultModule) - ? $this->config->Site->defaultModule : 'Search'; - $module = $this->authManager->isLoggedIn() - ? $loggedInModule : $loggedOutModule; - return $this->forward()->dispatch($module, ['action' => 'Home']); + // Load different configurations depending on whether we're logged in or not: + if ($this->authManager->isLoggedIn()) { + $controller = isset($this->config->Site->defaultLoggedInModule) + ? $this->config->Site->defaultLoggedInModule : 'MyResearch'; + $actionConfig = 'defaultLoggedInAction'; + } else { + $controller = isset($this->config->Site->defaultModule) + ? $this->config->Site->defaultModule : 'Search'; + $actionConfig = 'defaultAction'; + } + $action = isset($this->config->Site->$actionConfig) + ? $this->config->Site->$actionConfig : 'Home'; + + // Forward to the appropriate controller and action: + return $this->forward()->dispatch($controller, compact('action')); } } diff --git a/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php b/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..40d94a426b43c3144422f4c7c2e7504c94983178 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Index controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Index controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class IndexControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $authManager = $container->get('VuFind\Auth\Manager'); + return new $requestedName($config, $authManager); + } +} diff --git a/module/VuFind/src/VuFind/Controller/InstallController.php b/module/VuFind/src/VuFind/Controller/InstallController.php index 41da1a674e0192de95106a46abae3a636f246513..4ca799742f22a479c38b572d512da5ae62dac14a 100644 --- a/module/VuFind/src/VuFind/Controller/InstallController.php +++ b/module/VuFind/src/VuFind/Controller/InstallController.php @@ -2,7 +2,7 @@ /** * Install Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,11 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; -use VuFind\Config\Locator as ConfigLocator, - VuFind\Config\Writer as ConfigWriter, - Zend\Mvc\MvcEvent, - Zend\Crypt\Password\Bcrypt; + +use VuFind\Config\Locator as ConfigLocator; +use VuFind\Config\Writer as ConfigWriter; +use Zend\Crypt\Password\Bcrypt; +use Zend\Mvc\MvcEvent; /** * Class controls VuFind auto-configuration. @@ -165,7 +166,7 @@ class InstallController extends AbstractBase */ protected function checkCache() { - $cache = $this->serviceLocator->get('VuFind\CacheManager'); + $cache = $this->serviceLocator->get('VuFind\Cache\Manager'); return [ 'title' => 'Cache', 'status' => !$cache->hasDirectoryCreationError(), @@ -180,7 +181,7 @@ class InstallController extends AbstractBase */ public function fixcacheAction() { - $cache = $this->serviceLocator->get('VuFind\CacheManager'); + $cache = $this->serviceLocator->get('VuFind\Cache\Manager'); $view = $this->createViewModel(); $view->cacheDir = $cache->getCacheDir(); if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { @@ -224,8 +225,8 @@ class InstallController extends AbstractBase return false; } - // We need at least PHP v5.3.3: - return PHP_VERSION_ID >= 50303; + // We need at least PHP v7.0.8: + return PHP_VERSION_ID >= 70008; } /** @@ -258,7 +259,7 @@ class InstallController extends AbstractBase // Is our version new enough? if (!$this->phpVersionIsNewEnough()) { - $msg = "VuFind requires PHP version 5.3.3 or newer; you are running " + $msg = "VuFind requires PHP version 7.0.8 or newer; you are running " . phpversion() . ". Please upgrade."; $this->flashMessenger()->addMessage($msg, 'error'); $problems++; @@ -334,16 +335,16 @@ class InstallController extends AbstractBase if (!preg_match('/^\w*$/', $view->dbname)) { $this->flashMessenger() ->addMessage('Database name must be alphanumeric.', 'error'); - } else if (!preg_match('/^\w*$/', $view->dbuser)) { + } elseif (!preg_match('/^\w*$/', $view->dbuser)) { $this->flashMessenger() ->addMessage('Database user must be alphanumeric.', 'error'); - } else if ($skip || $this->formWasSubmitted('submit')) { + } elseif ($skip || $this->formWasSubmitted('submit')) { $newpass = $this->params()->fromPost('dbpass'); $newpassConf = $this->params()->fromPost('dbpassconfirm'); if ((empty($newpass) || empty($newpassConf))) { $this->flashMessenger() ->addMessage('Password fields must not be blank.', 'error'); - } else if ($newpass != $newpassConf) { + } elseif ($newpass != $newpassConf) { $this->flashMessenger() ->addMessage('Password fields must match.', 'error'); } else { @@ -354,7 +355,7 @@ class InstallController extends AbstractBase try { $dbName = ($view->driver == 'pgsql') ? 'template1' : $view->driver; - $db = $this->serviceLocator->get('VuFind\DbAdapterFactory') + $db = $this->serviceLocator->get('VuFind\Db\AdapterFactory') ->getAdapterFromConnectionString("{$connection}/{$dbName}"); } catch (\Exception $e) { $this->flashMessenger() @@ -391,7 +392,7 @@ class InstallController extends AbstractBase $db->query($query, $db::QUERY_MODE_EXECUTE); } $dbFactory = $this->serviceLocator - ->get('VuFind\DbAdapterFactory'); + ->get('VuFind\Db\AdapterFactory'); $db = $dbFactory->getAdapterFromConnectionString( $connection . '/' . $view->dbname ); @@ -555,10 +556,13 @@ class InstallController extends AbstractBase $drivers = []; $blacklist = [ 'Sample.php', 'Demo.php', 'DriverInterface.php', 'AbstractBase.php', - 'PluginManager.php', 'PluginFactory.php' + 'PluginManager.php', ]; while ($line = readdir($dir)) { - if (stristr($line, '.php') && !in_array($line, $blacklist)) { + if (stristr($line, '.php') && !in_array($line, $blacklist) + && substr($line, -11) !== 'Factory.php' + && substr($line, -9) !== 'Trait.php' + ) { $drivers[] = str_replace('.php', '', $line); } } @@ -582,7 +586,7 @@ class InstallController extends AbstractBase protected function testSearchService() { // Try to retrieve an arbitrary ID -- this will fail if Solr is down: - $searchService = $this->serviceLocator->get('VuFind\Search'); + $searchService = $this->serviceLocator->get('VuFindSearch\Service'); $searchService->retrieve('Solr', '1'); } @@ -797,7 +801,7 @@ class InstallController extends AbstractBase { // Try to retrieve an SSL URL; if we're misconfigured, it will fail. try { - $this->serviceLocator->get('VuFind\Http') + $this->serviceLocator->get('VuFindHttp\HttpService') ->get('https://google.com'); $status = true; } catch (\VuFindHttp\Exception\RuntimeException $e) { diff --git a/module/VuFind/src/VuFind/Controller/LibGuidesController.php b/module/VuFind/src/VuFind/Controller/LibGuidesController.php index b73f97a2cf3e8dc17e6e22c94a530aaa1fa8d07b..a31fa3c6bf383f268449aba4e4d38f6d1f0e4ba6 100644 --- a/module/VuFind/src/VuFind/Controller/LibGuidesController.php +++ b/module/VuFind/src/VuFind/Controller/LibGuidesController.php @@ -2,7 +2,7 @@ /** * LibGuides Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -50,16 +51,6 @@ class LibGuidesController extends AbstractSearch parent::__construct($sm); } - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - return $this->createViewModel(); - } - /** * Is the result scroller active? * @@ -67,8 +58,9 @@ class LibGuidesController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('LibGuides'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('LibGuides'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Controller/LibraryCardsController.php b/module/VuFind/src/VuFind/Controller/LibraryCardsController.php index 0d447863b5161ccea066378dc361e0327c96696a..f700a488a7be8182bd7692b72a0b277ddcc252dd 100644 --- a/module/VuFind/src/VuFind/Controller/LibraryCardsController.php +++ b/module/VuFind/src/VuFind/Controller/LibraryCardsController.php @@ -2,7 +2,7 @@ /** * LibraryCards Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015. @@ -192,6 +192,16 @@ class LibraryCardsController extends AbstractBase $cardID = $this->params()->fromQuery('cardID'); $user->activateLibraryCard($cardID); + // Connect to the ILS and check that the credentials are correct: + $catalog = $this->getILS(); + $patron = $catalog->patronLogin( + $user->cat_username, $user->getCatPassword() + ); + if (!$patron) { + $this->flashMessenger() + ->addMessage('authentication_error_invalid', 'error'); + } + $this->setFollowupUrlToReferer(); if ($url = $this->getFollowupUrl()) { $this->clearFollowupUrl(); @@ -239,7 +249,7 @@ class LibraryCardsController extends AbstractBase $user->saveLibraryCard( $id == 'NEW' ? null : $id, $cardName, $username, $password ); - } catch(\VuFind\Exception\LibraryCard $e) { + } catch (\VuFind\Exception\LibraryCard $e) { $this->flashMessenger()->addMessage($e->getMessage(), 'error'); return false; } diff --git a/module/VuFind/src/VuFind/Controller/MissingrecordController.php b/module/VuFind/src/VuFind/Controller/MissingrecordController.php index 9998aab0dac920b6d62d3f81cd4e58469e5094d8..bc53a67da825e843fddcfca5ecfe5f92245326bc 100644 --- a/module/VuFind/src/VuFind/Controller/MissingrecordController.php +++ b/module/VuFind/src/VuFind/Controller/MissingrecordController.php @@ -2,7 +2,7 @@ /** * Missing Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Controller/MyResearchController.php b/module/VuFind/src/VuFind/Controller/MyResearchController.php index ad6ff2e4018d63404214aead32df5d09394b553c..f0f331671899ff0245125c22c79612c4fd1eb834 100644 --- a/module/VuFind/src/VuFind/Controller/MyResearchController.php +++ b/module/VuFind/src/VuFind/Controller/MyResearchController.php @@ -2,7 +2,7 @@ /** * MyResearch Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,14 +27,14 @@ */ namespace VuFind\Controller; -use VuFind\Exception\Auth as AuthException, - VuFind\Exception\Forbidden as ForbiddenException, - VuFind\Exception\ILS as ILSException, - VuFind\Exception\Mail as MailException, - VuFind\Exception\ListPermission as ListPermissionException, - VuFind\Exception\RecordMissing as RecordMissingException, - VuFind\Search\RecommendListener, Zend\Stdlib\Parameters, - Zend\View\Model\ViewModel; +use VuFind\Exception\Auth as AuthException; +use VuFind\Exception\Forbidden as ForbiddenException; +use VuFind\Exception\ILS as ILSException; +use VuFind\Exception\ListPermission as ListPermissionException; +use VuFind\Exception\Mail as MailException; +use VuFind\Search\RecommendListener; +use Zend\Stdlib\Parameters; +use Zend\View\Model\ViewModel; /** * Controller for the user account area. @@ -47,6 +47,30 @@ use VuFind\Exception\Auth as AuthException, */ class MyResearchController extends AbstractBase { + /** + * Are we currently in a lightbox context? + * + * @return bool + */ + protected function inLightbox() + { + return $this->getRequest()->getQuery('layout', 'no') === 'lightbox' + || 'layout/lightbox' == $this->layout()->getTemplate(); + } + + /** + * Construct an HTTP 205 (refresh) response. Useful for reporting success + * in the lightbox without actually rendering content. + * + * @return \Zend\Http\Response + */ + protected function getRefreshResponse() + { + $response = $this->getResponse(); + $response->setStatusCode(205); + return $response; + } + /** * Process an authentication error. * @@ -128,6 +152,14 @@ class MyResearchController extends AbstractBase try { if (!$this->getAuthManager()->isLoggedIn()) { $this->getAuthManager()->login($this->getRequest()); + // Return early to avoid unnecessary processing if we are being + // called from login lightbox and don't have a followup action. + if ($this->params()->fromPost('processLogin') + && $this->inLightbox() + && empty($this->getFollowupUrl()) + ) { + return $this->getRefreshResponse(); + } } } catch (AuthException $e) { $this->processAuthenticationException($e); @@ -136,7 +168,10 @@ class MyResearchController extends AbstractBase // Not logged in? Force user to log in: if (!$this->getAuthManager()->isLoggedIn()) { - $this->setFollowupUrlToReferer(); + // Allow bypassing of post-login redirect + if ($this->params()->fromQuery('redirect', true)) { + $this->setFollowupUrlToReferer(); + } return $this->forwardTo('MyResearch', 'Login'); } // Logged in? Forward user to followup action @@ -255,15 +290,9 @@ class MyResearchController extends AbstractBase { // Don't log in if already logged in! if ($this->getAuthManager()->isLoggedIn()) { - // inLightbox (only instance) - if ($this->getRequest()->getQuery('layout', 'no') === 'lightbox' - || 'layout/lightbox' == $this->layout()->getTemplate() - ) { - $response = $this->getResponse(); - $response->setStatusCode(205); - return $response; - } - return $this->redirect()->toRoute('home'); + return $this->inLightbox() // different behavior for lightbox context + ? $this->getRefreshResponse() + : $this->redirect()->toRoute('home'); } $this->clearFollowupUrl(); $this->setFollowupUrlToReferer(); @@ -281,7 +310,7 @@ class MyResearchController extends AbstractBase public function logoutAction() { $config = $this->getConfig(); - if (isset($config->Site->logOutRoute)) { + if (!empty($config->Site->logOutRoute)) { $logoutTarget = $this->getServerUrl($config->Site->logOutRoute); } else { $logoutTarget = $this->getRequest()->getServer()->get('HTTP_REFERER'); @@ -326,7 +355,7 @@ class MyResearchController extends AbstractBase protected function setSavedFlagSecurely($searchId, $saved, $userId) { $searchTable = $this->getTable('Search'); - $sessId = $this->serviceLocator->get('VuFind\SessionManager')->getId(); + $sessId = $this->serviceLocator->get('Zend\Session\SessionManager')->getId(); $row = $searchTable->getOwnedRowById($searchId, $sessId, $userId); if (empty($row)) { throw new ForbiddenException('Access denied.'); @@ -344,7 +373,7 @@ class MyResearchController extends AbstractBase public function savesearchAction() { // Fail if saved searches are disabled. - $check = $this->serviceLocator->get('VuFind\AccountCapabilities'); + $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); if ($check->getSavedSearchSetting() === 'disabled') { throw new ForbiddenException('Saved searches disabled.'); } @@ -358,7 +387,7 @@ class MyResearchController extends AbstractBase if (($id = $this->params()->fromQuery('save', false)) !== false) { $this->setSavedFlagSecurely($id, true, $user->id); $this->flashMessenger()->addMessage('search_save_success', 'success'); - } else if (($id = $this->params()->fromQuery('delete', false)) !== false) { + } elseif (($id = $this->params()->fromQuery('delete', false)) !== false) { $this->setSavedFlagSecurely($id, false, $user->id); $this->flashMessenger()->addMessage('search_unsave_success', 'success'); } else { @@ -383,40 +412,45 @@ class MyResearchController extends AbstractBase */ public function profileAction() { - // Stop now if the user does not have valid catalog credentials available: - if (!is_array($patron = $this->catalogLogin())) { - return $patron; - } - - // User must be logged in at this point, so we can assume this is non-false: - $user = $this->getUser(); - - // Process home library parameter (if present): - $homeLibrary = $this->params()->fromPost('home_library', false); - if (!empty($homeLibrary)) { - $user->changeHomeLibrary($homeLibrary); - $this->getAuthManager()->updateSession($user); - $this->flashMessenger()->addMessage('profile_update', 'success'); + if (!($user = $this->getUser())) { + return $this->forceLogin(); } // Begin building view object: - $view = $this->createViewModel(); + $view = $this->createViewModel(['user' => $user]); + + $patron = $this->catalogLogin(); + if (is_array($patron)) { + // Process home library parameter (if present): + $homeLibrary = $this->params()->fromPost('home_library', false); + if (!empty($homeLibrary)) { + $user->changeHomeLibrary($homeLibrary); + $this->getAuthManager()->updateSession($user); + $this->flashMessenger()->addMessage('profile_update', 'success'); + } - // Obtain user information from ILS: - $catalog = $this->getILS(); - $this->addAccountBlocksToFlashMessenger($catalog, $patron); - $profile = $catalog->getMyProfile($patron); - $profile['home_library'] = $user->home_library; - $view->profile = $profile; - try { - $view->pickup = $catalog->getPickUpLocations($patron); - $view->defaultPickupLocation - = $catalog->getDefaultPickUpLocation($patron); - } catch (\Exception $e) { - // Do nothing; if we're unable to load information about pickup - // locations, they are not supported and we should ignore them. + // Obtain user information from ILS: + $catalog = $this->getILS(); + $this->addAccountBlocksToFlashMessenger($catalog, $patron); + $profile = $catalog->getMyProfile($patron); + $profile['home_library'] = $user->home_library; + $view->profile = $profile; + try { + $view->pickup = $catalog->getPickUpLocations($patron); + $view->defaultPickupLocation + = $catalog->getDefaultPickUpLocation($patron); + } catch (\Exception $e) { + // Do nothing; if we're unable to load information about pickup + // locations, they are not supported and we should ignore them. + } + } else { + $view->patronLoginView = $patron; } + $config = $this->getConfig(); + $view->accountDeletion + = !empty($config->Authentication->account_deletion); + return $view; } @@ -496,7 +530,7 @@ class MyResearchController extends AbstractBase : $this->url()->fromRoute('userList', ['id' => $listID]); // Fail if we have nothing to delete: - $ids = is_null($this->params()->fromPost('selectAll')) + $ids = null === $this->params()->fromPost('selectAll') ? $this->params()->fromPost('ids') : $this->params()->fromPost('idsAll'); if (!is_array($ids) || empty($ids)) { @@ -581,10 +615,11 @@ class MyResearchController extends AbstractBase */ protected function processEditSubmit($user, $driver, $listID) { - $lists = $this->params()->fromPost('lists'); + $lists = $this->params()->fromPost('lists', []); $tagParser = $this->serviceLocator->get('VuFind\Tags'); $favorites = $this->serviceLocator ->get('VuFind\Favorites\FavoritesService'); + $didSomething = false; foreach ($lists as $list) { $tags = $this->params()->fromPost('tags' . $list); $favorites->save( @@ -595,15 +630,19 @@ class MyResearchController extends AbstractBase ], $user, $driver ); + $didSomething = true; } // add to a new list? $addToList = $this->params()->fromPost('addToList'); if ($addToList > -1) { + $didSomething = true; $favorites->save(['list' => $addToList], $user, $driver); } - $this->flashMessenger()->addMessage('edit_list_success', 'success'); + if ($didSomething) { + $this->flashMessenger()->addMessage('edit_list_success', 'success'); + } - $newUrl = is_null($listID) + $newUrl = null === $listID ? $this->url()->fromRoute('myresearch-favorites') : $this->url()->fromRoute('userList', ['id' => $listID]); return $this->redirect()->toUrl($newUrl); @@ -739,7 +778,7 @@ class MyResearchController extends AbstractBase // If we got this far, we just need to display the favorites: try { - $runner = $this->serviceLocator->get('VuFind\SearchRunner'); + $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); // We want to merge together GET, POST and route parameters to // initialize our search object: @@ -749,7 +788,7 @@ class MyResearchController extends AbstractBase // Set up listener for recommendations: $rManager = $this->serviceLocator - ->get('VuFind\RecommendPluginManager'); + ->get('VuFind\Recommend\PluginManager'); $setupCallback = function ($runner, $params, $searchId) use ($rManager) { $listener = new RecommendListener($rManager, $searchId); $listener->setConfig( @@ -819,7 +858,7 @@ class MyResearchController extends AbstractBase return $this->redirect()->toRoute('userList', ['id' => $finalId]); } catch (\Exception $e) { - switch(get_class($e)) { + switch (get_class($e)) { case 'VuFind\Exception\ListPermission': case 'VuFind\Exception\MissingField': $this->flashMessenger()->addMessage($e->getMessage(), 'error'); @@ -902,7 +941,7 @@ class MyResearchController extends AbstractBase // Success Message $this->flashMessenger()->addMessage('fav_list_delete', 'success'); } catch (\Exception $e) { - switch(get_class($e)) { + switch (get_class($e)) { case 'VuFind\Exception\LoginRequired': case 'VuFind\Exception\ListPermission': $user = $this->getUser(); @@ -937,10 +976,9 @@ class MyResearchController extends AbstractBase */ protected function getDriverForILSRecord($current) { - $id = isset($current['id']) ? $current['id'] : null; - $source = isset($current['source']) - ? $current['source'] : DEFAULT_SEARCH_BACKEND; - $record = $this->serviceLocator->get('VuFind\RecordLoader') + $id = $current['id'] ?? ''; + $source = $current['source'] ?? DEFAULT_SEARCH_BACKEND; + $record = $this->serviceLocator->get('VuFind\Record\Loader') ->load($id, $source, true); $record->setExtraDetail('ils_details', $current); return $record; @@ -1200,10 +1238,138 @@ class MyResearchController extends AbstractBase } } + $displayItemBarcode + = !empty($config->Catalog->display_checked_out_item_barcode); + return $this->createViewModel( compact( 'transactions', 'renewForm', 'renewResult', 'paginator', - 'hiddenTransactions' + 'hiddenTransactions', 'displayItemBarcode' + ) + ); + } + + /** + * Send list of historic loans to view + * + * @return mixed + */ + public function historicloansAction() + { + // Stop now if the user does not have valid catalog credentials available: + if (!is_array($patron = $this->catalogLogin())) { + return $patron; + } + + // Connect to the ILS: + $catalog = $this->getILS(); + + // Check function config + $functionConfig = $catalog->checkFunction( + 'getMyTransactionHistory', $patron + ); + if (false === $functionConfig) { + $this->flashMessenger()->addErrorMessage('ils_action_unavailable'); + return $this->createViewModel(); + } + + // Get page and page size: + $page = (int)$this->params()->fromQuery('page', 1); + $config = $this->getConfig(); + $limit = isset($config->Catalog->historic_loan_page_size) + ? $config->Catalog->historic_loan_page_size : 50; + $ilsPaging = true; + if (isset($functionConfig['max_results'])) { + $limit = min([$functionConfig['max_results'], $limit]); + } elseif (isset($functionConfig['page_size'])) { + if (!in_array($limit, $functionConfig['page_size'])) { + $limit = $functionConfig['default_page_size'] + ?? $functionConfig['page_size'][0]; + } + } else { + $ilsPaging = false; + } + + // Get sort settings + $sort = false; + if (!empty($functionConfig['sort'])) { + $sort = $this->params()->fromQuery('sort'); + if (!isset($functionConfig['sort'][$sort])) { + if (isset($functionConfig['default_sort'])) { + $sort = $functionConfig['default_sort']; + } else { + reset($functionConfig['sort']); + $sort = key($functionConfig['sort']); + } + } + } + + // Configure call params + $params = [ + 'sort' => $sort + ]; + if ($ilsPaging) { + $params['page'] = $page; + $params['limit'] = $limit; + } + + // Get checked out item details: + $result = $catalog->getMyTransactionHistory($patron, $params); + + if (isset($result['success']) && !$result['success']) { + $this->flashMessenger()->addErrorMessage($result['status']); + return $this->createViewModel(); + } + + // Build paginator if needed: + if ($ilsPaging && $limit < $result['count']) { + $adapter = new \Zend\Paginator\Adapter\NullFill($result['count']); + $paginator = new \Zend\Paginator\Paginator($adapter); + $paginator->setItemCountPerPage($limit); + $paginator->setCurrentPageNumber($page); + $pageStart = $paginator->getAbsoluteItemNumber(1) - 1; + $pageEnd = $paginator->getAbsoluteItemNumber($limit) - 1; + } elseif ($limit > 0 && $limit < $result['count']) { + $adapter = new \Zend\Paginator\Adapter\ArrayAdapter( + $result['transactions'] + ); + $paginator = new \Zend\Paginator\Paginator($adapter); + $paginator->setItemCountPerPage($limit); + $paginator->setCurrentPageNumber($page); + $pageStart = $paginator->getAbsoluteItemNumber(1) - 1; + $pageEnd = $paginator->getAbsoluteItemNumber($limit) - 1; + } else { + $paginator = false; + $pageStart = 0; + $pageEnd = $result['count']; + } + + $transactions = $hiddenTransactions = []; + foreach ($result['transactions'] as $i => $current) { + // Build record driver (only for the current visible page): + if ($ilsPaging || ($i >= $pageStart && $i <= $pageEnd)) { + $transactions[] = $this->getDriverForILSRecord($current); + } else { + $hiddenTransactions[] = $current; + } + } + + // Handle view params for sorting + $sortList = []; + if (!empty($functionConfig['sort'])) { + foreach ($functionConfig['sort'] as $key => $value) { + $sortList[$key] = [ + 'desc' => $value, + 'url' => '?sort=' . urlencode($key), + 'selected' => $sort == $key + ]; + } + } + + return $this->createViewModel( + compact( + 'transactions', 'paginator', 'params', + 'hiddenTransactions', 'sortList', 'functionConfig' ) ); } @@ -1229,20 +1395,20 @@ class MyResearchController extends AbstractBase foreach ($result as $row) { // Attempt to look up and inject title: try { - if (!isset($row['id']) || empty($row['id'])) { - throw new \Exception(); - } - $source = isset($row['source']) - ? $row['source'] : DEFAULT_SEARCH_BACKEND; - $row['driver'] = $this->serviceLocator - ->get('VuFind\RecordLoader')->load($row['id'], $source); - if (empty($row['title'])) { - $row['title'] = $row['driver']->getShortTitle(); + if (strlen($row['id'] ?? '') > 0) { + $source = $row['source'] ?? DEFAULT_SEARCH_BACKEND; + $row['driver'] = $this->serviceLocator + ->get('VuFind\Record\Loader')->load($row['id'], $source); + if (empty($row['title'])) { + $row['title'] = $row['driver']->getShortTitle(); + } } } catch (\Exception $e) { - if (!isset($row['title'])) { - $row['title'] = null; - } + // Ignore record loading exceptions... + } + // In case we skipped or failed record loading, make sure title is set. + if (!isset($row['title'])) { + $row['title'] = null; } $fines[] = $row; } @@ -1341,7 +1507,7 @@ class MyResearchController extends AbstractBase . $user->verify_hash . '&auth_method=' . $method ] ); - $this->serviceLocator->get('VuFind\Mailer')->send( + $this->serviceLocator->get('VuFind\Mailer\Mailer')->send( $user->email, $config->Site->email, $this->translate('recovery_email_subject'), @@ -1550,4 +1716,45 @@ class MyResearchController extends AbstractBase $this->getAuthManager()->setAuthMethod($method); } } + + /** + * Account deletion + * + * @return mixed + */ + public function deleteAccountAction() + { + // Force login: + if (!($user = $this->getUser())) { + return $this->forceLogin(); + } + + $config = $this->getConfig(); + if (empty($config->Authentication->account_deletion)) { + throw new \VuFind\Exception\BadRequest(); + } + + $view = $this->createViewModel(['accountDeleted' => false]); + if ($this->formWasSubmitted('submit')) { + $csrf = $this->serviceLocator->get('VuFind\Validator\Csrf'); + if (!$csrf->isValid($this->getRequest()->getPost()->get('csrf'))) { + throw new \VuFind\Exception\BadRequest( + 'error_inconsistent_parameters' + ); + } else { + // After successful token verification, clear list to shrink session: + $csrf->trimTokenList(0); + } + $user->delete( + $config->Authentication->delete_comments_with_user ?? true + ); + $view->accountDeleted = true; + $view->redirectUrl = $this->getAuthManager()->logout( + $this->getServerUrl('home') + ); + } elseif ($this->formWasSubmitted('reset')) { + return $this->redirect()->toRoute('myresearch-profile'); + } + return $view; + } } diff --git a/module/VuFind/src/VuFind/Controller/OaiController.php b/module/VuFind/src/VuFind/Controller/OaiController.php index bc09f50ae7f1fa21bc78a58765cbf36ffed77d90..0024247a4874f9d9a602b98a90c4f0520d80b066 100644 --- a/module/VuFind/src/VuFind/Controller/OaiController.php +++ b/module/VuFind/src/VuFind/Controller/OaiController.php @@ -2,7 +2,7 @@ /** * OAI Module Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -100,13 +100,13 @@ class OaiController extends AbstractBase $this->getRequest()->getPost()->toArray() ); $server = new $serverClass( - $this->serviceLocator->get('VuFind\SearchResultsPluginManager'), - $this->serviceLocator->get('VuFind\RecordLoader'), - $this->serviceLocator->get('VuFind\DbTablePluginManager'), + $this->serviceLocator->get('VuFind\Search\Results\PluginManager'), + $this->serviceLocator->get('VuFind\Record\Loader'), + $this->serviceLocator->get('VuFind\Db\Table\PluginManager'), $config, $baseURL, $params ); $server->setRecordLinkHelper( - $this->getViewRenderer()->plugin('recordlink') + $this->getViewRenderer()->plugin('recordLink') ); $xml = $server->getResponse(); } catch (\Exception $e) { diff --git a/module/VuFind/src/VuFind/Controller/Pazpar2Controller.php b/module/VuFind/src/VuFind/Controller/Pazpar2Controller.php index a8d0e81fa5c0595625851a2c392077e7f86d0d27..d49a9c6af5c37319b40ce545fe138c0eaecc34e7 100644 --- a/module/VuFind/src/VuFind/Controller/Pazpar2Controller.php +++ b/module/VuFind/src/VuFind/Controller/Pazpar2Controller.php @@ -2,7 +2,7 @@ /** * Pazpar2 Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -50,17 +51,6 @@ class Pazpar2Controller extends AbstractSearch parent::__construct($sm); } - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - // Set up default parameters: - return $this->createViewModel(); - } - /** * Search action -- call standard results action * diff --git a/module/VuFind/src/VuFind/Controller/Pazpar2recordController.php b/module/VuFind/src/VuFind/Controller/Pazpar2recordController.php index 4ebefc7de515f5c4e9d2cd39fe105d248c2cd191..63805efdfd5a46f1aa55a04e8cb55f0512f41fbb 100644 --- a/module/VuFind/src/VuFind/Controller/Pazpar2recordController.php +++ b/module/VuFind/src/VuFind/Controller/Pazpar2recordController.php @@ -2,7 +2,7 @@ /** * Pazpar2 Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** diff --git a/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBase.php b/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBase.php index adf71e0df94f901c1c243fb8998e70521212071d..665f4d20c3df9eda106d3c8b88c500bb90575a0f 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBase.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBase.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Requests Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,12 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; -use VuFind\Crypt\HMAC, VuFind\ILS\Connection; -use Zend\Mvc\Controller\Plugin\AbstractPlugin, Zend\Session\Container, - Zend\Session\SessionManager; + +use VuFind\Crypt\HMAC; +use VuFind\ILS\Connection; +use Zend\Mvc\Controller\Plugin\AbstractPlugin; +use Zend\Session\Container; +use Zend\Session\SessionManager; /** * Zend action helper base class to perform request-related actions @@ -199,7 +202,7 @@ abstract class AbstractRequestBase extends AbstractPlugin } // If we got this far, something is wrong! - return false; + return false; } /** @@ -247,7 +250,7 @@ abstract class AbstractRequestBase extends AbstractPlugin } // If we got this far, something is wrong! - return false; + return false; } /** diff --git a/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php b/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php index 6e56bcc252d00b8d7f483c14a7bb3d9b76846697..1be1b4663c8bb57982ad48bddce2a6271316f748 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Database upgrade tools * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -22,12 +22,15 @@ * @category VuFind * @package Controller_Plugins * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; -use Zend\Db\Adapter\Adapter as DbAdapter, Zend\Db\Metadata\Metadata as DbMetadata, - Zend\Mvc\Controller\Plugin\AbstractPlugin; + +use Zend\Db\Adapter\Adapter as DbAdapter; +use Zend\Db\Metadata\Metadata as DbMetadata; +use Zend\Mvc\Controller\Plugin\AbstractPlugin; /** * Zend action helper to perform database upgrades @@ -35,6 +38,7 @@ use Zend\Db\Adapter\Adapter as DbAdapter, Zend\Db\Metadata\Metadata as DbMetadat * @category VuFind * @package Controller_Plugins * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ @@ -398,7 +402,11 @@ class DbUpgrade extends AbstractPlugin $constraints = isset($info[$table]) ? $info[$table]->getConstraints() : []; $retVal = []; foreach ($constraints as $current) { - $fields = ['fields' => $current->getColumns()]; + $fields = [ + 'fields' => $current->getColumns(), + 'deleteRule' => $current->getDeleteRule(), + 'updateRule' => $current->getUpdateRule() + ]; switch ($current->getType()) { case 'FOREIGN KEY': $retVal['foreign'][$current->getName()] = $fields; @@ -619,6 +627,131 @@ class DbUpgrade extends AbstractPlugin return $missing; } + /** + * Compare expected vs. actual constraint actions and return an array of SQL + * clauses required to create the modified constraints. + * + * @param array $expected Expected constraints (based on mysql.sql) + * @param array $actual Actual constraints (pulled from database metadata) + * + * @return array + */ + protected function compareConstraintActions($expected, $actual) + { + $modified = []; + foreach ($expected as $type => $constraints) { + foreach ($constraints as $name => $constraint) { + if (!isset($actual[$type][$name])) { + throw new \Exception( + "Could not find constraint '$name' in actual constraints" + ); + } + $actualConstr = $actual[$type][$name]; + if ($constraint['deleteRule'] !== $actualConstr['deleteRule'] + || $constraint['updateRule'] !== $actualConstr['updateRule'] + ) { + $modified[$name] = $constraint; + } + } + } + return $modified; + } + + /** + * Support method for getModifiedConstraints() -- check if the current constraint + * is in the missing constraint list so we can avoid modifying something that + * does not exist. + * + * @param string $constraint Column to check + * @param array $missing Missing constraint list for constraint's table. + * + * @return bool + */ + public function constraintIsMissing($constraint, $missing) + { + foreach ($missing as $current) { + preg_match('/^\s*CONSTRAINT\s*`([^`]*)`.*$/', $current, $matches); + if ($constraint == $matches[1]) { + return true; + } + } + return false; + } + + /** + * Get a list of modified constraints in the database tables (associative array, + * key = table name, value = array of modified constraint definitions). + * + * @param array $missingTables List of missing tables + * @param array $missingConstraints List of missing constraints + * + * @throws \Exception + * @return array + */ + public function getModifiedConstraints($missingTables = [], + $missingConstraints = [] + ) { + $modified = []; + foreach ($this->dbCommands as $table => $sql) { + // Skip missing tables if we're logging + if (in_array($table, $missingTables)) { + continue; + } + + $expectedConstraints = []; + + // Parse column names out of the CREATE TABLE SQL, which will always be + // the first entry in the array; we assume the standard mysqldump + // formatting is used here. + preg_match_all( + '/^\s*CONSTRAINT `([^`]+)` FOREIGN KEY \(`([^)]*)`\)(.*)$/m', + $sql[0], $foreignKeyMatches + ); + foreach ($foreignKeyMatches[0] as $i => $sql) { + $fkName = $foreignKeyMatches[1][$i]; + // Skip constraint if we're logging and it's missing + if (isset($missingConstraints[$table]) + && $this->constraintIsMissing( + $fkName, $missingConstraints[$table] + ) + ) { + continue; + } + + $deleteRule = 'RESTRICT'; + $updateRule = 'RESTRICT'; + $options = 'RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT'; + $actions = $foreignKeyMatches[3][$i] ?? ''; + if (preg_match("/ON DELETE ($options)/", $actions, $matches)) { + $deleteRule = $matches[1]; + } + if (preg_match("/ON UPDATE ($options)/", $actions, $matches)) { + $updateRule = $matches[1]; + } + + // Fix trailing whitespace/punctuation: + $sql = trim(trim($sql), ',;'); + + $expectedConstraints['foreign'][$fkName] = [ + 'sql' => $sql, + 'fields' => $this->explodeFields($foreignKeyMatches[2][$i]), + 'deleteRule' => $deleteRule, + 'updateRule' => $updateRule + ]; + } + + // Now check for missing columns and build our return array: + $actualConstraints = $this->getTableConstraints($table); + + $mismatches = $this + ->compareConstraintActions($expectedConstraints, $actualConstraints); + if (!empty($mismatches)) { + $modified[$table]['foreign'] = $mismatches; + } + } + return $modified; + } + /** * Given a current row default, return true if the current default matches the * one found in the SQL provided as the $sql parameter. Return false if there @@ -632,13 +765,13 @@ class DbUpgrade extends AbstractPlugin protected function defaultMatches($currentDefault, $sql) { preg_match("/.* DEFAULT (.*)$/", $sql, $matches); - $expectedDefault = isset($matches[1]) ? $matches[1] : null; + $expectedDefault = $matches[1] ?? null; if (null !== $expectedDefault) { $expectedDefault = trim(rtrim($expectedDefault, ','), "'"); $expectedDefault = (strtoupper($expectedDefault) == 'NULL') ? null : $expectedDefault; } - return ($expectedDefault === $currentDefault); + return $expectedDefault === $currentDefault; } /** @@ -684,7 +817,7 @@ class DbUpgrade extends AbstractPlugin * not exist. * * @param string $column Column to check - * @param string $missing Missing column list for column's table. + * @param array $missing Missing column list for column's table. * * @return bool */ @@ -834,4 +967,37 @@ class DbUpgrade extends AbstractPlugin } return $sqlcommands; } + + /** + * Modify constraints based on the output of getModifiedConstraints(). + * + * @param array $constraints Output of getModifiedConstraints() + * @param bool $logsql Should we return the SQL as a string rather than + * execute it? + * + * @throws \Exception + * @return string SQL if $logsql is true, empty string otherwise + */ + public function updateModifiedConstraints($constraints, $logsql = false) + { + $sqlcommands = ''; + foreach ($constraints as $table => $constraintTypeList) { + foreach ($constraintTypeList as $type => $constraintList) { + if ('foreign' !== $type) { + throw new \Exception( + "Unable to handle modification of constraint type '$type'" + ); + } + foreach ($constraintList as $name => $constraint) { + $sqlcommands .= $this->query( + "ALTER TABLE `{$table}` DROP FOREIGN KEY `{$name}`", $logsql + ); + $sqlcommands .= $this->query( + "ALTER TABLE $table ADD {$constraint['sql']}", $logsql + ); + } + } + } + return $sqlcommands; + } } diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Factory.php b/module/VuFind/src/VuFind/Controller/Plugin/Factory.php index ea857354b75394d150bdb2ebfed078242f7bc6ac..247a8b5217612b0278d09d1918237ede6630c700 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Factory.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Factory.php @@ -2,7 +2,7 @@ /** * Factory for controller plugins. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Controller\Plugin; + use Zend\ServiceManager\ServiceManager; /** @@ -51,9 +52,9 @@ class Factory public static function getFavorites(ServiceManager $sm) { return new Favorites( - $sm->getServiceLocator()->get('VuFind\RecordLoader'), - $sm->getServiceLocator()->get('VuFind\RecordCache'), - $sm->getServiceLocator()->get('VuFind\Tags') + $sm->get('VuFind\Record\Loader'), + $sm->get('VuFind\Record\Cache'), + $sm->get('VuFind\Tags') ); } @@ -62,12 +63,12 @@ class Factory * * @param ServiceManager $sm Service manager. * - * @return \Zend\Mvc\Controller\Plugin\FlashMessenger + * @return \Zend\Mvc\Plugin\FlashMessenger\FlashMessenger */ public static function getFlashMessenger(ServiceManager $sm) { - $plugin = new \Zend\Mvc\Controller\Plugin\FlashMessenger(); - $sessionManager = $sm->getServiceLocator()->get('VuFind\SessionManager'); + $plugin = new \Zend\Mvc\Plugin\FlashMessenger\FlashMessenger(); + $sessionManager = $sm->get('Zend\Session\SessionManager'); $plugin->setSessionManager($sessionManager); return $plugin; } @@ -83,7 +84,7 @@ class Factory { return new Followup( new \Zend\Session\Container( - 'Followup', $sm->getServiceLocator()->get('VuFind\SessionManager') + 'Followup', $sm->get('Zend\Session\SessionManager') ) ); } @@ -98,8 +99,8 @@ class Factory public static function getHolds(ServiceManager $sm) { return new Holds( - $sm->getServiceLocator()->get('VuFind\HMAC'), - $sm->getServiceLocator()->get('VuFind\SessionManager') + $sm->get('VuFind\Crypt\HMAC'), + $sm->get('Zend\Session\SessionManager') ); } @@ -113,8 +114,8 @@ class Factory public static function getILLRequests(ServiceManager $sm) { return new ILLRequests( - $sm->getServiceLocator()->get('VuFind\HMAC'), - $sm->getServiceLocator()->get('VuFind\SessionManager') + $sm->get('VuFind\Crypt\HMAC'), + $sm->get('Zend\Session\SessionManager') ); } @@ -127,7 +128,7 @@ class Factory */ public static function getNewItems(ServiceManager $sm) { - $search = $sm->getServiceLocator()->get('VuFind\Config')->get('searches'); + $search = $sm->get('VuFind\Config\PluginManager')->get('searches'); $config = isset($search->NewItem) ? $search->NewItem : new \Zend\Config\Config([]); return new NewItems($config); @@ -142,9 +143,9 @@ class Factory */ public static function getPermission(ServiceManager $sm) { - $pdm = $sm->getServiceLocator()->get('VuFind\Role\PermissionDeniedManager'); - $pm = $sm->getServiceLocator()->get('VuFind\Role\PermissionManager'); - $auth = $sm->getServiceLocator()->get('VuFind\AuthManager'); + $pdm = $sm->get('VuFind\Role\PermissionDeniedManager'); + $pm = $sm->get('VuFind\Role\PermissionManager'); + $auth = $sm->get('VuFind\Auth\Manager'); return new Permission($pm, $pdm, $auth); } @@ -157,11 +158,8 @@ class Factory */ public static function getRecaptcha(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Recaptcha( - $sm->getServiceLocator()->get('VuFind\Recaptcha'), - $config - ); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); + return new Recaptcha($sm->get('VuFind\Service\ReCaptcha'), $config); } /** @@ -173,10 +171,10 @@ class Factory */ public static function getReserves(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $useIndex = isset($config->Reserves->search_enabled) && $config->Reserves->search_enabled; - $ss = $useIndex ? $sm->getServiceLocator()->get('VuFind\Search') : null; + $ss = $useIndex ? $sm->get('VuFindSearch\Service') : null; return new Reserves($useIndex, $ss); } @@ -192,9 +190,9 @@ class Factory return new ResultScroller( new \Zend\Session\Container( 'ResultScroller', - $sm->getServiceLocator()->get('VuFind\SessionManager') + $sm->get('Zend\Session\SessionManager') ), - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Search\Results\PluginManager') ); } @@ -208,8 +206,8 @@ class Factory public static function getStorageRetrievalRequests(ServiceManager $sm) { return new StorageRetrievalRequests( - $sm->getServiceLocator()->get('VuFind\HMAC'), - $sm->getServiceLocator()->get('VuFind\SessionManager') + $sm->get('VuFind\Crypt\HMAC'), + $sm->get('Zend\Session\SessionManager') ); } } diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Favorites.php b/module/VuFind/src/VuFind/Controller/Plugin/Favorites.php index 8682f28f4f6ca3f60578d3d20d43a7619b5dd2f2..49a9373e113bae150414e52942db8d9f2a9a009a 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Favorites.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Favorites.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Favorites Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; -use VuFind\Exception\LoginRequired as LoginRequiredException; + use VuFind\Db\Row\User; +use VuFind\Exception\LoginRequired as LoginRequiredException; use VuFind\Record\Cache; use VuFind\Record\Loader; use VuFind\Tags; @@ -146,7 +147,7 @@ class Favorites extends \Zend\Mvc\Controller\Plugin\AbstractPlugin } // Load helper objects needed for the saving process: - $list = $this->getList(isset($params['list']) ? $params['list'] : '', $user); + $list = $this->getList($params['list'] ?? '', $user); $this->cache->setContext(Cache::CONTEXT_FAVORITE); $cacheRecordIds = []; // list of record IDs to save to cache diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Followup.php b/module/VuFind/src/VuFind/Controller/Plugin/Followup.php index c47d40e6ce44498b15725e1556f5663252e3f340..ce7bd23b8075fd3daa0a5d1d66cf42d536d419af 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Followup.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Followup.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Followup * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; -use Zend\Mvc\Controller\Plugin\AbstractPlugin, Zend\Session\Container; + +use Zend\Mvc\Controller\Plugin\AbstractPlugin; +use Zend\Session\Container; /** * Zend action helper to deal with login followup; responsible for remembering URLs @@ -90,7 +92,6 @@ class Followup extends AbstractPlugin } return isset($this->session->$key) ? $this->session->$key : $default; - } /** diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Holds.php b/module/VuFind/src/VuFind/Controller/Plugin/Holds.php index e39e55017a83ebc832c5621385579d864f1ffbb6..68bcbae7e450116dfc971c23bea3fa236b6cb6f3 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Holds.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Holds.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Holds Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -58,7 +58,7 @@ class Holds extends AbstractRequestBase // Build OPAC URL $ilsDetails['cancel_link'] = $catalog->getCancelHoldLink($ilsDetails); - } else if (isset($ilsDetails['cancel_details'])) { + } elseif (isset($ilsDetails['cancel_details'])) { // The ILS driver provided cancel details up front. If the // details are an empty string (flagging lack of support), we // should unset it to prevent confusion; otherwise, we'll leave it @@ -106,7 +106,7 @@ class Holds extends AbstractRequestBase $selected = $params->fromPost('cancelSelected'); if (!empty($all)) { $details = $params->fromPost('cancelAllIDS'); - } else if (!empty($selected)) { + } elseif (!empty($selected)) { $details = $params->fromPost('cancelSelectedIDS'); } else { // No button pushed -- no action needed @@ -169,7 +169,7 @@ class Holds extends AbstractRequestBase return $cancelResults; } } else { - $flashMsg->addMessage('hold_empty_selection', 'error'); + $flashMsg->addMessage('hold_empty_selection', 'error'); } return []; } diff --git a/module/VuFind/src/VuFind/Controller/Plugin/ILLRequests.php b/module/VuFind/src/VuFind/Controller/Plugin/ILLRequests.php index dbbcea26ddfc4b5d8c0735e628c8cb975ab43c55..6c5537d7b5a1d9e12a2f7af46eae68164007ea66 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/ILLRequests.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/ILLRequests.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - ILL Requests Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2014. @@ -101,7 +101,7 @@ class ILLRequests extends AbstractRequestBase $selected = $params->fromPost('cancelSelected'); if (!empty($all)) { $details = $params->fromPost('cancelAllIDS'); - } else if (!empty($selected)) { + } elseif (!empty($selected)) { $details = $params->fromPost('cancelSelectedIDS'); } else { // No button pushed -- no action needed diff --git a/module/VuFind/src/VuFind/Controller/Plugin/NewItems.php b/module/VuFind/src/VuFind/Controller/Plugin/NewItems.php index ab4f9be985656498049001f1a4b00966c08164b4..c18a4195be689581287531fbbe1d32ab7a1ef716 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/NewItems.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/NewItems.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - New Items Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; -use Zend\Mvc\Controller\Plugin\AbstractPlugin, Zend\Config\Config; + +use Zend\Config\Config; +use Zend\Mvc\Controller\Plugin\AbstractPlugin; +use Zend\Mvc\Plugin\FlashMessenger\FlashMessenger; /** * Zend action helper to perform new items-related actions @@ -59,11 +62,11 @@ class NewItems extends AbstractPlugin /** * Figure out which bib IDs to load from the ILS. * - * @param \VuFind\ILS\Connection $catalog ILS connection - * @param \VuFind\Search\Solr\Params $params Solr parameters - * @param string $range Range setting - * @param string $dept Department setting - * @param \Zend\Mvc\Controller\Plugin\FlashMessenger $flash Flash messenger + * @param \VuFind\ILS\Connection $catalog ILS connection + * @param \VuFind\Search\Solr\Params $params Solr parameters + * @param string $range Range setting + * @param string $dept Department setting + * @param FlashMessenger $flash Flash messenger * * @return array */ diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Permission.php b/module/VuFind/src/VuFind/Controller/Plugin/Permission.php index d4d143857581ef94290145b81782d20fda31c875..26acd4afad54a8d7f26643ef984545044934f494 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Permission.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Permission.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Permission Checker * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; + use VuFind\Exception\Forbidden as ForbiddenException; use VuFind\I18n\Translator\TranslatorAwareInterface; use VuFind\Role\PermissionDeniedManager; @@ -115,8 +116,7 @@ class Permission extends AbstractPlugin implements LoggerAwareInterface, if ($dl === false) { return null; } - $exceptionDescription = isset($dl['exceptionMessage']) - ? $dl['exceptionMessage'] : 'Access denied.'; + $exceptionDescription = $dl['exceptionMessage'] ?? 'Access denied.'; switch (strtolower($dl['action'])) { case 'promptlogin': // If the user is already logged in, but we're getting a "prompt diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php b/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php index b5c15efc686cda999b797b41efb20a8f8a77ec37..2142f86351095ab374586eec4ad4e63d16a948a5 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Recaptcha handler * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; + use Zend\Mvc\Controller\Plugin\AbstractPlugin; /** diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Renewals.php b/module/VuFind/src/VuFind/Controller/Plugin/Renewals.php index 3ec476ce84a0a6b8decc3040a4943801dd351d3f..9767d9ed20b1db5bcd9509d1e00cd6d015dc712e 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Renewals.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Renewals.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Renewals Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; + use Zend\Mvc\Controller\Plugin\AbstractPlugin; /** @@ -85,7 +86,7 @@ class Renewals extends AbstractPlugin $selected = $request->get('renewSelected'); if (!empty($all)) { $ids = $request->get('renewAllIDS'); - } else if (!empty($selected)) { + } elseif (!empty($selected)) { $ids = $request->get('selectAll') ? $request->get('selectAllIDS') : $request->get('renewSelectedIDS'); @@ -117,7 +118,7 @@ class Renewals extends AbstractPlugin // System failure: $flashMsg->addMessage('renew_error', 'error'); } - } else if (!empty($all) || !empty($selected)) { + } elseif (!empty($all) || !empty($selected)) { // Button was clicked but no items were selected: $flashMsg->addMessage('renew_empty_selection', 'error'); } diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Reserves.php b/module/VuFind/src/VuFind/Controller/Plugin/Reserves.php index 51f8e25c4c1ab434443334785be5ed49eacb8f36..801cc28de342acb80188fbd27ca82a852478af56 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Reserves.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Reserves.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Reserves Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller\Plugin; + use VuFindSearch\Service; use Zend\Mvc\Controller\Plugin\AbstractPlugin; diff --git a/module/VuFind/src/VuFind/Controller/Plugin/ResultScroller.php b/module/VuFind/src/VuFind/Controller/Plugin/ResultScroller.php index 6f5fed38eb6b230bb061ebfb3d3838a0e1d5a0af..02390cde766ccce96cb436387906beb65da2469f 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/ResultScroller.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/ResultScroller.php @@ -2,7 +2,7 @@ /** * Class for managing "next" and "previous" navigation within result sets. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010 * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Controller\Plugin; + use VuFind\Search\Results\PluginManager as ResultsManager; use Zend\Mvc\Controller\Plugin\AbstractPlugin; use Zend\Session\Container as SessionContainer; @@ -371,7 +372,7 @@ class ResultScroller extends AbstractPlugin if (count($this->data->currIds) > 1) { $pos = count($this->data->currIds) - 2; $retVal['previousRecord'] = $this->data->currIds[$pos]; - } else if (count($this->data->prevIds) > 0) { + } elseif (count($this->data->prevIds) > 0) { $prevPos = count($this->data->prevIds) - 1; $retVal['previousRecord'] = $this->data->prevIds[$prevPos]; } @@ -484,11 +485,11 @@ class ResultScroller extends AbstractPlugin // the current record is somewhere in the middle of the current // page, ie: not first or last return $this->scrollOnCurrentPage($retVal, $pos); - } else if ($pos == 0) { + } elseif ($pos == 0) { // this record is first record on the current page return $this ->fetchPreviousPage($retVal, $lastSearch, $pos, $count); - } else if ($pos == $count - 1) { + } elseif ($pos == $count - 1) { // this record is last record on the current page return $this->fetchNextPage($retVal, $lastSearch, $pos); } diff --git a/module/VuFind/src/VuFind/Controller/Plugin/StorageRetrievalRequests.php b/module/VuFind/src/VuFind/Controller/Plugin/StorageRetrievalRequests.php index e524f79107bc7f149585a81f80f6a03b112ab350..11bc1817730aa93d8d01c6bb0bad3a765b71d704 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/StorageRetrievalRequests.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/StorageRetrievalRequests.php @@ -2,7 +2,7 @@ /** * VuFind Action Helper - Storage Retrieval Requests Support Methods * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2014. @@ -101,7 +101,7 @@ class StorageRetrievalRequests extends AbstractRequestBase $selected = $params->fromPost('cancelSelected'); if (!empty($all)) { $details = $params->fromPost('cancelAllIDS'); - } else if (!empty($selected)) { + } elseif (!empty($selected)) { $details = $params->fromPost('cancelSelectedIDS'); } else { // No button pushed -- no action needed diff --git a/module/VuFind/src/VuFind/Controller/PrimoController.php b/module/VuFind/src/VuFind/Controller/PrimoController.php index 0edcf335cff6847577ccbca2e6e003c07bfdffe9..f511f36cdf6e58d47758002951769d58245650eb 100644 --- a/module/VuFind/src/VuFind/Controller/PrimoController.php +++ b/module/VuFind/src/VuFind/Controller/PrimoController.php @@ -2,7 +2,7 @@ /** * Primo Central Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -51,16 +52,6 @@ class PrimoController extends AbstractSearch parent::__construct($sm); } - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - return $this->createViewModel(); - } - /** * Is the result scroller active? * @@ -68,9 +59,10 @@ class PrimoController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('Primo'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('Primo'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } /** diff --git a/module/VuFind/src/VuFind/Controller/PrimorecordController.php b/module/VuFind/src/VuFind/Controller/PrimorecordController.php index f07a7ce072146b1db5bbdb9a2e480c9d20553ade..c34d535f9aef5564836f6bd5263863efe003019f 100644 --- a/module/VuFind/src/VuFind/Controller/PrimorecordController.php +++ b/module/VuFind/src/VuFind/Controller/PrimorecordController.php @@ -2,7 +2,7 @@ /** * Primo Central Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -62,8 +63,9 @@ class PrimorecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('Primo'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('Primo'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Controller/QRCodeController.php b/module/VuFind/src/VuFind/Controller/QRCodeController.php index 0a7aabbc93aa34275fef4d0a312401b748a797b2..81766dab4e9cf45a9502be0fbe8768c156c9a4bb 100644 --- a/module/VuFind/src/VuFind/Controller/QRCodeController.php +++ b/module/VuFind/src/VuFind/Controller/QRCodeController.php @@ -2,7 +2,7 @@ /** * QRCode Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,7 +27,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Controller; + use VuFind\QRCode\Loader; +use VuFind\Session\Settings as SessionSettings; /** * Generates qrcodes @@ -39,7 +41,7 @@ use VuFind\QRCode\Loader; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ -class QRCodeController extends AbstractBase +class QRCodeController extends \Zend\Mvc\Controller\AbstractActionController { /** * QR Code loader @@ -49,23 +51,22 @@ class QRCodeController extends AbstractBase protected $loader = false; /** - * Get the cover loader object + * Session settings + * + * @var SessionSettings + */ + protected $sessionSettings = null; + + /** + * Constructor * - * @return Loader + * @param Loader $loader QR Code Loader + * @param SessionSettings $ss Session settings */ - protected function getLoader() + public function __construct(Loader $loader, SessionSettings $ss) { - // Construct object for QRCodes if it does not already exist: - if (!$this->loader) { - $this->loader = new Loader( - $this->getConfig(), - $this->serviceLocator->get('VuFindTheme\ThemeInfo') - ); - \VuFind\ServiceManager\Initializer::initInstance( - $this->loader, $this->serviceLocator - ); - } - return $this->loader; + $this->loader = $loader; + $this->sessionSettings = $ss; } /** @@ -75,16 +76,13 @@ class QRCodeController extends AbstractBase */ public function showAction() { - $this->disableSessionWrites(); // avoid session write timing bug + $this->sessionSettings->disableWrite(); // avoid session write timing bug - $this->getLoader()->loadQRCode( - $this->params()->fromQuery('text'), - [ - 'level' => $this->params()->fromQuery('level', "L"), - 'size' => $this->params()->fromQuery('size', "3"), - 'margin' => $this->params()->fromQuery('margin', "4"), - ] - ); + $params = []; + foreach ($this->loader->getDefaults() as $param => $default) { + $params[$param] = $this->params()->fromQuery($param, $default); + } + $this->loader->loadQRCode($this->params()->fromQuery('text'), $params); return $this->displayQRCode(); } @@ -95,8 +93,8 @@ class QRCodeController extends AbstractBase */ public function unavailableAction() { - $this->disableSessionWrites(); // avoid session write timing bug - $this->getLoader()->loadUnavailable(); + $this->sessionSettings->disableWrite(); // avoid session write timing bug + $this->loader->loadUnavailable(); return $this->displayQRCode(); } @@ -109,11 +107,10 @@ class QRCodeController extends AbstractBase protected function displayQRCode() { $response = $this->getResponse(); - $headers = $response->getHeaders(); - $headers->addHeaderLine( - 'Content-type', $this->getLoader()->getContentType() + $response->getHeaders()->addHeaderLine( + 'Content-type', $this->loader->getContentType() ); - $response->setContent($this->getLoader()->getImage()); + $response->setContent($this->loader->getImage()); return $response; } } diff --git a/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php b/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dc3051698f5fd6defc126c1a7e0cd4ff759b98e2 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * QRCode controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * QRCode controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class QRCodeControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\QRCode\Loader'), + $container->get('VuFind\Session\Settings') + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/RecordController.php b/module/VuFind/src/VuFind/Controller/RecordController.php index af8aa12c9388bc351a7303a0c44304b1c25ccec2..a44027b1dd25418d60ba121bddf08ba49670c98e 100644 --- a/module/VuFind/src/VuFind/Controller/RecordController.php +++ b/module/VuFind/src/VuFind/Controller/RecordController.php @@ -2,7 +2,7 @@ /** * Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\Config\Config; use Zend\ServiceManager\ServiceLocatorInterface; @@ -67,8 +68,9 @@ class RecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Controller/RecordsController.php b/module/VuFind/src/VuFind/Controller/RecordsController.php index 5d28ea2eeceffd7715604e8b34b68a24d52280fc..7f6b09114cf27bab3ee6ccbe16d34bee046ccffd 100644 --- a/module/VuFind/src/VuFind/Controller/RecordsController.php +++ b/module/VuFind/src/VuFind/Controller/RecordsController.php @@ -2,7 +2,7 @@ /** * Records Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** diff --git a/module/VuFind/src/VuFind/Controller/RelaisController.php b/module/VuFind/src/VuFind/Controller/RelaisController.php new file mode 100644 index 0000000000000000000000000000000000000000..7975838a6bcff1caddd8ae3fd283964fea5ee067 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/RelaisController.php @@ -0,0 +1,90 @@ +<?php +/** + * Relais Controller + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Controller; + +/** + * Relais Controller + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class RelaisController extends AbstractBase +{ + /** + * Relais login action + * + * @return mixed + */ + public function loginAction() + { + // Fatal error if not configured correctly: + $config = $this->getConfig(); + $baseUrl = $config->Relais->loginUrl ?? null; + if (empty($baseUrl)) { + throw new \Exception('Relais login URL not set.'); + } + + // Stop now if the user does not have valid catalog credentials available: + if (!is_array($patron = $this->catalogLogin())) { + return $patron; + } + + // Send user credentials through to Relais: + $symbol = $config->Relais->symbol ?? ''; + $q = $this->params()->fromQuery('query'); + $url = $baseUrl . '?LS=' . urlencode($symbol) + . '&dest=discovery&group=patron&PI=' + . urlencode($patron['cat_username']); + if (!empty($q)) { + $url .= '&query=' . urlencode($q); + } + return $this->redirect()->toUrl($url); + } + + /** + * Relais request action. + * + * @return mixed + */ + public function requestAction() + { + // Stop now if the user does not have valid catalog credentials available: + if (!is_array($patron = $this->catalogLogin())) { + return $patron; + } + return $this->createViewModel( + [ + 'oclc' => $this->params()->fromQuery('oclc'), + 'failLink' => $this->params()->fromQuery('failLink'), + ] + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Search2Controller.php b/module/VuFind/src/VuFind/Controller/Search2Controller.php new file mode 100644 index 0000000000000000000000000000000000000000..c7b3907ac76dbd7785069eb6cacb98b2d969e05f --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Search2Controller.php @@ -0,0 +1,67 @@ +<?php + +/** + * Search2 Controller + * + * PHP version 7 + * + * Copyright (C) Staats- und Universitätsbibliothek Hamburg 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller; + +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Search2 Controller + * + * @category VuFind + * @package Controller + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Search2Controller extends AbstractSolrSearch +{ + /** + * Constructor + * + * @param ServiceLocatorInterface $sm Service locator + */ + public function __construct(ServiceLocatorInterface $sm) + { + $this->searchClassId = 'Search2'; + parent::__construct($sm); + } + + /** + * Is the result scroller active? + * + * @return bool + */ + protected function resultScrollerActive() + { + $config = $this->serviceLocator->get('VuFind\Config') + ->get('Search2'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; + } +} diff --git a/module/VuFind/src/VuFind/Controller/Search2recordController.php b/module/VuFind/src/VuFind/Controller/Search2recordController.php new file mode 100644 index 0000000000000000000000000000000000000000..479ead7f1edd855059e74b7b9ec93e1b2c840e0b --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Search2recordController.php @@ -0,0 +1,68 @@ +<?php + +/** + * Search2 Record Controller + * + * PHP version 7 + * + * Copyright (C) Staats- und Universitätsbibliothek Hamburg 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller; + +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Search2 Record Controller + * + * @category VuFind + * @package Controller + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Search2recordController extends AbstractRecord +{ + /** + * Constructor + * + * @param ServiceLocatorInterface $sm Service locator + */ + public function __construct(ServiceLocatorInterface $sm) + { + $this->searchClassId = 'Search2'; + $this->fallbackDefaultTab = 'Description'; + parent::__construct($sm); + } + + /** + * Is the result scroller active? + * + * @return bool + */ + protected function resultScrollerActive() + { + $config = $this->serviceLocator->get('VuFind\Config') + ->get('Search2'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; + } +} diff --git a/module/VuFind/src/VuFind/Controller/SearchController.php b/module/VuFind/src/VuFind/Controller/SearchController.php index c52008cc5ecd2758fff11bb5c47746a2480ecace..1e81635d7a89eb57cb03e44b0ae8a27d4bb3e75c 100644 --- a/module/VuFind/src/VuFind/Controller/SearchController.php +++ b/module/VuFind/src/VuFind/Controller/SearchController.php @@ -2,7 +2,7 @@ /** * Default Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,38 +38,17 @@ use VuFind\Exception\Mail as MailException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site */ -class SearchController extends AbstractSearch +class SearchController extends AbstractSolrSearch { /** - * Handle an advanced search + * Show facet list for Solr-driven collections. * * @return mixed */ - public function advancedAction() + public function collectionfacetlistAction() { - // Standard setup from base class: - $view = parent::advancedAction(); - - // Set up facet information: - $view->facetList = $this->processAdvancedFacets( - $this->getAdvancedFacets(), $view->saved - ); - $specialFacets = $this->parseSpecialFacetsSetting( - $view->options->getSpecialAdvancedFacets() - ); - if (isset($specialFacets['illustrated'])) { - $view->illustratedLimit - = $this->getIllustrationSettings($view->saved); - } - if (isset($specialFacets['checkboxes'])) { - $view->checkboxFacets = $this->processAdvancedCheckboxes( - $specialFacets['checkboxes'], $view->saved - ); - } - $view->ranges = $this->getAllRangeSettings($specialFacets, $view->saved); - $view->hierarchicalFacets = $this->getHierarchicalFacets(); - - return $view; + $this->searchClassId = 'SolrCollection'; + return $this->facetListAction(); } /** @@ -81,7 +60,7 @@ class SearchController extends AbstractSearch { // If a URL was explicitly passed in, use that; otherwise, try to // find the HTTP referrer. - $mailer = $this->serviceLocator->get('VuFind\Mailer'); + $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); $view = $this->createEmailViewModel(null, $mailer->getDefaultLinkSubject()); $mailer->setMaxRecipients($view->maxRecipients); // Set up reCaptcha @@ -133,97 +112,6 @@ class SearchController extends AbstractSearch return $view; } - /** - * Get the possible legal values for the illustration limit radio buttons. - * - * @param object $savedSearch Saved search object (false if none) - * - * @return array Legal options, with selected value flagged. - */ - protected function getIllustrationSettings($savedSearch = false) - { - $illYes = [ - 'text' => 'Has Illustrations', 'value' => 1, 'selected' => false - ]; - $illNo = [ - 'text' => 'Not Illustrated', 'value' => 0, 'selected' => false - ]; - $illAny = [ - 'text' => 'No Preference', 'value' => -1, 'selected' => false - ]; - - // Find the selected value by analyzing facets -- if we find match, remove - // the offending facet to avoid inappropriate items appearing in the - // "applied filters" sidebar! - if ($savedSearch - && $savedSearch->getParams()->hasFilter('illustrated:Illustrated') - ) { - $illYes['selected'] = true; - $savedSearch->getParams()->removeFilter('illustrated:Illustrated'); - } else if ($savedSearch - && $savedSearch->getParams()->hasFilter('illustrated:"Not Illustrated"') - ) { - $illNo['selected'] = true; - $savedSearch->getParams()->removeFilter('illustrated:"Not Illustrated"'); - } else { - $illAny['selected'] = true; - } - return [$illYes, $illNo, $illAny]; - } - - /** - * Process the facets to be used as limits on the Advanced Search screen. - * - * @param array $facetList The advanced facet values - * @param object $searchObject Saved search object (false if none) - * - * @return array Sorted facets, with selected values flagged. - */ - protected function processAdvancedFacets($facetList, $searchObject = false) - { - // Process the facets - $hierarchicalFacets = $this->getHierarchicalFacets(); - $facetHelper = null; - if (!empty($hierarchicalFacets)) { - $facetHelper = $this->serviceLocator - ->get('VuFind\HierarchicalFacetHelper'); - } - foreach ($facetList as $facet => &$list) { - // Hierarchical facets: format display texts and sort facets - // to a flat array according to the hierarchy - if (in_array($facet, $hierarchicalFacets)) { - $tmpList = $list['list']; - $facetHelper->sortFacetList($tmpList, true); - $tmpList = $facetHelper->buildFacetArray( - $facet, - $tmpList - ); - $list['list'] = $facetHelper->flattenFacetHierarchy($tmpList); - } - - foreach ($list['list'] as $key => $value) { - // Build the filter string for the URL: - $fullFilter = ($value['operator'] == 'OR' ? '~' : '') - . $facet . ':"' . $value['value'] . '"'; - - // If we haven't already found a selected facet and the current - // facet has been applied to the search, we should store it as - // the selected facet for the current control. - if ($searchObject - && $searchObject->getParams()->hasFilter($fullFilter) - ) { - $list['list'][$key]['selected'] = true; - // Remove the filter from the search object -- we don't want - // it to show up in the "applied filters" sidebar since it - // will already be accounted for by being selected in the - // filter select list! - $searchObject->getParams()->removeFilter($fullFilter); - } - } - } - return $facetList; - } - /** * Handle search history display && purge * @@ -236,61 +124,18 @@ class SearchController extends AbstractSearch if ($this->params()->fromQuery('require_login', 'no') !== 'no' && !$user) { return $this->forceLogin(); } + $userId = is_object($user) ? $user->id : null; - // Retrieve search history - $search = $this->getTable('Search'); - $searchHistory = $search->getSearches( - $this->serviceLocator->get('VuFind\SessionManager')->getId(), - is_object($user) ? $user->id : null - ); - - // Build arrays of history entries - $saved = $unsaved = []; - - // Loop through the history - foreach ($searchHistory as $current) { - $minSO = $current->getSearchObject(); - - // Saved searches - if ($current->saved == 1) { - $saved[] = $minSO->deminify($this->getResultsManager()); - } else { - // All the others... - - // If this was a purge request we don't need this - if ($this->params()->fromQuery('purge') == 'true') { - $current->delete(); - - // We don't want to remember the last search after a purge: - $this->getSearchMemory()->forgetSearch(); - } else { - // Otherwise add to the list - $unsaved[] = $minSO->deminify($this->getResultsManager()); - } - } - } + $searchHistoryHelper = $this->serviceLocator->get('VuFind\Search\History'); - return $this->createViewModel( - ['saved' => $saved, 'unsaved' => $unsaved] - ); - } + if ($this->params()->fromQuery('purge')) { + $searchHistoryHelper->purgeSearchHistory($userId); - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - return $this->createViewModel( - [ - 'results' => $this->getResultsObjectWithHiddenFilters('Solr'), - 'facetList' => $this->getHomePageFacets(), - 'hierarchicalFacets' => $this->getHierarchicalFacets(), - 'hierarchicalFacetSortOptions' - => $this->getHierarchicalFacetSortSettings() - ] - ); + // We don't want to remember the last search after a purge: + $this->getSearchMemory()->forgetSearch(); + } + $lastSearches = $searchHistoryHelper->getSearchHistory($userId); + return $this->createViewModel($lastSearches); } /** @@ -435,7 +280,7 @@ class SearchController extends AbstractSearch + $this->getRequest()->getPost()->toArray() ); $view = $this->createViewModel(); - $runner = $this->serviceLocator->get('VuFind\SearchRunner'); + $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); $view->results = $runner->run( $request, 'SolrReserves', $this->getSearchSetupCallback() ); @@ -530,110 +375,6 @@ class SearchController extends AbstractSearch return parent::resultsAction(); } - /** - * Get active hidden filter settings. - * - * @return array - */ - protected function getActiveHiddenFilters() - { - return $this->serviceLocator->get('VuFind\SearchTabsHelper') - ->getHiddenFilters($this->searchClassId); - } - - /** - * Create a results object with hidden filters pre-populated. - * - * @param string $backend ID of results object to create - * @param array $filters Hidden filter settings (null for defaults) - * - * @return \VuFind\Search\Base\Results - */ - protected function getResultsObjectWithHiddenFilters($backend, $filters = null) - { - if (null === $filters) { - $filters = $this->getActiveHiddenFilters(); - } - $results = $this->getResultsManager()->get($backend); - $params = $results->getParams(); - foreach ($filters as $key => $subFilters) { - foreach ($subFilters as $filter) { - $params->addHiddenFilter("$key:$filter"); - } - } - return $results; - } - - /** - * Return a Search Results object containing requested facet information. This - * data may come from the cache. - * - * @param string $initMethod Name of params method to use to request facets - * @param string $cacheName Cache key for facet data - * - * @return array - */ - protected function getFacetResults($initMethod, $cacheName) - { - // Check if we have facet results cached, and build them if we don't. - $cache = $this->serviceLocator->get('VuFind\CacheManager') - ->getCache('object'); - $language = $this->serviceLocator->get('VuFind\Translator')->getLocale(); - $hiddenFilters = $this->getActiveHiddenFilters(); - $hiddenFiltersHash = md5(json_encode($hiddenFilters)); - $cacheName .= "List-$hiddenFiltersHash-$language"; - if (!($list = $cache->getItem($cacheName))) { - // Use advanced facet settings to get summary facets on the front page; - // we may want to make this more flexible later. Also keep in mind that - // the template is currently looking for certain hard-coded fields; this - // should also be made smarter. - $results = $this->getResultsObjectWithHiddenFilters( - 'Solr', $hiddenFilters - ); - $params = $results->getParams(); - $params->$initMethod(); - - // Avoid a backend request if there are no facets configured by the given - // init method. - if (!empty($params->getFacetConfig())) { - // We only care about facet lists, so don't get any results (this - // helps prevent problems with serialized File_MARC objects in the - // cache): - $params->setLimit(0); - $list = $results->getFacetList(); - } else { - $list = []; - } - $cache->setItem($cacheName, $list); - } - - return $list; - } - - /** - * Return a Search Results object containing advanced facet information. This - * data may come from the cache. - * - * @return array - */ - protected function getAdvancedFacets() - { - return $this->getFacetResults( - 'initAdvancedFacets', 'solrSearchAdvancedFacets' - ); - } - - /** - * Return a Search Results object containing homepage facet information. This - * data may come from the cache. - * - * @return array - */ - protected function getHomePageFacets() - { - return $this->getFacetResults('initHomePageFacets', 'solrSearchHomeFacets'); - } - /** * Handle OpenSearch. * @@ -674,11 +415,8 @@ class SearchController extends AbstractSearch // Get suggestions and make sure they are an array (we don't want to JSON // encode them into an object): - $autocompleteManager = $this->serviceLocator - ->get('VuFind\AutocompletePluginManager'); - $suggestions = $autocompleteManager->getSuggestions( - $query, 'type', 'lookfor' - ); + $suggester = $this->serviceLocator->get('VuFind\Autocomplete\Suggester'); + $suggestions = $suggester->getSuggestions($query, 'type', 'lookfor'); // Send the JSON response: $response = $this->getResponse(); @@ -697,35 +435,9 @@ class SearchController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('config'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } - - /** - * Get an array of hierarchical facets - * - * @return array Facets - */ - protected function getHierarchicalFacets() - { - $facetConfig = $this->getConfig('facets'); - return isset($facetConfig->SpecialFacets->hierarchical) - ? $facetConfig->SpecialFacets->hierarchical->toArray() - : []; - } - - /** - * Get hierarchical facet sort settings - * - * @return array Array of sort settings keyed by facet - */ - protected function getHierarchicalFacetSortSettings() - { - $facetConfig = $this->getConfig('facets'); - return isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions) - ? $facetConfig->SpecialFacets->hierarchicalFacetSortOptions->toArray() - : []; - } - } diff --git a/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php b/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php index 1cb7b77f7df3b62b8c4a04584fea091d97055edd..56c836a91683d210dceb4db0ab60fc188e9eb75a 100644 --- a/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php +++ b/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php @@ -2,7 +2,7 @@ /** * Shibboleth Logout Notification API Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2016. * @@ -106,10 +106,9 @@ class ShibbolethLogoutNotificationController extends AbstractBase if (empty($row)) { return; } - $sessionManager = $this->serviceLocator->get('VuFind\SessionManager'); + $sessionManager = $this->serviceLocator->get('Zend\Session\SessionManager'); $handler = $sessionManager->getSaveHandler(); $handler->destroy($row['session_id']); - return; } /** diff --git a/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php b/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php index 272ff2af2ee92f2c959af2a33349a9df2de72ac4..221c6087d53eb50a978ed211464fd0534254f6b0 100644 --- a/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php +++ b/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php @@ -2,7 +2,7 @@ /** * Storage retrieval requests trait (for subclasses of AbstractRecord) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -93,7 +93,7 @@ trait StorageRetrievalRequestsTrait ? explode(":", $checkRequests['extraFields']) : []; // Process form submissions if necessary: - if (!is_null($this->params()->fromPost('placeStorageRetrievalRequest'))) { + if (null !== $this->params()->fromPost('placeStorageRetrievalRequest')) { // If we made it this far, we're ready to place the hold; // if successful, we will redirect and can stop here. @@ -132,7 +132,7 @@ trait StorageRetrievalRequestsTrait // Find and format the default required date: $defaultRequired = $this->storageRetrievalRequests() ->getDefaultRequiredDate($checkRequests); - $defaultRequired = $this->serviceLocator->get('VuFind\DateConverter') + $defaultRequired = $this->serviceLocator->get('VuFind\Date\Converter') ->convertToDisplayDate("U", $defaultRequired); try { $defaultPickup @@ -149,8 +149,7 @@ trait StorageRetrievalRequestsTrait 'homeLibrary' => $this->getUser()->home_library, 'extraFields' => $extraFields, 'defaultRequiredDate' => $defaultRequired, - 'helpText' => isset($checkRequests['helpText']) - ? $checkRequests['helpText'] : null + 'helpText' => $checkRequests['helpText'] ?? null ] ); $view->setTemplate('record/storageretrievalrequest'); diff --git a/module/VuFind/src/VuFind/Controller/SummonController.php b/module/VuFind/src/VuFind/Controller/SummonController.php index 175eec6b6b3f05342f2f81db83b0798f225f51bc..fe12aa78700b54602ae11ece777c9ba10aca38bf 100644 --- a/module/VuFind/src/VuFind/Controller/SummonController.php +++ b/module/VuFind/src/VuFind/Controller/SummonController.php @@ -2,7 +2,7 @@ /** * Summon Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\Mvc\MvcEvent; use Zend\ServiceManager\ServiceLocatorInterface; @@ -58,9 +59,10 @@ class SummonController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('Summon'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('Summon'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } /** @@ -103,9 +105,10 @@ class SummonController extends AbstractSearch $view = parent::advancedAction(); // Set up facet information: - $view->facetList = $this->processAdvancedFacets( - $this->getAdvancedFacets(), $view->saved - ); + $facets = $this->serviceLocator + ->get('VuFind\Search\FacetCache\PluginManager')->get('Summon') + ->getList('Advanced'); + $view->facetList = $this->processAdvancedFacets($facets, $view->saved); $specialFacets = $this->parseSpecialFacetsSetting( $view->options->getSpecialAdvancedFacets() ); @@ -120,21 +123,6 @@ class SummonController extends AbstractSearch return $view; } - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - return $this->createViewModel( - [ - 'results' => $this->getResultsManager()->get('Summon'), - 'facetList' => $this->getHomePageFacets(), - ] - ); - } - /** * Search action -- call standard results action * @@ -145,68 +133,6 @@ class SummonController extends AbstractSearch return $this->resultsAction(); } - /** - * Return a Search Results object containing advanced facet information. This - * data may come from the cache. - * - * @return array - */ - protected function getAdvancedFacets() - { - // Check if we have facet results cached, and build them if we don't. - $cache = $this->serviceLocator->get('VuFind\CacheManager') - ->getCache('object'); - $language = $this->serviceLocator->get('VuFind\Translator')->getLocale(); - $cacheKey = 'summonSearchAdvancedFacetsList-' . $language; - if (!($list = $cache->getItem($cacheKey))) { - $config = $this->serviceLocator->get('VuFind\Config') - ->get('Summon'); - $limit = isset($config->Advanced_Facet_Settings->facet_limit) - ? $config->Advanced_Facet_Settings->facet_limit : 100; - $results = $this->getResultsManager()->get('Summon'); - $params = $results->getParams(); - $facetsToShow = isset($config->Advanced_Facets) - ? $config->Advanced_Facets - : ['Language' => 'Language', 'ContentType' => 'Format']; - if (isset($config->Advanced_Facet_Settings->orFacets)) { - $orFields = array_map( - 'trim', explode(',', $config->Advanced_Facet_Settings->orFacets) - ); - } else { - $orFields = []; - } - foreach ($facetsToShow as $facet => $label) { - $useOr = (isset($orFields[0]) && $orFields[0] == '*') - || in_array($facet, $orFields); - $params->addFacet( - $facet . ',or,1,' . $limit, $label, $useOr - ); - } - - // We only care about facet lists, so don't get any results: - $params->setLimit(0); - - // force processing for cache - $list = $results->getFacetList(); - - $cache->setItem('summonSearchAdvancedFacetsList', $list); - } - - return $list; - } - - /** - * Return a Search Results object containing homepage facet information. This - * data may come from the cache. - * - * @return array - */ - protected function getHomePageFacets() - { - // For now, we'll use the same fields as the advanced search screen. - return $this->getAdvancedFacets(); - } - /** * Process the facets to be used as limits on the Advanced Search screen. * diff --git a/module/VuFind/src/VuFind/Controller/SummonrecordController.php b/module/VuFind/src/VuFind/Controller/SummonrecordController.php index f5cc870535cf726d55de76ce325887e80a5a8585..07fb137d09b15e0b857ec75438fd320306bc292d 100644 --- a/module/VuFind/src/VuFind/Controller/SummonrecordController.php +++ b/module/VuFind/src/VuFind/Controller/SummonrecordController.php @@ -2,7 +2,7 @@ /** * Summon Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\Mvc\MvcEvent; use Zend\ServiceManager\ServiceLocatorInterface; @@ -62,9 +63,10 @@ class SummonrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('Summon'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('Summon'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } /** diff --git a/module/VuFind/src/VuFind/Controller/TagController.php b/module/VuFind/src/VuFind/Controller/TagController.php index e7f66af4bca40cf351a15a4e79d5254276c8d20c..e23e0b9390b2274261e10c0843e2a175e0a97d3e 100644 --- a/module/VuFind/src/VuFind/Controller/TagController.php +++ b/module/VuFind/src/VuFind/Controller/TagController.php @@ -2,7 +2,7 @@ /** * Tag Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use VuFind\Exception\Forbidden as ForbiddenException; use Zend\ServiceManager\ServiceLocatorInterface; @@ -61,6 +62,6 @@ class TagController extends AbstractSearch if (!$this->tagsEnabled()) { throw new ForbiddenException('Tags disabled'); } - return $this->resultsAction(); + return parent::resultsAction(); } } diff --git a/module/VuFind/src/VuFind/Controller/UpgradeController.php b/module/VuFind/src/VuFind/Controller/UpgradeController.php index fed899adb550914e72abba2c57506041451c6d09..13c0b96da11ff3546497b6aca2b05bd346b7f2f8 100644 --- a/module/VuFind/src/VuFind/Controller/UpgradeController.php +++ b/module/VuFind/src/VuFind/Controller/UpgradeController.php @@ -2,7 +2,7 @@ /** * Upgrade Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. @@ -28,13 +28,15 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; -use ArrayObject, VuFind\Config\Locator as ConfigLocator, - VuFind\Cookie\Container as CookieContainer, - VuFind\Cookie\CookieManager, - VuFind\Exception\RecordMissing as RecordMissingException, - Zend\Mvc\MvcEvent, - Zend\ServiceManager\ServiceLocatorInterface, - Zend\Session\Container; + +use ArrayObject; +use VuFind\Config\Locator as ConfigLocator; +use VuFind\Cookie\Container as CookieContainer; +use VuFind\Cookie\CookieManager; +use VuFind\Exception\RecordMissing as RecordMissingException; +use Zend\Mvc\MvcEvent; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Session\Container; /** * Class controls VuFind upgrading. @@ -235,7 +237,7 @@ class UpgradeController extends AbstractBase // subsequent calls. static $adapter = false; if (!$adapter) { - $factory = $this->serviceLocator->get('VuFind\DbAdapterFactory'); + $factory = $this->serviceLocator->get('VuFind\Db\AdapterFactory'); $adapter = $factory->getAdapter( $this->session->dbRootUser, $this->session->dbRootPass ); @@ -300,7 +302,7 @@ class UpgradeController extends AbstractBase protected function fixSearchChecksumsInDatabase() { $manager = $this->serviceLocator - ->get('VuFind\SearchResultsPluginManager'); + ->get('VuFind\Search\Results\PluginManager'); $search = $this->getTable('search'); $searchWhere = ['checksum' => null, 'saved' => 1]; $searchRows = $search->select($searchWhere); @@ -412,6 +414,25 @@ class UpgradeController extends AbstractBase ->createMissingConstraints($missingConstraints, $this->logsql); } + // Check for modified constraints. + $mC = $this->logsql ? $missingConstraints : []; + $modifiedConstraints = $this->dbUpgrade()->getModifiedConstraints($mT, $mC); + if (!empty($modifiedConstraints)) { + // Only manipulate DB if we're not in logging mode: + if (!$this->logsql) { + if (!$this->hasDatabaseRootCredentials()) { + return $this->forwardTo('Upgrade', 'GetDbCredentials'); + } + $this->dbUpgrade()->setAdapter($this->getRootDbAdapter()); + $this->session->warnings->append( + "Modified constraint(s) in table(s): " + . implode(', ', array_keys($modifiedConstraints)) + ); + } + $sql .= $this->dbUpgrade() + ->updateModifiedConstraints($modifiedConstraints, $this->logsql); + } + // Check for encoding problems. $encProblems = $this->dbUpgrade()->getEncodingProblems(); if (!empty($encProblems)) { @@ -477,7 +498,7 @@ class UpgradeController extends AbstractBase // If this is a MySQL connection, we can do an automatic upgrade; // if VuFind is using a different database, we have to prompt the // user to check the migrations directory and upgrade manually. - $adapter = $this->serviceLocator->get('VuFind\DbAdapter'); + $adapter = $this->serviceLocator->get('Zend\Db\Adapter\Adapter'); $platform = $adapter->getDriver()->getDatabasePlatformName(); if (strtolower($platform) == 'mysql') { $upgradeResult = $this->upgradeMySQL($adapter); @@ -576,7 +597,7 @@ class UpgradeController extends AbstractBase try { // Query a table known to exist $factory = $this->serviceLocator - ->get('VuFind\DbAdapterFactory'); + ->get('VuFind\Db\AdapterFactory'); $db = $factory->getAdapter($dbrootuser, $pass); $db->query("SELECT * FROM user;"); $this->session->dbRootUser = $dbrootuser; @@ -604,7 +625,7 @@ class UpgradeController extends AbstractBase if ($action == 'Change') { $this->session->dbChangeEncoding = true; return $this->forwardTo('Upgrade', 'FixDatabase'); - } else if ($action == 'Keep') { + } elseif ($action == 'Keep') { $this->session->dbChangeEncoding = false; return $this->forwardTo('Upgrade', 'FixDatabase'); } @@ -704,7 +725,7 @@ class UpgradeController extends AbstractBase // Process submit button: if ($this->formWasSubmitted('submit')) { - $converter = $this->serviceLocator->get('VuFind\DateConverter'); + $converter = $this->serviceLocator->get('VuFind\Date\Converter'); foreach ($problems as $problem) { try { $driver = $this->getRecordLoader() @@ -735,7 +756,7 @@ class UpgradeController extends AbstractBase if (!is_dir($dir)) { $this->flashMessenger() ->addMessage($dir . ' does not exist.', 'error'); - } else if (!file_exists($dir . '/build.xml')) { + } elseif (!file_exists($dir . '/build.xml')) { $this->flashMessenger()->addMessage( 'Could not find build.xml in source directory;' . ' upgrade does not support VuFind versions prior to 1.1.', @@ -779,7 +800,7 @@ class UpgradeController extends AbstractBase if (floor($version) < 2) { $this->flashMessenger() ->addMessage('Illegal version number.', 'error'); - } else if ($version >= $this->cookie->newVersion) { + } elseif ($version >= $this->cookie->newVersion) { $this->flashMessenger()->addMessage( "Source version must be less than {$this->cookie->newVersion}.", 'error' @@ -807,7 +828,7 @@ class UpgradeController extends AbstractBase { // If the cache is messed up, nothing is going to work right -- check that // first: - $cache = $this->serviceLocator->get('VuFind\CacheManager'); + $cache = $this->serviceLocator->get('VuFind\Cache\Manager'); if ($cache->hasDirectoryCreationError()) { return $this->redirect()->toRoute('install-fixcache'); } diff --git a/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php b/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..91ee8f310f52c6f99cb177ca74c9dd373ccd3865 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Upgrade controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Upgrade controller factory. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UpgradeControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $cookieManager = $container->get('VuFind\Cookie\CookieManager'); + $session = new \Zend\Session\Container( + 'upgrade', $container->get('Zend\Session\SessionManager') + ); + return new $requestedName($container, $cookieManager, $session); + } +} diff --git a/module/VuFind/src/VuFind/Controller/WebController.php b/module/VuFind/src/VuFind/Controller/WebController.php index 34b12c2e7510cd6374d0d4d43d23140794690e43..59f5228f96a00979154e7d2c8335439db892e552 100644 --- a/module/VuFind/src/VuFind/Controller/WebController.php +++ b/module/VuFind/src/VuFind/Controller/WebController.php @@ -2,7 +2,7 @@ /** * Web Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -50,17 +51,6 @@ class WebController extends AbstractSearch parent::__construct($sm); } - /** - * Home action - * - * @return \Zend\View\Model\ViewModel - */ - public function homeAction() - { - // Do nothing -- just display template - return $this->createViewModel(); - } - /** * Process the jumpto parameter -- either redirect to a specific record and * return view model, or ignore the parameter and return false. diff --git a/module/VuFind/src/VuFind/Controller/Widget/Solr/FacetInterface.php b/module/VuFind/src/VuFind/Controller/Widget/Solr/FacetInterface.php index efed73c8592e0c1c6d07a7ab4fdee1a4f9936305..3e69399743bd3c8b0761df727979f6991a1d3d6e 100644 --- a/module/VuFind/src/VuFind/Controller/Widget/Solr/FacetInterface.php +++ b/module/VuFind/src/VuFind/Controller/Widget/Solr/FacetInterface.php @@ -3,7 +3,7 @@ /** * SOLR facet widget interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFind\Controller\Widget\Solr; -use VuFindSearch\Backend\Solr\Response\Json\Facets; use VuFind\Controller\Widget\WidgetInterface; +use VuFindSearch\Backend\Solr\Response\Json\Facets; /** * SOLR facet widget interface definition. diff --git a/module/VuFind/src/VuFind/Controller/Widget/WidgetInterface.php b/module/VuFind/src/VuFind/Controller/Widget/WidgetInterface.php index e2a17aa3b619742547317fd54dadbb00500cef93..0cf28d6e1fe76efc97bc5224df6c699d079fd307 100644 --- a/module/VuFind/src/VuFind/Controller/Widget/WidgetInterface.php +++ b/module/VuFind/src/VuFind/Controller/Widget/WidgetInterface.php @@ -3,7 +3,7 @@ /** * Widget interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -64,5 +64,4 @@ interface WidgetInterface * @return array */ public function getState(); - } diff --git a/module/VuFind/src/VuFind/Controller/WorldcatController.php b/module/VuFind/src/VuFind/Controller/WorldcatController.php index afea1a75129fb4c1a46a62636c1b61185aff3a41..8754974368221baf2c4ff1256c6e926226f12047 100644 --- a/module/VuFind/src/VuFind/Controller/WorldcatController.php +++ b/module/VuFind/src/VuFind/Controller/WorldcatController.php @@ -2,7 +2,7 @@ /** * WorldCat Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -57,20 +58,10 @@ class WorldcatController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('WorldCat'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); - } - - /** - * Home action - * - * @return mixed - */ - public function homeAction() - { - // Set up default parameters: - return $this->createViewModel(); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('WorldCat'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } /** diff --git a/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php b/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php index 974f23c7a99f06248ea96c0a357e524a7e79f836..99898d3d1a78107f8735d1caa186840c682603d5 100644 --- a/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php +++ b/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php @@ -2,7 +2,7 @@ /** * WorldCat Record Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Controller; + use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -60,8 +61,9 @@ class WorldcatrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config')->get('WorldCat'); - return (isset($config->Record->next_prev_navigation) - && $config->Record->next_prev_navigation); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('WorldCat'); + return isset($config->Record->next_prev_navigation) + && $config->Record->next_prev_navigation; } } diff --git a/module/VuFind/src/VuFind/Cookie/Container.php b/module/VuFind/src/VuFind/Cookie/Container.php index 5de312f2a63a077cf14123c49252d4f4030cd39b..2b5014e8513bdbc1af63e9ca2d51b4de41ea697e 100644 --- a/module/VuFind/src/VuFind/Cookie/Container.php +++ b/module/VuFind/src/VuFind/Cookie/Container.php @@ -3,7 +3,7 @@ * Class for treating a set of cookies as an object (inspired by * \Zend\Session\Container). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2012. * diff --git a/module/VuFind/src/VuFind/Cookie/CookieManager.php b/module/VuFind/src/VuFind/Cookie/CookieManager.php index 66d95e0c99ea2ba659514a16f02cfb048d3fae04..7bdd0ea6ddb89fd401b450f3ebed1b5b1f59e330 100644 --- a/module/VuFind/src/VuFind/Cookie/CookieManager.php +++ b/module/VuFind/src/VuFind/Cookie/CookieManager.php @@ -2,7 +2,7 @@ /** * Cookie Manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * diff --git a/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php b/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5b84634381439d8527c1ebd6a195807826526a7b --- /dev/null +++ b/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php @@ -0,0 +1,87 @@ +<?php +/** + * Cookie Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cookie + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Cookie; + +use Interop\Container\ContainerInterface; +use Zend\Console\Console; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cookie Manager factory. + * + * @category VuFind + * @package Cookie + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CookieManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $path = '/'; + if (isset($config->Cookies->limit_by_path) + && $config->Cookies->limit_by_path + ) { + $path = Console::isConsole() + ? '' : $container->get('Request')->getBasePath(); + if (empty($path)) { + $path = '/'; + } + } + $secure = isset($config->Cookies->only_secure) + ? $config->Cookies->only_secure + : false; + $domain = isset($config->Cookies->domain) + ? $config->Cookies->domain + : null; + $session_name = isset($config->Cookies->session_name) + ? $config->Cookies->session_name + : null; + return new $requestedName($_COOKIE, $path, $domain, $secure, $session_name); + } +} diff --git a/module/VuFind/src/VuFind/Cover/CachingProxy.php b/module/VuFind/src/VuFind/Cover/CachingProxy.php index 6c2c8255b2cfcac3ba6900c356ca3ff0df44275c..403b55dd0768014dc7ff02f9ae9ee952bb2cdc54 100644 --- a/module/VuFind/src/VuFind/Cover/CachingProxy.php +++ b/module/VuFind/src/VuFind/Cover/CachingProxy.php @@ -2,7 +2,7 @@ /** * Caching Proxy for Cover Images * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/configuration:external_content Wiki */ namespace VuFind\Cover; -use Zend\Http\Client, Zend\Http\Response; + +use Zend\Http\Client; +use Zend\Http\Response; /** * Caching Proxy for Cover Images diff --git a/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php b/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..068783abfdeaa8c7fb261285787aa5be2f986555 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Cover caching proxy factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Cover; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cover caching proxy factory. + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CachingProxyFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $cacheDir = $container->get('VuFind\Cache\Manager') + ->getCache('cover')->getOptions()->getCacheDir(); + $client = $container->get('VuFindHttp\HttpService')->createClient(); + $config = $container->get('VuFind\Config\PluginManager')->get('config') + ->toArray(); + $whitelist = isset($config['Content']['coverproxyCache']) + ? (array)$config['Content']['coverproxyCache'] : []; + return new $requestedName($client, $cacheDir . '/proxy', $whitelist); + } +} diff --git a/module/VuFind/src/VuFind/Cover/Generator.php b/module/VuFind/src/VuFind/Cover/Generator.php index cb6d76b62322884cdaa0e5e0d4ac402afb2b04eb..299f5498b7600093bc83d282b156456f64f66ef4 100644 --- a/module/VuFind/src/VuFind/Cover/Generator.php +++ b/module/VuFind/src/VuFind/Cover/Generator.php @@ -2,7 +2,7 @@ /** * Dynamic Book Cover Generator * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -27,6 +27,10 @@ */ namespace VuFind\Cover; +use VuFind\Cover\Layer\LayerInterface; +use VuFind\Cover\Layer\PluginManager as LayerManager; +use VuFindTheme\ThemeInfo; + /** * Dynamic Book Cover Generator * @@ -39,46 +43,40 @@ namespace VuFind\Cover; class Generator { /** - * Style configuration + * Default settings (values used by setOptions() if not overridden). * * @var array */ - protected $settings = []; - - /** - * Base color used to fill initially created image. - * - * @var int - */ - protected $baseColor; - - /** - * Title's fill color - * - * @var int - */ - protected $titleFillColor; - - /** - * Title's border color - * - * @var int - */ - protected $titleBorderColor; - - /** - * Author's fill color - * - * @var int - */ - protected $authorFillColor; - - /** - * Author's border color - * - * @var int - */ - protected $authorBorderColor; + protected $defaultSettings = [ + 'backgroundMode' => 'grid', + 'textMode' => 'default', + 'authorFont' => 'DroidSerif-Bold.ttf', + 'titleFontSize' => 7, + 'authorFontSize' => 6, + 'lightness' => 220, + 'maxTitleLines' => 5, + 'minAuthorFontSize' => 5, + 'saturation' => 80, + 'size' => 84, + 'textAlign' => 'center', + 'titleFont' => 'DroidSerif-Bold.ttf', + 'topPadding' => 19, + 'bottomPadding' => 3, + 'wrapWidth' => 80, + 'titleFillColor' => 'black', + 'titleBorderColor' => 'none', + 'authorFillColor' => 'white', + 'authorBorderColor' => 'black', + 'baseColor' => 'white', + 'accentColor' => 'random', + ]; + + /** + * Active style configuration + * + * @var object + */ + protected $settings; /** * Base for image @@ -88,75 +86,63 @@ class Generator protected $im; /** - * Width of image (pixels) + * ThemeInfo object * - * @var int + * @var ThemeInfo */ - protected $width; + protected $themeTools; /** - * Height of image (pixels) + * Layer manager * - * @var int + * @var LayerManager */ - protected $height; + protected $layerManager; /** * Constructor * - * @param \VuFindTheme\ThemeInfo $themeTools For font loading - * @param array $settings Overwrite styles + * @param ThemeInfo $themeTools For font loading + * @param LayerManager $lm Layer manager + * @param array $settings Overwrite styles */ - public function __construct($themeTools, $settings = []) - { + public function __construct(ThemeInfo $themeTools, LayerManager $lm, + array $settings = [] + ) { $this->themeTools = $themeTools; - $default = [ - 'backgroundMode' => 'grid', - 'textMode' => 'default', - 'authorFont' => 'DroidSerif-Bold.ttf', - 'titleFontSize' => 7, - 'authorFontSize' => 6, - 'lightness' => 220, - 'maxTitleLines' => 5, - 'minAuthorFontSize' => 5, - 'saturation' => 80, - 'size' => 84, - 'textAlign' => 'center', - 'titleFont' => 'DroidSerif-Bold.ttf', - 'topPadding' => 19, - 'bottomPadding' => 3, - 'wrapWidth' => 80, - 'titleFillColor' => 'black', - 'titleBorderColor' => 'none', - 'authorFillColor' => 'white', - 'authorBorderColor' => 'black', - 'baseColor' => 'white', - 'accentColor' => 'random', - ]; - foreach ($settings as $i => $setting) { - $default[$i] = $setting; - } - $default['authorFont'] = $this->fontPath($default['authorFont']); - $default['titleFont'] = $this->fontPath($default['titleFont']); - $this->settings = (object) $default; - $this->initImage(); - $this->initColors(); + $this->layerManager = $lm; + $this->setOptions($settings); } /** - * Initialize colors to be used in the image. + * Set the generator options. + * + * @param array $rawSettings Overwrite styles * * @return void */ - protected function initColors() + public function setOptions($rawSettings) { - $this->baseColor = $this->getColor($this->settings->baseColor); - $this->titleFillColor = $this->getColor($this->settings->titleFillColor); - $this->titleBorderColor = $this->getColor($this->settings->titleBorderColor); - $this->authorFillColor = $this->getColor($this->settings->authorFillColor); - $this->authorBorderColor = $this->getColor( - $this->settings->authorBorderColor - ); + // Merge incoming settings with defaults: + $settings = $rawSettings + $this->defaultSettings; + + // Adjust font paths: + $settings['authorFont'] = $this->fontPath($settings['authorFont']); + $settings['titleFont'] = $this->fontPath($settings['titleFont']); + + // Determine final dimensions: + $parts = explode('x', strtolower($settings['size'])); + if (count($parts) < 2) { + $settings['width'] = $settings['height'] = $parts[0]; + } else { + list($settings['width'], $settings['height']) = $parts; + } + + // Store the results as an object: + $this->settings = (object)$settings; + + // Reinitialize everything based on settings: + $this->initImage(); } /** @@ -167,14 +153,9 @@ class Generator protected function initImage() { // Create image - $parts = explode('x', strtolower($this->settings->size)); - if (count($parts) < 2) { - $this->width = $this->height = $parts[0]; - } else { - list($this->width, $this->height) = $parts; - } - if (!($this->im = imagecreate($this->width, $this->height))) { - throw new \Exception("Cannot Initialize new GD image stream"); + $this->im = imagecreate($this->settings->width, $this->settings->height); + if (!$this->im) { + throw new \Exception('Cannot Initialize new GD image stream'); } } @@ -202,59 +183,6 @@ class Generator return $img; } - /** - * Check and allocates color - * - * @param string $color Legal color name from HTML4 - * - * @return allocated color - */ - protected function getColor($color) - { - switch (strtolower($color)){ - case 'black': - return imagecolorallocate($this->im, 0, 0, 0); - case 'silver': - return imagecolorallocate($this->im, 192, 192, 192); - case 'gray': - return imagecolorallocate($this->im, 128, 128, 128); - case 'white': - return imagecolorallocate($this->im, 255, 255, 255); - case 'maroon': - return imagecolorallocate($this->im, 128, 0, 0); - case 'red': - return imagecolorallocate($this->im, 255, 0, 0); - case 'purple': - return imagecolorallocate($this->im, 128, 0, 128); - case 'fuchsia': - return imagecolorallocate($this->im, 255, 0, 255); - case 'green': - return imagecolorallocate($this->im, 0, 128, 0); - case 'lime': - return imagecolorallocate($this->im, 0, 255, 0); - case 'olive': - return imagecolorallocate($this->im, 128, 128, 0); - case 'yellow': - return imagecolorallocate($this->im, 255, 255, 0); - case 'navy': - return imagecolorallocate($this->im, 0, 0, 128); - case 'blue': - return imagecolorallocate($this->im, 0, 0, 255); - case 'teal': - return imagecolorallocate($this->im, 0, 128, 128); - case 'aqua': - return imagecolorallocate($this->im, 0, 255, 255); - default: - if (substr($color, 0, 1) == '#' && strlen($color) == 7) { - $r = hexdec(substr($color, 1, 2)); - $g = hexdec(substr($color, 3, 2)); - $b = hexdec(substr($color, 5, 2)); - return imagecolorallocate($this->im, $r, $g, $b); - } - return false; - } - } - /** * Generates a dynamic cover image from elements of the item * @@ -266,12 +194,11 @@ class Generator */ public function generate($title, $author, $callnumber = null) { - // Generate seed from callnumber, title back up - $seed = $this->createSeed($title, $callnumber); + $details = compact('title', 'author', 'callnumber'); // Build the image - $this->drawBackgroundLayer($seed); - $this->drawTextLayer($title, $author); + $this->getBackgroundLayer()->render($this->im, $details, $this->settings); + $this->getTextLayer()->render($this->im, $details, $this->settings); // Render the image $png = $this->renderPng(); @@ -280,311 +207,28 @@ class Generator } /** - * Draw the background behind the text. - * - * @param int $seed Seed value + * Get the layer plugin for the background * - * @return void + * @return LayerInterface */ - protected function drawBackgroundLayer($seed) + protected function getBackgroundLayer() { - // Construct a method name using the mode setting; if the method is not - // defined, use the default drawGridBackground(). - $mode = ucwords(strtolower($this->settings->backgroundMode)); - $method = "draw{$mode}Background"; - return method_exists($this, $method) - ? $this->$method($seed) : $this->drawGridBackground($seed); - } - - /** - * Position the text on the image. - * - * @param string $title Title of the book - * @param string $author Author of the book - * - * @return void - */ - protected function drawTextLayer($title, $author) - { - // Construct a method name using the mode setting; if the method is not - // defined, use the default drawGridBackground(). - $mode = ucwords(strtolower($this->settings->textMode)); - $method = "draw{$mode}Text"; - return method_exists($this, $method) - ? $this->$method($title, $author) - : $this->drawDefaultText($title, $author); - } - - /** - * Position the text on the image using default rules. - * - * @param string $title Title of the book - * @param string $author Author of the book - * - * @return void - */ - protected function drawDefaultText($title, $author) - { - if (null !== $title) { - $this->drawTitle($title, $this->height / 8); - } - if (null !== $author) { - $this->drawAuthor($author); - } - } - - /** - * Position the text on the image using "initials" rules. - * - * @param string $title Title of the book - * @param string $author Author of the book - * - * @return void - */ - protected function drawInitialText($title, $author) - { - // Get the first letter of title or author... - $initial = mb_substr($title . $author, 0, 1, 'UTF-8'); - - // Get the height of a character with no descenders: - $heightWithoutDescenders = $this->textHeight( - 'O', $this->settings->titleFont, $this->settings->titleFontSize - ); - - // Get the height of the currently selected character: - $textHeight = $this->textHeight( - $initial, $this->settings->titleFont, $this->settings->titleFontSize - ); - - // Draw the letter... Note that the way we are using $textHeight and - // $heightWithoutDescenders is something of a fudge driven by the fact - // that PHP measures text in total pixels, but positions text using the - // baseline (thus not accounting for descenders). To truly vertically - // center something, we need more information than we can get without - // using an extension or library to read more information from the font - // file. The formula here is not particularly well-informed but seems - // to produce acceptable results for many scenarios. - $this->drawText( - $initial, - $heightWithoutDescenders + ($this->height - $textHeight) / 2, - $this->settings->titleFont, - $this->settings->titleFontSize, - $this->titleFillColor, - $this->titleBorderColor, - $this->settings->textAlign + $service = strtolower($this->settings->backgroundMode) . 'background'; + return $this->layerManager->get( + $this->layerManager->has($service) ? $service : 'gridbackground' ); } /** - * Generate an accent color from a seed value. - * - * @param int $seed Seed value + * Get the layer plugin for the text * - * @return int + * @return LayerInterface */ - protected function getAccentColor($seed) + protected function getTextLayer() { - // Number to color, hsb to control saturation and lightness - if ($this->settings->accentColor == 'random') { - return $this->makeHSBColor( - $seed % 256, - $this->settings->saturation, - $this->settings->lightness - ); - } - return $this->getColor($this->settings->accentColor); - } - - /** - * Generates a solid color background, ala Google - * - * @param int $seed Seed value - * - * @return void - */ - protected function drawSolidBackground($seed) - { - // Fill solid color - imagefilledrectangle( - $this->im, - 0, - 0, - $this->width, - $this->height, - $this->getAccentColor($seed) - ); - } - - /** - * Generates a grid of colors as primary feature - * - * @param int $seed Seed value - * - * @return void - */ - protected function drawGridBackground($seed) - { - // Render the grid - $this->renderGrid($this->createPattern($seed), $this->getAccentColor($seed)); - } - - /** - * Generates a dynamic cover image from elements of the book - * - * @param string $title Title of the book - * @param string $callnumber Callnumber of the book - * - * @return int unique number for this record - */ - protected function createSeed($title, $callnumber) - { - // Turn callnumber into number - if (null == $callnumber) { - $callnumber = $title; - } - if (null !== $callnumber) { - $cv = 0; - for ($i = 0;$i < strlen($callnumber);$i++) { - $cv += ord($callnumber[$i]); - } - return $cv; - } else { - // If no callnumber, random - return ceil(rand(pow(2, 4), pow(2, 32))); - } - } - - /** - * Turn number into pattern - * - * @param int $seed Seed used to generate the pattern - * - * @return string binary string describing a quarter of the pattern - */ - protected function createPattern($seed) - { - // Convert to binary - $bc = decbin($seed); - // If we have less that a half of a quarter - if (strlen($bc) < 8) { - // Rotate square of the first 4 into a 4x2 - // Simulate matrix rotation on string - $bc = substr($bc, 0, 3) - . substr($bc, 0, 1) - . substr($bc, 2, 2) - . substr($bc, 3, 1) - . substr($bc, 1, 1); - } - // If we have less than a quarter - if (strlen($bc) < 16) { - // Rotate the first 8 as a 4x2 into a 4x4 - $bc .= strrev($bc); - } - return $bc; - } - - /** - * Render title in wrapped, black text with white border - * - * @param string $title Title to write - * @param int $lineHeight Pixels we move down each line - * - * @return void - */ - protected function drawTitle($title, $lineHeight) - { - $words = explode(' ', $title); - // Wrap words into image - // Add words until off image, go back and print - $line = ''; - $lineCount = 0; - $i = 0; - while ($i < count($words) - && $lineCount < $this->settings->maxTitleLines - 1 - ) { - $pline = $line; - // Format - $text = $words[$i]; - $line .= $text . ' '; - $textWidth = $this->textWidth( - rtrim($line, ' '), - $this->settings->titleFont, - $this->settings->titleFontSize - ); - if ($textWidth > $this->settings->wrapWidth) { - // Print black with white border - $this->drawText( - rtrim($pline, ' '), - $this->settings->topPadding + $lineHeight * $lineCount, - $this->settings->titleFont, - $this->settings->titleFontSize, - $this->titleFillColor, - $this->titleBorderColor - ); - $line = $text . ' '; - $lineCount++; - } - $i++; - } - // Print the last words - $this->drawText( - rtrim($line, ' '), - $this->settings->topPadding + $lineHeight * $lineCount, - $this->settings->titleFont, - $this->settings->titleFontSize, - $this->titleFillColor, - $this->titleBorderColor - ); - // Add ellipses if we've truncated - if ($i < count($words) - 1) { - $this->drawText( - '...', - $this->settings->topPadding - + $this->settings->maxTitleLines * $lineHeight, - $this->settings->titleFont, - $this->settings->titleFontSize + 1, - $this->titleFillColor, - $this->titleBorderColor - ); - } - } - - /** - * Render author at bottom in wrapped, white text with black border - * - * @param string $author Author to write - * - * @return void - */ - protected function drawAuthor($author) - { - // Scale author to fit by incrementing fontsizes down - $fontSize = $this->settings->authorFontSize + 1; - do { - $fontSize--; - $textWidth = $this->textWidth( - $author, - $this->settings->authorFont, - $fontSize - ); - } while ($textWidth > $this->settings->wrapWidth && - $fontSize > $this->settings->minAuthorFontSize - ); - // Too small to read? Align left - $textWidth = $this->textWidth( - $author, - $this->settings->authorFont, - $fontSize - ); - $align = $textWidth > $this->width ? 'left' : null; - $this->drawText( - $author, - $this->height - $this->settings->bottomPadding, - $this->settings->authorFont, - $fontSize, - $this->authorFillColor, - $this->authorBorderColor, - $align + $service = strtolower($this->settings->textMode) . 'text'; + return $this->layerManager->get( + $this->layerManager->has($service) ? $service : 'defaulttext' ); } @@ -602,171 +246,4 @@ class Generator $fileMatch = $this->themeTools->findContainingTheme($filenames, true); return empty($fileMatch) ? false : $fileMatch; } - - /** - * Returns the width a string would render to - * - * @param string $text Text to test - * @param string $font Full font path - * @param string $size Size of the font - * - * @return int - */ - protected function textWidth($text, $font, $size) - { - $p = imagettfbbox($size, 0, $font, $text); - return $p[2] - $p[0]; - } - - /** - * Returns the height a string would render to - * - * @param string $text Text to test - * @param string $font Full font path - * @param string $size Size of the font - * - * @return int - */ - protected function textHeight($text, $font, $size) - { - $p = imagettfbbox($size, 0, $font, $text); - return $p[1] - $p[5]; - } - - /** - * Simulate outlined text - * - * @param string $text Text to render - * @param int $y Top position - * @param string $font Full path to font - * @param int $fontSize Size of the font - * @param int $mcolor Main text color - * @param int $scolor Secondary border color - * @param string $align 'left','center','right' - * - * @return void - */ - protected function drawText($text, $y, $font, $fontSize, $mcolor, - $scolor = false, $align = null - ) { - // If the wrap width is smaller than the image width, we want to account - // for this when right or left aligning to maintain padding on the image. - $wrapGap = ($this->width - $this->settings->wrapWidth) / 2; - - $textWidth = $this->textWidth( - $text, - $font, - $fontSize - ); - if ($textWidth > $this->width) { - $align = 'left'; - $wrapGap = 0; // kill wrap gap to maximize text fit - } - if (null == $align) { - $align = $this->settings->textAlign; - } - if ($align == 'left') { - $x = $wrapGap; - } - if ($align == 'center') { - $x = ($this->width - $textWidth) / 2; - } - if ($align == 'right') { - $x = $this->width - ($textWidth + $wrapGap); - } - - // Generate 5 lines of text, 4 offset in a border color - if ($scolor) { - imagettftext( - $this->im, $fontSize, 0, $x, $y + 1, $scolor, $font, $text - ); - imagettftext( - $this->im, $fontSize, 0, $x, $y - 1, $scolor, $font, $text - ); - imagettftext( - $this->im, $fontSize, 0, $x + 1, $y, $scolor, $font, $text - ); - imagettftext( - $this->im, $fontSize, 0, $x - 1, $y, $scolor, $font, $text - ); - } - // 1 centered in main color - imagettftext($this->im, $fontSize, 0, $x, $y, $mcolor, $font, $text); - } - - /** - * Convert 16 long binary string to 8x8 color grid - * Reflects vertically and horizontally - * - * @param string $bc Binary string of pattern - * @param int $color Fill color - * - * @return void - */ - protected function renderGrid($bc, $color) - { - $halfWidth = $this->width / 2; - $halfHeight = $this->height / 2; - $boxWidth = $this->width / 8; - $boxHeight = $this->height / 8; - - $bc = str_split($bc); - for ($k = 0;$k < 4;$k++) { - $x = $k % 2 ? $halfWidth : $halfWidth - $boxWidth; - $y = $k / 2 < 1 ? $halfHeight : $halfHeight - $boxHeight; - $u = $k % 2 ? $boxWidth : -$boxWidth; - $v = $k / 2 < 1 ? $boxHeight : -$boxHeight; - for ($i = 0;$i < 16;$i++) { - if ($bc[$i] == "1") { - imagefilledrectangle( - $this->im, $x, $y, - $x + $boxWidth - 1, $y + $boxHeight - 1, $color - ); - } - $x += $u; - if ($x >= $this->width || $x < 0) { - $x = $k % 2 ? $halfWidth : $halfWidth - $boxWidth; - $y += $v; - } - } - } - //imagefilledrectangle($this->im,0,$size-11,$size-1,$size,$color); - } - - /** - * Using HSB allows us to control the contrast while allowing randomness - * - * @param int $h Hue (0-255) - * @param int $s Saturation (0-255) - * @param int $v Lightness (0-255) - * - * @return int - */ - protected function makeHSBColor($h, $s, $v) - { - $s /= 256.0; - if ($s == 0.0) { - return imagecolorallocate($this->im, $v, $v, $v); - } - $h /= (256.0 / 6.0); - $i = floor($h); - $f = $h - $i; - $p = (int)($v * (1.0 - $s)); - $q = (int)($v * (1.0 - $s * $f)); - $t = (int)($v * (1.0 - $s * (1.0 - $f))); - switch($i) { - case 0: - return imagecolorallocate($this->im, $v, $t, $p); - case 1: - return imagecolorallocate($this->im, $q, $v, $p); - case 2: - return imagecolorallocate($this->im, $p, $v, $t); - case 3: - return imagecolorallocate($this->im, $p, $q, $v); - case 4: - return imagecolorallocate($this->im, $t, $p, $v); - default: - return imagecolorallocate($this->im, $v, $p, $q); - } - } } diff --git a/module/VuFind/src/VuFind/Cover/GeneratorFactory.php b/module/VuFind/src/VuFind/Cover/GeneratorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..36938fc0dde5cf5e5acb089d6f3f27b199aa35cd --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/GeneratorFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Cover generator factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Cover; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cover generator factory. + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GeneratorFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFindTheme\ThemeInfo'), + $container->get('VuFind\Cover\Layer\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/Cover/Layer/AbstractBackgroundLayer.php b/module/VuFind/src/VuFind/Cover/Layer/AbstractBackgroundLayer.php new file mode 100644 index 0000000000000000000000000000000000000000..c529094743d7ca2d0d60b28bc7e62f318f7bba29 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/AbstractBackgroundLayer.php @@ -0,0 +1,84 @@ +<?php +/** + * Abstract cover background layer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Abstract cover background layer + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +abstract class AbstractBackgroundLayer extends AbstractLayer +{ + /** + * Generates a dynamic cover image from elements of the book + * + * @param string $title Title of the book + * @param string $callnumber Callnumber of the book + * + * @return int unique number for this record + */ + protected function createSeed($title, $callnumber) + { + // Turn callnumber into number + if (null == $callnumber) { + $callnumber = $title; + } + if (null !== $callnumber) { + $cv = 0; + for ($i = 0;$i < strlen($callnumber);$i++) { + $cv += ord($callnumber[$i]); + } + return $cv; + } else { + // If no callnumber, random + return ceil(rand(pow(2, 4), pow(2, 32))); + } + } + + /** + * Generate an accent color from a seed value. + * + * @param resource $im Active image resource + * @param int $seed Seed value + * @param object $settings Generator settings object + * + * @return int + */ + protected function getAccentColor($im, $seed, $settings) + { + // Number to color, hsb to control saturation and lightness + return $settings->accentColor == 'random' + ? $this->getHSBColor( + $im, $seed % 256, $settings->saturation, $settings->lightness + ) : $this->getColor($im, $settings->accentColor); + } +} diff --git a/module/VuFind/src/VuFind/Cover/Layer/AbstractLayer.php b/module/VuFind/src/VuFind/Cover/Layer/AbstractLayer.php new file mode 100644 index 0000000000000000000000000000000000000000..b7f7a8333e13f7bd4ceb145fca043d9b2515996c --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/AbstractLayer.php @@ -0,0 +1,128 @@ +<?php +/** + * Abstract cover layer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Abstract cover layer + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +abstract class AbstractLayer implements LayerInterface +{ + /** + * Mapping of color names to RGB values. + * + * @var array + */ + protected $colorMap = [ + 'black' => [0, 0, 0], + 'silver' => [192, 192, 192], + 'gray' => [128, 128, 128], + 'white' => [255, 255, 255], + 'maroon' => [128, 0, 0], + 'red' => [255, 0, 0], + 'purple' => [128, 0, 128], + 'fuchsia' => [255, 0, 255], + 'green' => [0, 128, 0], + 'lime' => [0, 255, 0], + 'olive' => [128, 128, 0], + 'yellow' => [255, 255, 0], + 'navy' => [0, 0, 128], + 'blue' => [0, 0, 255], + 'teal' => [0, 128, 128], + 'aqua' => [0, 255, 255], + ]; + + /** + * Check and allocates color + * + * @param resource $im Image resource being updated + * @param string $color Legal color name from HTML4 + * + * @return allocated color + */ + protected function getColor($im, $color) + { + // Case one: named color found in map + $key = strtolower($color); + if (isset($this->colorMap[$key])) { + return imagecolorallocate($im, ...$this->colorMap[$key]); + } + // Case two: hex color + if (substr($color, 0, 1) == '#' && strlen($color) == 7) { + $r = hexdec(substr($color, 1, 2)); + $g = hexdec(substr($color, 3, 2)); + $b = hexdec(substr($color, 5, 2)); + return imagecolorallocate($im, $r, $g, $b); + } + // Default case: unsupported color + return false; + } + + /** + * Using HSB allows us to control the contrast while allowing randomness + * + * @param resource $im Active image resource + * @param int $h Hue (0-255) + * @param int $s Saturation (0-255) + * @param int $v Lightness (0-255) + * + * @return int + */ + protected function getHSBColor($im, $h, $s, $v) + { + $s /= 256.0; + if ($s == 0.0) { + return imagecolorallocate($im, $v, $v, $v); + } + $h /= (256.0 / 6.0); + $i = floor($h); + $f = $h - $i; + $p = (int)($v * (1.0 - $s)); + $q = (int)($v * (1.0 - $s * $f)); + $t = (int)($v * (1.0 - $s * (1.0 - $f))); + switch ($i) { + case 0: + return imagecolorallocate($im, $v, $t, $p); + case 1: + return imagecolorallocate($im, $q, $v, $p); + case 2: + return imagecolorallocate($im, $p, $v, $t); + case 3: + return imagecolorallocate($im, $p, $q, $v); + case 4: + return imagecolorallocate($im, $t, $p, $v); + default: + return imagecolorallocate($im, $v, $p, $q); + } + } +} diff --git a/module/VuFind/src/VuFind/Cover/Layer/AbstractTextLayer.php b/module/VuFind/src/VuFind/Cover/Layer/AbstractTextLayer.php new file mode 100644 index 0000000000000000000000000000000000000000..d914d59367134fed13e205b6f86b8e83cd9ffdf9 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/AbstractTextLayer.php @@ -0,0 +1,125 @@ +<?php +/** + * Abstract cover text layer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Abstract cover text layer + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +abstract class AbstractTextLayer extends AbstractLayer +{ + /** + * Returns the width a string would render to + * + * @param string $text Text to test + * @param string $font Full font path + * @param string $size Size of the font + * + * @return int + */ + protected function textWidth($text, $font, $size) + { + $p = imagettfbbox($size, 0, $font, $text); + return $p[2] - $p[0]; + } + + /** + * Returns the height a string would render to + * + * @param string $text Text to test + * @param string $font Full font path + * @param string $size Size of the font + * + * @return int + */ + protected function textHeight($text, $font, $size) + { + $p = imagettfbbox($size, 0, $font, $text); + return $p[1] - $p[5]; + } + + /** + * Simulate outlined text + * + * @param resource $im Active image resource + * @param object $settings Generator settings object + * @param string $text Text to render + * @param int $y Top position + * @param string $font Full path to font + * @param int $fontSize Size of the font + * @param int $mcolor Main text color + * @param int $scolor Secondary border color + * @param string $align 'left','center','right' + * + * @return void + */ + protected function drawText($im, $settings, $text, $y, $font, $fontSize, $mcolor, + $scolor = false, $align = null + ) { + // In case the text contains non-normalized UTF-8, fix that for proper + // display: + $text = \Normalizer::normalize($text); + + // If the wrap width is smaller than the image width, we want to account + // for this when right or left aligning to maintain padding on the image. + $wrapGap = ($settings->width - $settings->wrapWidth) / 2; + + $textWidth = $this->textWidth($text, $font, $fontSize); + if ($textWidth > $settings->width) { + $align = 'left'; + $wrapGap = 0; // kill wrap gap to maximize text fit + } + if (null == $align) { + $align = $settings->textAlign; + } + if ($align == 'left') { + $x = $wrapGap; + } + if ($align == 'center') { + $x = ($settings->width - $textWidth) / 2; + } + if ($align == 'right') { + $x = $settings->width - ($textWidth + $wrapGap); + } + + // Generate 5 lines of text, 4 offset in a border color + if ($scolor) { + imagettftext($im, $fontSize, 0, $x, $y + 1, $scolor, $font, $text); + imagettftext($im, $fontSize, 0, $x, $y - 1, $scolor, $font, $text); + imagettftext($im, $fontSize, 0, $x + 1, $y, $scolor, $font, $text); + imagettftext($im, $fontSize, 0, $x - 1, $y, $scolor, $font, $text); + } + // 1 centered in main color + imagettftext($im, $fontSize, 0, $x, $y, $mcolor, $font, $text); + } +} diff --git a/module/VuFind/src/VuFind/Cover/Layer/DefaultText.php b/module/VuFind/src/VuFind/Cover/Layer/DefaultText.php new file mode 100644 index 0000000000000000000000000000000000000000..28dd5f78eb43f2ec7bec0fd458022a7b752c85aa --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/DefaultText.php @@ -0,0 +1,180 @@ +<?php +/** + * Default cover text layer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Default cover text layer + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class DefaultText extends AbstractTextLayer +{ + /** + * Render the layer + * + * @param resource $im Image resource to draw on + * @param array $details Cover details array (with title/author/call_number) + * @param object $settings Settings object + * + * @return void + */ + public function render($im, $details, $settings) + { + if (null !== $details['title']) { + $lineHeight = $settings->height / 8; + $this->drawTitle($im, $settings, $details['title'], $lineHeight); + } + if (null !== $details['author']) { + $this->drawAuthor($im, $settings, $details['author']); + } + } + + /** + * Render title in wrapped, black text with white border + * + * @param resource $im Image resource to draw on + * @param object $settings Settings object + * @param string $title Title to write + * @param int $lineHeight Pixels we move down each line + * + * @return void + */ + protected function drawTitle($im, $settings, $title, $lineHeight) + { + $titleFillColor = $this->getColor($im, $settings->titleFillColor); + $titleBorderColor = $this->getColor($im, $settings->titleBorderColor); + $words = explode(' ', $title); + // Wrap words into image + // Add words until off image, go back and print + $line = ''; + $lineCount = 0; + $i = 0; + while ($i < count($words) + && $lineCount < $settings->maxTitleLines - 1 + ) { + $pline = $line; + // Format + $text = $words[$i]; + $line .= $text . ' '; + $textWidth = $this->textWidth( + rtrim($line, ' '), + $settings->titleFont, + $settings->titleFontSize + ); + if ($textWidth > $settings->wrapWidth) { + // Print black with white border + $this->drawText( + $im, + $settings, + rtrim($pline, ' '), + $settings->topPadding + $lineHeight * $lineCount, + $settings->titleFont, + $settings->titleFontSize, + $titleFillColor, + $titleBorderColor + ); + $line = $text . ' '; + $lineCount++; + } + $i++; + } + // Print the last words + $this->drawText( + $im, + $settings, + rtrim($line, ' '), + $settings->topPadding + $lineHeight * $lineCount, + $settings->titleFont, + $settings->titleFontSize, + $titleFillColor, + $titleBorderColor + ); + // Add ellipses if we've truncated + if ($i < count($words) - 1) { + $this->drawText( + $im, + $settings, + '...', + $settings->topPadding + $settings->maxTitleLines * $lineHeight, + $settings->titleFont, + $settings->titleFontSize + 1, + $titleFillColor, + $titleBorderColor + ); + } + } + + /** + * Render author at bottom in wrapped, white text with black border + * + * @param resource $im Image resource to draw on + * @param object $settings Settings object + * @param string $author Author to write + * + * @return void + */ + protected function drawAuthor($im, $settings, $author) + { + $authorFillColor = $this->getColor($im, $settings->authorFillColor); + $authorBorderColor = $this->getColor($im, $settings->authorBorderColor); + // Scale author to fit by incrementing fontsizes down + $fontSize = $settings->authorFontSize + 1; + do { + $fontSize--; + $textWidth = $this->textWidth( + $author, + $settings->authorFont, + $fontSize + ); + } while ($textWidth > $settings->wrapWidth && + $fontSize > $settings->minAuthorFontSize + ); + // Too small to read? Align left + $textWidth = $this->textWidth( + $author, + $settings->authorFont, + $fontSize + ); + $align = $textWidth > $settings->width ? 'left' : null; + $this->drawText( + $im, + $settings, + $author, + $settings->height - $settings->bottomPadding, + $settings->authorFont, + $fontSize, + $authorFillColor, + $authorBorderColor, + $align + ); + } +} diff --git a/module/VuFind/src/VuFind/Cover/Layer/GridBackground.php b/module/VuFind/src/VuFind/Cover/Layer/GridBackground.php new file mode 100644 index 0000000000000000000000000000000000000000..2626a68e9514234e9de5ac44f518ffde6c0ce745 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/GridBackground.php @@ -0,0 +1,131 @@ +<?php +/** + * Grid cover background layer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Grid cover background layer + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class GridBackground extends AbstractBackgroundLayer +{ + /** + * Render the layer + * + * @param resource $im Image resource to draw on + * @param array $details Cover details array (with title/author/call_number) + * @param object $settings Settings object + * + * @return void + */ + public function render($im, $details, $settings) + { + // Generate a grid of colors as primary feature + $seed = $this->createSeed($details['title'], $details['callnumber']); + $pattern = $this->createPattern($seed); + $accentColor = $this->getAccentColor($im, $seed, $settings); + $this->renderGrid($im, $pattern, $accentColor, $settings); + } + + /** + * Turn number into pattern + * + * @param int $seed Seed used to generate the pattern + * + * @return string binary string describing a quarter of the pattern + */ + protected function createPattern($seed) + { + // Convert to binary + $bc = decbin($seed); + // If we have less that a half of a quarter + if (strlen($bc) < 8) { + // Rotate square of the first 4 into a 4x2 + // Simulate matrix rotation on string + $bc = substr($bc, 0, 3) + . substr($bc, 0, 1) + . substr($bc, 2, 2) + . substr($bc, 3, 1) + . substr($bc, 1, 1); + } + // If we have less than a quarter + if (strlen($bc) < 16) { + // Rotate the first 8 as a 4x2 into a 4x4 + $bc .= strrev($bc); + } + return $bc; + } + + /** + * Convert 16 long binary string to 8x8 color grid + * Reflects vertically and horizontally + * + * @param resource $im Active image resource + * @param string $pattern Binary string of pattern + * @param int $color Fill color + * @param object $settings Generator settings object + * + * @return void + */ + protected function renderGrid($im, $pattern, $color, $settings) + { + imagefilledrectangle( + $im, 0, 0, $settings->width, $settings->height, + $this->getColor($im, $settings->baseColor) + ); + $halfWidth = $settings->width / 2; + $halfHeight = $settings->height / 2; + $boxWidth = $settings->width / 8; + $boxHeight = $settings->height / 8; + + $bc = str_split($pattern); + for ($k = 0;$k < 4;$k++) { + $x = $k % 2 ? $halfWidth : $halfWidth - $boxWidth; + $y = $k / 2 < 1 ? $halfHeight : $halfHeight - $boxHeight; + $u = $k % 2 ? $boxWidth : -$boxWidth; + $v = $k / 2 < 1 ? $boxHeight : -$boxHeight; + for ($i = 0;$i < 16;$i++) { + if ($bc[$i] == "1") { + imagefilledrectangle( + $im, $x, $y, + $x + $boxWidth - 1, $y + $boxHeight - 1, $color + ); + } + $x += $u; + if ($x >= $settings->width || $x < 0) { + $x = $k % 2 ? $halfWidth : $halfWidth - $boxWidth; + $y += $v; + } + } + } + } +} diff --git a/module/VuFind/src/VuFind/Cover/Layer/InitialText.php b/module/VuFind/src/VuFind/Cover/Layer/InitialText.php new file mode 100644 index 0000000000000000000000000000000000000000..8519166bc729e9e5d5249dda79cecc6d6fe1d692 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/InitialText.php @@ -0,0 +1,83 @@ +<?php +/** + * Initial cover text layer + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Initial cover text layer + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class InitialText extends AbstractTextLayer +{ + /** + * Render the layer + * + * @param resource $im Image resource to draw on + * @param array $details Cover details array (with title/author/call_number) + * @param object $settings Settings object + * + * @return void + */ + public function render($im, $details, $settings) + { + // Get the first letter of title or author... + $initial = mb_substr($details['title'] . $details['author'], 0, 1, 'UTF-8'); + + // Get the height of a character with no descenders: + $heightWithoutDescenders + = $this->textHeight('O', $settings->titleFont, $settings->titleFontSize); + + // Get the height of the currently selected character: + $textHeight = $this + ->textHeight($initial, $settings->titleFont, $settings->titleFontSize); + + // Draw the letter... Note that the way we are using $textHeight and + // $heightWithoutDescenders is something of a fudge driven by the fact + // that PHP measures text in total pixels, but positions text using the + // baseline (thus not accounting for descenders). To truly vertically + // center something, we need more information than we can get without + // using an extension or library to read more information from the font + // file. The formula here is not particularly well-informed but seems + // to produce acceptable results for many scenarios. + $this->drawText( + $im, + $settings, + $initial, + $heightWithoutDescenders + ($settings->height - $textHeight) / 2, + $settings->titleFont, + $settings->titleFontSize, + $this->getColor($im, $settings->titleFillColor), + $this->getColor($im, $settings->titleBorderColor), + $settings->textAlign + ); + } +} diff --git a/module/VuFind/src/VuFind/Search/Params/Factory.php b/module/VuFind/src/VuFind/Cover/Layer/LayerInterface.php similarity index 63% rename from module/VuFind/src/VuFind/Search/Params/Factory.php rename to module/VuFind/src/VuFind/Cover/Layer/LayerInterface.php index db5f46a72a99ac44d1af33aa75d68a7ae1a6bd76..faf9e6886349d261477f36382c10d32cd724ce40 100644 --- a/module/VuFind/src/VuFind/Search/Params/Factory.php +++ b/module/VuFind/src/VuFind/Cover/Layer/LayerInterface.php @@ -1,10 +1,10 @@ <?php /** - * Search Params Object Factory Class + * Cover layer interface * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2017. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,38 +20,32 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Search + * @package Cover_Generator * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ -namespace VuFind\Search\Params; -use Zend\ServiceManager\ServiceManager; +namespace VuFind\Cover\Layer; /** - * Search Params Object Factory Class + * Cover layer interface * * @category VuFind - * @package Search + * @package Cover_Generator * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore */ -class Factory +interface LayerInterface { /** - * Factory for Solr params object. + * Render the layer * - * @param ServiceManager $sm Service manager. + * @param resource $im Image resource to draw on + * @param array $details Cover details array (with title/author/call_number) + * @param object $settings Settings object * - * @return \VuFind\Search\Solr\Params + * @return void */ - public static function getSolr(ServiceManager $sm) - { - $factory = new PluginFactory(); - $helper = $sm->getServiceLocator()->get('VuFind\HierarchicalFacetHelper'); - return $factory->createServiceWithName($sm, 'solr', 'Solr', [$helper]); - } + public function render($im, $details, $settings); } diff --git a/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php b/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..303ec2450e55fb9859f42eb4b898d351dbc72120 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php @@ -0,0 +1,79 @@ +<?php +/** + * Cover layer plugin manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Cover\Layer; + +/** + * Cover layer plugin manager + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'defaulttext' => 'VuFind\Cover\Layer\DefaultText', + 'gridbackground' => 'VuFind\Cover\Layer\GridBackground', + 'initialtext' => 'VuFind\Cover\Layer\InitialText', + 'solidbackground' => 'VuFind\Cover\Layer\SolidBackground', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Cover\Layer\DefaultText' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Cover\Layer\GridBackground' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Cover\Layer\InitialText' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Cover\Layer\SolidBackground' => + 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\Cover\Layer\LayerInterface'; + } +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/Factory.php b/module/VuFind/src/VuFind/Cover/Layer/SolidBackground.php similarity index 59% rename from module/VuFind/src/VuFind/Hierarchy/TreeRenderer/Factory.php rename to module/VuFind/src/VuFind/Cover/Layer/SolidBackground.php index 650de94b91ded74753aa4192df2972ccd28122e5..2adbc2daa5893f416656e6bbc3e10c001f8f8616 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/Factory.php +++ b/module/VuFind/src/VuFind/Cover/Layer/SolidBackground.php @@ -1,10 +1,10 @@ <?php /** - * Hierarchy Renderer Factory Class + * Solid cover background layer * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2010. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,40 +20,40 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package HierarchyTree_Renderer + * @package Cover_Generator * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ -namespace VuFind\Hierarchy\TreeRenderer; -use Zend\ServiceManager\ServiceManager; +namespace VuFind\Cover\Layer; /** - * Hierarchy Renderer Factory Class - * - * This is a factory class to build objects for rendering hierarchies. + * Solid cover background layer * * @category VuFind - * @package HierarchyTree_DataSource + * @package Cover_Generator * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore */ -class Factory +class SolidBackground extends AbstractBackgroundLayer { /** - * Factory for JSTree renderer. + * Render the layer * - * @param ServiceManager $sm Service manager. + * @param resource $im Image resource to draw on + * @param array $details Cover details array (with title/author/call_number) + * @param object $settings Settings object * - * @return JSTree + * @return void */ - public static function getJSTree(ServiceManager $sm) + public function render($im, $details, $settings) { - return new JSTree( - $sm->getServiceLocator()->get('ControllerPluginManager')->get('Url') + // Fill solid color + $seed = $this->createSeed($details['title'], $details['callnumber']); + $accentColor = $this->getAccentColor($im, $seed, $settings); + imagefilledrectangle( + $im, 0, 0, $settings->width, $settings->height, $accentColor ); } } diff --git a/module/VuFind/src/VuFind/Cover/Loader.php b/module/VuFind/src/VuFind/Cover/Loader.php index 92be709afd9964f4b350a47c3dd05ba22f3a3b41..1ca347798e586f35e9f7f9e78df2d2e1c1194443 100644 --- a/module/VuFind/src/VuFind/Cover/Loader.php +++ b/module/VuFind/src/VuFind/Cover/Loader.php @@ -2,7 +2,7 @@ /** * Book Cover Generator * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,7 +27,9 @@ * @link https://vufind.org/wiki/configuration:external_content Wiki */ namespace VuFind\Cover; -use VuFindCode\ISBN, VuFind\Content\Covers\PluginManager as ApiManager; + +use VuFind\Content\Covers\PluginManager as ApiManager; +use VuFindCode\ISBN; /** * Book Cover Generator @@ -41,6 +43,14 @@ use VuFindCode\ISBN, VuFind\Content\Covers\PluginManager as ApiManager; */ class Loader extends \VuFind\ImageLoader { + /** + * Class for rendering cover images dynamically if no API match found. Omit + * to disable functionality. + * + * @var Generator + */ + protected $generator = null; + /** * Filename constructed from ISBN * @@ -70,11 +80,11 @@ class Loader extends \VuFind\ImageLoader protected $apiManager; /** - * HTTP client + * HTTP client factory * - * @var \Zend\Http\Client + * @var \VuFindHttp\HttpService */ - protected $client; + protected $httpService; /** * Directory to store downloaded images @@ -142,22 +152,23 @@ class Loader extends \VuFind\ImageLoader /** * Constructor * - * @param \Zend\Config\Config $config VuFind configuration - * @param ApiManager $manager Plugin manager for API handlers - * @param \VuFindTheme\ThemeInfo $theme VuFind theme tools - * @param \Zend\Http\Client $client HTTP client - * @param string $baseDir Directory to store downloaded images - * (set to system temp dir if not otherwise specified) + * @param \Zend\Config\Config $config VuFind configuration + * @param ApiManager $manager Plugin manager for API handlers + * @param \VuFindTheme\ThemeInfo $theme VuFind theme tools + * @param \VuFindHttp\HttpService $httpService HTTP client factory + * @param string $baseDir Directory to store downloaded + * images (set to system temp dir if not otherwise specified) */ public function __construct($config, ApiManager $manager, - \VuFindTheme\ThemeInfo $theme, \Zend\Http\Client $client, $baseDir = null + \VuFindTheme\ThemeInfo $theme, \VuFindHttp\HttpService $httpService, + $baseDir = null ) { $this->setThemeInfo($theme); $this->config = $config; $this->configuredFailImage = isset($config->Content->noCoverAvailableImage) ? $config->Content->noCoverAvailableImage : null; $this->apiManager = $manager; - $this->client = $client; + $this->httpService = $httpService; $this->baseDir = (null === $baseDir) ? rtrim(sys_get_temp_dir(), '\\/') . '/covers' : rtrim($baseDir, '\\/'); @@ -191,19 +202,15 @@ class Loader extends \VuFind\ImageLoader } /** - * Get Cover Generator Object (or return false if disabled) + * Set Cover Generator Object * - * @return VuFind\Cover\Generator|bool + * @param Generator $generator Cover generator + * + * @return void */ - public function getCoverGenerator() + public function setCoverGenerator(Generator $generator) { - if (isset($this->config->Content->makeDynamicCovers) - && $this->config->Content->makeDynamicCovers - ) { - $settings = $this->getCoverGeneratorSettings(); - return new \VuFind\Cover\Generator($this->themeTools, $settings); - } - return false; + $this->generator = $generator; } /** @@ -301,11 +308,12 @@ class Loader extends \VuFind\ImageLoader // are able to display an ISBN or content-type-based image. if (!in_array($this->size, $this->validSizes)) { $this->loadUnavailable(); - } else if (!$this->fetchFromAPI() + } elseif (!$this->fetchFromAPI() && !$this->fetchFromContentType() ) { - if ($generator = $this->getCoverGenerator()) { - $this->image = $generator->generate( + if ($this->generator) { + $this->generator->setOptions($this->getCoverGeneratorSettings()); + $this->image = $this->generator->generate( $settings['title'], $settings['author'], $settings['callnumber'] ); $this->contentType = 'image/png'; @@ -333,13 +341,13 @@ class Loader extends \VuFind\ImageLoader return $this->getCachePath($this->size, $ids['isbn']->get10()); } return $file; - } else if (isset($ids['issn'])) { + } elseif (isset($ids['issn'])) { return $this->getCachePath($this->size, $ids['issn']); - } else if (isset($ids['oclc'])) { + } elseif (isset($ids['oclc'])) { return $this->getCachePath($this->size, 'OCLC' . $ids['oclc']); - } else if (isset($ids['upc'])) { + } elseif (isset($ids['upc'])) { return $this->getCachePath($this->size, 'UPC' . $ids['upc']); - } else if (isset($ids['recordid']) && isset($ids['source'])) { + } elseif (isset($ids['recordid']) && isset($ids['source'])) { return $this->getCachePath( $this->size, 'ID' . md5($ids['source'] . '|' . $ids['recordid']) @@ -397,7 +405,7 @@ class Loader extends \VuFind\ImageLoader $this->contentType = 'image/jpeg'; $this->image = file_get_contents($this->localFile); return true; - } else if (isset($this->config->Content->coverimages)) { + } elseif (isset($this->config->Content->coverimages)) { $providers = explode(',', $this->config->Content->coverimages); foreach ($providers as $provider) { $provider = explode(':', trim($provider)); @@ -561,7 +569,7 @@ class Loader extends \VuFind\ImageLoader ? trim(strtolower($this->config->Content->coverimagesCache)) : true; if ($conf === true || $conf === 1 || $conf === '1' || $conf === 'true') { $cache = true; - } else if ($conf === false || $conf === 0 || $conf === '0' + } elseif ($conf === false || $conf === 0 || $conf === '0' || $conf === 'false' ) { $cache = false; @@ -596,7 +604,7 @@ class Loader extends \VuFind\ImageLoader return true; } else { // Attempt to pull down the image: - $result = $this->client->setUri($url)->send(); + $result = $this->httpService->createClient($url)->send(); if (!$result->isSuccess()) { $this->debug('Failed to retrieve image from ' . $url); return false; diff --git a/module/VuFind/src/VuFind/Cover/LoaderFactory.php b/module/VuFind/src/VuFind/Cover/LoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a8bd85075a701009fb6c453c0ac610de9eb7e95d --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/LoaderFactory.php @@ -0,0 +1,80 @@ +<?php +/** + * Cover loader factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Cover; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cover loader factory. + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class LoaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $cacheDir = $container->get('VuFind\Cache\Manager') + ->getCache('cover')->getOptions()->getCacheDir(); + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $loader = new $requestedName( + $config, + $container->get('VuFind\Content\Covers\PluginManager'), + $container->get('VuFindTheme\ThemeInfo'), + $container->get('VuFindHttp\HttpService'), + $cacheDir + ); + // Add cover generator if enabled: + if ($config->Content->makeDynamicCovers ?? false) { + $loader->setCoverGenerator($container->get('VuFind\Cover\Generator')); + } + return $loader; + } +} diff --git a/module/VuFind/src/VuFind/Cover/Router.php b/module/VuFind/src/VuFind/Cover/Router.php index b9f7293a66a2e8964a1edcb1e24a22d6de9a7880..fcb53a3cb9b61ae9653569a020fa9d9da7805ba1 100644 --- a/module/VuFind/src/VuFind/Cover/Router.php +++ b/module/VuFind/src/VuFind/Cover/Router.php @@ -2,7 +2,7 @@ /** * Cover image router * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/configuration:external_content Wiki */ namespace VuFind\Cover; + use VuFind\RecordDriver\AbstractBase as RecordDriver; /** diff --git a/module/VuFind/src/VuFind/Cover/RouterFactory.php b/module/VuFind/src/VuFind/Cover/RouterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..53d9d8351cc006dfe3dcc307d105f4279dd4d831 --- /dev/null +++ b/module/VuFind/src/VuFind/Cover/RouterFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Cover router factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Cover; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cover router factory. + * + * @category VuFind + * @package Cover_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RouterFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $base = $container->get('ControllerPluginManager')->get('url') + ->fromRoute('cover-show'); + return new $requestedName($base); + } +} diff --git a/module/VuFind/src/VuFind/Crypt/HMAC.php b/module/VuFind/src/VuFind/Crypt/HMAC.php index 13936143f8c606faf2d47078c6392ce05ec03d83..fc9878684983079ff899a87cfa817096b2c6ed9b 100644 --- a/module/VuFind/src/VuFind/Crypt/HMAC.php +++ b/module/VuFind/src/VuFind/Crypt/HMAC.php @@ -2,7 +2,7 @@ /** * HMAC hash generator * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -67,7 +67,7 @@ class HMAC { $str = ''; foreach ($keysToHash as $key) { - $value = isset($keyValueArray[$key]) ? $keyValueArray[$key] : ''; + $value = $keyValueArray[$key] ?? ''; $str .= $key . '=' . $value . '|'; } return hash_hmac('md5', $str, $this->hashKey); diff --git a/module/VuFind/src/VuFind/Crypt/HMACFactory.php b/module/VuFind/src/VuFind/Crypt/HMACFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f645ebb9d8934fa49afde3e70c7d0d995a564d1f --- /dev/null +++ b/module/VuFind/src/VuFind/Crypt/HMACFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Plugin Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Crypt + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Crypt; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Plugin Manager factory. + * + * @category VuFind + * @package Crypt + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HMACFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config->Security->HMACkey); + } +} diff --git a/module/VuFind/src/VuFind/Crypt/RC4.php b/module/VuFind/src/VuFind/Crypt/RC4.php deleted file mode 100644 index bf500bb54abebc154ee347ab09f65fea2f606118..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Crypt/RC4.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -/** - * RC4 encryption class (wrapper around code borrowed from a third-party - * developer -- see embedded copyright information on encrypt method) - * - * PHP version 5 - * - * Copyright (C) Villanova University 2007. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Crypt - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Page - */ -namespace VuFind\Crypt; - -/** - * RC4 encryption class (wrapper around code borrowed from a third-party - * developer -- see embedded copyright information on encrypt method) - * - * @category VuFind - * @package Crypt - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Page - */ -class RC4 -{ - /** - * Encrypt given plain text using the key with RC4 algorithm. - * All parameters and return value are in binary format. - * - * @param string $key secret key for encryption - * @param string $pt plain text to be encrypted - * - * @return string - */ - public static function encrypt($key, $pt) - { - /* RC4 symmetric cipher encryption/decryption - * Copyright (c) 2006 by Ali Farhadi. - * released under the terms of the Gnu Public License. - * see the GPL for details. - * - * Email: ali[at]farhadi[dot]ir - * Website: http://farhadi.ir/ - */ - $s = []; - for ($i = 0; $i < 256; $i++) { - $s[$i] = $i; - } - $j = 0; - $x; - for ($i = 0; $i < 256; $i++) { - $j = ($j + $s[$i] + ord($key[$i % strlen($key)])) % 256; - $x = $s[$i]; - $s[$i] = $s[$j]; - $s[$j] = $x; - } - $i = 0; - $j = 0; - $ct = ''; - $y; - for ($y = 0; $y < strlen($pt); $y++) { - $i = ($i + 1) % 256; - $j = ($j + $s[$i]) % 256; - $x = $s[$i]; - $s[$i] = $s[$j]; - $s[$j] = $x; - $ct .= $pt[$y] ^ chr($s[($s[$i] + $s[$j]) % 256]); - } - return $ct; - } - - /** - * Decrypt given cipher text using the key with RC4 algorithm. - * All parameters and return value are in binary format. - * - * @param string $key secret key for decryption - * @param string $ct cipher text to be decrypted - * - * @return string - */ - public static function decrypt($key, $ct) - { - return static::encrypt($key, $ct); - } -} diff --git a/module/VuFind/src/VuFind/Date/Converter.php b/module/VuFind/src/VuFind/Date/Converter.php deleted file mode 100644 index dbd01ddb97930e75aabe37f75333ed59d1d690f9..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Date/Converter.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php -/** - * Date/time conversion functionality. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2011. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Date - * @author Demian Katz <demian.katz@villanova.edu> - * @author Luke O'Sullivan <l.osullivan@swansea.ac.uk> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Date; -use DateTime, DateTimeZone, VuFind\Exception\Date as DateException; - -/** - * Date/time conversion functionality. - * - * @category VuFind - * @package Date - * @author Demian Katz <demian.katz@villanova.edu> - * @author Luke O'Sullivan <l.osullivan@swansea.ac.uk> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -class Converter -{ - /** - * Format string for dates - * - * @var string - */ - protected $displayDateFormat; - - /** - * Format string for times - * - * @var string - */ - protected $displayTimeFormat; - - /** - * Time zone to use for conversions - * - * @var DateTimeZone - */ - protected $timezone; - - /** - * Constructor - * - * @param \Zend\Config\Config $config Configuration to use (set to null to use - * defaults) - */ - public function __construct($config = null) - { - // Set Display Date Format - $this->displayDateFormat - = isset($config->Site->displayDateFormat) - ? $config->Site->displayDateFormat : "m-d-Y"; - - // Set Display Date Format - $this->displayTimeFormat - = isset($config->Site->displayTimeFormat) - ? $config->Site->displayTimeFormat : "H:i"; - - // Set time zone - $zone = isset($config->Site->timezone) - ? $config->Site->timezone : 'America/New_York'; - $this->timezone = new DateTimeZone($zone); - } - - /** - * Generic method for conversion of a time / date string - * - * @param string $inputFormat The format of the time string to be changed - * @param string $outputFormat The desired output format - * @param string $dateString The date string - * - * @throws DateException - * @return string A re-formatted time string - */ - public function convert($inputFormat, $outputFormat, $dateString) - { - // These are date formats that we definitely know how to handle, and some - // benefit from special processing. However, items not found in this list - // will still be attempted in a generic fashion before giving up. - $validFormats = [ - "m-d-Y", "m-d-y", "m/d/Y", "m/d/y", "U", "m-d-y H:i", "Y-m-d", - "Y-m-d H:i" - ]; - $isValid = in_array($inputFormat, $validFormats); - if ($isValid) { - if ($inputFormat == 'U') { - // Special case for Unix timestamps (including workaround for - // floating point numbers): - $dateString = '@' - . (is_float($dateString) ? intval($dateString) : $dateString); - } else { - // Strip leading zeroes from date string and normalize date separator - // to slashes: - $regEx = '/0*([0-9]+)(-|\/)0*([0-9]+)(-|\/)0*([0-9]+)/'; - $dateString = trim(preg_replace($regEx, '$1/$3/$5', $dateString)); - } - $errors = [ - 'warning_count' => 0, 'error_count' => 0, 'errors' => [] - ]; - try { - $date = new DateTime($dateString, $this->timezone); - } catch (\Exception $e) { - $errors['error_count']++; - $errors['errors'][] = $e->getMessage(); - } - } else { - $date = DateTime::createFromFormat( - $inputFormat, $dateString, $this->timezone - ); - $errors = DateTime::getLastErrors(); - } - - if ($errors['warning_count'] == 0 && $errors['error_count'] == 0 && $date) { - $date->setTimeZone($this->timezone); - return $date->format($outputFormat); - } - throw new DateException($this->getDateExceptionMessage($errors)); - } - - /** - * Build an exception message from a detailed error array. - * - * @param array $details Error details - * - * @return string - */ - protected function getDateExceptionMessage($details) - { - $errors = "Date/time problem: Details: "; - if (is_array($details['errors']) && $details['error_count'] > 0) { - foreach ($details['errors'] as $error) { - $errors .= $error . " "; - } - } else if (is_array($details['warnings'])) { - foreach ($details['warnings'] as $warning) { - $errors .= $warning . " "; - } - } - return $errors; - } - - /** - * Convert a date string to admin-defined format. - * - * @param string $createFormat The format of the date string to be changed - * @param string $dateString The date string - * - * @throws DateException - * @return string A re-formatted date string - */ - public function convertToDisplayDate($createFormat, $dateString) - { - return $this->convert($createFormat, $this->displayDateFormat, $dateString); - } - - /** - * Public method for conversion of an admin defined date string - * to a driver required date string - * - * @param string $outputFormat The format of the required date string - * @param string $displayDate The display formatted date string - * - * @throws DateException - * @return string A re-formatted date string - */ - public function convertFromDisplayDate($outputFormat, $displayDate) - { - return $this->convert( - $this->displayDateFormat, $outputFormat, $displayDate - ); - } - - /** - * Public support method for conversion of a time string to admin defined - * time string. - * - * @param string $createFormat The format of the time string to be changed - * @param string $timeString The time string - * - * @throws DateException - * @return string A re-formatted time string - */ - public function convertToDisplayTime($createFormat, $timeString) - { - return $this->convert($createFormat, $this->displayTimeFormat, $timeString); - } - - /** - * Public method for getting a date prepended to a time. - * - * @param string $createFormat The format of the time string to be changed - * @param string $timeString The time string - * @param string $separator String between time/date - * - * @throws DateException - * @return string A re-formatted time string - */ - public function convertToDisplayDateAndTime($createFormat, $timeString, - $separator = ' ' - ) { - return $this->convertToDisplayDate($createFormat, $timeString) - . $separator . $this->convertToDisplayTime($createFormat, $timeString); - } - - /** - * Public method for getting a time prepended to a date. - * - * @param string $createFormat The format of the time string to be changed - * @param string $timeString The time string - * @param string $separator String between time/date - * - * @throws DateException - * @return string A re-formatted time string - */ - public function convertToDisplayTimeAndDate($createFormat, $timeString, - $separator = ' ' - ) { - return $this->convertToDisplayTime($createFormat, $timeString) - . $separator . $this->convertToDisplayDate($createFormat, $timeString); - } -} diff --git a/module/VuFind/src/VuFind/Db/AdapterFactory.php b/module/VuFind/src/VuFind/Db/AdapterFactory.php index 22c07fd2ecb3676a8659f9bb76a8fd8f2a4b4f46..71f74665ced916c7f2d1616464327df58a4a91dd 100644 --- a/module/VuFind/src/VuFind/Db/AdapterFactory.php +++ b/module/VuFind/src/VuFind/Db/AdapterFactory.php @@ -2,7 +2,7 @@ /** * Database utility class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db; + use Zend\Db\Adapter\Adapter; /** @@ -145,7 +146,7 @@ class AdapterFactory ) { list($type, $details) = explode('://', $connectionString); preg_match('/(.+)@([^@]+)\/(.+)/', $details, $matches); - $credentials = isset($matches[1]) ? $matches[1] : null; + $credentials = $matches[1] ?? null; if (isset($matches[2])) { if (strpos($matches[2], ':') !== false) { list($host, $port) = explode(':', $matches[2]); @@ -153,20 +154,20 @@ class AdapterFactory $host = $matches[2]; } } - $dbName = isset($matches[3]) ? $matches[3] : null; + $dbName = $matches[3] ?? null; if (strstr($credentials, ':')) { list($username, $password) = explode(':', $credentials, 2); } else { $username = $credentials; $password = null; } - $username = !is_null($overrideUser) ? $overrideUser : $username; - $password = !is_null($overridePass) ? $overridePass : $password; + $username = null !== $overrideUser ? $overrideUser : $username; + $password = null !== $overridePass ? $overridePass : $password; // Set up default options: $options = [ 'driver' => $this->getDriverName($type), - 'hostname' => isset($host) ? $host : null, + 'hostname' => $host ?? null, 'username' => $username, 'password' => $password, 'database' => $dbName diff --git a/module/VuFind/src/VuFind/Db/Row/ChangeTracker.php b/module/VuFind/src/VuFind/Db/Row/ChangeTracker.php index 4bcc1e1898ab6ccbc9a9e764682740bbaa1af26c..06d08874197b9bfc34bbdacebc42ba68750bfb71 100644 --- a/module/VuFind/src/VuFind/Db/Row/ChangeTracker.php +++ b/module/VuFind/src/VuFind/Db/Row/ChangeTracker.php @@ -2,7 +2,7 @@ /** * Row Definition for change_tracker * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/Comments.php b/module/VuFind/src/VuFind/Db/Row/Comments.php index 97ebc8055951b2723abb904ecfd75e634b6f165b..fb21cfc7f7d9443edecf56bc7d0eb0c6dd4203a3 100644 --- a/module/VuFind/src/VuFind/Db/Row/Comments.php +++ b/module/VuFind/src/VuFind/Db/Row/Comments.php @@ -2,7 +2,7 @@ /** * Row Definition for comments * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/ExternalSession.php b/module/VuFind/src/VuFind/Db/Row/ExternalSession.php index 07452cfbe24f525d9c3ea98749c11a4be7421760..41cf4e613e2b7fed7288caeb7c9c5010547860b6 100644 --- a/module/VuFind/src/VuFind/Db/Row/ExternalSession.php +++ b/module/VuFind/src/VuFind/Db/Row/ExternalSession.php @@ -2,7 +2,7 @@ /** * Row Definition for external_session * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyrught (C) The National Library of Finland 2016. diff --git a/module/VuFind/src/VuFind/Db/Row/Factory.php b/module/VuFind/src/VuFind/Db/Row/Factory.php deleted file mode 100644 index 7ec15d670d32b0cb5abe5516cb107f441096aaf8..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Db/Row/Factory.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php -/** - * Factory for DB rows. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Db_Table - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Db\Row; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for DB tables. - * - * @category VuFind - * @package Db_Table - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct a generic row object. - * - * @param string $name Name of row to construct (fully qualified - * class name, or else a class name within the current namespace) - * @param ServiceManager $sm Service manager - * @param array $args Extra constructor arguments for row object - * - * @return object - */ - public static function getGenericRow($name, ServiceManager $sm, $args = []) - { - // Prepend the current namespace unless we receive a FQCN: - $class = (strpos($name, '\\') === false) - ? __NAMESPACE__ . '\\' . $name : $name; - if (!class_exists($class)) { - throw new \Exception('Cannot construct ' . $class); - } - $adapter = $sm->getServiceLocator()->get('VuFind\DbAdapter'); - return new $class($adapter, ...$args); - } - - /** - * Default factory behavior. - * - * @param string $name Method name being called - * @param array $args Method arguments - * - * @return object - */ - public static function __callStatic($name, $args) - { - // Strip "get" off method name, and use the remainder as the table name; - // grab the first argument to pass through as the service manager. - return static::getGenericRow(substr($name, 3), array_shift($args)); - } - - /** - * Construct the User row prototype. - * - * @param ServiceManager $sm Service manager. - * - * @return User - */ - public static function getUser(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $privacy = isset($config->Authentication->privacy) - && $config->Authentication->privacy; - $rowClass = $privacy ? 'PrivateUser' : 'User'; - $prototype = static::getGenericRow($rowClass, $sm); - $prototype->setConfig($config); - if ($privacy) { - $sessionManager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - $session = new \Zend\Session\Container('Account', $sessionManager); - $prototype->setSession($session); - } - return $prototype; - } - - /** - * Construct the UserList row prototype. - * - * @param ServiceManager $sm Service manager. - * - * @return UserList - */ - public static function getUserList(ServiceManager $sm) - { - $sessionManager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - $session = new \Zend\Session\Container('List', $sessionManager); - return static::getGenericRow('UserList', $sm, [$session]); - } -} diff --git a/module/VuFind/src/VuFind/Db/Row/OaiResumption.php b/module/VuFind/src/VuFind/Db/Row/OaiResumption.php index 7fac85d9ece6c8a643a2e5b25e4619ad79043b9f..95513e51607a7f5e8760aa02b21e4d7775c3381e 100644 --- a/module/VuFind/src/VuFind/Db/Row/OaiResumption.php +++ b/module/VuFind/src/VuFind/Db/Row/OaiResumption.php @@ -2,7 +2,7 @@ /** * Row Definition for oai_resumption * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/PluginManager.php b/module/VuFind/src/VuFind/Db/Row/PluginManager.php index b3e8b0da52b5bb6a96ad4cbe3b6f82518c65c425..c5cc9009a52fdba72a5be7ee5356c386130f0611 100644 --- a/module/VuFind/src/VuFind/Db/Row/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Row/PluginManager.php @@ -2,7 +2,7 @@ /** * Database row plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -38,6 +38,50 @@ namespace VuFind\Db\Row; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'changetracker' => 'VuFind\Db\Row\ChangeTracker', + 'comments' => 'VuFind\Db\Row\Comments', + 'externalsession' => 'VuFind\Db\Row\ExternalSession', + 'oairesumption' => 'VuFind\Db\Row\OaiResumption', + 'record' => 'VuFind\Db\Row\Record', + 'resource' => 'VuFind\Db\Row\Resource', + 'resourcetags' => 'VuFind\Db\Row\ResourceTags', + 'search' => 'VuFind\Db\Row\Search', + 'session' => 'VuFind\Db\Row\Session', + 'tags' => 'VuFind\Db\Row\Tags', + 'user' => 'VuFind\Db\Row\User', + 'usercard' => 'VuFind\Db\Row\UserCard', + 'userlist' => 'VuFind\Db\Row\UserList', + 'userresource' => 'VuFind\Db\Row\UserResource', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Db\Row\ChangeTracker' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\Comments' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\ExternalSession' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\OaiResumption' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\Record' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\Resource' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\ResourceTags' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\Search' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\Session' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\Tags' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\User' => 'VuFind\Db\Row\UserFactory', + 'VuFind\Db\Row\UserCard' => 'VuFind\Db\Row\RowGatewayFactory', + 'VuFind\Db\Row\UserList' => 'VuFind\Db\Row\UserListFactory', + 'VuFind\Db\Row\UserResource' => 'VuFind\Db\Row\RowGatewayFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Db/Row/PrivateUser.php b/module/VuFind/src/VuFind/Db/Row/PrivateUser.php index 9c2a634b38f94e5c0c789fe7cdec5583a26b490a..6fe181ff19914291586e39c8289b1ee2f13f940e 100644 --- a/module/VuFind/src/VuFind/Db/Row/PrivateUser.php +++ b/module/VuFind/src/VuFind/Db/Row/PrivateUser.php @@ -2,7 +2,7 @@ /** * Fake database row to represent a user in privacy mode. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * diff --git a/module/VuFind/src/VuFind/Db/Row/Record.php b/module/VuFind/src/VuFind/Db/Row/Record.php index a4b91fde65a1cfd804fb100ad307dc1af0604431..813723a60fcc44f6afd502382a2b6103593a5a28 100644 --- a/module/VuFind/src/VuFind/Db/Row/Record.php +++ b/module/VuFind/src/VuFind/Db/Row/Record.php @@ -2,7 +2,7 @@ /** * Row Definition for record * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/Resource.php b/module/VuFind/src/VuFind/Db/Row/Resource.php index 8d936d65ca43d4a52c256ace3e632e6f26774d4c..e2ecfd618b77628f4a73eb978fdb2d85a11b9d71 100644 --- a/module/VuFind/src/VuFind/Db/Row/Resource.php +++ b/module/VuFind/src/VuFind/Db/Row/Resource.php @@ -2,7 +2,7 @@ /** * Row Definition for resource * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Row; -use VuFind\Date\Converter as DateConverter, - VuFind\Exception\Date as DateException, - VuFind\Exception\LoginRequired as LoginRequiredException; + +use VuFind\Date\DateException; +use VuFind\Exception\LoginRequired as LoginRequiredException; /** * Row Definition for resource @@ -190,7 +190,7 @@ class Resource extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterf $year = ''; } } else { - $year = isset($dates[0]) ? $dates[0] : ''; + $year = $dates[0] ?? ''; } if (!empty($year)) { $this->year = intval($year); diff --git a/module/VuFind/src/VuFind/Db/Row/ResourceTags.php b/module/VuFind/src/VuFind/Db/Row/ResourceTags.php index c0693cb47a9c3f79ea82c7b73688df03cbb44667..8e7599ec43b5e3269f8cd551dc298d3f6a71875d 100644 --- a/module/VuFind/src/VuFind/Db/Row/ResourceTags.php +++ b/module/VuFind/src/VuFind/Db/Row/ResourceTags.php @@ -2,7 +2,7 @@ /** * Row Definition for resource_tags * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/RowGateway.php b/module/VuFind/src/VuFind/Db/Row/RowGateway.php index dc66a6a24b7ac8960ba73e649e94af92bb33fcab..5dfb63b3d92c12754e2147409b10ebc73083ef39 100644 --- a/module/VuFind/src/VuFind/Db/Row/RowGateway.php +++ b/module/VuFind/src/VuFind/Db/Row/RowGateway.php @@ -2,7 +2,7 @@ /** * Abstract base class for DB rows. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php b/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..6cbff5b00bc80851226d6f65c57205f8dbcb97be --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php @@ -0,0 +1,63 @@ +<?php +/** + * Generic row gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Row; + +use Interop\Container\ContainerInterface; + +/** + * Generic row gateway factory. + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RowGatewayFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $adapter = $container->get('Zend\Db\Adapter\Adapter'); + return new $requestedName($adapter, ...($options !== null ? $options : [])); + } +} diff --git a/module/VuFind/src/VuFind/Db/Row/Search.php b/module/VuFind/src/VuFind/Db/Row/Search.php index 51c43a4c0bcd9ed88da1f1920c5844bd5fdc1f65..f349b7ffad63864018898fc603e2f4a0dc42d72e 100644 --- a/module/VuFind/src/VuFind/Db/Row/Search.php +++ b/module/VuFind/src/VuFind/Db/Row/Search.php @@ -2,7 +2,7 @@ /** * Row Definition for search * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/Session.php b/module/VuFind/src/VuFind/Db/Row/Session.php index 1fb9a4c39bc1c40a884c993eb0a8991d333ec466..6fac690f2f985d619bf313be52cdd40369c2ebb1 100644 --- a/module/VuFind/src/VuFind/Db/Row/Session.php +++ b/module/VuFind/src/VuFind/Db/Row/Session.php @@ -2,7 +2,7 @@ /** * Row Definition for session * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Row/Tags.php b/module/VuFind/src/VuFind/Db/Row/Tags.php index 4fb70da2cc00e5162d3ec37d274148b1a23412fa..34151fc9e912dfa9826fc85bcd27fa15220b21d2 100644 --- a/module/VuFind/src/VuFind/Db/Row/Tags.php +++ b/module/VuFind/src/VuFind/Db/Row/Tags.php @@ -2,7 +2,7 @@ /** * Row Definition for tags * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Row; -use VuFind\Db\Table\Resource as ResourceTable, Zend\Db\Sql\Expression; + +use VuFind\Db\Table\Resource as ResourceTable; +use Zend\Db\Sql\Expression; /** * Row Definition for tags diff --git a/module/VuFind/src/VuFind/Db/Row/User.php b/module/VuFind/src/VuFind/Db/Row/User.php index 446722e3ec9fbb6dd160b7df955bcc747a6b5ac9..a0de0e5de28d5af9f9d3248de6429f313d4debda 100644 --- a/module/VuFind/src/VuFind/Db/Row/User.php +++ b/module/VuFind/src/VuFind/Db/Row/User.php @@ -2,7 +2,7 @@ /** * Row Definition for user * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,12 +26,10 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Row; -use Zend\Db\Sql\Expression, - Zend\Db\Sql\Predicate\Predicate, - Zend\Db\Sql\Sql, - Zend\Crypt\Symmetric\Openssl, - Zend\Crypt\Password\Bcrypt, - Zend\Crypt\BlockCipher as BlockCipher; + +use Zend\Crypt\BlockCipher as BlockCipher; +use Zend\Crypt\Symmetric\Openssl; +use Zend\Db\Sql\Expression; /** * Row Definition for user @@ -239,12 +237,12 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, * @param int $listId Filter for tags tied to a specific list (null for no * filter). * @param string $source Filter for tags tied to a specific record source. + * (null for no filter). * * @return \Zend\Db\ResultSet\AbstractResultSet */ - public function getTags($resourceId = null, $listId = null, - $source = DEFAULT_SEARCH_BACKEND - ) { + public function getTags($resourceId = null, $listId = null, $source = null) + { return $this->getDbTable('Tags') ->getForUser($this->id, $resourceId, $listId, $source); } @@ -257,13 +255,13 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, * for no filter). * @param int $listId Filter for tags tied to a specific list (null for no * filter). - * @param string $source Filter for tags tied to a specific record source. + * @param string $source Filter for tags tied to a specific record source + * (null for no filter). * * @return string */ - public function getTagString($resourceId = null, $listId = null, - $source = DEFAULT_SEARCH_BACKEND - ) { + public function getTagString($resourceId = null, $listId = null, $source = null) + { $myTagList = $this->getTags($resourceId, $listId, $source); $tagStr = ''; if (count($myTagList) > 0) { @@ -613,9 +611,11 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, /** * Destroy the user. * + * @param bool $removeComments Whether to remove user's comments + * * @return int The number of rows deleted. */ - public function delete() + public function delete($removeComments = true) { // Remove all lists owned by the user: $lists = $this->getLists(); @@ -628,6 +628,10 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, } $resourceTags = $this->getDbTable('ResourceTags'); $resourceTags->destroyLinks(null, $this->id); + if ($removeComments) { + $comments = $this->getDbTable('Comments'); + $comments->deleteByUser($this); + } // Remove the user itself: return parent::delete(); @@ -642,7 +646,7 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, { $hash = md5($this->username . $this->password . $this->pass_hash . rand()); // Make totally sure the timestamp is exactly 10 characters: - $time = str_pad(substr((string) time(), 0, 10), 10, '0', STR_PAD_LEFT); + $time = str_pad(substr((string)time(), 0, 10), 10, '0', STR_PAD_LEFT); $this->verify_hash = $hash . $time; return $this->save(); } diff --git a/module/VuFind/src/VuFind/Db/Row/UserCard.php b/module/VuFind/src/VuFind/Db/Row/UserCard.php index 56c4daff2afaa378676b407ffcc2013a6d6a4f5e..bbdff2cfeb2ccc865712b9e622ca9e94d17ddb5e 100644 --- a/module/VuFind/src/VuFind/Db/Row/UserCard.php +++ b/module/VuFind/src/VuFind/Db/Row/UserCard.php @@ -2,7 +2,7 @@ /** * Row Definition for user_card * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * diff --git a/module/VuFind/src/VuFind/Db/Row/UserFactory.php b/module/VuFind/src/VuFind/Db/Row/UserFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..eb74dcd025e4f1e29d2bf5c5a5d276b01a456004 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Row/UserFactory.php @@ -0,0 +1,83 @@ +<?php +/** + * User row gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Row; + +use Interop\Container\ContainerInterface; + +/** + * User row gateway factory. + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserFactory extends RowGatewayFactory +{ + /** + * Class name for private user class. + * + * @var string + */ + protected $privateUserClass = __NAMESPACE__ . '\PrivateUser'; + + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $privacy = isset($config->Authentication->privacy) + && $config->Authentication->privacy; + $rowClass = $privacy ? $this->privateUserClass : $requestedName; + $prototype = parent::__invoke($container, $rowClass, $options); + $prototype->setConfig($config); + if ($privacy) { + $sessionManager = $container->get('Zend\Session\SessionManager'); + $session = new \Zend\Session\Container('Account', $sessionManager); + $prototype->setSession($session); + } + return $prototype; + } +} diff --git a/module/VuFind/src/VuFind/Db/Row/UserList.php b/module/VuFind/src/VuFind/Db/Row/UserList.php index 89384d0a2020cfc7b71aeaab8d6f451e423af141..0f94a88f19ba42773f59a1bd3f2815f6453f7b08 100644 --- a/module/VuFind/src/VuFind/Db/Row/UserList.php +++ b/module/VuFind/src/VuFind/Db/Row/UserList.php @@ -2,7 +2,7 @@ /** * Row Definition for user_list * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Row; + use VuFind\Exception\ListPermission as ListPermissionException; use VuFind\Exception\MissingField as MissingFieldException; use Zend\Session\Container; diff --git a/module/VuFind/src/VuFind/Db/Row/UserListFactory.php b/module/VuFind/src/VuFind/Db/Row/UserListFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d0a8c1f2fd529043e550720e007b94314c0f4412 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Row/UserListFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * UserList row gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Row; + +use Interop\Container\ContainerInterface; + +/** + * UserList row gateway factory. + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserListFactory extends RowGatewayFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $sessionManager = $container->get('Zend\Session\SessionManager'); + $session = new \Zend\Session\Container('List', $sessionManager); + return parent::__invoke($container, $requestedName, [$session]); + } +} diff --git a/module/VuFind/src/VuFind/Db/Row/UserResource.php b/module/VuFind/src/VuFind/Db/Row/UserResource.php index 03546b0d44b31e554370d83aaa7a124d5e39c1e9..0a2916a1a448856bce576c970ab40e83e31476a9 100644 --- a/module/VuFind/src/VuFind/Db/Row/UserResource.php +++ b/module/VuFind/src/VuFind/Db/Row/UserResource.php @@ -2,7 +2,7 @@ /** * Row Definition for user_resource * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php b/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8c3e790d6fbb56eaa74fb9cc4afbdeeef42aadb7 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Shared Tags / ResourceTags table gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Table; + +use Interop\Container\ContainerInterface; + +/** + * Shared Tags / ResourceTags table gateway factory. + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CaseSensitiveTagsFactory extends GatewayFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $caseSensitive = isset($config->Social->case_sensitive_tags) + && $config->Social->case_sensitive_tags; + return parent::__invoke($container, $requestedName, [$caseSensitive]); + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php b/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php index 174cc7d6747e1a62d6c51f03df65d34197be30d6..c7b3e225cd34ba94d4d3007e9013cda9cbb61014 100644 --- a/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php +++ b/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php @@ -2,7 +2,7 @@ /** * Table Definition for change_tracker * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; diff --git a/module/VuFind/src/VuFind/Db/Table/Comments.php b/module/VuFind/src/VuFind/Db/Table/Comments.php index b3afb81a5fd5fd004f1f1e46b65c520ad730028b..d3fea98739b422d2a317b8a92992f1f4ae5bcf19 100644 --- a/module/VuFind/src/VuFind/Db/Table/Comments.php +++ b/module/VuFind/src/VuFind/Db/Table/Comments.php @@ -2,7 +2,7 @@ /** * Table Definition for comments * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2012. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; use Zend\Db\Sql\Expression; @@ -76,9 +77,10 @@ class Comments extends Gateway $select->columns(['*']); $select->join( ['u' => 'user'], 'u.id = comments.user_id', - ['firstname', 'lastname'] + ['firstname', 'lastname'], + $select::JOIN_LEFT ); - $select->where->equalTo('comments.resource_id', $resource->id); + $select->where->equalTo('comments.resource_id', $resource->id); $select->order('comments.created'); }; @@ -116,6 +118,18 @@ class Comments extends Gateway return true; } + /** + * Deletes all comments by a user. + * + * @param \VuFind\Db\Row\User $user User object + * + * @return void + */ + public function deleteByUser($user) + { + $this->delete(['user_id' => $user->id]); + } + /** * Get statistics on use of comments. * diff --git a/module/VuFind/src/VuFind/Db/Table/DbTableAwareInterface.php b/module/VuFind/src/VuFind/Db/Table/DbTableAwareInterface.php index 098e0af852aead2d07fb9ceffd3b0fd372e8785b..4941c49cea03e9e33411db917534b965140cdbaf 100644 --- a/module/VuFind/src/VuFind/Db/Table/DbTableAwareInterface.php +++ b/module/VuFind/src/VuFind/Db/Table/DbTableAwareInterface.php @@ -2,7 +2,7 @@ /** * Marker interface for classes that depend on the \VuFind\Db\Table\PluginManager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Table/DbTableAwareTrait.php b/module/VuFind/src/VuFind/Db/Table/DbTableAwareTrait.php index 14e9d1f5d17024267fabf83d073a97d912b50502..b8f8ee5cb1025d77b2fa402eee5c5fac2af8dad4 100644 --- a/module/VuFind/src/VuFind/Db/Table/DbTableAwareTrait.php +++ b/module/VuFind/src/VuFind/Db/Table/DbTableAwareTrait.php @@ -2,7 +2,7 @@ /** * Default implementation of DbTableAwareInterface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * diff --git a/module/VuFind/src/VuFind/Db/Table/ExpirationTrait.php b/module/VuFind/src/VuFind/Db/Table/ExpirationTrait.php index 87dc27ad61fe8ec76a5c845d46eeb0c837bc598e..45d6d38c4687f434ef50feb4a241b663c8e6bc7f 100644 --- a/module/VuFind/src/VuFind/Db/Table/ExpirationTrait.php +++ b/module/VuFind/src/VuFind/Db/Table/ExpirationTrait.php @@ -2,7 +2,7 @@ /** * Trait for tables that support expiration * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. @@ -28,6 +28,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use Zend\Db\Sql\Expression; use Zend\Db\Sql\Select; diff --git a/module/VuFind/src/VuFind/Db/Table/ExternalSession.php b/module/VuFind/src/VuFind/Db/Table/ExternalSession.php index 6f7a738dc546fedaadba8a0e0155fb7d1fe9fa21..74ad485f677578d070f5520a46d6f7984420ab9a 100644 --- a/module/VuFind/src/VuFind/Db/Table/ExternalSession.php +++ b/module/VuFind/src/VuFind/Db/Table/ExternalSession.php @@ -2,7 +2,7 @@ /** * Table Definition for external_session * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. @@ -28,6 +28,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; diff --git a/module/VuFind/src/VuFind/Db/Table/Factory.php b/module/VuFind/src/VuFind/Db/Table/Factory.php deleted file mode 100644 index e5868f949692d37ce8ac251e80a7381ff81f273f..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Db/Table/Factory.php +++ /dev/null @@ -1,188 +0,0 @@ -<?php -/** - * Factory for DB tables. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Db_Table - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Db\Table; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for DB tables. - * - * @category VuFind - * @package Db_Table - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Return row prototype object (null if unavailable) - * - * @param ServiceManager $sm Service manager - * @param string $name Name of row prototype to retrieve - * - * @return object - */ - public static function getRowPrototype(ServiceManager $sm, $name) - { - if ($name) { - $rowManager = $sm->getServiceLocator()->get('VuFind\DbRowPluginManager'); - return $rowManager->has($name) ? $rowManager->get($name) : null; - } - return null; - } - - /** - * Construct a generic table object. - * - * @param string $name Name of table to construct (fully qualified - * class name, or else a class name within the current namespace) - * @param ServiceManager $sm Service manager - * @param string $rowName Name of custom row prototype object to - * retrieve (null for none). - * @param array $args Extra constructor arguments for table object - * - * @return object - */ - public static function getGenericTable($name, ServiceManager $sm, - $rowName = null, $args = [] - ) { - // Prepend the current namespace unless we receive a FQCN: - $class = (strpos($name, '\\') === false) - ? __NAMESPACE__ . '\\' . $name : $name; - if (!class_exists($class)) { - throw new \Exception('Cannot construct ' . $class); - } - $adapter = $sm->getServiceLocator()->get('VuFind\DbAdapter'); - $config = $sm->getServiceLocator()->get('config'); - return new $class( - $adapter, $sm, $config, static::getRowPrototype($sm, $rowName), ...$args - ); - } - - /** - * Default factory behavior. - * - * @param string $name Method name being called - * @param array $args Method arguments - * - * @return object - */ - public static function __callStatic($name, $args) - { - // Strip "get" off method name, and use the remainder as the table name; - // grab the first argument to pass through as the service manager. - $dbName = substr($name, 3); - return static::getGenericTable( - $dbName, array_shift($args), strtolower($dbName) - ); - } - - /** - * Construct the Resource table. - * - * @param ServiceManager $sm Service manager. - * - * @return Resource - */ - public static function getResource(ServiceManager $sm) - { - $converter = $sm->getServiceLocator()->get('VuFind\DateConverter'); - $loader = $sm->getServiceLocator()->get('VuFind\RecordLoader'); - return static::getGenericTable( - 'Resource', $sm, 'resource', [$converter, $loader] - ); - } - - /** - * Construct the ResourceTags table. - * - * @param ServiceManager $sm Service manager. - * - * @return User - */ - public static function getResourceTags(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $caseSensitive = isset($config->Social->case_sensitive_tags) - && $config->Social->case_sensitive_tags; - return static::getGenericTable( - 'ResourceTags', $sm, 'resourcetags', [$caseSensitive] - ); - } - - /** - * Construct the Tags table. - * - * @param ServiceManager $sm Service manager. - * - * @return User - */ - public static function getTags(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $caseSensitive = isset($config->Social->case_sensitive_tags) - && $config->Social->case_sensitive_tags; - return static::getGenericTable('Tags', $sm, 'tags', [$caseSensitive]); - } - - /** - * Construct the User table. - * - * @param ServiceManager $sm Service manager. - * - * @return User - */ - public static function getUser(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $privacy = isset($config->Authentication->privacy) - && $config->Authentication->privacy; - $session = null; - if ($privacy) { - $sessionManager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - $session = new \Zend\Session\Container('Account', $sessionManager); - } - return static::getGenericTable('User', $sm, 'user', [$config, $session]); - } - - /** - * Construct the UserList table. - * - * @param ServiceManager $sm Service manager. - * - * @return UserList - */ - public static function getUserList(ServiceManager $sm) - { - $sessionManager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - $session = new \Zend\Session\Container('List', $sessionManager); - return static::getGenericTable('UserList', $sm, 'userlist', [$session]); - } -} diff --git a/module/VuFind/src/VuFind/Db/Table/Gateway.php b/module/VuFind/src/VuFind/Db/Table/Gateway.php index 773339fbcbc627de9d63a7e881067f1d7cfd4e85..402327cf7185ea47361cbd50e7c9983b44bb1614 100644 --- a/module/VuFind/src/VuFind/Db/Table/Gateway.php +++ b/module/VuFind/src/VuFind/Db/Table/Gateway.php @@ -2,7 +2,7 @@ /** * Generic VuFind table gateway. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; use Zend\Db\TableGateway\AbstractTableGateway; @@ -85,8 +86,7 @@ class Gateway extends AbstractTableGateway { // Special case for PostgreSQL sequences: if ($this->adapter->getDriver()->getDatabasePlatformName() == "Postgresql") { - $maps = isset($cfg['vufind']['pgsql_seq_mapping']) - ? $cfg['vufind']['pgsql_seq_mapping'] : null; + $maps = $cfg['vufind']['pgsql_seq_mapping'] ?? null; if (isset($maps[$this->table])) { if (!is_object($this->featureSet)) { $this->featureSet = new Feature\FeatureSet(); @@ -107,7 +107,7 @@ class Gateway extends AbstractTableGateway */ public function createRow() { - $obj = clone($this->getResultSetPrototype()->getArrayObjectPrototype()); + $obj = clone $this->getResultSetPrototype()->getArrayObjectPrototype(); // If this is a PostgreSQL connection, we may need to initialize the ID // from a sequence: diff --git a/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php b/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1f124b44efe06b52eaeda0a52ae3fcbed0e3bd26 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php @@ -0,0 +1,85 @@ +<?php +/** + * Generic table gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Table; + +use Interop\Container\ContainerInterface; + +/** + * Generic table gateway factory. + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GatewayFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Return row prototype object (null if unavailable) + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * + * @return object + */ + protected function getRowPrototype(ContainerInterface $container, $requestedName) + { + $rowManager = $container->get('VuFind\Db\Row\PluginManager'); + // Map Table class to matching Row class. + $name = str_replace("\\Table\\", "\\Row\\", $requestedName); + return $rowManager->has($name) ? $rowManager->get($name) : null; + } + + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $adapter = $container->get('Zend\Db\Adapter\Adapter'); + $tm = $container->get('VuFind\Db\Table\PluginManager'); + $config = $container->get('config'); + $rowPrototype = $this->getRowPrototype($container, $requestedName); + $args = $options ? $options : []; + return new $requestedName( + $adapter, $tm, $config, $rowPrototype, ...$args + ); + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/OaiResumption.php b/module/VuFind/src/VuFind/Db/Table/OaiResumption.php index 57da6b8b892b589f33b7a6729b2a1051d2db2266..e8a962a73629e5d7aa3c3026e52d23bbb2396c80 100644 --- a/module/VuFind/src/VuFind/Db/Table/OaiResumption.php +++ b/module/VuFind/src/VuFind/Db/Table/OaiResumption.php @@ -2,7 +2,7 @@ /** * Table Definition for oai_resumption * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; diff --git a/module/VuFind/src/VuFind/Db/Table/PluginFactory.php b/module/VuFind/src/VuFind/Db/Table/PluginFactory.php index ad2484f4969a3910a6935c8ed10c60b76d683295..d9b4f0a65fc8a5c7a69486ef0e476ed4d62718f0 100644 --- a/module/VuFind/src/VuFind/Db/Table/PluginFactory.php +++ b/module/VuFind/src/VuFind/Db/Table/PluginFactory.php @@ -2,7 +2,7 @@ /** * Database table plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Db/Table/PluginManager.php b/module/VuFind/src/VuFind/Db/Table/PluginManager.php index cd36600bef1d8665c955902f340c622973c2c6b9..f753d55d43dc9744c5ad0f35bc5570a3f00a325c 100644 --- a/module/VuFind/src/VuFind/Db/Table/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Table/PluginManager.php @@ -2,7 +2,7 @@ /** * Database table plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,66 @@ namespace VuFind\Db\Table; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'changetracker' => 'VuFind\Db\Table\ChangeTracker', + 'comments' => 'VuFind\Db\Table\Comments', + 'externalsession' => 'VuFind\Db\Table\ExternalSession', + 'oairesumption' => 'VuFind\Db\Table\OaiResumption', + 'record' => 'VuFind\Db\Table\Record', + 'resource' => 'VuFind\Db\Table\Resource', + 'resourcetags' => 'VuFind\Db\Table\ResourceTags', + 'search' => 'VuFind\Db\Table\Search', + 'session' => 'VuFind\Db\Table\Session', + 'tags' => 'VuFind\Db\Table\Tags', + 'user' => 'VuFind\Db\Table\User', + 'usercard' => 'VuFind\Db\Table\UserCard', + 'userlist' => 'VuFind\Db\Table\UserList', + 'userresource' => 'VuFind\Db\Table\UserResource', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Db\Table\ChangeTracker' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\Comments' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\ExternalSession' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\OaiResumption' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\Record' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\Resource' => 'VuFind\Db\Table\ResourceFactory', + 'VuFind\Db\Table\ResourceTags' => 'VuFind\Db\Table\CaseSensitiveTagsFactory', + 'VuFind\Db\Table\Search' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\Session' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\Tags' => 'VuFind\Db\Table\CaseSensitiveTagsFactory', + 'VuFind\Db\Table\User' => 'VuFind\Db\Table\UserFactory', + 'VuFind\Db\Table\UserCard' => 'VuFind\Db\Table\GatewayFactory', + 'VuFind\Db\Table\UserList' => 'VuFind\Db\Table\UserListFactory', + 'VuFind\Db\Table\UserResource' => 'VuFind\Db\Table\GatewayFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Db\Table\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Db/Table/Record.php b/module/VuFind/src/VuFind/Db/Table/Record.php index ee06382137b99b297ccbbe92bde12a6192f92cbd..67f91cd0885b7ee28102ee2d5d9d7b9d465be247 100644 --- a/module/VuFind/src/VuFind/Db/Table/Record.php +++ b/module/VuFind/src/VuFind/Db/Table/Record.php @@ -2,7 +2,7 @@ /** * Table Definition for record * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) University of Freiburg 2014. diff --git a/module/VuFind/src/VuFind/Db/Table/Resource.php b/module/VuFind/src/VuFind/Db/Table/Resource.php index fe48f39213d9206099fc0f3d7ddc16194c672d6e..4fe1921e2568360d68aa59368f696ae8c845bb6d 100644 --- a/module/VuFind/src/VuFind/Db/Table/Resource.php +++ b/module/VuFind/src/VuFind/Db/Table/Resource.php @@ -2,7 +2,7 @@ /** * Table Definition for resource * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Date\Converter as DateConverter; use VuFind\Db\Row\RowGateway; use VuFind\Record\Loader; @@ -173,14 +174,14 @@ class Resource extends Gateway $s->where->equalTo('ur.user_id', $user); // Adjust for list if necessary: - if (!is_null($list)) { + if (null !== $list) { $s->where->equalTo('ur.list_id', $list); } if ($offset > 0) { $s->offset($offset); } - if (!is_null($limit)) { + if (null !== $limit) { $s->limit($limit); } diff --git a/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php b/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..02e7c52f4c69fa28d9e3fd2bc46ca0f7711a273b --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Resource table gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Table; + +use Interop\Container\ContainerInterface; + +/** + * Resource table gateway factory. + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResourceFactory extends GatewayFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $converter = $container->get('VuFind\Date\Converter'); + $loader = $container->get('VuFind\Record\Loader'); + return parent::__invoke($container, $requestedName, [$converter, $loader]); + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/ResourceTags.php b/module/VuFind/src/VuFind/Db/Table/ResourceTags.php index 8bbbe2ff42136a8cf2af29ab4765fac43034c66a..6dfe17c2b1b17a9b08c198551de0ab1c0628f12f 100644 --- a/module/VuFind/src/VuFind/Db/Table/ResourceTags.php +++ b/module/VuFind/src/VuFind/Db/Table/ResourceTags.php @@ -2,7 +2,7 @@ /** * Table Definition for resource_tags * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; use Zend\Db\Sql\Expression; diff --git a/module/VuFind/src/VuFind/Db/Table/Search.php b/module/VuFind/src/VuFind/Db/Table/Search.php index be014d837f6ff72642c95a4e5f58e57558561d1d..bfa6ed443c0baa7b087c8d8e63de2e4a88362087 100644 --- a/module/VuFind/src/VuFind/Db/Table/Search.php +++ b/module/VuFind/src/VuFind/Db/Table/Search.php @@ -2,7 +2,7 @@ /** * Table Definition for search * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016-2017. @@ -28,6 +28,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use minSO; use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; @@ -109,15 +110,23 @@ class Search extends Gateway } /** - * Delete unsaved searches for a particular session. + * Destroy unsaved searches belonging to the specified session/user. * * @param string $sid Session ID of current user. + * @param int $uid User ID of current user (optional). * * @return void */ - public function destroySession($sid) + public function destroySession($sid, $uid = null) { - $this->delete(['session_id' => $sid, 'saved' => 0]); + $callback = function ($select) use ($sid, $uid) { + $select->where->equalTo('session_id', $sid)->and->equalTo('saved', 0); + if ($uid !== null) { + $select->where->OR + ->equalTo('user_id', $uid)->and->equalTo('saved', 0); + } + }; + return $this->delete($callback); } /** @@ -132,7 +141,7 @@ class Search extends Gateway { $callback = function ($select) use ($sid, $uid) { $select->where->equalTo('session_id', $sid)->and->equalTo('saved', 0); - if ($uid != null) { + if ($uid !== null) { $select->where->OR->equalTo('user_id', $uid); } $select->order('created'); diff --git a/module/VuFind/src/VuFind/Db/Table/Session.php b/module/VuFind/src/VuFind/Db/Table/Session.php index 9be95feadef88f5dfeb6adcf2cb35eddeacbb3e2..73973e37da582f2245a6725e5a98727f445a915e 100644 --- a/module/VuFind/src/VuFind/Db/Table/Session.php +++ b/module/VuFind/src/VuFind/Db/Table/Session.php @@ -2,7 +2,7 @@ /** * Table Definition for session * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. @@ -28,6 +28,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use VuFind\Exception\SessionExpired as SessionExpiredException; use Zend\Db\Adapter\Adapter; diff --git a/module/VuFind/src/VuFind/Db/Table/Tags.php b/module/VuFind/src/VuFind/Db/Table/Tags.php index 94b09ffd4ffb65221a192b8932c75bc1302ca984..a7fb22ccf7783eaddb19baa53d9df60e4afde01d 100644 --- a/module/VuFind/src/VuFind/Db/Table/Tags.php +++ b/module/VuFind/src/VuFind/Db/Table/Tags.php @@ -2,7 +2,7 @@ /** * Table Definition for tags * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; use Zend\Db\Sql\Expression; @@ -150,7 +151,7 @@ class Tags extends Gateway ); if ($fuzzy) { $select->where->literal('lower(tags.tag) like lower(?)', [$q]); - } else if (!$this->caseSensitive) { + } elseif (!$this->caseSensitive) { $select->where->literal('lower(tags.tag) = lower(?)', [$q]); } else { $select->where->equalTo('tags.tag', $q); @@ -240,7 +241,7 @@ class Tags extends Gateway if ($sort == 'count') { $select->order(['cnt DESC', new Expression('lower(tags.tag)')]); - } else if ($sort == 'tag') { + } elseif ($sort == 'tag') { $select->order([new Expression('lower(tags.tag)')]); } @@ -249,9 +250,9 @@ class Tags extends Gateway } if ($list === true) { $select->where->isNotNull('rt.list_id'); - } else if ($list === false) { + } elseif ($list === false) { $select->where->isNull('rt.list_id'); - } else if (null !== $list) { + } elseif (null !== $list) { $select->where->equalTo('rt.list_id', $list); } if (null !== $user) { @@ -271,12 +272,13 @@ class Tags extends Gateway * for no filter). * @param int $listId Filter for tags tied to a specific list (null for no * filter). - * @param string $source Filter for tags tied to a specific record source. + * @param string $source Filter for tags tied to a specific record source + * (null for no filter). * * @return \Zend\Db\ResultSet\AbstractResultSet */ public function getForUser($userId, $resourceId = null, $listId = null, - $source = DEFAULT_SEARCH_BACKEND + $source = null ) { $callback = function ($select) use ($userId, $resourceId, $listId, $source) { $select->columns( @@ -309,8 +311,11 @@ class Tags extends Gateway ->equalTo( 'ur.list_id', 'rt.list_id', Predicate::TYPE_IDENTIFIER, Predicate::TYPE_IDENTIFIER - ) - ->equalTo('r.source', $source); + ); + + if (null !== $source) { + $select->where->equalTo('r.source', $source); + } if (null !== $resourceId) { $select->where->equalTo('r.record_id', $resourceId); diff --git a/module/VuFind/src/VuFind/Db/Table/User.php b/module/VuFind/src/VuFind/Db/Table/User.php index c971b5b6abc3095337a9b0ffe800ed1cce27e11a..17fb1a3c6114d7fd1d0ce5841c56c7ff47e06f97 100644 --- a/module/VuFind/src/VuFind/Db/Table/User.php +++ b/module/VuFind/src/VuFind/Db/Table/User.php @@ -2,7 +2,7 @@ /** * Table Definition for user * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Config\Config; use Zend\Db\Adapter\Adapter; diff --git a/module/VuFind/src/VuFind/Db/Table/UserCard.php b/module/VuFind/src/VuFind/Db/Table/UserCard.php index 53fdd329e499e61a0a6ca7badbe23003efc45d96..ac157baaf34e47418850d3783b7d0c8599cd5e0d 100644 --- a/module/VuFind/src/VuFind/Db/Table/UserCard.php +++ b/module/VuFind/src/VuFind/Db/Table/UserCard.php @@ -2,7 +2,7 @@ /** * Table Definition for user_card * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; diff --git a/module/VuFind/src/VuFind/Db/Table/UserFactory.php b/module/VuFind/src/VuFind/Db/Table/UserFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4cbe25991cab9b5409df449ad27c30ecf9d904b4 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/UserFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * User table gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Table; + +use Interop\Container\ContainerInterface; + +/** + * User table gateway factory. + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserFactory extends GatewayFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $session = null; + if (isset($config->Authentication->privacy) + && $config->Authentication->privacy + ) { + $sessionManager = $container->get('Zend\Session\SessionManager'); + $session = new \Zend\Session\Container('Account', $sessionManager); + } + return parent::__invoke($container, $requestedName, [$config, $session]); + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/UserList.php b/module/VuFind/src/VuFind/Db/Table/UserList.php index 718504e5be679316e9bd11d5e9300bb74314ccaf..882bb4a893af709c2a56fdf65f7d2d204bb43018 100644 --- a/module/VuFind/src/VuFind/Db/Table/UserList.php +++ b/module/VuFind/src/VuFind/Db/Table/UserList.php @@ -2,7 +2,7 @@ /** * Table Definition for user_list * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use VuFind\Exception\LoginRequired as LoginRequiredException; use VuFind\Exception\RecordMissing as RecordMissingException; @@ -141,7 +142,7 @@ class UserList extends Gateway ->equalTo('r.record_id', $resourceId); $select->order(['title']); - if (!is_null($userId)) { + if (null !== $userId) { $select->where->equalTo('ur.user_id', $userId); } }; diff --git a/module/VuFind/src/VuFind/Db/Table/UserListFactory.php b/module/VuFind/src/VuFind/Db/Table/UserListFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5fea27b4880b5e84192dc7d410ec4c390c51de9d --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/UserListFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * UserList table gateway factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Db\Table; + +use Interop\Container\ContainerInterface; + +/** + * UserList table gateway factory. + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserListFactory extends GatewayFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $sessionManager = $container->get('Zend\Session\SessionManager'); + $session = new \Zend\Session\Container('List', $sessionManager); + return parent::__invoke($container, $requestedName, [$session]); + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/UserResource.php b/module/VuFind/src/VuFind/Db/Table/UserResource.php index 7552f769c819995e84d85cd1ea49838102fb2300..ce1ef8a8cb2d724b60677f509e47bd9a69d6c9f5 100644 --- a/module/VuFind/src/VuFind/Db/Table/UserResource.php +++ b/module/VuFind/src/VuFind/Db/Table/UserResource.php @@ -2,7 +2,7 @@ /** * Table Definition for user_resource * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Db\Table; + use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; use Zend\Db\Sql\Expression; @@ -92,10 +93,10 @@ class UserResource extends Gateway $select->where->equalTo('r.source', $source) ->equalTo('r.record_id', $resourceId); - if (!is_null($userId)) { + if (null !== $userId) { $select->where->equalTo('user_resource.user_id', $userId); } - if (!is_null($listId)) { + if (null !== $listId) { $select->where->equalTo('user_resource.list_id', $listId); } }; diff --git a/module/VuFind/src/VuFind/Exception/Auth.php b/module/VuFind/src/VuFind/Exception/Auth.php index 6c55a69edba5eabe035dcff0346ac60bf9546627..509ee6dcd78c6125366cab5292f3ab3ba23dbfe3 100644 --- a/module/VuFind/src/VuFind/Exception/Auth.php +++ b/module/VuFind/src/VuFind/Exception/Auth.php @@ -2,7 +2,7 @@ /** * Authentication Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/BadRequest.php b/module/VuFind/src/VuFind/Exception/BadRequest.php index defff0e7c15bf7a85a911e50485a781c037e177c..31c4d08847a645a2c93a93f71d6ed75ddb0679f6 100644 --- a/module/VuFind/src/VuFind/Exception/BadRequest.php +++ b/module/VuFind/src/VuFind/Exception/BadRequest.php @@ -2,7 +2,7 @@ /** * "Bad Request" Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFind/src/VuFind/Exception/FileAccess.php b/module/VuFind/src/VuFind/Exception/FileAccess.php index 3802a74cfd82f6faac87dc1eea6c40532b0f9b1a..3a24ae7f1b2cd6285e32d9101e81fe90b06dd95c 100644 --- a/module/VuFind/src/VuFind/Exception/FileAccess.php +++ b/module/VuFind/src/VuFind/Exception/FileAccess.php @@ -2,7 +2,7 @@ /** * File Access Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/Forbidden.php b/module/VuFind/src/VuFind/Exception/Forbidden.php index 42245d3777b427e2a762025ad420d87ba20bbe28..18cf684a1d8d7443aa9d58afde6b1a4b56fb1ce3 100644 --- a/module/VuFind/src/VuFind/Exception/Forbidden.php +++ b/module/VuFind/src/VuFind/Exception/Forbidden.php @@ -2,7 +2,7 @@ /** * "Forbidden Access" Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/HttpStatusInterface.php b/module/VuFind/src/VuFind/Exception/HttpStatusInterface.php index 821d7d16769256df5a9540cd4d5a0e546eaabeca..6bc451823ece944f5f71d53673d99513b654fd6e 100644 --- a/module/VuFind/src/VuFind/Exception/HttpStatusInterface.php +++ b/module/VuFind/src/VuFind/Exception/HttpStatusInterface.php @@ -3,7 +3,7 @@ * Interface for exceptions that should trigger specific HTTP status codes * when unhandled. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/ILS.php b/module/VuFind/src/VuFind/Exception/ILS.php index 84393627782ece565057e91cf63048406a1d6728..93470180126745fdcf4544372068b423f90a4527 100644 --- a/module/VuFind/src/VuFind/Exception/ILS.php +++ b/module/VuFind/src/VuFind/Exception/ILS.php @@ -2,7 +2,7 @@ /** * ILS Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/LibraryCard.php b/module/VuFind/src/VuFind/Exception/LibraryCard.php index f0d7bbf22f0376ba30b558478176b21d8a87e25e..8070ee04d4b1192c50cfe42dde3d5ddc250b4d7c 100644 --- a/module/VuFind/src/VuFind/Exception/LibraryCard.php +++ b/module/VuFind/src/VuFind/Exception/LibraryCard.php @@ -2,7 +2,7 @@ /** * Library Card Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * diff --git a/module/VuFind/src/VuFind/Exception/ListPermission.php b/module/VuFind/src/VuFind/Exception/ListPermission.php index b84168341d146bc34d83a7b02a5f491373fbf742..5c9ac523f68d891a74712dd81fdd529d465d4c5e 100644 --- a/module/VuFind/src/VuFind/Exception/ListPermission.php +++ b/module/VuFind/src/VuFind/Exception/ListPermission.php @@ -2,7 +2,7 @@ /** * List Permission Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/LoginRequired.php b/module/VuFind/src/VuFind/Exception/LoginRequired.php index 59e5ca594f1059b70f81099c2f9346599242d5a1..1319c9889f5952f722785d2972ff40bff8b95d1e 100644 --- a/module/VuFind/src/VuFind/Exception/LoginRequired.php +++ b/module/VuFind/src/VuFind/Exception/LoginRequired.php @@ -2,7 +2,7 @@ /** * Login Required Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/Mail.php b/module/VuFind/src/VuFind/Exception/Mail.php index 727cc037fda3380cda0dc0e782608253d9779da9..2c2141abf8ac97bb04af562793006a8305e217de 100644 --- a/module/VuFind/src/VuFind/Exception/Mail.php +++ b/module/VuFind/src/VuFind/Exception/Mail.php @@ -2,7 +2,7 @@ /** * Mail Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/MissingField.php b/module/VuFind/src/VuFind/Exception/MissingField.php index cd9e532591c2a8e373c4e50c9ba172a1bd02c2a7..8a431130555343e5d7638b11d3b9f050088a7709 100644 --- a/module/VuFind/src/VuFind/Exception/MissingField.php +++ b/module/VuFind/src/VuFind/Exception/MissingField.php @@ -2,7 +2,7 @@ /** * Missing Field Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/PasswordSecurity.php b/module/VuFind/src/VuFind/Exception/PasswordSecurity.php index 198404a3adbff0925309162d4c9eea23d47239f9..ad563b29d45cf0f2568f015242ea45f49f941e97 100644 --- a/module/VuFind/src/VuFind/Exception/PasswordSecurity.php +++ b/module/VuFind/src/VuFind/Exception/PasswordSecurity.php @@ -2,7 +2,7 @@ /** * Password Security Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/RecordMissing.php b/module/VuFind/src/VuFind/Exception/RecordMissing.php index 598312f32e14219c38eb06c39a035565f18be170..d3f31e1d3e61f4d68268540c2b1f597acd234d0e 100644 --- a/module/VuFind/src/VuFind/Exception/RecordMissing.php +++ b/module/VuFind/src/VuFind/Exception/RecordMissing.php @@ -2,7 +2,7 @@ /** * Record Missing Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Exception/SessionExpired.php b/module/VuFind/src/VuFind/Exception/SessionExpired.php index dee31eeb4675ae7bd1c486a23207e7b37df1cb2a..8294bfe3beca4e1d1dd42068defdc73f45034d99 100644 --- a/module/VuFind/src/VuFind/Exception/SessionExpired.php +++ b/module/VuFind/src/VuFind/Exception/SessionExpired.php @@ -2,7 +2,7 @@ /** * Session Expired Exception * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Export.php b/module/VuFind/src/VuFind/Export.php index 256492b8438cb3557d1fd3d292d68792d982b6bc..27966018627315e18d18c812ba527feb5ae63922 100644 --- a/module/VuFind/src/VuFind/Export.php +++ b/module/VuFind/src/VuFind/Export.php @@ -2,7 +2,7 @@ /** * Export support class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org Main Site */ namespace VuFind; -use VuFind\SimpleXML, Zend\Config\Config; + +use Zend\Config\Config; /** * Export support class @@ -159,7 +160,8 @@ class Export */ public function needsRedirect($format) { - return isset($this->exportConfig->$format->redirectUrl); + return !empty($this->exportConfig->$format->redirectUrl) + && 'link' === $this->getBulkExportType($format); } /** @@ -384,4 +386,31 @@ class Export } return $this->activeFormats[$context]; } + + /** + * Get the export POST field name. + * + * @param string $format Format identifier + * + * @return string + */ + public function getPostField($format) + { + return !empty($this->exportConfig->$format->postField) + ? $this->exportConfig->$format->postField : 'ImportData'; + } + + /** + * Get the export target window. + * + * @param string $format Format identifier + * + * @return string + */ + public function getTargetWindow($format) + { + return !empty($this->exportConfig->$format->targetWindow) + ? $this->exportConfig->$format->targetWindow + : $format . 'Main'; + } } diff --git a/module/VuFind/src/VuFind/ExportFactory.php b/module/VuFind/src/VuFind/ExportFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fdbfbe68b0bab999753866a2dbe9473ae0818618 --- /dev/null +++ b/module/VuFind/src/VuFind/ExportFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Export factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Export + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Export factory. + * + * @category VuFind + * @package Export + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ExportFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('config'), + $container->get('VuFind\Config\PluginManager')->get('export') + ); + } +} diff --git a/module/VuFind/src/VuFind/Favorites/FavoritesService.php b/module/VuFind/src/VuFind/Favorites/FavoritesService.php index 86778ea93e34ea402dac177a2c934bf664e73ddf..2217326a52a4a09df0740dd8e6d22c56cb9d9e36 100644 --- a/module/VuFind/src/VuFind/Favorites/FavoritesService.php +++ b/module/VuFind/src/VuFind/Favorites/FavoritesService.php @@ -2,7 +2,7 @@ /** * Favorites service * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,10 +26,11 @@ * @link https://vufind.org Main Page */ namespace VuFind\Favorites; -use VuFind\Record\Cache as RecordCache; -use VuFind\RecordDriver\AbstractBase as RecordDriver; + use VuFind\Db\Table\Resource as ResourceTable; use VuFind\Db\Table\UserList as UserListTable; +use VuFind\Record\Cache as RecordCache; +use VuFind\RecordDriver\AbstractBase as RecordDriver; /** * Favorites service @@ -151,7 +152,7 @@ class FavoritesService implements \VuFind\I18n\Translator\TranslatorAwareInterfa // Get or create a list object as needed: $list = $this->getListObject( - isset($params['list']) ? $params['list'] : '', + $params['list'] ?? '', $user ); @@ -166,8 +167,8 @@ class FavoritesService implements \VuFind\I18n\Translator\TranslatorAwareInterfa // Add the information to the user's account: $user->saveResource( $resource, $list, - isset($params['mytags']) ? $params['mytags'] : [], - isset($params['notes']) ? $params['notes'] : '' + $params['mytags'] ?? [], + $params['notes'] ?? '' ); return ['listId' => $list->id]; } diff --git a/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php b/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php index 9d73aa12bc72e2a769b92d6d4f23c3d4b55e3e1b..8ef1bf9226d5d70d91117ba14c8deb210092ded3 100644 --- a/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php +++ b/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php @@ -2,7 +2,7 @@ /** * Favorites service factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Favorites; -use Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Favorites service @@ -39,22 +41,26 @@ use Zend\ServiceManager\ServiceLocatorInterface; * * @codeCoverageIgnore */ -class FavoritesServiceFactory implements \Zend\ServiceManager\FactoryInterface +class FavoritesServiceFactory implements FactoryInterface { /** * Create service * - * @param ServiceLocatorInterface $sm Service manager + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return FavoritesService * - * @return mixed + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $sm) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $tableManager = $sm->get('VuFind\DbTablePluginManager'); + $tableManager = $sm->get('VuFind\Db\Table\PluginManager'); return new FavoritesService( $tableManager->get('userlist'), $tableManager->get('resource'), - $sm->get('VuFind\RecordCache') + $sm->get('VuFind\Record\Cache') ); } } diff --git a/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Entry.php b/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Entry.php index 2277a6ebcbf8e92fe17a4a21b5d57a1e43b458a3..9791ec625a977d4bff6e94ac583b1bf386becc56 100644 --- a/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Entry.php +++ b/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Entry.php @@ -2,7 +2,7 @@ /** * Zend\Feed\Entry extension for Dublin Core * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Feed\Writer\Extension\DublinCore; + use Zend\Feed\Writer\Extension\ITunes\Entry as ParentEntry; /** diff --git a/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Renderer/Entry.php b/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Renderer/Entry.php index cc1af3a5525fc512e0db88716713fb640e1a3681..94a4b907b124fbea752d1a5c3c66cd45ae0d9608 100644 --- a/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Renderer/Entry.php +++ b/module/VuFind/src/VuFind/Feed/Writer/Extension/DublinCore/Renderer/Entry.php @@ -2,7 +2,7 @@ /** * Zend\Feed\Renderer\Entry extension for Dublin Core * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Feed\Writer\Extension\DublinCore\Renderer; -use DOMDocument, DOMElement, - Zend\Feed\Writer\Extension\DublinCore\Renderer\Entry as ParentEntry; + +use DOMDocument; +use DOMElement; +use Zend\Feed\Writer\Extension\DublinCore\Renderer\Entry as ParentEntry; /** * Zend\Feed\Renderer\Entry extension for Dublin Core diff --git a/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Feed.php b/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Feed.php index 3d5d6a5399aa9dcc2824e44e1d6e7f3c1213bef1..6d5180d80063380e1ebd329e851b8159887aa8ea 100644 --- a/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Feed.php +++ b/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Feed.php @@ -2,7 +2,7 @@ /** * Zend\Feed\Feed extension for Open Search * - * PHP version 5 + * PHP version 7 * * Copyright (C) Deutsches Archäologisches Institut 2015. * @@ -26,9 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Feed\Writer\Extension\OpenSearch; -use Zend\Stdlib\StringUtils, - Zend\Feed\Uri, - Zend\Feed\Writer\Extension\ITunes\Feed as ParentFeed; + +use Zend\Feed\Uri; +use Zend\Feed\Writer\Extension\ITunes\Feed as ParentFeed; +use Zend\Stdlib\StringUtils; /** * Zend\Feed\Feed extension for Open Search @@ -117,6 +118,7 @@ class Feed extends ParentFeed $this->encoding = $enc; return $this; } + /** * Get feed encoding * @@ -259,5 +261,4 @@ class Feed extends ParentFeed { return $this->links; } - } diff --git a/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Renderer/Feed.php b/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Renderer/Feed.php index ccec269077c4d27892c179a3e05fcf389dfca5a0..5cdd58b44b02913234d7da6a86e2162112dbf116 100644 --- a/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Renderer/Feed.php +++ b/module/VuFind/src/VuFind/Feed/Writer/Extension/OpenSearch/Renderer/Feed.php @@ -2,7 +2,7 @@ /** * Zend\Feed\Renderer\Feed extension for Open Search * - * PHP version 5 + * PHP version 7 * * Copyright (C) Deutsches Archäologisches Institut 2015. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Feed\Writer\Extension\OpenSearch\Renderer; -use DOMDocument, DOMElement, - Zend\Feed\Writer\Extension\AbstractRenderer; + +use DOMDocument; +use DOMElement; +use Zend\Feed\Writer\Extension\AbstractRenderer; /** * Zend\Feed\Renderer\Feed extension for Open Search diff --git a/module/VuFind/src/VuFind/GeoFeatures/AbstractConfig.php b/module/VuFind/src/VuFind/GeoFeatures/AbstractConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..545ba0f15d0dafed98e1100ea587e5705a583670 --- /dev/null +++ b/module/VuFind/src/VuFind/GeoFeatures/AbstractConfig.php @@ -0,0 +1,81 @@ +<?php +/** + * Abstract Configuration Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +namespace VuFind\GeoFeatures; + +use Zend\Config\Config; + +/** + * MapTab Configuration Class + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class AbstractConfig +{ + /** + * Configuration loader + * + * @var \VuFind\Config\PluginManager + */ + protected $configLoader; + + /** + * Constructor + * + * @param \VuFind\Config\PluginManager $configLoader Configuration loader + */ + public function __construct(\VuFind\Config\PluginManager $configLoader) + { + $this->configLoader = $configLoader; + } + + /** + * Convert a config object to an options array; return empty array if + * configuration is missing or incomplete. + * + * @param string $configName Name of config file to read + * @param string $section Name of section to read + * @param array $validOptions List of valid fields to read + * + * @return array + */ + protected function getOptions($configName, $section, $validOptions) + { + $config = $this->configLoader->get($configName); + $options = []; + foreach ($validOptions as $field) { + if (isset($config->$section->$field)) { + $options[$field] = $config->$section->$field; + } + } + return $options; + } +} diff --git a/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php b/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dd18620c4c876e65d72be883c07b310e64ef62fd --- /dev/null +++ b/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php @@ -0,0 +1,69 @@ +<?php + +/** + * GeoFeatures Abstract Configuration Factory Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\GeoFeatures; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * GeoFeatures Abstract Config Factory Class + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + * + * @codeCoverageIgnore + */ +class AbstractConfigFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Config\PluginManager')); + } +} diff --git a/module/VuFind/src/VuFind/GeoFeatures/BasemapConfig.php b/module/VuFind/src/VuFind/GeoFeatures/BasemapConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..6f64945213ee9ebd218367dc1bdc399f3fc04936 --- /dev/null +++ b/module/VuFind/src/VuFind/GeoFeatures/BasemapConfig.php @@ -0,0 +1,124 @@ +<?php +/** + * Basemap Configuration Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +namespace VuFind\GeoFeatures; + +/** + * Basemap Configuration Class + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class BasemapConfig extends AbstractConfig +{ + /** + * Request origin + * + * @var string + */ + protected $requestOrigin; + + /** + * Valid options to retrieve from configuration + * + * @var string[] + */ + protected $options = ['basemap_url', 'basemap_attribution']; + + /** + * Set default options + * + * @return array + */ + protected function getDefaultOptions() + { + return [ + 'basemap_url' => 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png', + 'basemap_attribution' => '<a href="https://wikimediafoundation.org/' + . 'wiki/Maps_Terms_of_Use">Wikimedia</a> | © <a ' + . 'href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>', + ]; + } + + /** + * Get the basemap configuration settings. + * + * @param string $origin Origin of request MapTab or MapSelection + * + * @return array + */ + public function getBasemap($origin) + { + $options = []; + if ($origin == 'MapSelection') { + $options = $this->getMapSelectionBasemap(); + } elseif ($origin == 'MapTab') { + $options = $this->getMapTabBasemap(); + } + // Fail over to geofeatures.ini [Basemap] if no other settings found: + if (empty($options)) { + $options = $this->getOptions('geofeatures', 'Basemap', $this->options); + } + // Fill in any missing defaults: + return $options + $this->getDefaultOptions(); + } + + /** + * Get the basemap configuration settings for MapSelection. + * + * @return array + */ + protected function getMapSelectionBasemap() + { + // Check geofeatures.ini [MapSelection] + $options = $this->getOptions('geofeatures', 'MapSelection', $this->options); + + // Fail over to legacy configuration if empty + return empty($options) + ? $this->getOptions('searches', 'MapSelection', $this->options) + : $options; + } + + /** + * Get the basemap configuration settings for MapTab. + * + * @return array + */ + protected function getMapTabBasemap() + { + // Check geofeatures.ini [MapTab] + $options = $this->getOptions('geofeatures', 'MapTab', $this->options); + + // Fail over to legacy configuration if empty + return empty($options) + ? $this->getOptions('config', 'Content', $this->options) + : $options; + } +} diff --git a/module/VuFind/src/VuFind/GeoFeatures/MapSelectionConfig.php b/module/VuFind/src/VuFind/GeoFeatures/MapSelectionConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..bd9336bf32fce9fe69843f1e0776da5c8a2dbf42 --- /dev/null +++ b/module/VuFind/src/VuFind/GeoFeatures/MapSelectionConfig.php @@ -0,0 +1,73 @@ +<?php +/** + * MapSelection Configuration Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +namespace VuFind\GeoFeatures; + +/** + * MapSelection Configuration Class + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class MapSelectionConfig extends AbstractConfig +{ + /** + * Set default options + * + * @return array + */ + protected function getDefaultOptions() + { + return [ + 'default_coordinates' => '-95, 30, 72, 15', + 'height' => '320' + ]; + } + + /** + * Get the map tab configuration settings. + * + * @return array + */ + public function getMapSelectionOptions() + { + $validFields = ['default_coordinates', 'height']; + $options = []; + // Check geofeatures.ini + $options = $this->getOptions('geofeatures', 'MapSelection', $validFields); + + if (empty($options)) { + // Check legacy configuration + $options = $this->getOptions('searches', 'MapSelection', $validFields); + } + // Fill in any missing defaults: + return $options + $this->getDefaultOptions(); + } +} diff --git a/module/VuFind/src/VuFind/GeoFeatures/MapTabConfig.php b/module/VuFind/src/VuFind/GeoFeatures/MapTabConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..ab4b93f7c3fcd4875ed7c279706f57100f1f9a2f --- /dev/null +++ b/module/VuFind/src/VuFind/GeoFeatures/MapTabConfig.php @@ -0,0 +1,74 @@ +<?php +/** + * Map Tab Configuration Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +namespace VuFind\GeoFeatures; + +/** + * MapTab Configuration Class + * + * @category VuFind + * @package GeoFeatures + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class MapTabConfig extends AbstractConfig +{ + /** + * Set default options + * + * @return array + */ + protected function getDefaultOptions() + { + return [ + 'recordMap' => 'false', + 'displayCoords' => 'false', + 'mapLabels' => null, + 'graticule' => 'false' + ]; + } + + /** + * Get the map tab configuration settings. + * + * @return array + */ + public function getMapTabOptions() + { + $validFields = ['displayCoords', 'mapLabels', 'graticule', 'recordMap']; + $options = []; + // Check geofeatures.ini + $options = $this->getOptions('geofeatures', 'MapTab', $validFields); + // Check legacy configuration + if (empty($options)) { + $options = $this->getOptions('config', 'Content', $validFields); + } + // Fill in any missing defaults: + return $options + $this->getDefaultOptions(); + } +} diff --git a/module/VuFind/src/VuFind/Hierarchy/Driver/AbstractBase.php b/module/VuFind/src/VuFind/Hierarchy/Driver/AbstractBase.php index ebaefd8288fefcd39e21882e123932b6b1db9860..505bdef637fd6abe83925395a4ae249797fe75f1 100644 --- a/module/VuFind/src/VuFind/Hierarchy/Driver/AbstractBase.php +++ b/module/VuFind/src/VuFind/Hierarchy/Driver/AbstractBase.php @@ -2,7 +2,7 @@ /** * Hierarchy interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ namespace VuFind\Hierarchy\Driver; -use VuFind\Hierarchy\TreeDataSource\PluginManager as DataManager, - VuFind\Hierarchy\TreeRenderer\PluginManager as RendererManager; + +use VuFind\Hierarchy\TreeDataSource\PluginManager as DataManager; +use VuFind\Hierarchy\TreeRenderer\PluginManager as RendererManager; /** * Hierarchy interface class. @@ -95,7 +96,7 @@ abstract class AbstractBase $this->dataManager = $dataManager; $this->rendererManager = $rendererManager; if (isset($options['enabled'])) { - $this->enabled = (bool) $options['enabled']; + $this->enabled = (bool)$options['enabled']; } } diff --git a/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBased.php b/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBased.php index e2415e17e52d95c55a27adbdd9e9ff5f6915df7b..c891a691aed644b3ccab0a1fd7126f4d5455f75b 100644 --- a/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBased.php +++ b/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBased.php @@ -2,7 +2,7 @@ /** * Configuration-Based Hierarchy Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -56,7 +56,7 @@ class ConfigurationBased extends AbstractBase { $treeConfigDriver = isset($this->config->HierarchyTree->show) ? $this->config->HierarchyTree->show : false; - return ($this->enabled && $treeConfigDriver); + return $this->enabled && $treeConfigDriver; } /** @@ -124,6 +124,7 @@ class ConfigurationBased extends AbstractBase return isset($this->config->HierarchyTree) ? $this->config->HierarchyTree->toArray() : []; } + /** * Get Collection Link Type from the config file * diff --git a/module/VuFind/src/VuFind/Hierarchy/Driver/Factory.php b/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php similarity index 59% rename from module/VuFind/src/VuFind/Hierarchy/Driver/Factory.php rename to module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php index 0fc036ab206c84b163ba37fdffc4d834966dd581..4a55c3fcb38927fb63400ebdec51af963b2db2ff 100644 --- a/module/VuFind/src/VuFind/Hierarchy/Driver/Factory.php +++ b/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php @@ -2,7 +2,7 @@ /** * Hierarchy Driver Factory Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ namespace VuFind\Hierarchy\Driver; + use Zend\ServiceManager\ServiceManager; /** @@ -41,61 +42,44 @@ use Zend\ServiceManager\ServiceManager; * * @codeCoverageIgnore */ -class Factory +class ConfigurationBasedFactory { /** * This constructs a hierarchy driver using VuFind's service setup. * - * @param \Zend\ServiceManager\ServiceManager $sm Top-level service manager - * @param string $config Name of config to load - * @param string $class Name of driver class + * @param \Zend\ServiceManager\ServiceManager $sm Top-level service m. + * @param string $requestedName Service being built + * @param array|null $options Name of driver class * * @return object + * + * @throws Exception if options is populated */ - public static function get(ServiceManager $sm, $config, - $class = 'VuFind\Hierarchy\Driver\ConfigurationBased' + public function __invoke(ServiceManager $sm, $requestedName, + array $options = null ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + // Get config name from requestedName + $parts = explode('\\', $requestedName); + $config = end($parts); // Set up options based on global VuFind settings: - $configReader = $sm->get('VuFind\Config'); + $configReader = $sm->get('VuFind\Config\PluginManager'); $globalConfig = $configReader->get('config'); $options = [ - 'enabled' => isset($globalConfig->Hierarchy->showTree) - ? $globalConfig->Hierarchy->showTree : false + 'enabled' => $globalConfig->Hierarchy->showTree ?? false ]; // Load driver-specific configuration: $driverConfig = $configReader->get($config); // Build object: - return new $class( + return new ConfigurationBased( $driverConfig, - $sm->get('VuFind\HierarchyTreeDataSourcePluginManager'), - $sm->get('VuFind\HierarchyTreeRendererPluginManager'), + $sm->get('VuFind\Hierarchy\TreeDataSource\PluginManager'), + $sm->get('VuFind\Hierarchy\TreeRenderer\PluginManager'), $options ); } - - /** - * Factory for HierarchyDefault to be called from module.config.php. - * - * @param ServiceManager $sm Service manager. - * - * @return HierarchyDefault - */ - public static function getHierarchyDefault(ServiceManager $sm) - { - return static::get($sm->getServiceLocator(), 'HierarchyDefault'); - } - - /** - * Factory for HierarchyFlat to be called from module.config.php. - * - * @param ServiceManager $sm Service manager. - * - * @return HierarchyFlat - */ - public static function getHierarchyFlat(ServiceManager $sm) - { - return static::get($sm->getServiceLocator(), 'HierarchyFlat'); - } } diff --git a/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php index 53fbe1ef4bb1668135193af6584894b16255a72c..ac4437f53ecabeeb40505fdde4918d6a25722c1a 100644 --- a/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php @@ -2,7 +2,7 @@ /** * Hierarchy driver plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,28 @@ namespace VuFind\Hierarchy\Driver; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'default' => 'VuFind\Hierarchy\Driver\HierarchyDefault', + 'flat' => 'VuFind\Hierarchy\Driver\HierarchyFlat', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Hierarchy\Driver\HierarchyDefault' => + 'VuFind\Hierarchy\Driver\ConfigurationBasedFactory', + 'VuFind\Hierarchy\Driver\HierarchyFlat' => + 'VuFind\Hierarchy\Driver\ConfigurationBasedFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php index e396315a1e8dee630a8a833558131bf87599a84e..591fa9109772b6f79aaf65a27a55444c939c1548 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Formatter (abstract base) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -129,7 +129,7 @@ abstract class AbstractBase return $retVal; } - /** + /** * Get the titles of this item within parent collections. Returns an array * of parent ID => sequence number. * @@ -169,7 +169,7 @@ abstract class AbstractBase // Check config setting for what constitutes a collection switch ($this->collectionType) { case 'All': - return (isset($fields->is_hierarchy_id)); + return isset($fields->is_hierarchy_id); case 'Top': return isset($fields->is_hierarchy_id) && in_array($fields->is_hierarchy_id, $fields->hierarchy_top_id); diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php index 5f2645b2c0bcc0c312436d11634fc6ac450cb053..3a87cf1a06713ef9dd40f8a5b3cc4eb73e2b09ce 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Formatter (JSON) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -95,7 +95,7 @@ class Json extends AbstractBase // otherwise, we can just collect flat values. if ($this->sort) { $positions = $this->getHierarchyPositionsInParents($current); - $sequence = isset($positions[$parentID]) ? $positions[$parentID] : 0; + $sequence = $positions[$parentID] ?? 0; $json[] = [$sequence, $childNode]; } else { $json[] = $childNode; diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php index f244f866fa04dbaec4c2aceafc813475eeb9c11c..ccd65bdce601d7a119eb129f1a0faa13bba43ade 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php @@ -2,7 +2,7 @@ /** * Hierarchy tree data formatter plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,28 @@ namespace VuFind\Hierarchy\TreeDataFormatter; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'json' => 'VuFind\Hierarchy\TreeDataFormatter\Json', + 'xml' => 'VuFind\Hierarchy\TreeDataFormatter\Xml', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Hierarchy\TreeDataFormatter\Json' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Hierarchy\TreeDataFormatter\Xml' => + 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php index 2aecded13bf0b0f6a9d820f9e31788bc7c84a02f..28869e215daf113aab9b5d47f146027e41949c85 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Formatter (XML) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -94,7 +94,7 @@ class Xml extends AbstractBase // otherwise, we can just collect flat values. if ($this->sort) { $positions = $this->getHierarchyPositionsInParents($current); - $sequence = isset($positions[$parentID]) ? $positions[$parentID] : 0; + $sequence = $positions[$parentID] ?? 0; $parts[] = [$sequence, $childNode]; } else { $parts[] = $childNode; diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php index 8036b03fcfce05865d7ff6dca7c7089afbeb9727..653275eb32d555eb5b643df37dd975dd45d80d36 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Source (abstract base) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Factory.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Factory.php deleted file mode 100644 index 96581b5b01b1d1719a88b1d384deb9000b8861b9..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Factory.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Hierarchy Data Source Factory Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2010. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package HierarchyTree_DataSource - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\Hierarchy\TreeDataSource; -use Zend\ServiceManager\ServiceManager; - -/** - * Hierarchy Data Source Factory Class - * - * This is a factory class to build objects for managing hierarchies. - * - * @category VuFind - * @package HierarchyTree_DataSource - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for Solr driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Solr - */ - public static function getSolr(ServiceManager $sm) - { - $cacheDir = $sm->getServiceLocator()->get('VuFind\CacheManager') - ->getCacheDir(false); - $hierarchyFilters = $sm->getServiceLocator()->get('VuFind\Config') - ->get('HierarchyDefault'); - $filters = isset($hierarchyFilters->HierarchyTree->filterQueries) - ? $hierarchyFilters->HierarchyTree->filterQueries->toArray() - : []; - $solr = $sm->getServiceLocator()->get('VuFind\Search\BackendManager') - ->get('Solr')->getConnector(); - $formatterManager = $sm->getServiceLocator() - ->get('VuFind\HierarchyTreeDataFormatterPluginManager'); - return new Solr( - $solr, $formatterManager, rtrim($cacheDir, '/') . '/hierarchy', - $filters - ); - } -} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php index e96325f81bddbbe806b31977bc91dc533a54f7d8..abafb511aa5e2c1b75c21b3efc9c54ef9230b802 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php @@ -2,7 +2,7 @@ /** * Hierarchy tree data source plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,28 @@ namespace VuFind\Hierarchy\TreeDataSource; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'solr' => 'VuFind\Hierarchy\TreeDataSource\Solr', + 'xmlfile' => 'VuFind\Hierarchy\TreeDataSource\XMLFile', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Hierarchy\TreeDataSource\Solr' => + 'VuFind\Hierarchy\TreeDataSource\SolrFactory', + 'VuFind\Hierarchy\TreeDataSource\XMLFile' => + 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php index 7668c44e11895b1fc60ad63cbebcc5caff91a450..1fe4a1277ddea3631534041e9a7edbbe275a688e 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Source (Solr) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,11 @@ * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ namespace VuFind\Hierarchy\TreeDataSource; + use VuFind\Hierarchy\TreeDataFormatter\PluginManager as FormatterManager; -use VuFindSearch\Query\Query; use VuFindSearch\Backend\Solr\Connector; use VuFindSearch\ParamBag; +use VuFindSearch\Query\Query; /** * Hierarchy Tree Data Source (Solr) @@ -72,6 +73,13 @@ class Solr extends AbstractBase */ protected $filters = []; + /** + * Record batch size + * + * @var int + */ + protected $batchSize = 1000; + /** * Constructor. * @@ -79,9 +87,10 @@ class Solr extends AbstractBase * @param FormatterManager $fm Formatter manager * @param string $cacheDir Directory to hold cache results (optional) * @param array $filters Filters to apply to Solr tree queries + * @param int $batchSize Number of records retrieved in a batch */ public function __construct(Connector $connector, FormatterManager $fm, - $cacheDir = null, $filters = [] + $cacheDir = null, $filters = [], $batchSize = 1000 ) { $this->solrConnector = $connector; $this->formatterManager = $fm; @@ -89,6 +98,7 @@ class Solr extends AbstractBase $this->cacheDir = rtrim($cacheDir, '/'); } $this->filters = $filters; + $this->batchSize = $batchSize; } /** @@ -118,21 +128,43 @@ class Solr extends AbstractBase */ protected function searchSolr($q, $rows = 1073741823) { - $params = new ParamBag( - [ - 'q' => [$q], - 'fq' => $this->filters, - 'hl' => ['false'], - 'fl' => ['title,id,hierarchy_parent_id,hierarchy_top_id,' - . 'is_hierarchy_id,hierarchy_sequence,title_in_hierarchy'], - 'wt' => ['json'], - 'json.nl' => ['arrarr'], - 'rows' => [$rows], // Integer max - 'start' => [0] - ] - ); - $response = $this->solrConnector->search($params); - return json_decode($response); + $prevCursorMark = ''; + $cursorMark = '*'; + $records = []; + while ($cursorMark !== $prevCursorMark) { + $params = new ParamBag( + [ + 'q' => [$q], + 'fq' => $this->filters, + 'hl' => ['false'], + 'spellcheck' => ['false'], + 'fl' => ['title,id,hierarchy_parent_id,hierarchy_top_id,' + . 'is_hierarchy_id,hierarchy_sequence,title_in_hierarchy'], + 'wt' => ['json'], + 'json.nl' => ['arrarr'], + 'rows' => [min([$this->batchSize, $rows])], + // Start is always 0 when using cursorMark + 'start' => [0], + // Sort is required + 'sort' => ['id asc'], + // Override any default timeAllowed since it cannot be used with + // cursorMark + 'timeAllowed' => -1, + 'cursorMark' => $cursorMark + ] + ); + $results = json_decode($this->solrConnector->search($params)); + if (empty($results->response->docs)) { + break; + } + $records = array_merge($records, $results->response->docs); + if (count($records) >= $rows) { + break; + } + $prevCursorMark = $cursorMark; + $cursorMark = $results->nextCursorMark; + } + return $records; } /** @@ -154,15 +186,19 @@ class Solr extends AbstractBase } $lastId = $id; - $results = $this->searchSolr('hierarchy_top_id:"' . $id . '"'); - if ($results->response->numFound < 1) { + $records = $this->searchSolr('hierarchy_top_id:"' . $id . '"'); + if (!$records) { return []; } $map = [$id => []]; - foreach ($results->response->docs as $current) { + foreach ($records as $current) { $parents = isset($current->hierarchy_parent_id) ? $current->hierarchy_parent_id : []; foreach ($parents as $parentId) { + if ($current->id === $parentId) { + // Ignore circular reference + continue; + } if (!isset($map[$parentId])) { $map[$parentId] = [$current]; } else { @@ -192,9 +228,8 @@ class Solr extends AbstractBase } $lastId = $id; - $recordResults = $this->searchSolr('id:"' . $id . '"', 1); - $record = isset($recordResults->response->docs[0]) - ? $recordResults->response->docs[0] : false; + $records = $this->searchSolr('id:"' . $id . '"', 1); + $record = $records ? $records[0] : false; return $record; } diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dd154c2d64ef277895339046e9222e50fb9d904a --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php @@ -0,0 +1,82 @@ +<?php +/** + * Solr Hierarchy tree data source plugin factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package HierarchyTree_DataSource + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Hierarchy\TreeDataSource; + +use Interop\Container\ContainerInterface; + +/** + * Solr Hierarchy tree data source plugin factory. + * + * @category VuFind + * @package HierarchyTree_DataSource + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SolrFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + $cacheDir = $container->get('VuFind\Cache\Manager') + ->getCacheDir(false); + $hierarchyFilters = $container->get('VuFind\Config\PluginManager') + ->get('HierarchyDefault'); + $filters = isset($hierarchyFilters->HierarchyTree->filterQueries) + ? $hierarchyFilters->HierarchyTree->filterQueries->toArray() + : []; + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $batchSize = isset($config->Index->cursor_batch_size) + ? $config->Index->cursor_batch_size : 1000; + $solr = $container->get('VuFind\Search\BackendManager') + ->get('Solr')->getConnector(); + $formatterManager = $container + ->get('VuFind\Hierarchy\TreeDataFormatter\PluginManager'); + return new $requestedName( + $solr, $formatterManager, rtrim($cacheDir, '/') . '/hierarchy', + $filters, $batchSize + ); + } +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/XMLFile.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/XMLFile.php index f6b40040d570e8bcc0af93c7273955b02dcb0e01..612516bb4b228b1beb9d2b8748117f5e17479fa6 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/XMLFile.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/XMLFile.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Source (XML File) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -56,8 +56,7 @@ class XMLFile extends AbstractBase { if (null === $this->basePath) { $settings = $this->getHierarchyDriver()->getTreeSettings(); - $this->basePath = isset($settings['XMLFileDir']) - ? $settings['XMLFileDir'] : ''; + $this->basePath = $settings['XMLFileDir'] ?? ''; } return $this->basePath; } diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/AbstractBase.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/AbstractBase.php index 18361597ab216003701cae3e056f122224d8b890..75a9e6986a934f3d654de8a9bc4c0a585c1969f0 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/AbstractBase.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/AbstractBase.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Renderer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php index 07ddc43a75ff590207f3d579cebec625275c1965..d51686756fbd59189963e2ef656c7705c26cf506 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Renderer for the JS_Tree plugin * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -50,14 +50,26 @@ class JSTree extends AbstractBase */ protected $router = null; + /** + * Whether the collections functionality is enabled + * + * @var bool + */ + protected $collectionsEnabled; + /** * Constructor * - * @param \Zend\Mvc\Controller\Plugin\Url $router Router plugin for urls + * @param \Zend\Mvc\Controller\Plugin\Url $router Router plugin for + * urls + * @param bool $collectionsEnabled Whether the + * collections functionality is enabled */ - public function __construct(\Zend\Mvc\Controller\Plugin\Url $router) - { + public function __construct(\Zend\Mvc\Controller\Plugin\Url $router, + $collectionsEnabled + ) { $this->router = $router; + $this->collectionsEnabled = $collectionsEnabled; } /** @@ -92,8 +104,7 @@ class JSTree extends AbstractBase $hierarchies = []; foreach ($inHierarchies as $hierarchyTopID) { if ($this->getDataSource()->supports($hierarchyTopID)) { - $hierarchies[$hierarchyTopID] = isset($inHierarchiesTitle[$i]) - ? $inHierarchiesTitle[$i] : ''; + $hierarchies[$hierarchyTopID] = $inHierarchiesTitle[$i] ?? ''; } $i++; } @@ -205,8 +216,12 @@ class JSTree extends AbstractBase return $this->getUrlFromRouteCache('collection', $node->id) . '#tabnav'; } else { - $url = $this->getUrlFromRouteCache($node->type, $node->id); - return $node->type == 'collection' + $type = $node->type; + if ('collection' === $type && !$this->collectionsEnabled) { + $type = 'record'; + } + $url = $this->getUrlFromRouteCache($type, $node->id); + return $type === 'collection' ? $url . '#tabnav' : $url . '#tree-' . preg_replace('/\W/', '-', $node->id); } diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..77f61d133bb924513698239a926877564d94b1aa --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * JSTree hierarchy tree renderer plugin factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package HierarchyTree_Renderer + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Hierarchy\TreeRenderer; + +use Interop\Container\ContainerInterface; + +/** + * JSTree hierarchy tree renderer plugin factory. + * + * @category VuFind + * @package HierarchyTree_Renderer + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class JSTreeFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName( + $container->get('ControllerPluginManager')->get('Url'), + !empty($config->Collections->collections) + ); + } +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php index 4ea337b134451953328cf07d3192ac218bf92bb0..f6a8fcb79fe3e07f64b87acd6211d9c45605eadf 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php @@ -2,7 +2,7 @@ /** * Hierarchy tree renderer plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,25 @@ namespace VuFind\Hierarchy\TreeRenderer; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'jstree' => 'VuFind\Hierarchy\TreeRenderer\JSTree' + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Hierarchy\TreeRenderer\JSTree' => + 'VuFind\Hierarchy\TreeRenderer\JSTreeFactory' + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/I18n/ExtendedIniNormalizer.php b/module/VuFind/src/VuFind/I18n/ExtendedIniNormalizer.php index 951d5795a9eddbb252672bfc0579f2bf1358ee25..e05427c2270e631e319709550cf79fed0a63bfba 100644 --- a/module/VuFind/src/VuFind/I18n/ExtendedIniNormalizer.php +++ b/module/VuFind/src/VuFind/I18n/ExtendedIniNormalizer.php @@ -2,7 +2,7 @@ /** * Class to consistently format ExtendedIni language files. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\I18n; + use Zend\I18n\Translator\TextDomain; /** @@ -54,7 +55,7 @@ class ExtendedIniNormalizer $full = $dir . '/' . $file; if ($file != '.' && $file != '..' && is_dir($full)) { $this->normalizeDirectory($full); - } else if (substr($file, -4) == '.ini') { + } elseif (substr($file, -4) == '.ini') { $this->normalizeFile($full); } } diff --git a/module/VuFind/src/VuFind/I18n/TranslatableString.php b/module/VuFind/src/VuFind/I18n/TranslatableString.php index 51db34fa907d4a3c008425ec2d090091d1562de0..1c6ceca53ec8415a4880581efd40e3cbe34d8033 100644 --- a/module/VuFind/src/VuFind/I18n/TranslatableString.php +++ b/module/VuFind/src/VuFind/I18n/TranslatableString.php @@ -2,7 +2,7 @@ /** * Class for translatable string with a special default translation. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * diff --git a/module/VuFind/src/VuFind/I18n/TranslatableStringInterface.php b/module/VuFind/src/VuFind/I18n/TranslatableStringInterface.php index 9445e85f66f2418a5f728c41c8012cce0cac6e8b..53d60ab905bf1f2bd5e35cf320ccb89277f56274 100644 --- a/module/VuFind/src/VuFind/I18n/TranslatableStringInterface.php +++ b/module/VuFind/src/VuFind/I18n/TranslatableStringInterface.php @@ -2,7 +2,7 @@ /** * Interface for translatable strings. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * diff --git a/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php b/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php index f5e43ecde4a0199ebed09344b054d6d21a638a4e..6500db10dee56ac6cf3b522a0be902bcfb0fdd86 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php +++ b/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php @@ -2,7 +2,7 @@ /** * VuFind Translate Adapter ExtendedIni * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,10 @@ * @link https://vufind.org Main Site */ namespace VuFind\I18n\Translator\Loader; -use Zend\I18n\Exception\InvalidArgumentException, - Zend\I18n\Translator\Loader\FileLoaderInterface, - Zend\I18n\Translator\TextDomain; + +use Zend\I18n\Exception\InvalidArgumentException; +use Zend\I18n\Translator\Loader\FileLoaderInterface; +use Zend\I18n\Translator\TextDomain; /** * Handles the language loading and language file parsing diff --git a/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIniReader.php b/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIniReader.php index 93679eecda8082fb9d3c5e4dfb6e400ecbb7504e..c3fbc4bc7728a0934483cad4009e8875f46a2b1c 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIniReader.php +++ b/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIniReader.php @@ -2,7 +2,7 @@ /** * Helper class to load .ini files from disk. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\I18n\Translator\Loader; + use Zend\I18n\Translator\TextDomain; /** diff --git a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareInterface.php b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareInterface.php index 84957e5bac85041612fe3297bae2cf9ef00fad19..0c60cd9398455633ec0dcded9d46f0c8822366e8 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareInterface.php +++ b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareInterface.php @@ -2,7 +2,7 @@ /** * Lightweight translator aware marker interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\I18n\Translator; + use Zend\I18n\Translator\TranslatorInterface; /** diff --git a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php index faff89552ef9f5bcb952c335f326aca7ee547c93..bc654ff435eeb9bb20e7516aec7902a5950752c0 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php +++ b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php @@ -2,7 +2,7 @@ /** * Lightweight translator aware marker interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\I18n\Translator; + use Zend\I18n\Translator\TranslatorInterface; /** @@ -134,7 +135,6 @@ trait TranslatorAwareTrait protected function translateString($str, $tokens = [], $default = null, $domain = 'default' ) { - $msg = (null === $this->translator) ? $str : $this->translator->translate($str, $domain); diff --git a/module/VuFind/src/VuFind/ILS/Connection.php b/module/VuFind/src/VuFind/ILS/Connection.php index 31dc2b086695fc112b1c00ea2b511a7d799dda97..a121275de8b1e5e1f8e47960d26c84e03c839042 100644 --- a/module/VuFind/src/VuFind/ILS/Connection.php +++ b/module/VuFind/src/VuFind/ILS/Connection.php @@ -5,7 +5,7 @@ * This wrapper works with a driver class to pass information from the ILS to * VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -30,9 +30,10 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS; -use VuFind\Exception\ILS as ILSException, - VuFind\ILS\Driver\DriverInterface, - VuFind\I18n\Translator\TranslatorAwareInterface; + +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\ILS\Driver\DriverInterface; use Zend\Log\LoggerAwareInterface; /** @@ -337,7 +338,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface $response['consortium'] = $functionConfig['consortium']; } } else { - $id = isset($params['id']) ? $params['id'] : null; + $id = $params['id'] ?? null; if ($this->checkCapability('getHoldLink', [$id, []])) { $response = ['function' => "getHoldLink"]; } @@ -372,7 +373,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface && $this->checkCapability('cancelHolds', [$params ?: []]) ) { $response = ['function' => "cancelHolds"]; - } else if (isset($this->config->cancel_holds_enabled) + } elseif (isset($this->config->cancel_holds_enabled) && $this->config->cancel_holds_enabled == true && $this->checkCapability('getCancelHoldLink', [$params ?: []]) ) { @@ -407,7 +408,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface && $this->checkCapability('renewMyItems', [$params ?: []]) ) { $response = ['function' => "renewMyItems"]; - } else if (isset($this->config->renewals_enabled) + } elseif (isset($this->config->renewals_enabled) && $this->config->renewals_enabled == true && $this->checkCapability('renewMyItemsLink', [$params ?: []]) ) { @@ -486,7 +487,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface } else { $cancelParams = [ $params ?: [], - isset($params['patron']) ? $params['patron'] : null + $params['patron'] ?? null ]; $check2 = $this->checkCapability( 'getCancelStorageRetrievalRequestLink', $cancelParams @@ -572,7 +573,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface } else { $cancelParams = [ $params ?: [], - isset($params['patron']) ? $params['patron'] : null + $params['patron'] ?? null ]; $check2 = $this->checkCapability( 'getCancelILLRequestLink', $cancelParams @@ -611,6 +612,29 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface return false; } + /** + * Check Historic Loans + * + * A support method for checkFunction(). This is responsible for checking + * the driver configuration to determine if the system supports historic + * loans. + * + * @param array $functionConfig Function configuration + * @param array $params Patron data + * + * @return mixed On success, an associative array with specific function keys + * and values; on failure, false. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function checkMethodgetMyTransactionHistory($functionConfig, $params) + { + if ($this->checkCapability('getMyTransactionHistory', [$params ?: []])) { + return $functionConfig; + } + return false; + } + /** * Get proper help text from the function config * @@ -622,7 +646,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface { if (is_array($helpText)) { $lang = $this->getTranslatorLocale(); - return isset($helpText[$lang]) ? $helpText[$lang] : ''; + return $helpText[$lang] ?? ''; } return $helpText; } diff --git a/module/VuFind/src/VuFind/ILS/ConnectionFactory.php b/module/VuFind/src/VuFind/ILS/ConnectionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e9a7f0f7e847972ef5884808848d0b173734f5f1 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/ConnectionFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * ILS connection factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ILS connection factory + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ConnectionFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $catalog = new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('config')->Catalog, + $container->get('VuFind\ILS\Driver\PluginManager'), + $container->get('VuFind\Config\PluginManager') + ); + return $catalog->setHoldConfig($container->get('VuFind\ILS\HoldSettings')); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/AbstractBase.php b/module/VuFind/src/VuFind/ILS/Driver/AbstractBase.php index 683c70cdba23cdfcc1f9ea4d7e95a0bc10a5f5e8..0d91a4bd169d5e40c23b690fdef7423d33b6cac3 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/AbstractBase.php +++ b/module/VuFind/src/VuFind/ILS/Driver/AbstractBase.php @@ -2,7 +2,7 @@ /** * Default ILS driver base class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,8 +26,6 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use Zend\Cache\Storage\StorageInterface, - VuFind\Cache\KeyGeneratorTrait; /** * Default ILS driver base class. @@ -42,22 +40,6 @@ use Zend\Cache\Storage\StorageInterface, */ abstract class AbstractBase implements DriverInterface { - use KeyGeneratorTrait; - - /** - * Cache for storing ILS data temporarily (e.g. patron blocks) - * - * @var StorageInterface - */ - protected $cache = null; - - /** - * Lifetime of cache (in seconds). - * - * @var int - */ - protected $cacheLifetime = 30; - /** * Driver configuration * @@ -65,18 +47,6 @@ abstract class AbstractBase implements DriverInterface */ protected $config = []; - /** - * Set a cache storage object. - * - * @param StorageInterface $cache Cache storage interface - * - * @return void - */ - public function setCacheStorage(StorageInterface $cache = null) - { - $this->cache = $cache; - } - /** * Set configuration. * @@ -91,72 +61,4 @@ abstract class AbstractBase implements DriverInterface { $this->config = $config; } - - /** - * Helper function for fetching cached data. - * Data is cached for up to $this->cacheLifetime seconds so that it would be - * faster to process e.g. requests where multiple calls to the backend are made. - * - * @param string $key Cache entry key - * - * @return mixed|null Cached entry or null if not cached or expired - */ - protected function getCachedData($key) - { - // No cache object, no cached results! - if (null === $this->cache) { - return null; - } - - $fullKey = $this->getCacheKey($key); - $item = $this->cache->getItem($fullKey); - if (null !== $item) { - // Return value if still valid: - if (time() - $item['time'] < $this->cacheLifetime) { - return $item['entry']; - } - // Clear expired item from cache: - $this->cache->removeItem($fullKey); - } - return null; - } - - /** - * Helper function for storing cached data. - * Data is cached for up to $this->cacheLifetime seconds so that it would be - * faster to process e.g. requests where multiple calls to the backend are made. - * - * @param string $key Cache entry key - * @param mixed $entry Entry to be cached - * - * @return void - */ - protected function putCachedData($key, $entry) - { - // Don't write to cache if we don't have a cache! - if (null === $this->cache) { - return; - } - $item = [ - 'time' => time(), - 'entry' => $entry - ]; - $this->cache->setItem($this->getCacheKey($key), $item); - } - - /** - * Helper function for removing cached data. - * - * @param string $key Cache entry key - * - * @return void - */ - protected function removeCachedData($key) - { - // Don't write to cache if we don't have a cache! - if (null === $this->cache) { - return; - } - $this->cache->removeItem($this->getCacheKey($key)); - } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Aleph.php b/module/VuFind/src/VuFind/ILS/Driver/Aleph.php index 4a251de393d6052b68b9934431c44f0b2384edbe..527c3792e982794e983b06bf4083c35ec0d8a3a1 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Aleph.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Aleph.php @@ -2,7 +2,7 @@ /** * Aleph ILS driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) UB/FU Berlin * @@ -36,8 +36,9 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + +use VuFind\Date\DateException; use VuFind\Exception\ILS as ILSException; -use VuFind\Exception\Date as DateException; /** * Aleph Translator Class @@ -91,7 +92,8 @@ class AlephTranslator if (preg_match("/!!/", $line)) { $line = chop($line); $rgxp = AlephTranslator::regexp($line); - } if (preg_match("/!.*/", $line) || $rgxp == "" || $line == "") { + } + if (preg_match("/!.*/", $line) || $rgxp == "" || $line == "") { } else { $line = str_pad($line, 80); $matches = ""; @@ -150,7 +152,7 @@ class AlephTranslator { $tab15 = $this->tabSubLibraryTranslate($slc); if ($tab15 == null) { - print "tab15 is null!<br>"; + echo "tab15 is null!<br>"; } $findme = $tab15["tab15"] . "|" . $isc . "|" . $ipsc; $result = $this->table15[$findme]; @@ -484,9 +486,9 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $url = "http://$this->host:$this->dlfport/rest-dlf/" . $path; $url = $this->appendQueryString($url, $params); $result = $this->doHTTPRequest($url, $method, $body); - $replyCode = (string) $result->{'reply-code'}; + $replyCode = (string)$result->{'reply-code'}; if ($replyCode != "0000") { - $replyText = (string) $result->{'reply-text'}; + $replyText = (string)$result->{'reply-text'}; $this->logError( "DLF request failed", [ 'url' => $url, 'reply-code' => $replyCode, @@ -657,11 +659,11 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $temp[] = [ 'id' => $id, 'availability' => $availability, - 'status' => (string) $status[0], - 'location' => (string) $location[0], - 'signature' => (string) $signature[0], + 'status' => (string)$status[0], + 'location' => (string)$location[0], + 'signature' => (string)$signature[0], 'reserve' => $reserve, - 'callnumber' => (string) $signature[0] + 'callnumber' => (string)$signature[0] ]; } $holding[] = $temp; @@ -717,7 +719,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -731,15 +733,15 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $params = ['view' => 'full']; if (!empty($patron['id'])) { $params['patron'] = $patron['id']; - } else if (isset($this->defaultPatronId)) { + } elseif (isset($this->defaultPatronId)) { $params['patron'] = $this->defaultPatronId; } $xml = $this->doRestDLFRequest(['record', $resource, 'items'], $params); foreach ($xml->{'items'}->{'item'} as $item) { - $item_status = (string) $item->{'z30-item-status-code'}; // $isc + $item_status = (string)$item->{'z30-item-status-code'}; // $isc // $ipsc: - $item_process_status = (string) $item->{'z30-item-process-status-code'}; - $sub_library_code = (string) $item->{'z30-sub-library-code'}; // $slc + $item_process_status = (string)$item->{'z30-item-process-status-code'}; + $sub_library_code = (string)$item->{'z30-sub-library-code'}; // $slc $z30 = $item->z30; if ($this->translator) { $item_status = $this->translator->tab15Translate( @@ -749,8 +751,8 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $item_status = [ 'opac' => 'Y', 'request' => 'C', - 'desc' => (string) $z30->{'z30-item-status'}, - 'sub_lib_desc' => (string) $z30->{'z30-sub-library'} + 'desc' => (string)$z30->{'z30-item-status'}, + 'sub_lib_desc' => (string)$z30->{'z30-sub-library'} ]; } if ($item_status['opac'] != 'Y') { @@ -758,10 +760,10 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, } $availability = false; //$reserve = ($item_status['request'] == 'C')?'N':'Y'; - $collection = (string) $z30->{'z30-collection'}; + $collection = (string)$z30->{'z30-collection'}; $collection_desc = ['desc' => $collection]; if ($this->translator) { - $collection_code = (string) $item->{'z30-collection-code'}; + $collection_code = (string)$item->{'z30-collection-code'}; $collection_desc = $this->translator->tab40Translate( $collection_code, $sub_library_code ); @@ -769,7 +771,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $requested = false; $duedate = ''; $addLink = false; - $status = (string) $item->{'status'}; + $status = (string)$item->{'status'}; if (in_array($status, $this->available_statuses)) { $availability = true; } @@ -786,7 +788,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, if (preg_match($dueDateWithStatusRegEx, $status, $matches)) { $duedate = $this->parseDate($matches[1]); $requested = (trim($matches[2]) == "Requested"); - } else if (preg_match($dueDateRegEx, $status, $matches)) { + } elseif (preg_match($dueDateRegEx, $status, $matches)) { $duedate = $this->parseDate($matches[1]); } else { $duedate = null; @@ -810,47 +812,136 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, } $item_id = $item->attributes()->href; $item_id = substr($item_id, strrpos($item_id, '/') + 1); - $note = (string) $z30->{'z30-note-opac'}; + $note = (string)$z30->{'z30-note-opac'}; $holding[] = [ 'id' => $id, 'item_id' => $item_id, 'availability' => $availability, - 'status' => (string) $item_status['desc'], + 'status' => (string)$item_status['desc'], 'location' => $sub_library_code, 'reserve' => 'N', - 'callnumber' => (string) $z30->{'z30-call-no'}, - 'duedate' => (string) $duedate, - 'number' => (string) $z30->{'z30-inventory-number'}, - 'barcode' => (string) $z30->{'z30-barcode'}, - 'description' => (string) $z30->{'z30-description'}, + 'callnumber' => (string)$z30->{'z30-call-no'}, + 'duedate' => (string)$duedate, + 'number' => (string)$z30->{'z30-inventory-number'}, + 'barcode' => (string)$z30->{'z30-barcode'}, + 'description' => (string)$z30->{'z30-description'}, 'notes' => ($note == null) ? null : [$note], 'is_holdable' => true, 'addLink' => $addLink, 'holdtype' => 'hold', /* below are optional attributes*/ - 'collection' => (string) $collection, - 'collection_desc' => (string) $collection_desc['desc'], - 'callnumber_second' => (string) $z30->{'z30-call-no-2'}, - 'sub_lib_desc' => (string) $item_status['sub_lib_desc'], - 'no_of_loans' => (string) $z30->{'$no_of_loans'}, - 'requested' => (string) $requested + 'collection' => (string)$collection, + 'collection_desc' => (string)$collection_desc['desc'], + 'callnumber_second' => (string)$z30->{'z30-call-no-2'}, + 'sub_lib_desc' => (string)$item_status['sub_lib_desc'], + 'no_of_loans' => (string)$z30->{'$no_of_loans'}, + 'requested' => (string)$requested ]; } return $holding; } /** - * Get Patron Transaction History + * Get Patron Loan History * - * @param array $user The patron array from patronLogin + * @param array $user The patron array from patronLogin + * @param array $params Parameters * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException - * @return array Array of the patron's transactions on success. + * @return array Array of the patron's historic loans on success. */ - public function getMyHistory($user) + public function getMyTransactionHistory($user, $params = null) { - return $this->getMyTransactions($user, true); + $userId = $user['id']; + $historicLoans = []; + $requestParams = [ + "view" => "full", + "type" => "history", + ]; + + $xml = $this->doRestDLFRequest( + ['patron', $userId, 'circulationActions', 'loans'], $requestParams + ); + + foreach ($xml->xpath('//loan') as $item) { + $z36h = $item->z36h; + $z13 = $item->z13; + $z30 = $item->z30; + $group = $item->xpath('@href'); + $group = substr(strrchr($group[0], "/"), 1); + $location = (string)$z36h->{'z36_pickup_location'}; + $reqnum = (string)$z36h->{'z36-doc-number'} + . (string)$z36h->{'z36-item-sequence'} + . (string)$z36h->{'z36-sequence'}; + + $due = (string)$z36h->{'z36h-due-date'}; + $returned = (string)$z36h->{'z36h-returned-date'}; + $issued = (string)$z36h->{'z36h-loan-date'}; + $title = (string)$z13->{'z13-title'}; + $author = (string)$z13->{'z13-author'}; + $isbn = (string)$z13->{'z13-isbn-issn'}; + $barcode = (string)$z30->{'z30-barcode'}; + + $historicLoans[] = [ + 'id' => (string)$z30->{'z30-doc-number'}, + 'item_id' => $group, + 'location' => $location, + 'title' => $title, + 'author' => $author, + 'isbn' => [$isbn], + 'reqnum' => $reqnum, + 'barcode' => $barcode, + 'checkoutDate' => $this->parseDate($issued), + 'dueDate' => $this->parseDate($due), + 'returnDate' => $this->parseDate($returned), + '_checkoutDate' => $issued, + '_dueDate' => $due, + '_returnDate' => $returned, + ]; + } + + if (isset($params['sort'])) { + switch ($params['sort']) { + case 'checkout asc': + $sorter = function ($a, $b) { + return strcmp($a['_checkoutDate'], $b['_checkoutDate']); + }; + break; + case 'return desc': + $sorter = function ($a, $b) { + return strcmp($b['_returnDate'], $a['_returnDate']); + }; + break; + case 'return asc': + $sorter = function ($a, $b) { + return strcmp($a['_returnDate'], $b['_returnDate']); + }; + break; + case 'due desc': + $sorter = function ($a, $b) { + return strcmp($b['_dueDate'], $a['_dueDate']); + }; + break; + case 'due asc': + $sorter = function ($a, $b) { + return strcmp($a['_dueDate'], $b['_dueDate']); + }; + break; + default: + $sorter = function ($a, $b) { + return strcmp($b['_checkoutDate'], $a['_checkoutDate']); + }; + break; + } + + usort($historicLoans, $sorter); + } + + return [ + 'count' => count($historicLoans), + 'transactions' => $historicLoans, + ]; } /** @@ -859,25 +950,21 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, * This is responsible for retrieving all transactions (i.e. checked out items) * by a specific patron. * - * @param array $user The patron array from patronLogin - * @param bool $history Include history of transactions (true) or just get - * current ones (false). + * @param array $user The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return array Array of the patron's transactions on success. */ - public function getMyTransactions($user, $history = false) + public function getMyTransactions($user) { $userId = $user['id']; $transList = []; $params = ["view" => "full"]; - if ($history) { - $params["type"] = "history"; - } $xml = $this->doRestDLFRequest( ['patron', $userId, 'circulationActions', 'loans'], $params ); + foreach ($xml->xpath('//loan') as $item) { $z36 = $item->z36; $z13 = $item->z13; @@ -888,25 +975,22 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, //$docno = (string) $z36->{'z36-doc-number'}; //$itemseq = (string) $z36->{'z36-item-sequence'}; //$seq = (string) $z36->{'z36-sequence'}; - $location = (string) $z36->{'z36_pickup_location'}; - $reqnum = (string) $z36->{'z36-doc-number'} - . (string) $z36->{'z36-item-sequence'} - . (string) $z36->{'z36-sequence'}; - $due = $returned = null; - if ($history) { - $due = $item->z36h->{'z36h-due-date'}; - $returned = $item->z36h->{'z36h-returned-date'}; - } else { - $due = (string) $z36->{'z36-due-date'}; - } - //$loaned = (string) $z36->{'z36-loan-date'}; - $title = (string) $z13->{'z13-title'}; - $author = (string) $z13->{'z13-author'}; - $isbn = (string) $z13->{'z13-isbn-issn'}; - $barcode = (string) $z30->{'z30-barcode'}; + + $location = (string)$z36->{'z36_pickup_location'}; + $reqnum = (string)$z36->{'z36-doc-number'} + . (string)$z36->{'z36-item-sequence'} + . (string)$z36->{'z36-sequence'}; + + $due = (string)$z36->{'z36-due-date'}; + $issued = (string)$z36->{'z36-loan-date'}; + $title = (string)$z13->{'z13-title'}; + $author = (string)$z13->{'z13-author'}; + $isbn = (string)$z13->{'z13-isbn-issn'}; + $barcode = (string)$z30->{'z30-barcode'}; + $transList[] = [ //'type' => $type, - 'id' => ($history) ? null : $this->barcodeToID($barcode), + 'id' => $this->barcodeToID($barcode), 'item_id' => $group, 'location' => $location, 'title' => $title, @@ -914,14 +998,15 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, 'isbn' => [$isbn], 'reqnum' => $reqnum, 'barcode' => $barcode, + 'issuedate' => $this->parseDate($issued), 'duedate' => $this->parseDate($due), - 'returned' => $this->parseDate($returned), //'holddate' => $holddate, //'delete' => $delete, 'renewable' => true, //'create' => $this->parseDate($create) ]; } + return $transList; } @@ -982,7 +1067,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, * * @param array $user The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -1001,23 +1086,23 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $delete = $item->xpath('@delete'); $href = $item->xpath('@href'); $item_id = substr($href[0], strrpos($href[0], '/') + 1); - if ((string) $z37->{'z37-request-type'} == "Hold Request" || true) { + if ((string)$z37->{'z37-request-type'} == "Hold Request" || true) { $type = "hold"; //$docno = (string) $z37->{'z37-doc-number'}; //$itemseq = (string) $z37->{'z37-item-sequence'}; - $seq = (string) $z37->{'z37-sequence'}; - $location = (string) $z37->{'z37-pickup-location'}; - $reqnum = (string) $z37->{'z37-doc-number'} - . (string) $z37->{'z37-item-sequence'} - . (string) $z37->{'z37-sequence'}; - $expire = (string) $z37->{'z37-end-request-date'}; - $create = (string) $z37->{'z37-open-date'}; - $holddate = (string) $z37->{'z37-hold-date'}; - $title = (string) $z13->{'z13-title'}; - $author = (string) $z13->{'z13-author'}; - $isbn = (string) $z13->{'z13-isbn-issn'}; - $barcode = (string) $z30->{'z30-barcode'}; - $status = (string) $z37->{'z37-status'}; + $seq = (string)$z37->{'z37-sequence'}; + $location = (string)$z37->{'z37-pickup-location'}; + $reqnum = (string)$z37->{'z37-doc-number'} + . (string)$z37->{'z37-item-sequence'} + . (string)$z37->{'z37-sequence'}; + $expire = (string)$z37->{'z37-end-request-date'}; + $create = (string)$z37->{'z37-open-date'}; + $holddate = (string)$z37->{'z37-hold-date'}; + $title = (string)$z13->{'z13-title'}; + $author = (string)$z13->{'z13-author'}; + $isbn = (string)$z13->{'z13-isbn-issn'}; + $barcode = (string)$z30->{'z30-barcode'}; + $status = (string)$z37->{'z37-status'}; if ($holddate == "00000000") { $holddate = null; } else { @@ -1094,7 +1179,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, } $statuses[$id] = [ 'success' => false, 'status' => 'cancel_hold_failed', - 'sysMessage' => (string) $message + 'sysMessage' => (string)$message ]; } else { $count++; @@ -1113,7 +1198,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, * * @param array $user The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -1132,18 +1217,18 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $z13 = $item->z13; $z30 = $item->z30; //$delete = $item->xpath('@delete'); - $title = (string) $z13->{'z13-title'}; - $transactiondate = date('d-m-Y', strtotime((string) $z31->{'z31-date'})); - $transactiontype = (string) $z31->{'z31-credit-debit'}; - $id = (string) $z13->{'z13-doc-number'}; - $barcode = (string) $z30->{'z30-barcode'}; - $checkout = (string) $z31->{'z31-date'}; + $title = (string)$z13->{'z13-title'}; + $transactiondate = date('d-m-Y', strtotime((string)$z31->{'z31-date'})); + $transactiontype = (string)$z31->{'z31-credit-debit'}; + $id = (string)$z13->{'z13-doc-number'}; + $barcode = (string)$z30->{'z30-barcode'}; + $checkout = (string)$z31->{'z31-date'}; $id = $this->barcodeToID($barcode); $mult = ($transactiontype == "Credit") ? 100 : -100; $amount - = (float)(preg_replace("/[\(\)]/", "", (string) $z31->{'z31-sum'})) + = (float)(preg_replace("/[\(\)]/", "", (string)$z31->{'z31-sum'})) * $mult; - $cashref = (string) $z31->{'z31-sequence'}; + $cashref = (string)$z31->{'z31-sequence'}; //$cashdate = date('d-m-Y', strtotime((string) $z31->{'z31-date'})); $balance = 0; @@ -1223,17 +1308,17 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, 'library' => $user['college'], 'bor_id' => $user['id'] ], true ); - $id = (string) $xml->z303->{'z303-id'}; - $address1 = (string) $xml->z304->{'z304-address-2'}; - $address2 = (string) $xml->z304->{'z304-address-3'}; - $zip = (string) $xml->z304->{'z304-zip'}; - $phone = (string) $xml->z304->{'z304-telephone'}; - $barcode = (string) $xml->z304->{'z304-address-0'}; - $group = (string) $xml->z305->{'z305-bor-status'}; - $expiry = (string) $xml->z305->{'z305-expiry-date'}; - $credit_sum = (string) $xml->z305->{'z305-sum'}; - $credit_sign = (string) $xml->z305->{'z305-credit-debit'}; - $name = (string) $xml->z303->{'z303-name'}; + $id = (string)$xml->z303->{'z303-id'}; + $address1 = (string)$xml->z304->{'z304-address-2'}; + $address2 = (string)$xml->z304->{'z304-address-3'}; + $zip = (string)$xml->z304->{'z304-zip'}; + $phone = (string)$xml->z304->{'z304-telephone'}; + $barcode = (string)$xml->z304->{'z304-address-0'}; + $group = (string)$xml->z305->{'z305-bor-status'}; + $expiry = (string)$xml->z305->{'z305-expiry-date'}; + $credit_sum = (string)$xml->z305->{'z305-sum'}; + $credit_sign = (string)$xml->z305->{'z305-credit-debit'}; + $name = (string)$xml->z303->{'z303-name'}; if (strstr($name, ",")) { list($lastname, $firstname) = explode(",", $name); } else { @@ -1365,13 +1450,13 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $patron['college'] = $this->sublibadm["$home_lib"]; } } - $patron['id'] = (string) $id; - $patron['barcode'] = (string) $user; - $patron['firstname'] = (string) $firstName; - $patron['lastname'] = (string) $lastName; - $patron['cat_username'] = (string) $user; + $patron['id'] = (string)$id; + $patron['barcode'] = (string)$user; + $patron['firstname'] = (string)$firstName; + $patron['lastname'] = (string)$lastName; + $patron['cat_username'] = (string)$user; $patron['cat_password'] = $password; - $patron['email'] = (string) $email_addr; + $patron['email'] = (string)$email_addr; $patron['major'] = null; return $patron; } @@ -1397,8 +1482,8 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, if ($part) { foreach ($part[0]->children() as $node) { $arr = $node->attributes(); - $code = (string) $arr['code']; - $loc_name = (string) $node; + $code = (string)$arr['code']; + $loc_name = (string)$node; $locations[$code] = $loc_name; } } else { @@ -1537,9 +1622,9 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $xml = $this->doXRequest( "find", ["base" => $base, "request" => "BAR=$bar"], false ); - $docs = (int) $xml->{"no_records"}; + $docs = (int)$xml->{"no_records"}; if ($docs == 1) { - $set = (string) $xml->{"set_number"}; + $set = (string)$xml->{"set_number"}; $result = $this->doXRequest( "present", ["set_number" => $set, "set_entry" => "1"], false @@ -1568,12 +1653,12 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, { if ($date == null || $date == "") { return ""; - } else if (preg_match("/^[0-9]{8}$/", $date) === 1) { // 20120725 + } elseif (preg_match("/^[0-9]{8}$/", $date) === 1) { // 20120725 return $this->dateConverter->convertToDisplayDate('Ynd', $date); - } else if (preg_match("/^[0-9]+\/[A-Za-z]{3}\/[0-9]{4}$/", $date) === 1) { + } elseif (preg_match("/^[0-9]+\/[A-Za-z]{3}\/[0-9]{4}$/", $date) === 1) { // 13/jan/2012 return $this->dateConverter->convertToDisplayDate('d/M/Y', $date); - } else if (preg_match("/^[0-9]+\/[0-9]+\/[0-9]{4}$/", $date) === 1) { + } elseif (preg_match("/^[0-9]+\/[0-9]+\/[0-9]{4}$/", $date) === 1) { // 13/7/2012 return $this->dateConverter->convertToDisplayDate('d/m/Y', $date); } else { @@ -1582,8 +1667,29 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, } /** - * Public Function which retrieves renew, hold and cancel settings from the - * driver ini file. + * Helper method to determine whether or not a certain method can be + * called on this driver. Required method for any smart drivers. + * + * @param string $method The name of the called method. + * @param array $params Array of passed parameters + * + * @return bool True if the method can be called with the given parameters, + * false otherwise. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsMethod($method, $params) + { + // Loan history is only available if properly configured + if ($method == 'getMyTransactionHistory') { + return !empty($this->config['TransactionHistory']['enabled']); + } + return is_callable([$this, $method]); + } + + /** + * Public Function which retrieves historic loan, renew, hold and cancel + * settings from the driver ini file. * * @param string $func The name of the feature to be checked * @param array $params Optional feature-specific parameters (array) @@ -1603,6 +1709,21 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, "extraHoldFields" => "comments:requiredByDate:pickUpLocation", "defaultRequiredDate" => "0:1:0" ]; + } elseif ('getMyTransactionHistory' === $func) { + if (empty($this->config['TransactionHistory']['enabled'])) { + return false; + } + return [ + 'sort' => [ + 'checkout desc' => 'sort_checkout_date_desc', + 'checkout asc' => 'sort_checkout_date_asc', + 'return desc' => 'sort_return_date_desc', + 'return asc' => 'sort_return_date_asc', + 'due desc' => 'sort_due_date_desc', + 'due asc' => 'sort_due_date_asc' + ], + 'default_sort' => 'checkout desc', + ]; } else { return []; } @@ -1675,7 +1796,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, // locationId in pickupLocations array reset($pickupLocations); return key($pickupLocations); - } else if (isset($this->preferredPickUpLocations)) { + } elseif (isset($this->preferredPickUpLocations)) { return $this->preferredPickUpLocations[0]; } else { throw new ILSException( diff --git a/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php b/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a0a303d7be42b075aaae19b5390bdce5ae0aebc9 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Aleph ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Aleph ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AlephFactory extends DriverWithDateConverterFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return parent::__invoke( + $container, $requestedName, [$container->get('VuFind\Cache\Manager')] + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/Alma.php b/module/VuFind/src/VuFind/ILS/Driver/Alma.php new file mode 100644 index 0000000000000000000000000000000000000000..2e4c4a9e53ab5f6ad43f3b5205e0650ae3c6d50f --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/Alma.php @@ -0,0 +1,1507 @@ +<?php +/** + * Alma ILS Driver + * + * PHP version 5 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki + */ +namespace VuFind\ILS\Driver; + +use SimpleXMLElement; +use VuFind\Exception\ILS as ILSException; +use Zend\Http\Headers; + +/** + * Alma ILS Driver + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki + */ +class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface +{ + use \VuFindHttp\HttpServiceAwareTrait; + use CacheTrait; + + /** + * Alma API base URL. + * + * @var string + */ + protected $baseUrl; + + /** + * Alma API key. + * + * @var string + */ + protected $apiKey; + + /** + * Date converter + * + * @var \VuFind\Date\Converter + */ + protected $dateConverter; + + /** + * Configuration loader + * + * @var \VuFind\Config\PluginManager + */ + protected $configLoader; + + /** + * Constructor + * + * @param \VuFind\Date\Converter $dateConverter Date converter object + * @param \VuFind\Config\PluginManager $configLoader Plugin manager + */ + public function __construct( + \VuFind\Date\Converter $dateConverter, + \VuFind\Config\PluginManager $configLoader + ) { + $this->dateConverter = $dateConverter; + $this->configLoader = $configLoader; + } + + /** + * Initialize the driver. + * + * Validate configuration and perform all resource-intensive tasks needed to + * make the driver active. + * + * @throws ILSException + * @return void + */ + public function init() + { + if (empty($this->config)) { + throw new ILSException('Configuration needs to be set.'); + } + $this->baseUrl = $this->config['Catalog']['apiBaseUrl']; + $this->apiKey = $this->config['Catalog']['apiKey']; + } + + /** + * Make an HTTP request against Alma + * + * @param string $path Path to retrieve from API (excluding base + * URL/API key) + * @param array $paramsGet Additional GET params + * @param array $paramsPost Additional POST params + * @param string $method GET or POST. Default is GET. + * @param string $rawBody Request body. + * @param Headers|array $headers Add headers to the call. + * + * @throws ILSException + * @return NULL|SimpleXMLElement + */ + protected function makeRequest( + $path, + $paramsGet = [], + $paramsPost = [], + $method = 'GET', + $rawBody = null, + $headers = null + ) { + // Set some variables + $result = null; + $statusCode = null; + $returnValue = null; + + try { + // Set API key if it is not already available in the GET params + if (!isset($paramsGet['apiKey'])) { + $paramsGet['apiKey'] = $this->apiKey; + } + + // Create the API URL + $url = strpos($path, '://') === false ? $this->baseUrl . $path : $path; + + // Create client with API URL + $client = $this->httpService->createClient($url); + + // Set method + $client->setMethod($method); + + // Set other GET parameters + if ($method == 'GET') { + $client->setParameterGet($paramsGet); + } else { + // Always set API key as GET parameter + $client->setParameterGet(['apiKey' => $paramsGet['apiKey']]); + + // Set POST parameters + if ($method == 'POST') { + $client->setParameterPost($paramsPost); + } + } + + // Set body if applicable + if (isset($rawBody)) { + $client->setRawBody($rawBody); + } + + // Set headers if applicable + if (isset($headers)) { + $client->setHeaders($headers); + } + + // Execute HTTP call + $result = $client->send(); + } catch (\Exception $e) { + throw new ILSException($e->getMessage()); + } + + // Get the HTTP status code + $statusCode = $result->getStatusCode(); + + // Check for error + if ($result->isServerError()) { + throw new ILSException('HTTP error code: ' . $statusCode, $statusCode); + } + + $answer = $result->getBody(); + $answer = str_replace('xmlns=', 'ns=', $answer); + $xml = simplexml_load_string($answer); + + if ($result->isSuccess()) { + if (!$xml && $result->isServerError()) { + throw new ILSException( + 'XML is not valid or HTTP error, URL: ' . $url . + ', HTTP status code: ' . $statusCode, $statusCode + ); + } + $returnValue = $xml; + } else { + $almaErrorMsg = $xml->errorList->error[0]->errorMessage; + error_log( + '[ALMA] ' . $almaErrorMsg . ' | Call to: ' . $client->getUri() . + '. GET params: ' . var_export($paramsGet, true) . '. POST params: ' . + var_export($paramsPost, true) . '. Result body: ' . + $result->getBody() . '. HTTP status code: ' . $statusCode + ); + throw new ILSException( + 'Alma error message: ' . $almaErrorMsg . ' | HTTP error code: ' . + $statusCode, $statusCode + ); + } + + return $returnValue; + } + + /** + * Given an item, return the availability status. + * + * @param \SimpleXMLElement $item Item data + * + * @return bool + */ + protected function getAvailabilityFromItem($item) + { + return (string)$item->item_data->base_status === '1'; + } + + /** + * Get Holding + * + * This is responsible for retrieving the holding information of a certain + * record. + * + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * + * @return array On success an associative array with the following keys: + * id, source, availability (boolean), status, location, + * reserve, callnumber, duedate, returnDate, number, + * barcode, item_notes, item_id, holding_id, addLink. + */ + public function getHolding($id, array $patron = null) + { + // Get config data: + $fulfillementUnits = $this->config['FulfillmentUnits'] ?? null; + $requestableConfig = $this->config['Requestable'] ?? null; + + $results = []; + $copyCount = 0; + $bibPath = '/bibs/' . urlencode($id) . '/holdings'; + if ($holdings = $this->makeRequest($bibPath)) { + foreach ($holdings->holding as $holding) { + $holdingId = (string)$holding->holding_id; + $locationCode = (string)$holding->location; + $addLink = false; + if ($fulfillementUnits != null && $requestableConfig != null) { + $addLink = $this->requestsAllowed( + $fulfillementUnits, + $locationCode, + $requestableConfig, + $patron + ); + } + + $itemPath = $bibPath . '/' . urlencode($holdingId) . '/items'; + if ($currentItems = $this->makeRequest($itemPath)) { + foreach ($currentItems->item as $item) { + $itemId = (string)$item->item_data->pid; + $barcode = (string)$item->item_data->barcode; + $processType = (string)$item->item_data->process_type; + $itemNotes = null; + if ($item->item_data->public_note != null + && !empty($item->item_data->public_note) + ) { + $itemNotes = [(string)$item->item_data->public_note]; + } + $requested = ((string)$item->item_data->requested == 'false') + ? false + : true; + + $number = ++$copyCount; + $description = null; + if ($item->item_data->description != null + && !empty($item->item_data->description) + ) { + $number = (string)$item->item_data->description; + $description = (string)$item->item_data->description; + } + + // For some data we need to do additional API calls + // due to the Alma API architecture + $duedate = ($requested) ? 'requested' : null; + if ($processType == 'LOAN' && !$requested) { + $loanDataPath = '/bibs/' . urlencode($id) . '/holdings/' + . urlencode($holdingId) . '/items/' + . urlencode($itemId) . '/loans'; + $loanData = $this->makeRequest($loanDataPath); + $loan = $loanData->item_loan; + $duedate = $this->parseDate((string)$loan->due_date); + } + + $results[] = [ + 'id' => $id, + 'source' => 'Solr', + 'availability' => $this->getAvailabilityFromItem($item), + 'status' => (string)$item + ->item_data + ->base_status[0] + ->attributes()['desc'], + 'location' => $locationCode, + 'reserve' => 'N', // TODO: support reserve status + 'callnumber' => (string)$item->holding_data->call_number, + 'duedate' => $duedate, + 'returnDate' => false, // TODO: support recent returns + 'number' => $number,//++$copyCount, + 'barcode' => empty($barcode) ? 'n/a' : $barcode, + 'item_notes' => $itemNotes, + 'item_id' => $itemId, + 'holding_id' => $holdingId, + 'addLink' => $addLink, + // For Alma title-level hold requests + 'description' => $description + ]; + } + } + } + } + + return $results; + } + + /** + * Check if the user is allowed to place requests for an Alma fulfillment + * unit in general. We check for blocks on the patron account that could + * block a request in getRequestBlocks(). + * + * @param array $fulfillementUnits An array of fulfillment units and associated + * locations from Alma.ini (see section + * [FulfillmentUnits]) + * @param string $locationCode The location code of the holding to be + * checked + * @param array $requestableConfig An array of fulfillment units and associated + * patron groups and their request policy from + * Alma.ini (see section [Requestable]) + * @param array $patron An array with the patron details (username + * and password) + * + * @return boolean true if the the patron is allowed to place + * requests on holdings of this fulfillment + * unit, false otherwise. + * @author Michael Birkner + */ + protected function requestsAllowed( + $fulfillementUnits, + $locationCode, + $requestableConfig, + $patron + ) { + $requestsAllowed = false; + + // Get user group code + $cacheId = 'alma|user|' . $patron['cat_username'] . '|group_code'; + $userGroupCode = $this->getCachedData($cacheId); + if ($userGroupCode === null) { + $profile = $this->getMyProfile($patron); + $userGroupCode = (string)$profile['group_code']; + } + + // Get the fulfillment unit of the location. + $locationFulfillmentUnit = $this->getFulfillmentUnitByLocation( + $locationCode, + $fulfillementUnits + ); + + // Check if the group of the currently logged in user is allowed to place + // requests on items belonging to current fulfillment unit + if (($locationFulfillmentUnit != null && !empty($locationFulfillmentUnit)) + && ($userGroupCode != null && !empty($userGroupCode)) + ) { + $requestsAllowed = false; + if ($requestableConfig[$locationFulfillmentUnit][$userGroupCode] == 'Y' + ) { + $requestsAllowed = true; + } + } + + return $requestsAllowed; + } + + /** + * Check for request blocks. + * + * @param array $patron The patron array with username and password + * + * @return array|boolean An array of block messages or false if there are no + * blocks + * @author Michael Birkner + */ + public function getRequestBlocks($patron) + { + return $this->getAccountBlocks($patron); + } + + /** + * Check for account blocks in Alma and cache them. + * + * @param array $patron The patron array with username and password + * + * @return array|boolean An array of block messages or false if there are no + * blocks + * @author Michael Birkner + */ + public function getAccountBlocks($patron) + { + $patronId = $patron['cat_username']; + $cacheId = 'alma|user|' . $patronId . '|blocks'; + $cachedBlocks = $this->getCachedData($cacheId); + if ($cachedBlocks !== null) { + return $cachedBlocks; + } + + $xml = $this->makeRequest('/users/' . $patron['cat_username']); + if ($xml == null || empty($xml)) { + return false; + } + + $userBlocks = $xml->user_blocks->user_block; + if ($userBlocks == null || empty($userBlocks)) { + return false; + } + + $blocks = []; + foreach ($userBlocks as $block) { + $blockStatus = (string)$block->block_status; + if ($blockStatus === 'ACTIVE') { + $blockNote = (isset($block->block_note)) + ? (string)$block->block_note + : null; + $blockDesc = (string)$block->block_description->attributes()->desc; + $blockDesc = ($blockNote != null) + ? $blockDesc . '. ' . $blockNote + : $blockDesc; + $blocks[] = $blockDesc; + } + } + + if (!empty($blocks)) { + $this->putCachedData($cacheId, $blocks); + return $blocks; + } else { + $this->putCachedData($cacheId, false); + return false; + } + } + + /** + * Get an Alma fulfillment unit by an Alma location. + * + * @param string $locationCode A location code, e. g. "SCI" + * @param array $fulfillmentUnits An array of fulfillment units with all its + * locations. + * + * @return string|NULL Null if the location was not found or a + * string specifying the fulfillment unit of + * the location that was found. + * @author Michael Birkner + */ + protected function getFulfillmentUnitByLocation($locationCode, $fulfillmentUnits) + { + foreach ($fulfillmentUnits as $key => $val) { + if (array_search($locationCode, $val) !== false) { + return $key; + } + } + return null; + } + + /** + * Create a user in Alma via API call + * + * @param array $formParams The data from the "create new account" form + * + * @throws \VuFind\Exception\Auth + * + * @return NULL|SimpleXMLElement + * @author Michael Birkner + */ + public function createAlmaUser($formParams) + { + + // Get config for creating new Alma users from Alma.ini + $newUserConfig = $this->config['NewUser']; + + // Check if config params are all set + $configParams = [ + 'recordType', 'userGroup', 'preferredLanguage', + 'accountType', 'status', 'emailType', 'idType' + ]; + foreach ($configParams as $configParam) { + if (!isset($newUserConfig[$configParam]) + || empty(trim($newUserConfig[$configParam])) + ) { + $errorMessage = 'Configuration "' . $configParam . '" is not set ' . + 'in Alma.ini in the [NewUser] section!'; + error_log('[ALMA]: ' . $errorMessage); + throw new \VuFind\Exception\Auth($errorMessage); + } + } + + // Calculate expiry date based on config in Alma.ini + $dateNow = new \DateTime('now'); + $expiryDate = null; + if (isset($newUserConfig['expiryDate']) + && !empty(trim($newUserConfig['expiryDate'])) + ) { + try { + $expiryDate = $dateNow->add( + new \DateInterval($newUserConfig['expiryDate']) + ); + } catch (\Exception $exception) { + $errorMessage = 'Configuration "expiryDate" in Alma.ini (see ' . + '[NewUser] section) has the wrong format!'; + error_log('[ALMA]: ' . $errorMessage); + throw new \VuFind\Exception\Auth($errorMessage); + } + } else { + $expiryDate = $dateNow->add(new \DateInterval('P1Y')); + } + $expiryDateXml = ($expiryDate != null) + ? '<expiry_date>' . $expiryDate->format('Y-m-d') . 'Z</expiry_date>' + : ''; + + // Calculate purge date based on config in Alma.ini + $purgeDate = null; + if (isset($newUserConfig['purgeDate']) + && !empty(trim($newUserConfig['purgeDate'])) + ) { + try { + $purgeDate = $dateNow->add( + new \DateInterval($newUserConfig['purgeDate']) + ); + } catch (\Exception $exception) { + $errorMessage = 'Configuration "purgeDate" in Alma.ini (see ' . + '[NewUser] section) has the wrong format!'; + error_log('[ALMA]: ' . $errorMessage); + throw new \VuFind\Exception\Auth($errorMessage); + } + } + $purgeDateXml = ($purgeDate != null) + ? '<purge_date>' . $purgeDate->format('Y-m-d') . 'Z</purge_date>' + : ''; + + // Create user XML for Alma API + $userXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + . '<user>' + . '<record_type>' . $this->config['NewUser']['recordType'] . '</record_type>' + . '<first_name>' . $formParams['firstname'] . '</first_name>' + . '<last_name>' . $formParams['lastname'] . '</last_name>' + . '<user_group>' . $this->config['NewUser']['userGroup'] . '</user_group>' + . '<preferred_language>' . $this->config['NewUser']['preferredLanguage'] . + '</preferred_language>' + . $expiryDateXml + . $purgeDateXml + . '<account_type>' . $this->config['NewUser']['accountType'] . + '</account_type>' + . '<status>' . $this->config['NewUser']['status'] . '</status>' + . '<contact_info>' + . '<emails>' + . '<email preferred="true">' + . '<email_address>' . $formParams['email'] . '</email_address>' + . '<email_types>' + . '<email_type>' . $this->config['NewUser']['emailType'] . '</email_type>' + . '</email_types>' + . '</email>' + . '</emails>' + . '</contact_info>' + . '<user_identifiers>' + . '<user_identifier>' + . '<id_type>' . $this->config['NewUser']['idType'] . '</id_type>' + . '<value>' . $formParams['username'] . '</value>' + . '</user_identifier>' + . '</user_identifiers>' + . '</user>'; + + // Remove whitespaces from XML + $userXml = preg_replace("/\n/i", "", $userXml); + $userXml = preg_replace("/>\s*</i", "><", $userXml); + + // Create user in Alma + $almaAnswer = $this->makeRequest( + '/users', + [], + [], + 'POST', + $userXml, + ['Content-Type' => 'application/xml'] + ); + + // Return the XML from Alma on success. On error, an exception is thrown + // in makeRequest + return $almaAnswer; + } + + /** + * Patron Login + * + * This is responsible for authenticating a patron against the catalog. + * + * @param string $barcode The patrons barcode. + * @param string $password The patrons password. + * + * @return string[]|NULL + */ + public function patronLogin($barcode, $password) + { + // Create array of get parameters for API call + $getParams = [ + 'user_id_type' => 'all_unique', + 'view' => 'brief', + 'expand' => 'none' + ]; + + // Check for patron in Alma + $response = $this->makeRequest('/users/' . urlencode($barcode), $getParams); + + // Test once we have access + if ($response != null) { + return [ + 'cat_username' => trim($barcode), + 'cat_password' => trim($password) + ]; + } + + return null; + } + + /** + * Get Patron Profile + * + * This is responsible for retrieving the profile for a specific patron. + * + * @param array $patron The patron array + * + * @return array Array of the patron's profile data on success. + */ + public function getMyProfile($patron) + { + $patronId = $patron['cat_username']; + $xml = $this->makeRequest('/users/' . $patronId); + if (empty($xml)) { + return []; + } + $profile = [ + 'firstname' => (isset($xml->first_name)) + ? (string)$xml->first_name + : null, + 'lastname' => (isset($xml->last_name)) + ? (string)$xml->last_name + : null, + 'group' => (isset($xml->user_group['desc'])) + ? (string)$xml->user_group['desc'] + : null, + 'group_code' => (isset($xml->user_group)) + ? (string)$xml->user_group + : null + ]; + $contact = $xml->contact_info; + if ($contact) { + if ($contact->addresses) { + $address = $contact->addresses[0]->address; + $profile['address1'] = (isset($address->line1)) + ? (string)$address->line1 + : null; + $profile['address2'] = (isset($address->line2)) + ? (string)$address->line2 + : null; + $profile['address3'] = (isset($address->line3)) + ? (string)$address->line3 + : null; + $profile['zip'] = (isset($address->postal_code)) + ? (string)$address->postal_code + : null; + $profile['city'] = (isset($address->city)) + ? (string)$address->city + : null; + $profile['country'] = (isset($address->country)) + ? (string)$address->country + : null; + } + if ($contact->phones) { + $profile['phone'] = (isset($contact->phones[0]->phone->phone_number)) + ? (string)$contact->phones[0]->phone->phone_number + : null; + } + } + + // Cache the user group code + $cacheId = 'alma|user|' . $patronId . '|group_code'; + $this->putCachedData($cacheId, $profile['group_code'] ?? null); + + return $profile; + } + + /** + * Get Patron Fines + * + * This is responsible for retrieving all fines by a specific patron. + * + * @param array $patron The patron array from patronLogin + * + * @return mixed Array of the patron's fines on success. + */ + public function getMyFines($patron) + { + $xml = $this->makeRequest( + '/users/' . $patron['cat_username'] . '/fees' + ); + $fineList = []; + foreach ($xml as $fee) { + $checkout = (string)$fee->status_time; + $fineList[] = [ + "title" => (string)$fee->type, + "amount" => $fee->original_amount * 100, + "balance" => $fee->balance * 100, + "checkout" => $this->dateConverter->convert( + 'Y-m-d H:i', + 'm-d-Y', + $checkout + ), + "fine" => (string)$fee->type['desc'] + ]; + } + return $fineList; + } + + /** + * Get Patron Holds + * + * This is responsible for retrieving all holds by a specific patron. + * + * @param array $patron The patron array from patronLogin + * + * @return mixed Array of the patron's holds on success. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getMyHolds($patron) + { + $xml = $this->makeRequest( + '/users/' . $patron['cat_username'] . '/requests', + ['request_type' => 'HOLD'] + ); + $holdList = []; + foreach ($xml as $request) { + $holdList[] = [ + 'create' => (string)$request->request_date, + 'expire' => (string)$request->last_interest_date, + 'id' => (string)$request->request_id, + 'in_transit' => (string)$request->request_status !== 'On Hold Shelf', + 'item_id' => (string)$request->mms_id, + 'location' => (string)$request->pickup_location, + 'processed' => $request->item_policy === 'InterlibraryLoan' + && (string)$request->request_status !== 'Not Started', + 'title' => (string)$request->title, + /* + // VuFind keys + 'available' => $request->, + 'canceled' => $request->, + 'institution_dbkey' => $request->, + 'institution_id' => $request->, + 'institution_name' => $request->, + 'position' => $request->, + 'reqnum' => $request->, + 'requestGroup' => $request->, + 'source' => $request->, + // Alma keys + "author": null, + "comment": null, + "desc": "Book" + "description": null, + "material_type": { + "pickup_location": "Burns", + "pickup_location_library": "BURNS", + "pickup_location_type": "LIBRARY", + "place_in_queue": 1, + "request_date": "2013-11-12Z" + "request_id": "83013520000121", + "request_status": "NOT_STARTED", + "request_type": "HOLD", + "title": "Test title", + "value": "BK", + */ + ]; + } + return $holdList; + } + + /** + * Cancel hold requests. + * + * @param array $cancelDetails An associative array with two keys: patron + * (array returned by the driver's + * patronLogin method) and details (an array + * of strings eturned by the driver's + * getCancelHoldDetails method) + * + * @return array Associative array containing with keys 'count' + * (number of items successfully cancelled) and + * 'items' (array of successfull cancellations). + */ + public function cancelHolds($cancelDetails) + { + $returnArray = []; + $patronId = $cancelDetails['patron']['cat_username']; + $count = 0; + + foreach ($cancelDetails['details'] as $requestId) { + $item = []; + try { + // Get some details of the requested items as we need them below. + // We only can get them from an API request. + $requestDetails = $this->makeRequest( + $this->baseUrl . + '/users/' . urlencode($patronId) . + '/requests/' . urlencode($requestId) + ); + + $mmsId = (isset($requestDetails->mms_id)) + ? (string)$requestDetails->mms_id + : (string)$requestDetails->mms_id; + + // Delete the request in Alma + $apiResult = $this->makeRequest( + $this->baseUrl . + '/users/' . urlencode($patronId) . + '/requests/' . urlencode($requestId), + ['reason' => 'CancelledAtPatronRequest'], + [], + 'DELETE' + ); + + // Adding to "count" variable and setting values to return array + $count++; + $item[$mmsId]['success'] = true; + $item[$mmsId]['status'] = 'hold_cancel_success'; + } catch (ILSException $e) { + if (isset($apiResult['xml'])) { + $almaErrorCode = $apiResult['xml']->errorList->error->errorCode; + $sysMessage = $apiResult['xml']->errorList->error->errorMessage; + } else { + $almaErrorCode = 'No error code available'; + $sysMessage = 'HTTP status code: ' . + ($e->getCode() ?? 'Code not available'); + } + $item[$mmsId]['success'] = false; + $item[$mmsId]['status'] = 'hold_cancel_fail'; + $item[$mmsId]['sysMessage'] = $sysMessage . '. ' . + 'Alma MMS ID: ' . $mmsId . '. ' . + 'Alma request ID: ' . $requestId . '. ' . + 'Alma error code: ' . $almaErrorCode; + } + + $returnArray['items'] = $item; + } + + $returnArray['count'] = $count; + + return $returnArray; + } + + /** + * Get details of a single hold request. + * + * @param array $holdDetails One of the item arrays returned by the + * getMyHolds method + * + * @return string The Alma request ID + */ + public function getCancelHoldDetails($holdDetails) + { + return $holdDetails['id']; + } + + /** + * Get Patron Storage Retrieval Requests + * + * This is responsible for retrieving all call slips by a specific patron. + * + * @param array $patron The patron array from patronLogin + * + * @return mixed Array of the patron's holds + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getMyStorageRetrievalRequests($patron) + { + $xml = $this->makeRequest( + '/users/' . $patron['cat_username'] . '/requests', + ['request_type' => 'MOVE'] + ); + $holdList = []; + for ($i = 0; $i < count($xml->user_requests); $i++) { + $request = $xml->user_requests[$i]; + if (!isset($request->item_policy) + || $request->item_policy !== 'Archive' + ) { + continue; + } + $holdList[] = [ + 'create' => $request->request_date, + 'expire' => $request->last_interest_date, + 'id' => $request->request_id, + 'in_transit' => $request->request_status !== 'IN_PROCESS', + 'item_id' => $request->mms_id, + 'location' => $request->pickup_location, + 'processed' => $request->item_policy === 'InterlibraryLoan' + && $request->request_status !== 'NOT_STARTED', + 'title' => $request->title, + ]; + } + return $holdList; + } + + /** + * Get Patron ILL Requests + * + * This is responsible for retrieving all ILL requests by a specific patron. + * + * @param array $patron The patron array from patronLogin + * + * @return mixed Array of the patron's ILL requests + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getMyILLRequests($patron) + { + $xml = $this->makeRequest( + '/users/' . $patron['cat_username'] . '/requests', + ['request_type' => 'MOVE'] + ); + $holdList = []; + for ($i = 0; $i < count($xml->user_requests); $i++) { + $request = $xml->user_requests[$i]; + if (!isset($request->item_policy) + || $request->item_policy !== 'InterlibraryLoan' + ) { + continue; + } + $holdList[] = [ + 'create' => $request->request_date, + 'expire' => $request->last_interest_date, + 'id' => $request->request_id, + 'in_transit' => $request->request_status !== 'IN_PROCESS', + 'item_id' => $request->mms_id, + 'location' => $request->pickup_location, + 'processed' => $request->item_policy === 'InterlibraryLoan' + && $request->request_status !== 'NOT_STARTED', + 'title' => $request->title, + ]; + } + return $holdList; + } + + /** + * Get transactions of the current patron. + * + * @param array $patron The patron array from patronLogin + * + * @return string[] Transaction information as array or empty array if the + * patron has no transactions. + * + * @author Michael Birkner + */ + public function getMyTransactions($patron) + { + // Defining the return value + $returnArray = []; + + // Get the patrons user name + $patronUserName = $patron['cat_username']; + + // Create a timestamp for calculating the due / overdue status + $nowTS = mktime(); + + // Create parameters for the API call + // INFO: "order_by" does not seem to work as expected! + // This is an Alma API problem. + $params = [ + 'limit' => '100', + 'order_by' => 'due_date', + 'direction' => 'DESC', + 'expand' => 'renewable' + ]; + + // Get user loans from Alma API + $apiResult = $this->makeRequest( + '/users/' . $patronUserName . '/loans/', + $params + ); + + // If there is an API result, process it + if ($apiResult) { + // Iterate over all item loans + foreach ($apiResult->item_loan as $itemLoan) { + $loan['duedate'] = $this->parseDate( + (string)$itemLoan->due_date, + true + ); + //$loan['dueTime'] = ; + $loan['dueStatus'] = null; // Calculated below + $loan['id'] = (string)$itemLoan->mms_id; + //$loan['source'] = 'Solr'; + $loan['barcode'] = (string)$itemLoan->item_barcode; + //$loan['renew'] = ; + //$loan['renewLimit'] = ; + //$loan['request'] = ; + //$loan['volume'] = ; + $loan['publication_year'] = (string)$itemLoan->publication_year; + $loan['renewable'] + = (strtolower((string)$itemLoan->renewable) == 'true') + ? true + : false; + //$loan['message'] = ; + $loan['title'] = (string)$itemLoan->title; + $loan['item_id'] = (string)$itemLoan->loan_id; + $loan['institution_name'] = (string)$itemLoan->library; + //$loan['isbn'] = ; + //$loan['issn'] = ; + //$loan['oclc'] = ; + //$loan['upc'] = ; + $loan['borrowingLocation'] = (string)$itemLoan->circ_desk; + + // Calculate due status + $dueDateTS = strtotime($loan['duedate']); + if ($nowTS > $dueDateTS) { + // Loan is overdue + $loan['dueStatus'] = 'overdue'; + } elseif (($dueDateTS - $nowTS) < 86400) { + // Due date within one day + $loan['dueStatus'] = 'due'; + } + + $returnArray[] = $loan; + } + } + + return $returnArray; + } + + /** + * Get Alma loan IDs for use in renewMyItems. + * + * @param array $checkOutDetails An array from getMyTransactions + * + * @return string The Alma loan ID for this loan + * + * @author Michael Birkner + */ + public function getRenewDetails($checkOutDetails) + { + $loanId = $checkOutDetails['item_id']; + return $loanId; + } + + /** + * Renew loans via Alma API. + * + * @param array $renewDetails An array with the IDs of the loans returned by + * getRenewDetails and the patron information + * returned by patronLogin. + * + * @return array[] An array with the renewal details and a success or error + * message. + * + * @author Michael Birkner + */ + public function renewMyItems($renewDetails) + { + $returnArray = []; + $patronUserName = $renewDetails['patron']['cat_username']; + + foreach ($renewDetails['details'] as $loanId) { + // Create an empty array that holds the information for a renewal + $renewal = []; + + try { + // POST the renewals to Alma + $apiResult = $this->makeRequest( + '/users/' . $patronUserName . '/loans/' . $loanId . '/?op=renew', + [], + [], + 'POST' + ); + + // Add information to the renewal array + $blocks = false; + $renewal[$loanId]['success'] = true; + $renewal[$loanId]['new_date'] = $this->parseDate( + (string)$apiResult->due_date, + true + ); + //$renewal[$loanId]['new_time'] = ; + $renewal[$loanId]['item_id'] = (string)$apiResult->loan_id; + $renewal[$loanId]['sysMessage'] = 'renew_success'; + + // Add the renewal to the return array + $returnArray['details'] = $renewal; + } catch (ILSException $ilsEx) { + // Add the empty renewal array to the return array + $returnArray['details'] = $renewal; + + // Add a message that can be translated + $blocks[] = 'renew_fail'; + } + } + + $returnArray['blocks'] = $blocks; + + return $returnArray; + } + + /** + * Get Status + * + * This is responsible for retrieving the status information of a certain + * record. + * + * @param string $id The record id to retrieve the holdings for + * + * @return mixed On success, an associative array with the following keys: + * id, availability (boolean), status, location, reserve, callnumber. + */ + public function getStatus($id) + { + return $this->getHolding($id); + } + + /** + * Get Statuses + * + * This is responsible for retrieving the status information for a + * collection of records. + * + * @param array $ids The array of record ids to retrieve the status for + * + * @return array An array of getStatus() return values on success. + */ + public function getStatuses($ids) + { + $results = []; + $params = [ + 'mms_id' => implode(',', $ids), + 'expand' => 'p_avail,e_avail,d_avail' + ]; + if ($bibs = $this->makeRequest('/bibs', $params)) { + foreach ($bibs as $bib) { + $marc = new \File_MARCXML( + $bib->record->asXML(), + \File_MARCXML::SOURCE_STRING + ); + $status = []; + $tmpl = [ + 'id' => (string)$bib->mms_id, + 'source' => 'Solr', + 'callnumber' => isset($bib->isbn) + ? (string)$bib->isbn + : '' + ]; + if ($record = $marc->next()) { + // Physical + $physicalItems = $record->getFields('AVA'); + foreach ($physicalItems as $field) { + $avail = $field->getSubfield('e')->getData(); + $item = $tmpl; + $item['availability'] = strtolower($avail) === 'available'; + $item['location'] = (string)$field->getSubfield('c') + ->getData(); + $status[] = $item; + } + // Electronic + $electronicItems = $record->getFields('AVE'); + foreach ($electronicItems as $field) { + $avail = $field->getSubfield('e')->getData(); + $item = $tmpl; + $item['availability'] = strtolower($avail) === 'available'; + $status[] = $item; + } + // Digital + $digitalItems = $record->getFields('AVD'); + foreach ($digitalItems as $field) { + $avail = $field->getSubfield('e')->getData(); + $item = $tmpl; + $item['availability'] = strtolower($avail) === 'available'; + $status[] = $item; + } + } else { + // TODO: Throw error + error_log('no record'); + } + $results[] = $status; + } + } + return $results; + } + + /** + * Get Purchase History + * + * This is responsible for retrieving the acquisitions history data for the + * specific record (usually recently received issues of a serial). + * + * @param string $id The record id to retrieve the info for + * + * @return array An array with the acquisitions data on success. + */ + public function getPurchaseHistory($id) + { + // TODO: Alma getPurchaseHistory + return []; + } + + /** + * Public Function which retrieves renew, hold and cancel settings from the + * driver ini file. + * + * @param string $function The name of the feature to be checked + * @param array $params Optional feature-specific parameters (array) + * + * @return array An array with key-value pairs. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getConfig($function, $params = null) + { + if (isset($this->config[$function])) { + $functionConfig = $this->config[$function]; + } else { + $functionConfig = false; + } + + return $functionConfig; + } + + /** + * Place a hold request via Alma API. This could be a title level request or + * an item level request. + * + * @param array $holdDetails An associative array w/ atleast patron and item_id + * + * @return array success: bool, sysMessage: string + * + * @link https://developers.exlibrisgroup.com/alma/apis/bibs + */ + public function placeHold($holdDetails) + { + // Check for title or item level request + $level = $holdDetails['level'] ?? 'item'; + + // Get information that is valid for both, item level requests and title + // level requests. + $mmsId = $holdDetails['id']; + $holId = $holdDetails['holding_id']; + $itmId = $holdDetails['item_id']; + $patronCatUsername = $holdDetails['patron']['cat_username']; + $pickupLocation = $holdDetails['pickUpLocation'] ?? null; + $comment = $holdDetails['comment'] ?? null; + $requiredBy = (isset($holdDetails['requiredBy'])) + ? $this->dateConverter->convertFromDisplayDate( + 'Y-m-d', + $holdDetails['requiredBy'] + ) . 'Z' + : null; + + // Create body for API request + $body = []; + $body['request_type'] = 'HOLD'; + $body['pickup_location_type'] = 'LIBRARY'; + $body['pickup_location_library'] = $pickupLocation; + $body['comment'] = $comment; + $body['last_interest_date'] = $requiredBy; + + // Remove "null" values from body array + $body = array_filter($body); + + // Check if we have a title level request or an item level request + if ($level === 'title') { + // Add description if we have one for title level requests as Alma + // needs it under certain circumstances. See: https://developers. + // exlibrisgroup.com/alma/apis/xsd/rest_user_request.xsd?tags=POST + $description = isset($holdDetails['description']) ?? null; + if ($description) { + $body['description'] = $description; + } + + // Create HTTP client with Alma API URL for title level requests + $client = $this->httpService->createClient( + $this->baseUrl . '/bibs/' . urlencode($mmsId) + . '/requests?apiKey=' . urlencode($this->apiKey) + . '&user_id=' . urlencode($patronCatUsername) + . '&format=json' + ); + } else { + // Create HTTP client with Alma API URL for item level requests + $client = $this->httpService->createClient( + $this->baseUrl . '/bibs/' . urlencode($mmsId) + . '/holdings/' . urlencode($holId) + . '/items/' . urlencode($itmId) + . '/requests?apiKey=' . urlencode($this->apiKey) + . '&user_id=' . urlencode($patronCatUsername) + . '&format=json' + ); + } + + // Set headers + $client->setHeaders( + [ + 'Content-type: application/json', + 'Accept: application/json' + ] + ); + + // Set HTTP method + $client->setMethod(\Zend\Http\Request::METHOD_POST); + + // Set body + $client->setRawBody(json_encode($body)); + + // Send API call and get response + $response = $client->send(); + + // Check for success + if ($response->isSuccess()) { + return ['success' => true]; + } else { + // TODO: Throw an error + error_log($response->getBody()); + } + + // Get error message + $error = json_decode($response->getBody()); + if (!$error) { + $error = simplexml_load_string($response->getBody()); + } + + return [ + 'success' => false, + 'sysMessage' => $error->errorList->error[0]->errorMessage + ]; + } + + /** + * Get Pick Up Locations + * + * This is responsible get a list of valid library locations for holds / recall + * retrieval + * + * @param array $patron Patron information returned by the patronLogin method. + * + * @return array An array of associative arrays with locationID and + * locationDisplay keys + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getPickupLocations($patron) + { + $xml = $this->makeRequest('/conf/libraries'); + $libraries = []; + foreach ($xml as $library) { + $libraries[] = [ + 'locationID' => $library->code, + 'locationDisplay' => $library->name + ]; + } + return $libraries; + } + + /** + * Request from /courses. + * + * @return array with key = course ID, value = course name + */ + public function getCourses() + { + // https://developers.exlibrisgroup.com/alma/apis/courses + // GET /almaws/v1/courses + $xml = $this->makeRequest('/courses'); + $courses = []; + foreach ($xml as $course) { + $courses[$course->id] = $course->name; + } + return $courses; + } + + /** + * Get reserves by course + * + * @param string $courseID Value from getCourses + * @param string $instructorID Value from getInstructors (not used yet) + * @param string $departmentID Value from getDepartments (not used yet) + * + * @return array With key BIB_ID - The record ID of the current reserve item. + * Not currently used: + * DISPLAY_CALL_NO, AUTHOR, TITLE, PUBLISHER, PUBLISHER_DATE + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function findReserves($courseID, $instructorID, $departmentID) + { + // https://developers.exlibrisgroup.com/alma/apis/courses + // GET /almaws/v1/courses/{course_id}/reading-lists + $xml = $this->makeRequest('/courses/' . $courseID . '/reading-lists'); + $reserves = []; + foreach ($xml as $list) { + $listXML = $this->makeRequest( + "/courses/${$courseID}/reading-lists/${$list->id}/citations" + ); + foreach ($listXML as $citation) { + $reserves[$citation->id] = $citation->metadata; + } + } + return $reserves; + } + + /** + * Parse a date. + * + * @param string $date Date to parse + * @param boolean $withTime Add time to return if available? + * + * @return string + */ + public function parseDate($date, $withTime = false) + { + // Remove trailing Z from end of date + // e.g. from Alma we get dates like 2012-07-13Z without time, which is wrong) + if (strpos($date, 'Z', (strlen($date) - 1))) { + $date = preg_replace('/Z{1}$/', '', $date); + } + + $compactDate = "/^[0-9]{8}$/"; // e. g. 20120725 + $euroName = "/^[0-9]+\/[A-Za-z]{3}\/[0-9]{4}$/"; // e. g. 13/jan/2012 + $euro = "/^[0-9]+\/[0-9]+\/[0-9]{4}$/"; // e. g. 13/7/2012 + $euroPad = "/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2,4}$/"; // e. g. 13/07/2012 + $datestamp = "/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/"; // e. g. 2012-07-13 + $timestamp = "/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/"; + // e. g. 2017-07-09T18:00:00 + + if ($date == null || $date == '') { + return ''; + } elseif (preg_match($compactDate, $date) === 1) { + return $this->dateConverter->convertToDisplayDate('Ynd', $date); + } elseif (preg_match($euroName, $date) === 1) { + return $this->dateConverter->convertToDisplayDate('d/M/Y', $date); + } elseif (preg_match($euro, $date) === 1) { + return $this->dateConverter->convertToDisplayDate('d/m/Y', $date); + } elseif (preg_match($euroPad, $date) === 1) { + return $this->dateConverter->convertToDisplayDate('d/m/y', $date); + } elseif (preg_match($datestamp, $date) === 1) { + return $this->dateConverter->convertToDisplayDate('Y-m-d', $date); + } elseif (preg_match($timestamp, substr($date, 0, 19)) === 1) { + if ($withTime) { + return $this->dateConverter->convertToDisplayDateAndTime( + 'Y-m-d\TH:i:s', + substr($date, 0, 19) + ); + } else { + return $this->dateConverter->convertToDisplayDate( + 'Y-m-d', + substr($date, 0, 10) + ); + } + } else { + throw new \Exception("Invalid date: $date"); + } + } + + // @codingStandardsIgnoreStart + + /** + * @return array with key = course ID, value = course name + * / + * public function getFunds() { + * // https://developers.exlibrisgroup.com/alma/apis/acq + * // GET /almaws/v1/acq/funds + * } + */ + + /* ================= METHODS INACCESSIBLE OUTSIDE OF GET ================== */ + + /** + * @param array $cancelDetails An associative array with two keys: + * patron (array returned by the driver's patronLogin method) + * details (array returned by the driver's getCancelHoldDetails) + * + * @return array count – The number of items successfully cancelled + * items – Associative array where keyed by item_id (getMyHolds) + * success – Boolean true or false + * status – A status message from the language file (required) + * sysMessage - A system supplied failure message (optional) + * / + * public function cancelHolds($cancelDetails) { + * // https://developers.exlibrisgroup.com/alma/apis/users + * // DELETE /almaws/v1/users/{user_id}/requests/{request_id} + * } + */ + // @codingStandardsIgnoreEnd +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php b/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fad09dc0c16a72cbc7ecb8c76b1360d2e85a7484 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php @@ -0,0 +1,83 @@ +<?php +/** + * Factory for Alma ILS driver. + * + * PHP version 5 + * + * Copyright (C) AK Bibliothek Wien für Sozialwissenschaften 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Michael Birkner <michael.birkner@akwien.at> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Interop\Container\Exception\ContainerException; +use Zend\ServiceManager\Exception\ServiceNotCreatedException; +use Zend\ServiceManager\Exception\ServiceNotFoundException; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Alma ILS driver factory. + * + * @category VuFind + * @package ILS_Drivers + * @author Michael Birkner <michael.birkner@akwien.at> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AlmaFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Container interface + * @param string $requestedName Driver name + * @param null|array $options Options + * + * @return object Driver object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + // Set up the driver with the date converter (and any extra parameters + // passed in as options): + $driver = new $requestedName( + $container->get('VuFind\Date\Converter'), + $container->get('VuFind\Config\PluginManager'), + ...($options ?: []) + ); + + // Populate cache storage if a setCacheStorage method is present: + if (method_exists($driver, 'setCacheStorage')) { + $driver->setCacheStorage( + $container->get('VuFind\Cache\Manager')->getCache('object') + ); + } + + return $driver; + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/Amicus.php b/module/VuFind/src/VuFind/ILS/Driver/Amicus.php index 3f2adfe9eb8b227824157c110ade6f2b796320c0..e267986e82a5c7a10e895d4eec29e4df65e5a7ec 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Amicus.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Amicus.php @@ -2,7 +2,7 @@ /** * Amicus ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Scanbit 2011. * @@ -26,8 +26,11 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use PDO, PDOException, VuFind\Exception\ILS as ILSException, - VuFind\I18n\Translator\TranslatorAwareInterface; + +use PDO; +use PDOException; +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; /** * Amicus ILS Driver @@ -437,7 +440,7 @@ class Amicus extends AbstractBase implements TranslatorAwareInterface * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -592,7 +595,7 @@ class Amicus extends AbstractBase implements TranslatorAwareInterface * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -625,7 +628,7 @@ class Amicus extends AbstractBase implements TranslatorAwareInterface * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -662,7 +665,7 @@ class Amicus extends AbstractBase implements TranslatorAwareInterface * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ diff --git a/module/VuFind/src/VuFind/ILS/Driver/CacheTrait.php b/module/VuFind/src/VuFind/ILS/Driver/CacheTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..df126460fa94be5af6590839d4f6b2f891d32f62 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/CacheTrait.php @@ -0,0 +1,141 @@ +<?php +/** + * Trait for ILS drivers using cache. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2007. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki + */ +namespace VuFind\ILS\Driver; + +use VuFind\Cache\KeyGeneratorTrait; +use Zend\Cache\Storage\StorageInterface; + +/** + * Default ILS driver base class. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki + * + * @SuppressWarnings(PHPMD.NumberOfChildren) + */ +trait CacheTrait +{ + use KeyGeneratorTrait; + + /** + * Cache for storing ILS data temporarily (e.g. patron blocks) + * + * @var StorageInterface + */ + protected $cache = null; + + /** + * Lifetime of cache (in seconds). + * + * @var int + */ + protected $cacheLifetime = 30; + + /** + * Set a cache storage object. + * + * @param StorageInterface $cache Cache storage interface + * + * @return void + */ + public function setCacheStorage(StorageInterface $cache = null) + { + $this->cache = $cache; + } + + /** + * Helper function for fetching cached data. + * Data is cached for up to $this->cacheLifetime seconds so that it would be + * faster to process e.g. requests where multiple calls to the backend are made. + * + * @param string $key Cache entry key + * + * @return mixed|null Cached entry or null if not cached or expired + */ + protected function getCachedData($key) + { + // No cache object, no cached results! + if (null === $this->cache) { + return null; + } + + $fullKey = $this->getCacheKey($key); + $item = $this->cache->getItem($fullKey); + if (null !== $item) { + // Return value if still valid: + if (time() - $item['time'] < $this->cacheLifetime) { + return $item['entry']; + } + // Clear expired item from cache: + $this->cache->removeItem($fullKey); + } + return null; + } + + /** + * Helper function for storing cached data. + * Data is cached for up to $this->cacheLifetime seconds so that it would be + * faster to process e.g. requests where multiple calls to the backend are made. + * + * @param string $key Cache entry key + * @param mixed $entry Entry to be cached + * + * @return void + */ + protected function putCachedData($key, $entry) + { + // Don't write to cache if we don't have a cache! + if (null === $this->cache) { + return; + } + $item = [ + 'time' => time(), + 'entry' => $entry + ]; + $this->cache->setItem($this->getCacheKey($key), $item); + } + + /** + * Helper function for removing cached data. + * + * @param string $key Cache entry key + * + * @return void + */ + protected function removeCachedData($key) + { + // Don't write to cache if we don't have a cache! + if (null === $this->cache) { + return; + } + $this->cache->removeItem($this->getCacheKey($key)); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/ClaviusSQL.php b/module/VuFind/src/VuFind/ILS/Driver/ClaviusSQL.php deleted file mode 100644 index e0918ccba49c8654fd123b8ffff1fe9d24cb7752..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/ILS/Driver/ClaviusSQL.php +++ /dev/null @@ -1,853 +0,0 @@ -<?php -/** - * Clavius SQL ILS Driver - * - * PHP version 5 - * - * Copyright (C) Josef Moravec, Municipal Library Ústà nad Orlicà 2012. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package ILS_Drivers - * @author Josef Moravec <josef.moravec@knihovna-uo.cz> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki - */ -namespace VuFind\ILS\Driver; -use PDO, PDOException, VuFind\Exception\ILS as ILSException; - -/** - * VuFind Driver for Clavius SQL (version: 0.1 dev) - * - * @category VuFind - * @package ILS_Drivers - * @author Josef Moravec <josef.moravec@knihovna-uo.cz> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki - */ -class ClaviusSQL extends AbstractBase -{ - /** - * Database connection. - * - * @var object - */ - protected $db; - - /** - * URL to Clavius original katalog - * - * @var string - */ - protected $ilsBaseUrl; - - /** - * Library prefix in czech library system - used for ids and barcodes - * - * @var string - */ - protected $prefix; - - /** - * If is library prefix used also for record ids in vufind - * - * @var bool - */ - protected $idPrefix; - - /** - * If is library using manually entered barcodes - * - * @var bool - */ - protected $useBarcodes; - - /** - * Library departments and branches, filled from database by getDepartments - * - * @var array - */ - protected $locations; - - /** - * How many days is new document hidden in catalog - * - * @var int - */ - protected $hideNewItemsDays; - - /** - * Fine codes and descriptions, filled from database by getFineTypes - * - * @var array - */ - protected $fineTypes; - - /** - * Initialize the driver. - * - * Validate configuration and perform all resource-intensive tasks needed to - * make the driver active. - * - * @throws ILSException - * @return void - */ - public function init() - { - if (empty($this->config)) { - throw new ILSException('Configuration needs to be set.'); - } - //Connect to MySQL - $this->db = new PDO( - 'mysql:host=' . $this->config['Catalog']['host'] . - ';port=' . $this->config['Catalog']['port'] . - ';dbname=' . $this->config['Catalog']['database'], - $this->config['Catalog']['username'], - $this->config['Catalog']['password'] - ); - // Throw PDOExceptions if something goes wrong - $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - // Return result set like mysql_fetch_assoc() - $this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); - //character set utf8 - $this->db->exec("SET NAMES utf8"); - //Storing the base URL of ILS - $this->ilsBaseUrl = $this->config['Catalog']['url']; - //Boolean - use id prefixes like "KN31120" or not - $this->idPrefix = $this->config['Catalog']['id_prefix']; - //Boolean - indicates if library uses barcodes (true), or if barcodes are - // generated automatically (false) - $this->useBarcodes = $this->config['Catalog']['use_barcodes']; - //set number prefix for library - $this->prefix = $this->config['Catalog']['prefix']; - //how long (in days) hide new items - $this->hideNewItemsDays = 0; - if (isset($this->config['Catalog']['hide_days'])) { - $this->c = $this->config['Catalog']['hide_days']; - } - } - - /** - * Get list of departments - * - * This method queries the ILS for a list of departments to be used as input - * to the findReserves method - * - * @throws ILSException - * - * @return array An associative array with key = department ID, - * value = department name. - */ - public function getDepartments() - { - if (!is_array($this->locations)) { - $this->locations = []; - try { - // TODO - overit/upavit funkcnost na MSSQL a Oracle - $sqlLoc = "SELECT TRIM(lokace) as lokace, TRIM(jmeno) as jmeno " . - "FROM deflok ORDER BY lokace"; - $sqlSt = $this->db->prepare($sqlLoc); - $sqlSt->execute(); - foreach ($sqlSt->fetchAll() as $l) { - $this->locations[$l["lokace"]] = $l["jmeno"]; - } - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - } - return $this->locations; - } - /** - * Get list of fine types - * - * This method queries the ILS for a list of fine types - * - * @throws ILSException - * - * @return array An associative array with key = fine code, - * value = fine description - */ - public function getFineTypes() - { - if (!is_array($this->fineTypes)) { - $this->fineTypes = ["G" => "Registracnà poplatek", - "H" => "UpomÃnka", - "J" => "Poplatek za rezervaci", - "L" => "Poplatek za pujcenÃ", - "M" => "Kauce za výpujcku" - ]; - // TODO MSsql Oracle - $sql = "SELECT kod, nazev FROM defpopl"; - try { - $sqlSt = $this->db->prepare($sql); - $sqlSt->execute(); - foreach ($sqlSt->fetchAll() as $row) { - $this->fineTypes[$row['kod']] = $row['nazev']; - } - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - } - return $this->fineTypes; - } - - /** - * Get a list of funds that can be used to limit the "new item" search - * - * @throws ILSException - * - * @return array An associative array with key = fund ID, value = fund name. - */ - public function getFunds() - { - return $this->getDepartments(); - } - - /** - * Get New Items - * - * Retrieve the IDs of items recently added to the catalog. - * - * @param int $page Page number of results to retrieve (counting starts at 1) - * @param int $limit The size of each page of results to retrieve - * @param int $daysOld The maximum age of records to retrieve in days (max. 30) - * @param int $fundId optional fund ID to use for limiting results (use a value - * returned by getFunds, or exclude for no limit); note that "fund" may be a - * misnomer - if funds are not an appropriate way to limit your new item - * results, you can return a different set of values from getFunds. The - * important thing is that this parameter supports an ID returned by getFunds, - * whatever that may mean. - * - * @return array An associative array with two keys: 'count' (the number - * of items in the 'results' array) and 'results' (an array of associative - * arrays, each with a single key: 'id', a record ID). - */ - public function getNewItems($page, $limit, $daysOld, $fundId = null) - { - $limitFrom = ($page - 1) * $limit; - //TODO better escaping; mssql, oracle - $sql = "SELECT t.tcislo as tcislo, t.druhdoku as druhdoku " - . "FROM svazky s JOIN tituly t ON s.tcislo = t.tcislo " - . "WHERE s.datumvloz > DATE_SUB(CURDATE(),INTERVAL " - . $this->db->quote($daysOld) - . " DAY) AND s.datumvloz <= DATE_SUB(CURDATE(),INTERVAL " - . $this->db->quote($this->hideNewItemsDays) . " DAY)"; - if ($fundId) { - $sql .= " AND s.lokace = " . $this->db->quote($fundId); - } - $sql .= " ORDER BY s.datumvloz DESC LIMIT $limitFrom, $limit"; - try { - $sqlSt = $this->db->prepare($sql); - $sqlSt->execute(); - $result = $sqlSt->fetchAll(); - $return = ['count' => count($result), 'results' => []]; - foreach ($result as $row) { - $return['results'][] = [ - 'id' => $this->getLongId($row['tcislo'], $row['druhdoku']) - ]; - } - return $return; - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - } - - /** - * Get Short ID - * - * This method make short id (only title number - tcislo), from full identifier - * - * @param string $id The full record id - * - * @return string Short id - */ - protected function getShortID($id) - { - $shortId = $id; - if ($this->idPrefix) { - $shortId = ltrim(substr($id, -11), "0"); - } - return $shortId; - } - - /** - * Get Long ID - * - * This method make long id (full identifier) from short id (only title number) - * - * @param string $id The short record id - * @param string $docPrefix Two chars prefix indicated type of document - * - * @return string Long id - */ - protected function getLongID($id, $docPrefix = "KN") - { - $longId = $id; - if ($this->idPrefix) { - $prefix1 = $docPrefix . $this->prefix; - $longId = $prefix1 - . str_pad($id, 18 - strlen($prefix1), "0", STR_PAD_LEFT); - } - return $longId; - } - - /** - * Get Holding - * - * This is responsible for retrieving the holding information of a certain - * record. - * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data - * - * @throws \VuFind\Exception\Date - * @throws ILSException - * @return array On success, an associative array with the following - * keys: id, availability (bool), status, location, reserve, callnumber, - * duedate, number, barcode. - * - * todo: reserve - */ - public function getHolding($id, array $patron = null) - { - $holding = []; - $originalId = $id; - //if ($this->idPrefix) { $id = ltrim(substr($id, -11), "0"); } - $id = $this->getShortID($id); - // TODO - overit/upavit funkcnost na MSSQL a Oracle - $sql = "SELECT trim(pcislo) as number, TRIM(lokace) as location, scislo, " - . "TRIM(sign) as callnumber, TRIM(ckod) as barcode " - . "FROM svazky WHERE tcislo = :id ORDER BY number"; - $sql2 = "SELECT (co = 'V') as availability, " - . "IF(co = 'P',DATE_FORMAT(datum2,'%e. %c. %Y'),'') as duedate, " - . "IF(co = 'V', 'Available', 'Checked Out') as status " - . "FROM kpujcky WHERE scislo = :scislo ORDER BY datum2 DESC LIMIT 1"; - try { - $sqlSt = $this->db->prepare($sql); - $sqlSt->execute([':id' => $id]); - $sqlSt2 = $this->db->prepare($sql2); - /**** TODO reserve *******/ - foreach ($sqlSt->fetchAll() as $item) { - $reserve = "N"; - $sqlSt2->execute([':scislo' => $item['scislo']]); - $item2 = $sqlSt2->fetch(); - if (!$item2) { - $availability = true; - $status = "K dispozici"; - $duedate = ''; - } else { - $availability = ($item2['availability'] == 1) ? true : false; - $status = $item2['status']; - $duedate = $item2['duedate']; - } - $locs = $this->getDepartments(); - $holding[] = [ - 'id' => $originalId, - //'location' => $item['location'], - 'location' => $locs[$item['location']], - 'callnumber' => ($item['callnumber'] == "") - ? null : $item['callnumber'], - 'number' => intval($item['number']), - 'barcode' => ($this->useBarcodes) - ? $item['barcode'] : $item['number'], - 'availability' => $availability, - 'status' => $status, - 'duedate' => $duedate, - 'reserve' => $reserve, - ]; - } - return $holding; - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - } - - /** - * Get Hold Link - * - * The goal for this method is to return a URL to a "place hold" web page on - * the ILS OPAC. This is used for ILSs that do not support an API or method - * to place Holds. - * - * @param string $id The id of the bib record - * @param array $details Item details from getHoldings return array - * - * @return string URL to ILS's OPAC's place hold screen. - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getHoldLink($id, $details) - { - // Web link of the ILS for placing hold on the item - return $this->ilsBaseUrl . "l.dll?clr~" . $this->getShortID($id); - } - - /** - * Get Patron Fines - * - * This method queries the ILS for a patron's current fines - * - * @param array $patron The patron array from patronLogin - * - * @throws \VuFind\Exception\Date - * @throws ILSException - * @return mixed Array associative arrays of the patron's fines on - * success. - * <ul> - * <li>amount - The total amount of the fine IN PENNIES. Be sure to adjust - * decimal points appropriately (i.e. for a $1.00 fine, amount should be set to - * 100).</li> - * <li>checkout - A string representing the date when the item was checked - * out.</li> - * <li>fine - A string describing the reason for the fine (i.e. "Overdue", - * "Long Overdue").</li> - * <li>balance - The unpaid portion of the fine IN PENNIES.</li> - * <li>createdate - A string representing the date when the fine was accrued - * (optional)</li> - * <li>duedate - A string representing the date when the item was due.</li> - * <li>id - The bibliographic ID of the record involved in the fine.</li> - * </ul> - */ - public function getMyFines($patron) - { - $fines = []; - $reasons = $this->getFineTypes(); - // TODO mssql, oracle - $sql = "SELECT scislo as amount, co as reason, " - . "DATE_FORMAT(datum,'%e. %c. %Y') as createdate " - . "FROM poplatky WHERE ccislo = :patronId ORDER BY datum DESC"; - try { - $sqlSt = $this->db->prepare($sql); - $sqlSt->execute([':patronId' => $patron['id']]); - foreach ($sqlSt->fetchAll() as $fine) { - $fines[] = [ - 'amount' => abs($fine['amount']), - 'checkout' => null, // TODO maybe - 'fine' => $reasons[$fine['reason']], - 'balance' => ($fine['amount'] < 0) ? abs($fine['amount']) : 0, - 'createdate' => $fine['createdate'], - 'duedate' => null, // TODO maybe - 'id' => null, // TODO maybe - ]; - } - return $fines; - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - } - - /** - * Get Pick Up Locations - * - * This method returns a list of locations where a user may collect a hold. - * - * @param array $patron Patron information returned by the patronLogin - * method. - * @param array $holdDetails Optional array, only passed in when getting a list - * in the context of placing a hold; contains most of the same values passed to - * placeHold, minus the patron data. May be used to limit the pickup options - * or may be ignored. The driver must not add new options to the return array - * based on this data or other areas of VuFind may behave incorrectly. - * - * @return array An array of associative arrays with locationID and - * locationDisplay keys - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getPickUpLocations($patron = false, $holdDetails = null) - { - $locations = []; - foreach ($this->getDepartments() as $id => $text) { - $locations[] = ['locationID' => $id, - 'locationDisplay' => $text - ]; - } - return $locations; - } - - /** - * Get Patron Holds - * - * This is responsible for retrieving all holds by a specific patron. - * - * @param array $patron The patron array from patronLogin - * - * @throws \VuFind\Exception\Date - * @throws ILSException - * @return array Array of associative arrays, one for each hold associated - * with the specified account. Each associative array contains these keys: - * <ul> - * <li>type - A string describing the type of hold - i.e. hold vs. recall - * (optional).</li> - * <li>id - The bibliographic record ID associated with the hold - * (optional).</li> - * <li>location - A string describing the pickup location for the held item - * (optional). In VuFind 1.2, this should correspond with a locationID value from - * getPickUpLocations. In VuFind 1.3 and later, it may be either a locationID - * value or a raw ready-to-display string.</li> - * <li>reqnum - A control number for the request (optional).</li> - * <li>expire - The expiration date of the hold (a string).</li> - * <li>create - The creation date of the hold (a string).</li> - * <li>position - The position of the user in the holds queue (optional)</li> - * <li>available - Whether or not the hold is available (true) or not (false) - * (optional)</li> - * <li>item_id - The item id the request item (optional).</li> - * <li>volume - The volume number of the item (optional)</li> - * <li>publication_year - The publication year of the item (optional)</li> - * <li>title - The title of the item (optional - only used if the record - * cannot be found in VuFind's index).</li> - * </ul> - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getMyHolds($patron) - { - // TODO - return []; - } - - /** - * Get Patron Profile - * - * This is responsible for retrieving the profile for a specific patron. - * - * @param array $patron The patron array - * - * @throws ILSException - * @return array Array of the patron's profile data on success. - firstname - lastname - address1 - address2 - zip - phone - group - i.e. Student, Staff, Faculty, etc. - */ - public function getMyProfile($patron) - { - $profile = []; - $sql = "SELECT jmeno, tulice, tmesto, tpsc, telefon " - . "FROM ctenari WHERE ccislo = :userId"; - try { - $sqlSt = $this->db->prepare($sql); - $sqlSt->execute([':userId' => $patron['id']]); - $patron2 = $sqlSt->fetch(); - $names = $this->explodeName($patron2['jmeno']); - if ($patron2) { - $profile = [ - 'firstname' => $names['firstname'], - 'lastname' => $names['lastname'], - 'address1' => $patron2['tulice'], - 'address2' => $patron2['tmesto'], - 'zip' => $patron2['tpsc'], - 'phone' => $patron2['telefon'] ? $patron2['telefon'] : null, - 'group' => null //TODO - Maybe - ]; - } - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - return $profile; - } - - /** - * Get Purchase History - * - * This is responsible for retrieving the acquisitions history data for the - * specific record (usually recently received issues of a serial). - * - * @param string $id The record id to retrieve the info for - * - * @throws ILSException - * @return array An array with the acquisitions data on success. - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getPurchaseHistory($id) - { - // TODO - return []; - } - - /** - * Get Status - * - * This is responsible for retrieving the status information of a certain - * record. - * - * @param string $id The record id to retrieve the holdings for - * - * @throws ILSException - * @return mixed On success, an associative array with the following keys: - * id, availability (bool), status, location, reserve, callnumber. - */ - public function getStatus($id) - { - $statuses = $this->getHolding($id); - foreach ($statuses as $status) { - $status['status'] = ($status['availability']) - ? 'Available' : 'Unavailable'; - } - return $statuses; - } - - /** - * Get Statuses - * - * This is responsible for retrieving the status information for a - * collection of records. - * - * @param array $idLst The array of record ids to retrieve the status for - * - * @throws ILSException - * @return array An array of getStatus() return values on success. - */ - public function getStatuses($idLst) - { - $statusLst = []; - foreach ($idLst as $id) { - $statusLst[] = $this->getStatus($id); - } - return $statusLst; - } - - /** - * Get suppressed records. - * - * @throws ILSException - * @return array ID numbers of suppressed records in the system. - */ - public function getSuppressedRecords() - { - // TODO - MAYBE - return []; - } - - /** - * Get first and last name from name - * - * @param string $name Full Patron Name - * - * @return array associative array with keys firstname, lastname - */ - protected function explodeName($name) - { - $names = []; - $nameArray = explode(" ", $name); - $names['lastname'] = array_pop($nameArray); - $names['firstname'] = implode(" ", $nameArray); - return $names; - } - - /** - * Replace codes - * - * @param string $stringToCode encoded/decoded String - * @param bool $decode true if you want to decode string - * - * @return string coded string - */ - protected function replaceCodes($stringToCode, $decode = false) - { - $from = str_split( - "()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" - . "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}" - ); - $to = str_split( - "g5p{+.Yt|Cy8(oM)^LTuE-\\1OKxPwv=9@:n7adb2QkIG6XcVe]" - . "[,/ziS3Hf?m0<ZrD_ljA}4FU>Js*WBNhR`;q" - ); - if ($decode) { - $coding = array_combine($to, $from); - } else { - $coding = array_combine($from, $to); - } - $toCodeArray = str_split($stringToCode); - $output = ""; - foreach ($toCodeArray as $char) { - $output .= $coding[$char]; - } - return $output; - } - - /** - * Encode password - * - * @param string $password password given by user - * @param bool $woman true if user is woman - * - * @return string encoded password - */ - protected function encodePassword($password, $woman = false) - { - $password = str_pad($password, 6); - $kod3 = substr($password, 2, 1); - $kod3int = intval($kod3); - if ($kod3int > 4) { - $kod3int = $kod3int - 5; - } - substr_replace($password, chr($kod3int), 2); - $sexConstant = $woman ? 1 : 0; - $kod6 = substr($password, 5, 1); - $kod6r = chr(70 + (intval($kod6) * 2) + $sexConstant); - if ($kod6 != " ") { - $password = substr_replace($password, $kod6r, 5); - } - $password = trim($password); - $encoded = $this->replaceCodes($password); - return $encoded; - } - - /** - * Encode PIN number - * - * @param string $pin password given by user - * @param string $patron number of patron - * - * @return long encoded pin - */ - protected function encodePin($pin, $patron) - { - for ($i = 0; $i < strlen($pin); $i++) { - $char = substr($pin, $i, 1); - $return = 1 + intval($char) + $return * 12; - } - return 2109876543 - $return * 7 * (intval($patron) % 89 + 7); - } - - /** - * Patron Login - * - * This is responsible for authenticating a patron against the catalog. - * - * @param string $username The patron username - * @param string $password The patron's password - * - * @throws ILSException - * @return mixed Associative array of patron info on successful login, - * null on unsuccessful login. - */ - public function patronLogin($username, $password) - { - // TODO - oracle a mssql - $sqlPatron = "SELECT ccislo, jmeno, mail, SUBSTRING(rcislo,1,6) as rcislo," - . " pin, pohlavi FROM ctenari WHERE ccislo = :userId"; - try { - $sqlStPatron = $this->db->prepare($sqlPatron); - $sqlStPatron->execute([':userId' => $username]); - $patronRow = $sqlStPatron->fetch(); - if (!$patronRow) { - return null; - } - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - if ($patronRow['pin'] == "0") { - $encodedPassword - = $this->encodePassword($password, $patronRow['pohlavi']); - if ($encodedPassword != $patronRow['rcislo']) { - return null; - } - } else { - $encodedPin = $this->encodePin($password, $patronRow['ccislo']); - if ($encodedPin != $patronRow['pin']) { - return null; - } - } - $names = $this->explodeName($patronRow['jmeno']); - $patron = [ - 'id' => $patronRow['ccislo'], - 'firstname' => $names['firstname'], - 'lastname' => $names['lastname'], - 'cat_username' => $username, - 'cat_password' => $password, - 'email' => $patronRow['mail'] ? $patronRow['mail'] : null, - 'major' => null, - 'college' => null - ]; - return $patron; - } - - /** - * Get Patron Transactions - * - * This method queries the ILS for a patron's current checked out items - * - * @param array $user The patron array from patronLogin - * @param bool $history Include history of transactions (true) or just get - * current ones (false). - * - * @throws ILSException - * @return array Array of the patron's transactions on success. - * <ul> - * <li>duedate - The item's due date (a string).</li> - * <li>id - The bibliographic ID of the checked out item.</li> - * <li>barcode - The barcode of the item (optional).</li> - * <li>renew - The number of times the item has been renewed (optional).</li> - * <li>request - The number of pending requests for the item (optional).</li> - * <li>volume - The volume number of the item (optional)</li> - * <li>publication_year - The publication year of the item (optional)</li> - * <li>renewable - Whether or not an item is renewable (required for - * renewals)</li> - * <li>message - A message regarding the item (optional)</li> - * <li>title - The title of the item (optional - only used if the record - * cannot be found in VuFind's index).</li> - * <li>item_id - this is used to match up renew responses and must match - * the item_id in the renew response</li> - * </ul> - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getMyTransactions($user, $history = false) - { - //TODO mssql a Oracle - $sql = "SELECT DATE_FORMAT(k.datum2,'%e. %c. %Y') as duedate, - TRIM(s.ckod) as barcode, t.druhdoku as druhdoku, t.tcislo as tcislo, - t.rokvydani as year, CONCAT(t.nazev, t.big_nazev) as title, - TRIM(s.pcislo) as item_id - FROM kpujcky k - JOIN svazky s - ON s.scislo = k.scislo - JOIN tituly t - ON s.tcislo = t.tcislo - WHERE k.ccislo = :userId AND k.co = :action"; - try { - $sqlSt = $this->db->prepare($sql); - $sqlSt->execute([':userId' => $user['id'], ':action' => 'P']); - $transactions = []; - foreach ($sqlSt->fetchAll() as $item) { - $id = $this->getLongId($item['tcislo'], $item['druhdoku']); - //TODO - requests - //$requestsSql = ""; - $transactions[] = [ - 'duedate' => $item['duedate'], - 'id' => $id, - 'barcode' => $item['duedate'], - 'renew' => null, - 'request' => null, // TODO - 'volume' => null, // TODO - maybe - 'publication_year' => $item['year'], - 'renewable' => null, //TODO maybe - for renewals - 'message' => '', - 'title' => $item['title'], - 'item_id' => $item['item_id'] // TODO - maybe for renewals - ]; - } - return $transactions; - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - } -} diff --git a/module/VuFind/src/VuFind/ILS/Driver/DAIA.php b/module/VuFind/src/VuFind/ILS/Driver/DAIA.php index 0a0b8287c88be94f2138f4eb3bf1e9462f5c8828..c66ef8702a19af76accd33369df3f1ad71351805 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/DAIA.php +++ b/module/VuFind/src/VuFind/ILS/Driver/DAIA.php @@ -5,7 +5,7 @@ * Based on the proof-of-concept-driver by Till Kinstler, GBV. * Relaunch of the daia driver developed by Oliver Goldschmidt. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Jochen Lienhard 2014. * @@ -31,9 +31,11 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use DOMDocument, VuFind\Exception\ILS as ILSException, - VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface, - Zend\Log\LoggerAwareInterface as LoggerAwareInterface; + +use DOMDocument; +use VuFind\Exception\ILS as ILSException; +use VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface; +use Zend\Log\LoggerAwareInterface as LoggerAwareInterface; /** * ILS Driver for VuFind to query availability information via DAIA. @@ -49,6 +51,9 @@ use DOMDocument, VuFind\Exception\ILS as ILSException, class DAIA extends AbstractBase implements HttpServiceAwareInterface, LoggerAwareInterface { + use CacheTrait { + getCacheKey as protected getBaseCacheKey; + } use \VuFindHttp\HttpServiceAwareTrait; use \VuFind\Log\LoggerAwareTrait; @@ -205,7 +210,7 @@ class DAIA extends AbstractBase implements */ protected function getCacheKey($suffix = null) { - return parent::getCacheKey(md5($this->baseURL) . $suffix); + return $this->getBaseCacheKey(md5($this->baseUrl) . $suffix); } /** @@ -270,7 +275,7 @@ class DAIA extends AbstractBase implements // extract the DAIA document for the current id from the // HTTPRequest's result $doc = $this->extractDaiaDoc($id, $rawResult); - if (!is_null($doc)) { + if (null !== $doc) { // parse the extracted DAIA document and return the status info $data = $this->parseDaiaDoc($id, $doc); // cache the status information @@ -333,7 +338,7 @@ class DAIA extends AbstractBase implements // it is assumed that each DAIA document has a unique URI, // so get the document with the corresponding id $doc = $this->extractDaiaDoc($id, $rawResult); - if (!is_null($doc)) { + if (null !== $doc) { // a document with the corresponding id exists, which // means we got status information for that record $data = $this->parseDaiaDoc($id, $doc); @@ -353,7 +358,7 @@ class DAIA extends AbstractBase implements // extract the DAIA document for the current id from the // HTTPRequest's result $doc = $this->extractDaiaDoc($id, $rawResult); - if (!is_null($doc)) { + if (null !== $doc) { // parse the extracted DAIA document and save the status // info $data = $this->parseDaiaDoc($id, $doc); @@ -473,7 +478,6 @@ class DAIA extends AbstractBase implements 'HTTP status ' . $result->getStatusCode() . ' received, retrieving availability information for record: ' . $id ); - } // check if result matches daiaResponseFormat @@ -506,7 +510,7 @@ class DAIA extends AbstractBase implements } } - return ($result->getBody()); + return $result->getBody(); } /** @@ -749,7 +753,7 @@ class DAIA extends AbstractBase implements $result_item['item_id'] = $item['id']; // custom DAIA field used in getHoldLink() $result_item['ilslink'] - = (isset($item['href']) ? $item['href'] : $doc_href); + = ($item['href'] ?? $doc_href); // count items $number++; $result_item['number'] = $this->getItemNumber($item, $number); @@ -934,7 +938,7 @@ class DAIA extends AbstractBase implements $return['customData'] = $this->getCustomData($item); $return['limitation_types'] = $item_limitation_types; - + return $return; } @@ -1007,9 +1011,9 @@ class DAIA extends AbstractBase implements // Check if we have at least one service unavailable and a href field is set // (either as flag or as actual value for the next action). - return ($href && count( + return $href && count( array_diff($services['unavailable'], $services['available']) - )); + ); } /** @@ -1051,9 +1055,9 @@ class DAIA extends AbstractBase implements // Check if we have at least one service unavailable and a href field is set // (either as flag or as actual value for the next action). - return ($href && count( + return $href && count( array_diff($services['available'], $services['unavailable']) - )); + ); } /** @@ -1132,8 +1136,7 @@ class DAIA extends AbstractBase implements */ protected function getItemDepartmentLink($item) { - return isset($item['department']['href']) - ? $item['department']['href'] : false; + return $item['department']['href'] ?? false; } /** @@ -1305,7 +1308,7 @@ class DAIA extends AbstractBase implements foreach ($messages as $message) { if (isset($message['content'])) { $this->debug( - 'Message in DAIA response (' . (string) $context . '): ' . + 'Message in DAIA response (' . (string)$context . '): ' . $message['content'] ); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Demo.php b/module/VuFind/src/VuFind/ILS/Driver/Demo.php index 13ae7dbed147d9d659861656711244fe1528fd1c..eda909acabe9dc60e4f83f5af0971be9d2c6d24c 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Demo.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Demo.php @@ -6,7 +6,7 @@ * the session. You can log out and log back in to get a different set of * values. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * Copyright (C) The National Library of Finland 2014. @@ -32,10 +32,13 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use ArrayObject, VuFind\Exception\Date as DateException, - VuFind\Exception\ILS as ILSException, - VuFindSearch\Query\Query, VuFindSearch\Service as SearchService, - Zend\Session\Container as SessionContainer; + +use ArrayObject; +use VuFind\Date\DateException; +use VuFind\Exception\ILS as ILSException; +use VuFindSearch\Query\Query; +use VuFindSearch\Service as SearchService; +use Zend\Session\Container as SessionContainer; /** * Advanced Dummy ILS Driver -- Returns sample values based on Solr index. @@ -210,7 +213,7 @@ class Demo extends AbstractBase { // Load service configuration; return empty array if no services defined. $services = isset($this->config['Records']['services']) - ? (array) $this->config['Records']['services'] + ? (array)$this->config['Records']['services'] : []; if (empty($services)) { return []; @@ -218,7 +221,7 @@ class Demo extends AbstractBase // Make it more likely we have a single service than many: $count = rand(1, 5) == 1 ? rand(1, count($services)) : 1; - $keys = (array) array_rand($services, $count); + $keys = (array)array_rand($services, $count); $fakeServices = []; foreach ($keys as $key) { @@ -533,7 +536,7 @@ class Demo extends AbstractBase */ protected function getSimulatedStatus($id, array $patron = null) { - $id = (string) $id; + $id = (string)$id; // Do we have a fake status persisted in the session? $session = $this->getSession(); @@ -622,6 +625,7 @@ class Demo extends AbstractBase // Get basic status info: $status = $this->getSimulatedStatus($id, $patron); + $issue = 1; // Add notes and summary: foreach (array_keys($status) as $i) { $itemNum = $i + 1; @@ -637,6 +641,10 @@ class Demo extends AbstractBase for ($j = 1; $j <= $summCount; $j++) { $status[$i]['summary'][] = "Item $itemNum summary $j"; } + $volume = intdiv($issue, 4) + 1; + $seriesIssue = $issue % 4; + $issue = $issue + 1; + $status[$i]['enumchron'] = "volume $volume, issue $seriesIssue"; } // Send back final value: @@ -721,6 +729,7 @@ class Demo extends AbstractBase 'city' => 'City', 'country' => 'Country', 'phone' => '1900 CALL ME', + 'mobile_phone' => '1234567890', 'group' => 'Library Staff', 'expiration_date' => 'Someday' ]; @@ -968,6 +977,145 @@ class Demo extends AbstractBase return $session->transactions; } + /** + * Construct a historic transaction list for getMyTransactionHistory; may be + * random or pre-set depending on Demo.ini settings. + * + * @return array + */ + protected function getHistoricTransactionList() + { + $this->checkIntermittentFailure(); + // If Demo.ini includes a fixed set of transactions, load those; otherwise + // build some random ones. + return isset($this->config['Records']['historicTransactions']) + ? json_decode($this->config['Records']['historicTransactions'], true) + : $this->getRandomHistoricTransactionList(); + } + + /** + * Construct a random set of transactions for getMyTransactionHistory(). + * + * @return array + */ + protected function getRandomHistoricTransactionList() + { + // How many items are there? %10 - 1 = 10% chance of none, + // 90% of 1-150 (give or take some odd maths) + $trans = rand() % 10 - 1 > 0 ? rand() % 15 : 0; + + $transList = []; + for ($i = 0; $i < $trans; $i++) { + // Checkout date + $relative = rand() % 300; + $checkoutDate = strtotime("now -$relative days"); + // Due date (7-30 days from checkout) + $dueDate = $checkoutDate + 60 * 60 * 24 * (rand() % 23 + 7); + // Return date (1-40 days from checkout and < now) + $returnDate = min( + [$checkoutDate + 60 * 60 * 24 * (rand() % 39 + 1), time()] + ); + + // Create a generic transaction: + $transList[] = $this->getRandomItemIdentifier() + [ + 'checkoutDate' => $this->dateConverter->convertToDisplayDate( + 'U', $checkoutDate + ), + 'dueDate' => $this->dateConverter->convertToDisplayDate( + 'U', $dueDate + ), + 'returnDate' => $this->dateConverter->convertToDisplayDate( + 'U', $returnDate + ), + // Raw dates for sorting + '_checkoutDate' => $checkoutDate, + '_dueDate' => $dueDate, + '_returnDate' => $returnDate, + 'barcode' => sprintf("%08d", rand() % 50000), + 'item_id' => $i, + ]; + if ($this->idsInMyResearch) { + $transList[$i]['id'] = $this->getRandomBibId(); + $transList[$i]['source'] = $this->getRecordSource(); + } else { + $transList[$i]['title'] = 'Demo Title ' . $i; + } + } + return $transList; + } + + /** + * Get Patron Loan History + * + * This is responsible for retrieving all historic transactions for a specific + * patron. + * + * @param array $patron The patron array from patronLogin + * @param array $params Parameters + * + * @return mixed Array of the patron's historic transactions on success. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getMyTransactionHistory($patron, $params) + { + $this->checkIntermittentFailure(); + $session = $this->getSession(); + if (!isset($session->historicLoans)) { + $session->historicLoans = $this->getHistoricTransactionList(); + } + + // Sort and splice the list + $historicLoans = $session->historicLoans; + if (isset($params['sort'])) { + switch ($params['sort']) { + case 'checkout asc': + $sorter = function ($a, $b) { + return strcmp($a['_checkoutDate'], $b['_checkoutDate']); + }; + break; + case 'return desc': + $sorter = function ($a, $b) { + return strcmp($b['_returnDate'], $a['_returnDate']); + }; + break; + case 'return asc': + $sorter = function ($a, $b) { + return strcmp($a['_returnDate'], $b['_returnDate']); + }; + break; + case 'due desc': + $sorter = function ($a, $b) { + return strcmp($b['_dueDate'], $a['_dueDate']); + }; + break; + case 'due asc': + $sorter = function ($a, $b) { + return strcmp($a['_dueDate'], $b['_dueDate']); + }; + break; + default: + $sorter = function ($a, $b) { + return strcmp($b['_checkoutDate'], $a['_checkoutDate']); + }; + break; + } + + usort($historicLoans, $sorter); + } + + $limit = isset($params['limit']) ? (int)$params['limit'] : 50; + $start = isset($params['page']) + ? ((int)$params['page'] - 1) * $limit : 0; + + $historicLoans = array_splice($historicLoans, $start, $limit); + + return [ + 'count' => count($session->historicLoans), + 'transactions' => $historicLoans + ]; + } + /** * Get Pick Up Locations * @@ -2032,6 +2180,58 @@ class Demo extends AbstractBase ? $this->config['changePassword'] : ['minLength' => 4, 'maxLength' => 20]; } + if ($function == 'getMyTransactionHistory') { + if (empty($this->config['TransactionHistory']['enabled'])) { + return false; + } + return [ + 'max_results' => 100, + 'sort' => [ + 'checkout desc' => 'sort_checkout_date_desc', + 'checkout asc' => 'sort_checkout_date_asc', + 'return desc' => 'sort_return_date_desc', + 'return asc' => 'sort_return_date_asc', + 'due desc' => 'sort_due_date_desc', + 'due asc' => 'sort_due_date_asc' + ], + 'default_sort' => 'checkout desc' + ]; + } return []; } + + /** + * Get bib records for recently returned items. + * + * @param int $limit Maximum number of records to retrieve (default = 30) + * @param int $maxage The maximum number of days to consider "recently + * returned." + * @param array $patron Patron Data + * + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getRecentlyReturnedBibs($limit = 30, $maxage = 30, + $patron = null + ) { + // This is similar to getNewItems for demo purposes. + $results = $this->getNewItems(1, $limit, $maxage); + return $results['results']; + } + + /** + * Get bib records for "trending" items (recently returned with high usage). + * + * @param int $limit Maximum number of records to retrieve (default = 30) + * @param int $maxage The maximum number of days' worth of data to examine. + * @param array $patron Patron Data + * + * @return array + */ + public function getTrendingBibs($limit = 30, $maxage = 30, $patron = null) + { + // This is similar to getRecentlyReturnedBibs for demo purposes. + return $this->getRecentlyReturnedBibs($limit, $maxage, $patron); + } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php b/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..307b874ae53d3e80c78145f6d727261c2f54e5bc --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for Demo ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Demo ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DemoFactory extends DriverWithDateConverterFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $sessionFactory = function () use ($container) { + $manager = $container->get('Zend\Session\SessionManager'); + return new \Zend\Session\Container('DemoDriver', $manager); + }; + return parent::__invoke( + $container, $requestedName, + [$container->get('VuFindSearch\Service'), $sessionFactory] + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php b/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php index dc3ba6f6304fc4650311d50a387a220d8c7c19cd..37cad7d7a888044d40baefc3307c34ed5087b479 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php +++ b/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php @@ -2,7 +2,7 @@ /** * Interface for ILS Drivers * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php b/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8fd6268073a509a7df3b707fd1064f056641ba79 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php @@ -0,0 +1,76 @@ +<?php +/** + * Generic factory suitable for most ILS drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic factory suitable for most ILS drivers. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DriverWithDateConverterFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + // Set up the driver with the date converter (and any extra parameters + // passed in as options): + $driver = new $requestedName( + $container->get('VuFind\Date\Converter'), ...($options ?: []) + ); + + // Populate cache storage if a setCacheStorage method is present: + if (method_exists($driver, 'setCacheStorage')) { + $driver->setCacheStorage( + $container->get('VuFind\Cache\Manager')->getCache('object') + ); + } + + return $driver; + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php b/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php index f546054e10b5b3fccc90b25a9a8a5530677bd674..76d002315f0f5de2c1baa8675312c6d3c46b8953 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php @@ -2,7 +2,7 @@ /** * Evergreen ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,7 +26,10 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use PDO, PDOException, VuFind\Exception\ILS as ILSException; + +use PDO; +use PDOException; +use VuFind\Exception\ILS as ILSException; /** * VuFind Connector for Evergreen @@ -56,7 +59,7 @@ class Evergreen extends AbstractBase */ protected $dbName; - /** + /** * Initialize the driver. * * Validate configuration and perform all resource-intensive tasks needed to @@ -188,7 +191,7 @@ HERE; * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -350,7 +353,7 @@ HERE; * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -394,7 +397,7 @@ HERE; * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -451,7 +454,7 @@ HERE; * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -582,23 +585,23 @@ HERE; */ //public function placeHold($holdDetails) //{ - // Need to check asset.copy.status -> config.copy_status.holdable = true - // If it is holdable, place hold in action.hold_request: - // request_time to now, current_copy to asset.copy.id, - // usr to action.usr.id of requesting patron, - // phone_notify to phone number, email_notify to t/f - // set pickup_lib too? - - /* - $sql = ""; - - try { - $sqlStmt = $this->db->prepare($sql); - $sqlStmt->execute(); - } catch (PDOException $e) { - throw new ILSException($e->getMessage()); - } - */ + // Need to check asset.copy.status -> config.copy_status.holdable = true + // If it is holdable, place hold in action.hold_request: + // request_time to now, current_copy to asset.copy.id, + // usr to action.usr.id of requesting patron, + // phone_notify to phone number, email_notify to t/f + // set pickup_lib too? + + /* + $sql = ""; + + try { + $sqlStmt = $this->db->prepare($sql); + $sqlStmt->execute(); + } catch (PDOException $e) { + throw new ILSException($e->getMessage()); + } + */ //} /** diff --git a/module/VuFind/src/VuFind/ILS/Driver/Factory.php b/module/VuFind/src/VuFind/ILS/Driver/Factory.php deleted file mode 100644 index 69377188de4b2a64cbcce1ca0efaf8ac17c340ce..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/ILS/Driver/Factory.php +++ /dev/null @@ -1,297 +0,0 @@ -<?php -/** - * ILS Driver Factory Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package ILS_Drivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\ILS\Driver; -use Zend\ServiceManager\ServiceManager; - -/** - * ILS Driver Factory Class - * - * @category VuFind - * @package ILS_Drivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for Aleph driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Aleph - */ - public static function getAleph(ServiceManager $sm) - { - return new Aleph( - $sm->getServiceLocator()->get('VuFind\DateConverter'), - $sm->getServiceLocator()->get('VuFind\CacheManager') - ); - } - - /** - * Factory for DAIA driver. - * - * @param ServiceManager $sm Service manager. - * - * @return DAIA - */ - public static function getDAIA(ServiceManager $sm) - { - $daia = new DAIA( - $sm->getServiceLocator()->get('VuFind\DateConverter') - ); - - $daia->setCacheStorage( - $sm->getServiceLocator()->get('VuFind\CacheManager')->getCache('object') - ); - - return $daia; - } - - /** - * Factory for LBS4 driver. - * - * @param ServiceManager $sm Service manager. - * - * @return LBS4 - */ - public static function getLBS4(ServiceManager $sm) - { - return new LBS4( - $sm->getServiceLocator()->get('VuFind\DateConverter') - ); - } - - /** - * Factory for Demo driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Demo - */ - public static function getDemo(ServiceManager $sm) - { - $sessionFactory = function () use ($sm) { - $manager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - return new \Zend\Session\Container('DemoDriver', $manager); - }; - return new Demo( - $sm->getServiceLocator()->get('VuFind\DateConverter'), - $sm->getServiceLocator()->get('VuFind\Search'), $sessionFactory - ); - } - - /** - * Factory for Horizon driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Horizon - */ - public static function getHorizon(ServiceManager $sm) - { - return new Horizon($sm->getServiceLocator()->get('VuFind\DateConverter')); - } - - /** - * Factory for HorizonXMLAPI driver. - * - * @param ServiceManager $sm Service manager. - * - * @return HorizonXMLAPI - */ - public static function getHorizonXMLAPI(ServiceManager $sm) - { - return new HorizonXMLAPI( - $sm->getServiceLocator()->get('VuFind\DateConverter') - ); - } - - /** - * Factory for MultiBackend driver. - * - * @param ServiceManager $sm Service manager. - * - * @return MultiBackend - */ - public static function getMultiBackend(ServiceManager $sm) - { - return new MultiBackend( - $sm->getServiceLocator()->get('VuFind\Config'), - $sm->getServiceLocator()->get('VuFind\ILSAuthenticator'), - $sm - ); - } - - /** - * Factory for NoILS driver. - * - * @param ServiceManager $sm Service manager. - * - * @return NoILS - */ - public static function getNoILS(ServiceManager $sm) - { - return new NoILS($sm->getServiceLocator()->get('VuFind\RecordLoader')); - } - - /** - * Factory for PAIA driver. - * - * @param ServiceManager $sm Service manager. - * - * @return PAIA - */ - public static function getPAIA(ServiceManager $sm) - { - $paia = new PAIA( - $sm->getServiceLocator()->get('VuFind\DateConverter'), - $sm->getServiceLocator()->get('VuFind\SessionManager') - ); - - $paia->setCacheStorage( - $sm->getServiceLocator()->get('VuFind\CacheManager')->getCache('object') - ); - - return $paia; - } - - /** - * Factory for Koha driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Koha - */ - public static function getKoha(ServiceManager $sm) - { - return new Koha($sm->getServiceLocator()->get('VuFind\DateConverter')); - } - - /** - * Factory for KohaILSDI driver. - * - * @param ServiceManager $sm Service manager. - * - * @return KohaILSDI - */ - public static function getKohaILSDI(ServiceManager $sm) - { - $koha = new KohaILSDI($sm->getServiceLocator()->get('VuFind\DateConverter')); - $koha->setCacheStorage( - $sm->getServiceLocator()->get('VuFind\CacheManager')->getCache('object') - ); - return $koha; - } - - /** - * Factory for Sierra REST driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SierraRest - */ - public static function getSierraRest(ServiceManager $sm) - { - $sessionFactory = function ($namespace) use ($sm) { - $manager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - return new \Zend\Session\Container("SierraRest_$namespace", $manager); - }; - - $driver = new SierraRest( - $sm->getServiceLocator()->get('VuFind\DateConverter'), - $sessionFactory - ); - $driver->setCacheStorage( - $sm->getServiceLocator()->get('VuFind\CacheManager')->getCache('object') - ); - return $driver; - } - - /** - * Factory for Symphony driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Symphony - */ - public static function getSymphony(ServiceManager $sm) - { - return new Symphony( - $sm->getServiceLocator()->get('VuFind\RecordLoader'), - $sm->getServiceLocator()->get('VuFind\CacheManager') - ); - } - - /** - * Factory for Unicorn driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Unicorn - */ - public static function getUnicorn(ServiceManager $sm) - { - return new Unicorn($sm->getServiceLocator()->get('VuFind\DateConverter')); - } - - /** - * Factory for Voyager driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Voyager - */ - public static function getVoyager(ServiceManager $sm) - { - return new Voyager($sm->getServiceLocator()->get('VuFind\DateConverter')); - } - - /** - * Factory for VoyagerRestful driver. - * - * @param ServiceManager $sm Service manager. - * - * @return VoyagerRestful - */ - public static function getVoyagerRestful(ServiceManager $sm) - { - $ils = $sm->getServiceLocator()->get('VuFind\ILSHoldSettings'); - $vr = new VoyagerRestful( - $sm->getServiceLocator()->get('VuFind\DateConverter'), - $ils->getHoldsMode(), $ils->getTitleHoldsMode() - ); - $vr->setCacheStorage( - $sm->getServiceLocator()->get('VuFind\CacheManager')->getCache('object') - ); - return $vr; - } -} diff --git a/module/VuFind/src/VuFind/ILS/Driver/Horizon.php b/module/VuFind/src/VuFind/ILS/Driver/Horizon.php index 8f78419ed81ece404708617fe44ee3e60a3b9336..953767656f3202e56f4fffe9eaa3e51b852e5b8b 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Horizon.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Horizon.php @@ -2,7 +2,7 @@ /** * Horizon ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,7 +27,11 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + +use PDO; use VuFind\Exception\ILS as ILSException; +use VuFind\Log\LoggerAwareTrait; +use Zend\Log\LoggerAwareInterface; /** * Horizon ILS Driver @@ -39,8 +43,10 @@ use VuFind\Exception\ILS as ILSException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ -class Horizon extends AbstractBase +class Horizon extends AbstractBase implements LoggerAwareInterface { + use LoggerAwareTrait; + /** * Date converter object * @@ -81,15 +87,23 @@ class Horizon extends AbstractBase } // Connect to database - $this->db = mssql_pconnect( - $this->config['Catalog']['host'] . ':' - . $this->config['Catalog']['port'], - $this->config['Catalog']['username'], - $this->config['Catalog']['password'] - ); - - // Select the databse - mssql_select_db($this->config['Catalog']['database']); + try { + $this->db = new PDO( + 'dblib:host=' . $this->config['Catalog']['host'] . + ':' . $this->config['Catalog']['port'] . + ';dbname=' . $this->config['Catalog']['database'], + $this->config['Catalog']['username'], + $this->config['Catalog']['password'] + ); + + // throw an exception instead of false on sql errors + $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (\Exception $e) { + $this->logError($e->getMessage()); + throw new ILSException( + 'ILS Configuration problem : ' . $e->getMessage() + ); + } } /** @@ -147,13 +161,13 @@ class Horizon extends AbstractBase if (in_array(strtolower('available:0'), $arrayValues)) { $available = 0; } - if (in_array(strtolower('reserve:N'), $arrayValues)) { + if (in_array(strtolower('reserve:N'), $arrayValues)) { $reserve = 'N'; } - if (in_array(strtolower('reserve:Y'), $arrayValues)) { + if (in_array(strtolower('reserve:Y'), $arrayValues)) { $reserve = 'Y'; } - if (in_array(strtolower('duedate:0'), $arrayValues)) { + if (in_array(strtolower('duedate:0'), $arrayValues)) { $duedate = ''; } } else { @@ -321,7 +335,7 @@ class Horizon extends AbstractBase * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -334,12 +348,16 @@ class Horizon extends AbstractBase try { $holding = []; - $sqlStmt = mssql_query($sql); - while ($row = mssql_fetch_assoc($sqlStmt)) { + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { $holding[] = $this->processHoldingRow($id, $row, $patron); } + + $this->debug(json_encode($holding)); + return $holding; } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -357,12 +375,14 @@ class Horizon extends AbstractBase $item_status = $row['STATUS_CODE']; //get the item status code $statusValues = $this->parseStatus($item_status); - $status = ['id' => $id, - 'availability' => $statusValues['available'], - 'status' => $row['STATUS'], - 'location' => $row['LOCATION'], - 'reserve' => $statusValues['reserve'], - 'callnumber' => $row['CALLNUMBER']]; + $status = [ + 'id' => $id, + 'availability' => $statusValues['available'], + 'status' => $row['STATUS'], + 'location' => $row['LOCATION'], + 'reserve' => $statusValues['reserve'], + 'callnumber' => $row['CALLNUMBER'] + ]; return $status; } @@ -417,10 +437,12 @@ class Horizon extends AbstractBase $sqlWhere = ["i.bib# in (" . $bibIDs . ")", "i.staff_only = 0"]; - $sqlArray = ['expressions' => $sqlExpressions, - 'from' => $sqlFrom, - 'innerJoin' => $sqlInnerJoin, - 'where' => $sqlWhere]; + $sqlArray = [ + 'expressions' => $sqlExpressions, + 'from' => $sqlFrom, + 'innerJoin' => $sqlInnerJoin, + 'where' => $sqlWhere + ]; return $sqlArray; } @@ -454,13 +476,14 @@ class Horizon extends AbstractBase try { $status = []; - $sqlStmt = mssql_query($sql); - while ($row = mssql_fetch_assoc($sqlStmt)) { + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { $id = $row['ID']; $status[$id][] = $this->processStatusRow($id, $row); } return $status; } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -491,39 +514,46 @@ class Horizon extends AbstractBase * * @throws ILSException * @return mixed Associative array of patron info on successful login, - * null on unsuccessful login. + * ILSException on unsuccessful login. */ public function patronLogin($username, $password) { $sql = "select name_reconstructed as FULLNAME, " . - "email_address as EMAIL from borrower " . + "email_address as EMAIL " . + "from borrower " . "left outer join borrower_address on " . - "borrower_address.borrower#=borrower.borrower# " . + "borrower_address.borrower# = borrower.borrower# " . "inner join borrower_barcode on " . - "borrower.borrower#=borrower_barcode.borrower# " . - "where borrower_barcode.bbarcode=\"" . addslashes($username) . - "\" and pin# = \"" . addslashes($password) . "\""; + "borrower.borrower# = borrower_barcode.borrower# " . + "where borrower_barcode.bbarcode = " . + "'" . addslashes($username) . "' " . + "and pin# = '" . addslashes($password) . "'"; try { $user = []; - $sqlStmt = mssql_query($sql); - $row = mssql_fetch_assoc($sqlStmt); - if ($row) { - list($lastname,$firstname) = explode(', ', $row['FULLNAME']); - $user = ['id' => $username, - 'firstname' => $firstname, - 'lastname' => $lastname, - 'cat_username' => $username, - 'cat_password' => $password, - 'email' => $row['EMAIL'], - 'major' => null, - 'college' => null]; + + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { + list($lastname, $firstname) = explode(', ', $row['FULLNAME']); + $user = [ + 'id' => $username, + 'firstname' => $firstname, + 'lastname' => $lastname, + 'cat_username' => $username, + 'cat_password' => $password, + 'email' => $row['EMAIL'], + 'major' => null, + 'college' => null + ]; + + $this->debug(json_encode($user)); return $user; - } else { - return null; } + + throw new ILSException('Unable to login patron ' . $username); } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -577,8 +607,7 @@ class Horizon extends AbstractBase // Where $sqlWhere = [ - "bb.bbarcode=\"" . addslashes($patron['id']) . - "\"" + "bb.bbarcode='" . addslashes($patron['id']) . "'" ]; $sqlOrder = [ @@ -603,7 +632,7 @@ class Horizon extends AbstractBase * * @param array $row An sql row * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @return array Keyed data */ protected function processHoldsRow($row) @@ -661,7 +690,7 @@ class Horizon extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -671,16 +700,19 @@ class Horizon extends AbstractBase $sql = $this->buildSqlFromArray($sqlArray); try { - $sqlStmt = mssql_query($sql); - - while ($row = mssql_fetch_assoc($sqlStmt)) { + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { $hold = $this->processHoldsRow($row); if ($hold) { $holdList[] = $hold; } } + + $this->debug(json_encode($holdList)); + return $holdList; } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -692,7 +724,7 @@ class Horizon extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -749,7 +781,7 @@ class Horizon extends AbstractBase " on bu4.reference# = bu.reference# " . " and bu4.ord = 0 " . " and bu4.block in ('l', 'LostPro','fine','he') " . - " where bb.bbarcode = \"" . addslashes($patron['id']) . "\" " . + " where bb.bbarcode = '" . addslashes($patron['id']) . "' " . " and bu.ord = 0 " . " and bl.pac_display = 1 " . " order by FEEBLOCK desc " . @@ -759,20 +791,26 @@ class Horizon extends AbstractBase " , bu.date"; try { - $sqlStmt = mssql_query($sql); - - while ($row = mssql_fetch_assoc($sqlStmt)) { - $fineList[] = ['amount' => $row['AMOUNT'], - 'checkout' => $row['CHECKOUT'], - 'fine' => $row['FINE'], - 'balance' => $row['BALANCE'], - 'createdate' => $row['CREATEDATE'], - 'duedate' => $row['DUEDATE'], - 'id' => $row['ID'], - 'title' => $row['TITLE']]; + $sqlStmt = $this->db->query($sql); + $fineList = []; + foreach ($sqlStmt as $row) { + $fineList[] = [ + 'amount' => $row['AMOUNT'], + 'checkout' => $row['CHECKOUT'], + 'fine' => $row['FINE'], + 'balance' => $row['BALANCE'], + 'createdate' => $row['CREATEDATE'], + 'duedate' => $row['DUEDATE'], + 'id' => $row['ID'], + 'title' => $row['TITLE'] + ]; } + + $this->debug(json_encode($fineList)); + return $fineList; } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -785,7 +823,8 @@ class Horizon extends AbstractBase * @param array $patron The patron array * * @throws ILSException - * @return array Array of the patron's profile data on success. + * @return array Array of the patron's profile data on success, + * throw ILSException if none found */ public function getMyProfile($patron) { @@ -793,32 +832,38 @@ class Horizon extends AbstractBase "city_st.descr as ADDRESS2, postal_code as ZIP, phone_no as PHONE " . "from borrower " . "left outer join borrower_phone on " . - "borrower_phone.borrower#=borrower.borrower# " . + "borrower_phone.borrower#=borrower.borrower# " . "inner join borrower_address on " . - "borrower_address.borrower#=borrower.borrower# " . + "borrower_address.borrower#=borrower.borrower# " . "inner join city_st on city_st.city_st=borrower_address.city_st " . "inner join borrower_barcode on " . - "borrower_barcode.borrower#=borrower.borrower# " . - "where borrower_barcode.bbarcode=\"" . addslashes($patron['id']) . "\""; + "borrower_barcode.borrower# = borrower.borrower# " . + "where borrower_barcode.bbarcode = '" . addslashes($patron['id']) . "'"; try { - $sqlStmt = mssql_query($sql); - - $row = mssql_fetch_assoc($sqlStmt); - if ($row) { - list($lastname,$firstname) = explode(', ', $row['FULLNAME']); - $profile = ['lastname' => $lastname, - 'firstname' => $firstname, - 'address1' => $row['ADDRESS1'], - 'address2' => $row['ADDRESS2'], - 'zip' => $row['ZIP'], - 'phone' => $row['PHONE'], - 'group' => null]; + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { + list($lastname, $firstname) = explode(', ', $row['FULLNAME']); + $profile = [ + 'lastname' => $lastname, + 'firstname' => $firstname, + 'address1' => $row['ADDRESS1'], + 'address2' => $row['ADDRESS2'], + 'zip' => $row['ZIP'], + 'phone' => $row['PHONE'], + 'group' => null + ]; + + $this->debug(json_encode($profile)); + return $profile; - } else { - return null; } + + throw new ILSException( + 'Unable to retrieve profile for patron ' . $patron['id'] + ); } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -865,7 +910,7 @@ class Horizon extends AbstractBase // Where $sqlWhere = [ - "bb.bbarcode=\"" . addslashes($patron['id']) . "\""]; + "bb.bbarcode='" . addslashes($patron['id']) . "'"]; // Order by $sqlOrder = [ @@ -890,7 +935,7 @@ class Horizon extends AbstractBase * * @param array $row An array of keyed data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @return array Keyed data for display by template files */ protected function processTransactionsRow($row) @@ -908,7 +953,7 @@ class Horizon extends AbstractBase if (is_numeric($dueTimeStamp)) { if ($now > $dueTimeStamp) { $dueStatus = "overdue"; - } else if ($now > $dueTimeStamp - (1 * 24 * 60 * 60)) { + } elseif ($now > $dueTimeStamp - (1 * 24 * 60 * 60)) { $dueStatus = "due"; } } @@ -916,15 +961,15 @@ class Horizon extends AbstractBase return [ 'id' => $row['BIB_NUM'], - 'item_id' => $row['ITEM_NUM'], - 'duedate' => $dueDate, - 'barcode' => $row['ITEM_BARCODE'], - 'renew' => $row['RENEW'], - 'request' => $row['REQUEST'], - 'dueStatus' => $dueStatus, - 'volume' => $row['VOLUME'], - 'publication_year' => $row['PUBLICATION_YEAR'], - 'title' => $row['TITLE'] + 'item_id' => $row['ITEM_NUM'], + 'duedate' => $dueDate, + 'barcode' => $row['ITEM_BARCODE'], + 'renew' => $row['RENEW'], + 'request' => $row['REQUEST'], + 'dueStatus' => $dueStatus, + 'volume' => $row['VOLUME'], + 'publication_year' => $row['PUBLICATION_YEAR'], + 'title' => $row['TITLE'] ]; } @@ -936,7 +981,7 @@ class Horizon extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -947,12 +992,16 @@ class Horizon extends AbstractBase $sql = $this->buildSqlFromArray($sqlArray); try { - $sqlStmt = mssql_query($sql); - while ($row = mssql_fetch_assoc($sqlStmt)) { + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { $transList[] = $this->processTransactionsRow($row); } + + $this->debug(json_encode($transList)); + return $transList; } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } } @@ -1001,6 +1050,7 @@ class Horizon extends AbstractBase // Set the Sybase or MSSQL rowcount limit (TODO: account for $page) $limitsql = "set rowcount {$limit}"; + // for Sybase ASE 12.5 : "set rowcount $limit" // This is the actual query for IDs. $newsql = " select nb.bib# " @@ -1015,19 +1065,25 @@ class Horizon extends AbstractBase $results = []; // Set the rowcount limit before executing the query for IDs - mssql_query($limitsql); + $this->db->query($limitsql); // Actual query for IDs - $sqlStmt = mssql_query($newsql); - while ($row = mssql_fetch_assoc($sqlStmt)) { - $results[] = $row['bib#']; - } + try { + $sqlStmt = $this->db->query($newsql); + foreach ($sqlStmt as $row) { + $results[] = $row['bib#']; + } + + $retVal = ['count' => count($results), 'results' => []]; + foreach ($results as $result) { + $retVal['results'][] = ['id' => $result]; + } - $retVal = ['count' => count($results), 'results' => []]; - foreach ($results as $result) { - $retVal['results'][] = ['id' => $result]; + return $retVal; + } catch (\Exception $e) { + $this->logError($e->getMessage()); + throw new ILSException($e->getMessage()); } - return $retVal; } else { return ['count' => 0, 'results' => []]; } @@ -1047,9 +1103,14 @@ class Horizon extends AbstractBase { $checkHzVersionSQL = "select database_revision from matham"; - $versionResult = mssql_query($checkHzVersionSQL); - while ($row = mssql_fetch_assoc($versionResult)) { - $hzVersionFound = $row['database_revision']; + try { + $versionResult = $this->db->query($checkHzVersionSQL); + foreach ($versionResult as $row) { + $hzVersionFound = $row['database_revision']; + } + } catch (\Exception $e) { + $this->logError($e->getMessage()); + throw new ILSException($e->getMessage()); } /* The Horizon database version is made up of 4 numbers separated by periods. @@ -1094,11 +1155,12 @@ class Horizon extends AbstractBase " from bib_control bc" . " where bc.staff_only = 1"; try { - $sqlStmt = mssql_query($sql); - while ($row = mssql_fetch_assoc($sqlStmt)) { + $sqlStmt = $this->db->query($sql); + foreach ($sqlStmt as $row) { $list[] = $row['bib#']; } } catch (\Exception $e) { + $this->logError($e->getMessage()); throw new ILSException($e->getMessage()); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php b/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php index d95af141dd0470dd4a88e3b34d08f8508b99255f..3359cac62304244704cfe0efaca37ee19cd6c23c 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php +++ b/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php @@ -2,7 +2,7 @@ /** * Horizon ILS Driver (w/ XML API support) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use VuFind\Exception\ILS as ILSException; /** @@ -109,11 +110,11 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter 'level' => 'item' ]; - $holding = parent::processHoldingRow($id, $row, $patron); - $holding += [ + $holding = parent::processHoldingRow($id, $row, $patron); + $holding += [ 'addLink' => $this->checkRequestIsValid($id, $itemData, $patron) ]; - return $holding; + return $holding; } /** @@ -219,9 +220,9 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter $sql = $this->buildSqlFromArray($sqlArray); try { - $sqlStmt = mssql_query($sql); + $sqlStmt = $this->db->query($sql); - while ($row = mssql_fetch_assoc($sqlStmt)) { + foreach ($sqlStmt as $row) { $pickresponse[] = [ 'locationID' => $row['LOCATIONID'], 'locationDisplay' => $row['LOCATIONDISPLAY'] @@ -230,7 +231,6 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter } catch (\Exception $e) { throw new ILSException($e->getMessage()); } - } elseif (isset($this->wsPickUpLocations)) { foreach ($this->wsPickUpLocations as $code => $library) { $pickresponse[] = [ @@ -284,19 +284,17 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter $sql = $this->buildSqlFromArray($sqlArray); try { - $sqlStmt = mssql_query($sql); + $sqlStmt = $this->db->query($sql); - $row = mssql_fetch_assoc($sqlStmt); - if ($row) { + foreach ($sqlStmt as $row) { $defaultPickUpLocation = $row['LOCATION']; return $defaultPickUpLocation; - } else { - return null; } + // If we didn't return above, there were no values. + return null; } catch (\Exception $e) { throw new ILSException($e->getMessage()); } - } elseif (isset($this->wsDefaultPickUpLocation)) { return $this->wsDefaultPickUpLocation; } @@ -627,7 +625,6 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter // No Indication of Success or Failure if ($response !== false && !$response->error->message) { - $keys = []; // Get a list of bib keys from waiting items $currentHolds = $response->holdsdata->waiting->waitingitem; @@ -773,10 +770,8 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter $response['ids'] = $renewIDs; $i = 0; foreach ($origData->itemout as $item) { - $ikey = (string)$item->ikey; if (in_array($ikey, $renewIDs)) { - $response['details'][$ikey]['item_id'] = $ikey; $origRenewals = (string)$item->numrenewals; $currentRenewals = (string)$renewData->itemout[$i]->numrenewals; @@ -792,13 +787,11 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter } if ($currentRenewals > $origRenewals) { - $response['details'][$ikey] = [ 'item_id' => $ikey, 'new_date' => $currentDueDate, 'success' => true ]; - } else { $response['details'][$ikey] = [ 'item_id' => $ikey, diff --git a/module/VuFind/src/VuFind/ILS/Driver/Innovative.php b/module/VuFind/src/VuFind/ILS/Driver/Innovative.php index f4bee878da18588f75181680578757a75f9ae48b..127b000e90f85ab97ccd4fa94e6490c97db20cb1 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Innovative.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Innovative.php @@ -2,7 +2,7 @@ /** * III ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use VuFind\Exception\ILS as ILSException; /** @@ -175,7 +176,7 @@ class Innovative extends AbstractBase implements // replace non blocking space encodings with a space. $cols[$i] = str_replace(" ", " ", $cols[$i]); // remove html comment tags - $cols[$i] = ereg_replace("<!--([^(-->)]*)-->", "", $cols[$i]); + $cols[$i] = preg_replace("/<!--([^(-->)]*)-->/", "", $cols[$i]); // Remove closing th or td tag, trim whitespace and decode html // entities $cols[$i] = html_entity_decode( @@ -186,7 +187,7 @@ class Innovative extends AbstractBase implements // names if ($count == 1) { $keys[$i] = $cols[$i]; - } else if ($count > 1) { // not the first row, has holding info + } elseif ($count > 1) { // not the first row, has holding info //look for location column if (stripos($keys[$i], $loc_col_name) > -1) { $ret[$count - 2]['location'] = strip_tags($cols[$i]); @@ -269,7 +270,7 @@ class Innovative extends AbstractBase implements * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, diff --git a/module/VuFind/src/VuFind/ILS/Driver/Koha.php b/module/VuFind/src/VuFind/ILS/Driver/Koha.php index 2739ee6a058cd8a8341ce0deac889da647833f19..7999f80d4119dca3b8cc907fad1e111bceb540c9 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Koha.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Koha.php @@ -2,7 +2,7 @@ /** * Koha ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Ayesha Abed Library, BRAC University 2010. * @@ -27,7 +27,10 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use PDO, PDOException, VuFind\Exception\ILS as ILSException; + +use PDO; +use PDOException; +use VuFind\Exception\ILS as ILSException; /** * VuFind Driver for Koha (version: 3.02) @@ -157,7 +160,7 @@ class Koha extends AbstractBase * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -173,7 +176,8 @@ class Koha extends AbstractBase $sql = "select itemnumber as ITEMNO, location as LOCATION, " . "holdingbranch as HLDBRNCH, reserves as RESERVES, itemcallnumber as " . "CALLNO, barcode as BARCODE, copynumber as COPYNO, " . - "notforloan as NOTFORLOAN from items where biblionumber = :id" . + "enumchron AS ENUMCHRON, notforloan as NOTFORLOAN" . + " from items where biblionumber = :id" . " order by itemnumber"; try { $itemSqlStmt = $this->db->prepare($sql); @@ -243,12 +247,12 @@ class Koha extends AbstractBase 'barcode' => (null == $rowItem['BARCODE']) ? 'Unknown' : $rowItem['BARCODE'], 'number' => (null == $rowItem['COPYNO']) - ? 'Unknown' : $rowItem['COPYNO'] + ? 'Unknown' : $rowItem['COPYNO'], + 'enumchron' => $rowItem['ENUMCHRON'] ?? null, ]; } return $holding; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -280,7 +284,7 @@ class Koha extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -314,8 +318,7 @@ class Koha extends AbstractBase ]; } return $fineLst; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -327,7 +330,7 @@ class Koha extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -355,8 +358,7 @@ class Koha extends AbstractBase ]; } return $holdLst; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -397,8 +399,7 @@ class Koha extends AbstractBase return $profile; } return null; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -411,7 +412,7 @@ class Koha extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -437,8 +438,7 @@ class Koha extends AbstractBase ]; } return $transactionLst; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -479,14 +479,96 @@ class Koha extends AbstractBase $blocks[] = implode(' - ', $block); } - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } return count($blocks) ? $blocks : false; } + /** + * Get Patron Loan History + * + * This is responsible for retrieving all historic loans (i.e. items previously + * checked out and then returned), for a specific patron. + * + * @param array $patron The patron array from patronLogin + * @param array $params Parameters + * + * @throws VuFind\Date\DateException; + * @throws ILSException + * @return array Array of the patron's transactions on success. + */ + public function getMyTransactionHistory($patron, $params) + { + $id = 0; + $historicLoans = []; + $row = $sql = $sqlStmt = ''; + try { + if (!$this->db) { + $this->initDb(); + } + $id = $patron['id']; + + // Get total count first + $sql = "select count(*) as cnt from old_issues " . + "where old_issues.borrowernumber = :id"; + $sqlStmt = $this->db->prepare($sql); + $sqlStmt->execute([':id' => $id]); + $totalCount = $sqlStmt->fetch()['cnt']; + + // Get rows + $limit = isset($params['limit']) ? (int)$params['limit'] : 50; + $start = isset($params['page']) + ? ((int)$params['page'] - 1) * $limit : 0; + if (isset($params['sort'])) { + $parts = explode(' ', $params['sort'], 2); + switch ($parts[0]) { + case 'return': + $sort = 'RETURNED'; + break; + case 'due': + $sort = 'DUEDATE'; + break; + default: + $sort = 'ISSUEDATE'; + break; + } + $sort .= isset($parts[1]) && 'asc' === $parts[1] ? ' asc' : ' desc'; + } else { + $sort = 'ISSUEDATE desc'; + } + $sql = "select old_issues.issuedate as ISSUEDATE, " . + "old_issues.date_due as DUEDATE, items.biblionumber as " . + "BIBNO, items.barcode BARCODE, old_issues.returndate as RETURNED, " . + "biblio.title as TITLE " . + "from old_issues join items " . + "on old_issues.itemnumber = items.itemnumber " . + "join biblio on items.biblionumber = biblio.biblionumber " . + "where old_issues.borrowernumber = :id " . + "order by $sort limit $start,$limit"; + $sqlStmt = $this->db->prepare($sql); + + $sqlStmt->execute([':id' => $id]); + foreach ($sqlStmt->fetchAll() as $row) { + $historicLoans[] = [ + 'title' => $row['TITLE'], + 'checkoutDate' => $this->displayDateTime($row['ISSUEDATE']), + 'dueDate' => $this->displayDateTime($row['DUEDATE']), + 'id' => $row['BIBNO'], + 'barcode' => $row['BARCODE'], + 'returnDate' => $this->displayDateTime($row['RETURNED']), + ]; + } + return [ + 'count' => $totalCount, + 'transactions' => $historicLoans + ]; + } catch (PDOException $e) { + throw new ILSException($e->getMessage()); + } + } + /** * Get Purchase History * @@ -586,8 +668,7 @@ class Koha extends AbstractBase } else { return null; } - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } @@ -604,7 +685,7 @@ class Koha extends AbstractBase $sql = "select borrowernumber as ID, firstname as FNAME, " . "surname as LNAME, email as EMAIL from borrowers " . "where userid = :username"; - + $parameters = [':username' => $username]; if ($this->validatePasswords) { @@ -636,8 +717,7 @@ class Koha extends AbstractBase return $patron; } return null; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -653,10 +733,10 @@ class Koha extends AbstractBase { if (empty($date)) { return ""; - } else if (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/", $date) === 1) { + } elseif (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/", $date) === 1) { // YYYY-MM-DD HH:MM:SS return $this->dateConverter->convertToDisplayDate('Y-m-d H:i:s', $date); - } else if (preg_match("/^\d{4}-\d{2}-\d{2}$/", $date) === 1) { // YYYY-MM-DD + } elseif (preg_match("/^\d{4}-\d{2}-\d{2}$/", $date) === 1) { // YYYY-MM-DD return $this->dateConverter->convertToDisplayDate('Y-m-d', $date); } else { error_log("Unexpected date format: $date"); @@ -675,7 +755,7 @@ class Koha extends AbstractBase { if (empty($date)) { return ""; - } else if (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/", $date) === 1) { + } elseif (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/", $date) === 1) { // YYYY-MM-DD HH:MM:SS return $this->dateConverter->convertToDisplayDateAndTime( @@ -686,4 +766,36 @@ class Koha extends AbstractBase return $date; } } + + /** + * Public Function which retrieves renew, hold and cancel settings from the + * driver ini file. + * + * @param string $function The name of the feature to be checked + * + * @return array An array with key-value pairs. + */ + public function getConfig($function) + { + if ('getMyTransactionHistory' === $function) { + if (empty($this->config['TransactionHistory']['enabled'])) { + return false; + } + return [ + 'max_results' => 100, + 'sort' => [ + 'checkout desc' => 'sort_checkout_date_desc', + 'checkout asc' => 'sort_checkout_date_asc', + 'return desc' => 'sort_return_date_desc', + 'return asc' => 'sort_return_date_asc', + 'due desc' => 'sort_due_date_desc', + 'due asc' => 'sort_due_date_asc' + ], + 'default_sort' => 'checkout desc' + ]; + } + return isset($this->config[$function]) + ? $this->config[$function] + : false; + } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php b/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php index a65ceed3d4ddbb3fe2ba081e15a69f5bc5a33dc8..268745b5e041d71b6a29a7935dfbd13b23f90ed8 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php +++ b/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php @@ -2,7 +2,7 @@ /** * KohaILSDI ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Alex Sassmannshausen, PTFS Europe 2014. * @@ -27,10 +27,12 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use PDO, PDOException; + +use PDO; +use PDOException; +use VuFind\Date\DateException; use VuFind\Exception\ILS as ILSException; use Zend\Log\LoggerInterface; -use VuFind\Exception\Date as DateException; /** * VuFind Driver for Koha, using web APIs (ILSDI) @@ -47,6 +49,9 @@ use VuFind\Exception\Date as DateException; class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements \VuFindHttp\HttpServiceAwareInterface, \Zend\Log\LoggerAwareInterface { + use CacheTrait { + getCacheKey as protected getBaseCacheKey; + } use \VuFindHttp\HttpServiceAwareTrait; use \VuFind\Log\LoggerAwareTrait; @@ -177,6 +182,12 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $this->validatePasswords = empty($this->config['Catalog']['dontValidatePasswords']); + // The Authorised Values Category use for locations should default to 'LOC' + $this->locationAuthorisedValuesCategory + = isset($this->config['Catalog']['locationAuthorisedValuesCategory']) + ? $this->config['Catalog']['locationAuthorisedValuesCategory'] + : 'LOC'; + $this->debug("Config Summary:"); $this->debug("DB Host: " . $this->host); $this->debug("ILS URL: " . $this->ilsBaseUrl); @@ -242,6 +253,24 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); // set communication enoding to utf8 $this->db->exec("SET NAMES utf8"); + + // Drop the ONLY_FULL_GROUP_BY entry from sql_mode as it breaks this + // ILS Driver on modern + $setSqlModes = $this->db->prepare("SET sql_mode = :sqlMode"); + + $sqlModes = $this->db->query("SELECT @@sql_mode"); + foreach ($sqlModes as $row) { + $sqlMode = implode( + ',', + array_filter( + explode(',', $row['@@sql_mode']), + function ($mode) { + return $mode != "ONLY_FULL_GROUP_BY"; + } + ) + ); + $setSqlModes->execute(['sqlMode' => $sqlMode]); + } } catch (PDOException $e) { $this->debug('Connection failed: ' . $e->getMessage()); throw new ILSException($e->getMessage()); @@ -297,7 +326,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements */ protected function getCacheKey($suffix = null) { - return \VuFind\ILS\Driver\AbstractBase::getCacheKey( + return $this->getBaseCacheKey( md5($this->ilsBaseUrl) . $suffix ); } @@ -314,8 +343,8 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements */ protected function getField($contents, $default = "Unknown") { - if ((string) $contents != "") { - return (string) $contents; + if ((string)$contents != "") { + return (string)$contents; } else { return $default; } @@ -485,13 +514,26 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements */ public function getConfig($function) { - $functionConfig = ""; - if (isset($this->config[$function])) { - $functionConfig = $this->config[$function]; - } else { - $functionConfig = false; + if ('getMyTransactionHistory' === $function) { + if (empty($this->config['TransactionHistory']['enabled'])) { + return false; + } + return [ + 'max_results' => 100, + 'sort' => [ + 'checkout desc' => 'sort_checkout_date_desc', + 'checkout asc' => 'sort_checkout_date_asc', + 'return desc' => 'sort_return_date_desc', + 'return asc' => 'sort_return_date_asc', + 'due desc' => 'sort_due_date_desc', + 'due asc' => 'sort_due_date_asc' + ], + 'default_sort' => 'checkout desc' + ]; } - return $functionConfig; + return isset($this->config[$function]) + ? $this->config[$function] + : false; } /** @@ -536,8 +578,8 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $sqlSt->execute(); $this->pickupEnableBranchcodes = $sqlSt->fetch(); } catch (PDOException $e) { - $this->debug('Connection failed: ' . $e->getMessage()); - throw new ILSException($e->getMessage()); + $this->debug('Connection failed: ' . $e->getMessage()); + throw new ILSException($e->getMessage()); } } elseif (!empty($holdDetails['level']) && $holdDetails['level'] == 'title' @@ -554,8 +596,8 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $this->pickupEnableBranchcodes[] = $row['holdingbranch']; } } catch (PDOException $e) { - $this->debug('Connection failed: ' . $e->getMessage()); - throw new ILSException($e->getMessage()); + $this->debug('Connection failed: ' . $e->getMessage()); + throw new ILSException($e->getMessage()); } } } @@ -577,17 +619,17 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements } return $this->locations; - // we get them from the API - // FIXME: Not yet possible: API incomplete. - // TODO: When API: pull locations dynamically from API. - /* $response = $this->makeRequest("organizations/branch"); */ - /* $locations_response_array = $response->OrganizationsGetRows; */ - /* foreach ($locations_response_array as $location_response) { */ - /* $locations[] = array( */ - /* 'locationID' => $location_response->OrganizationID, */ - /* 'locationDisplay' => $location_response->Name, */ - /* ); */ - /* } */ + // we get them from the API + // FIXME: Not yet possible: API incomplete. + // TODO: When API: pull locations dynamically from API. + /* $response = $this->makeRequest("organizations/branch"); */ + /* $locations_response_array = $response->OrganizationsGetRows; */ + /* foreach ($locations_response_array as $location_response) { */ + /* $locations[] = array( */ + /* 'locationID' => $location_response->OrganizationID, */ + /* 'locationDisplay' => $location_response->Name, */ + /* ); */ + /* } */ } /** @@ -628,7 +670,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements { $patron = $holdDetails['patron']; $patron_id = $patron['id']; - $request_location = isset($patron['ip']) ? $patron['ip'] : "127.0.0.1"; + $request_location = $patron['ip'] ?? "127.0.0.1"; $bib_id = $holdDetails['id']; $item_id = $holdDetails['item_id']; $pickup_location = !empty($holdDetails['pickUpLocation']) @@ -694,23 +736,23 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements //TODO - test this new functionality /* if ( $level == "title" ) { - $rsp2 = $this->makeIlsdiRequest("HoldTitle", - array("patron_id" => $patron_id, - "bib_id" => $bib_id, - "request_location" => $request_location, - "pickup_location" => $pickup_location, - "pickup_expiry_date" => $needed_before_date, - "needed_before_date" => $needed_before_date - )); + $rsp2 = $this->makeIlsdiRequest("HoldTitle", + array("patron_id" => $patron_id, + "bib_id" => $bib_id, + "request_location" => $request_location, + "pickup_location" => $pickup_location, + "pickup_expiry_date" => $needed_before_date, + "needed_before_date" => $needed_before_date + )); } else { - $rsp2 = $this->makeIlsdiRequest("HoldItem", - array("patron_id" => $patron_id, - "bib_id" => $bib_id, - "item_id" => $item_id, - "pickup_location" => $pickup_location, - "pickup_expiry_date" => $needed_before_date, - "needed_before_date" => $needed_before_date - )); + $rsp2 = $this->makeIlsdiRequest("HoldItem", + array("patron_id" => $patron_id, + "bib_id" => $bib_id, + "item_id" => $item_id, + "pickup_location" => $pickup_location, + "pickup_expiry_date" => $needed_before_date, + "needed_before_date" => $needed_before_date + )); } */ $this->debug("Title: " . $rsp->{'title'}); @@ -739,7 +781,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -747,7 +789,6 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements */ public function getHolding($id, array $patron = null) { - $this->debug( "Function getHolding($id, " . implode(",", (array)$patron) @@ -766,6 +807,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements i.holdingbranch as HLDBRNCH, i.homebranch as HOMEBRANCH, i.reserves as RESERVES, i.itemcallnumber as CALLNO, i.barcode as BARCODE, i.copynumber as COPYNO, i.notforloan as NOTFORLOAN, + i.enumchron AS ENUMCHRON, i.itemnotes as PUBLICNOTES, b.frameworkcode as DOCTYPE, t.frombranch as TRANSFERFROM, t.tobranch as TRANSFERTO, i.itemlost as ITEMLOST, i.itemlost_on AS LOSTON @@ -774,7 +816,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements (SELECT itemnumber, frombranch, tobranch from branchtransfers where datearrived IS NULL) as t USING (itemnumber) left join authorised_values as av on i.location = av.authorised_value - where i.biblionumber = :id AND av.category = 'LOC' + where i.biblionumber = :id AND av.category = :av_category order by i.itemnumber DESC"; $sqlReserves = "select count(*) as RESERVESCOUNT from reserves " . "WHERE biblionumber = :id AND found IS NULL"; @@ -795,7 +837,12 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements } try { $itemSqlStmt = $this->db->prepare($sql); - $itemSqlStmt->execute([':id' => $id]); + $itemSqlStmt->execute( + [ + ':id' => $id, + ':av_category' => $this->locationAuthorisedValuesCategory, + ] + ); $sqlStmtReserves = $this->db->prepare($sqlReserves); $sqlStmtWaitingReserve = $this->db->prepare($sqlWaitingReserve); $sqlStmtReserves->execute([':id' => $id]); @@ -862,7 +909,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $status = 'Lost/Missing'; } - $duedate_formatted = date_format(new \DateTime($duedate), "m/d/Y"); + $duedate_formatted = $this->displayDate($duedate); //Retrieving the full branch name if ($rowItem['HLDBRNCH'] == null) { @@ -915,7 +962,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements } $holding[] = [ 'id' => $id, - 'availability' => (string) $available, + 'availability' => (string)$available, 'item_id' => $rowItem['ITEMNO'], 'status' => $status, 'location' => $loc, @@ -929,15 +976,15 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements ((null == $rowItem['CALLNO']) || ($rowItem['DOCTYPE'] == "PE")) ? '' : $rowItem['CALLNO'], 'duedate' => ($onTransfer || $waiting) - ? '' : (string) $duedate_formatted, + ? '' : (string)$duedate_formatted, 'barcode' => (null == $rowItem['BARCODE']) ? 'Unknown' : $rowItem['BARCODE'], 'number' => (null == $rowItem['COPYNO']) ? '' : $rowItem['COPYNO'], + 'enumchron' => $rowItem['ENUMCHRON'] ?? null, 'requests_placed' => $reservesCount ? $reservesCount : 0, 'frameworkcode' => $rowItem['DOCTYPE'], ]; - } //file_put_contents('holding.txt', print_r($holding,TRUE), FILE_APPEND); @@ -968,7 +1015,6 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements */ public function getNewItems($page, $limit, $daysOld, $fundId = null) { - $this->debug("getNewItems called $page|$limit|$daysOld|$fundId"); $items = []; @@ -1030,7 +1076,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -1044,9 +1090,11 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $sql = "SELECT al.amount*100 as amount, " . "al.amountoutstanding*100 as balance, al.accounttype as fine, " . "al.date as createdat, items.biblionumber as id, " . - "al.description as title " . + "al.description as title, issues.date_due as duedate, " . + "issues.issuedate as issuedate " . "FROM `accountlines` al " . "LEFT JOIN items USING (itemnumber) " . + "LEFT JOIN issues USING (issue_id) " . "WHERE al.borrowernumber = :id "; if (!$this->db) { $this->initDB(); @@ -1054,8 +1102,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $sqlStmt = $this->db->prepare($sql); $sqlStmt->execute([':id' => $id]); foreach ($sqlStmt->fetchAll() as $row) { - switch ($row['fine']) - { + switch ($row['fine']) { case 'A': $fineValue = "Account Management Fee"; break; @@ -1117,24 +1164,20 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $fineValue = "Unknown Charge"; break; } - + $transactionLst[] = [ - 'amount' => $row['amount'], - 'checkout' => "N/A", - 'title' => $row['title'], - 'fine' => $fineValue, - 'balance' => $row['balance'], - 'createdate' => $row['createdat'], - 'duedate' => date_format( - new \DateTime($row['duedate']), "m/d/Y" - ), - 'duedate' => "N/A", - 'id' => isset($row['id']) ? $row['id'] : -1, + 'amount' => $row['amount'], + 'checkout' => $this->displayDateTime($row['issuedate']), + 'title' => $row['title'], + 'fine' => $fineValue, + 'balance' => $row['balance'], + 'createdate' => $this->displayDate($row['createdat']), + 'duedate' => $this->displayDate($row['duedate']), + 'id' => $row['id'] ?? -1, ]; } return $transactionLst; - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } } @@ -1146,7 +1189,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -1169,7 +1212,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements 'checkout' => "N/A", 'fine' => $this->getField($fine->{'description'}), 'balance' => 100 * $this->getField($fine->{'amountoutstanding'}), - 'createdate' => $this->getField($fine->{'date'}), + 'createdate' => $this->displayDate($this->getField($fine->{'date'})), // FIXME: require accountlines.itemnumber -> issues.date_due data. 'duedate' => "N/A", // FIXME: require accountlines.itemnumber -> items.biblionumber data @@ -1186,7 +1229,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -1205,10 +1248,13 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $holdLst[] = [ 'id' => $this->getField($hold->{'biblionumber'}), 'location' => $this->getField($hold->{'branchname'}), - // FIXME: require exposure of reserves.expirationdate - 'expire' => "N/A", - 'create' => date_format( - new \DateTime($this->getField($hold->{'reservedate'})), "m/d/Y" + 'expire' => isset($hold->{'expirationdate'}) + ? $this->displayDate( + $this->getField($hold->{'expirationdate'}) + ) + : "N/A", + 'create' => $this->displayDate( + $this->getField($hold->{'reservedate'}) ), 'position' => $this->getField($hold->{'priority'}), 'title' => $this->getField($hold->{'title'}), @@ -1349,14 +1395,96 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $blocks[] = implode(' - ', $block); } - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } return count($blocks) ? $blocks : false; } + /** + * Get Patron Loan History + * + * This is responsible for retrieving all historic loans (i.e. items previously + * checked out and then returned), for a specific patron. + * + * @param array $patron The patron array from patronLogin + * @param array $params Parameters + * + * @throws DateException + * @throws ILSException + * @return array Array of the patron's transactions on success. + */ + public function getMyTransactionHistory($patron, $params) + { + $id = 0; + $historicLoans = []; + $row = $sql = $sqlStmt = ''; + try { + if (!$this->db) { + $this->initDb(); + } + $id = $patron['id']; + + // Get total count first + $sql = "select count(*) as cnt from old_issues " . + "where old_issues.borrowernumber = :id"; + $sqlStmt = $this->db->prepare($sql); + $sqlStmt->execute([':id' => $id]); + $totalCount = $sqlStmt->fetch()['cnt']; + + // Get rows + $limit = isset($params['limit']) ? (int)$params['limit'] : 50; + $start = isset($params['page']) + ? ((int)$params['page'] - 1) * $limit : 0; + if (isset($params['sort'])) { + $parts = explode(' ', $params['sort'], 2); + switch ($parts[0]) { + case 'return': + $sort = 'RETURNED'; + break; + case 'due': + $sort = 'DUEDATE'; + break; + default: + $sort = 'ISSUEDATE'; + break; + } + $sort .= isset($parts[1]) && 'asc' === $parts[1] ? ' asc' : ' desc'; + } else { + $sort = 'ISSUEDATE desc'; + } + $sql = "select old_issues.issuedate as ISSUEDATE, " . + "old_issues.date_due as DUEDATE, items.biblionumber as " . + "BIBNO, items.barcode BARCODE, old_issues.returndate as RETURNED, " . + "biblio.title as TITLE " . + "from old_issues join items " . + "on old_issues.itemnumber = items.itemnumber " . + "join biblio on items.biblionumber = biblio.biblionumber " . + "where old_issues.borrowernumber = :id " . + "order by $sort limit $start,$limit"; + $sqlStmt = $this->db->prepare($sql); + + $sqlStmt->execute([':id' => $id]); + foreach ($sqlStmt->fetchAll() as $row) { + $historicLoans[] = [ + 'title' => $row['TITLE'], + 'checkoutDate' => $this->displayDateTime($row['ISSUEDATE']), + 'dueDate' => $this->displayDateTime($row['DUEDATE']), + 'id' => $row['BIBNO'], + 'barcode' => $row['BARCODE'], + 'returnDate' => $this->displayDateTime($row['RETURNED']), + ]; + } + return [ + 'count' => $totalCount, + 'transactions' => $historicLoans + ]; + } catch (PDOException $e) { + throw new ILSException($e->getMessage()); + } + } + /** * Get Patron Transactions * @@ -1365,7 +1493,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws DateException * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -1401,11 +1529,8 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements } $transactionLst[] = [ - 'duedate' => date_format( - new \DateTime( - $this->getField($loan->{'date_due'}) - ), - "m/d/Y" + 'duedate' => $this->displayDate( + $this->getField($loan->{'date_due'}) ), 'id' => $this->getField($loan->{'biblionumber'}), 'item_id' => $this->getField($loan->{'itemnumber'}), @@ -1464,7 +1589,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements = explode(" ", $this->getField($rsp->{'date_due'})); $retVal['details'][$renewItem] = [ "success" => true, - "new_date" => $date, + "new_date" => $this->displayDate($date), "new_time" => $time, "item_id" => $renewItem, ]; @@ -1499,6 +1624,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements if (!$this->db) { $this->initDb(); } + $sql = "SELECT b.title, b.biblionumber, MAX(CONCAT(s.publisheddate, ' / ',s.serialseq)) AS 'date and enumeration' @@ -1799,4 +1925,79 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements return null; } } + + /** + * Convert a database date to a displayable date. + * + * @param string $date Date to convert + * + * @return string + */ + public function displayDate($date) + { + if (empty($date)) { + return ""; + } elseif (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/", $date) === 1) { + // YYYY-MM-DD HH:MM:SS + return $this->dateConverter->convertToDisplayDate('Y-m-d H:i:s', $date); + } elseif (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d$/", $date) === 1) { + // YYYY-MM-DD HH:MM + return $this->dateConverter->convertToDisplayDate('Y-m-d H:i', $date); + } elseif (preg_match("/^\d{4}-\d{2}-\d{2}$/", $date) === 1) { // YYYY-MM-DD + return $this->dateConverter->convertToDisplayDate('Y-m-d', $date); + } else { + error_log("Unexpected date format: $date"); + return $date; + } + } + + /** + * Convert a database datetime to a displayable date and time. + * + * @param string $date Datetime to convert + * + * @return string + */ + public function displayDateTime($date) + { + if (empty($date)) { + return ""; + } elseif (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/", $date) === 1) { + // YYYY-MM-DD HH:MM:SS + return + $this->dateConverter->convertToDisplayDateAndTime( + 'Y-m-d H:i:s', $date + ); + } elseif (preg_match("/^\d{4}-\d\d-\d\d \d\d:\d\d$/", $date) === 1) { + // YYYY-MM-DD HH:MM + return + $this->dateConverter->convertToDisplayDateAndTime( + 'Y-m-d H:i', $date + ); + } else { + error_log("Unexpected date format: $date"); + return $date; + } + } + + /** + * Helper method to determine whether or not a certain method can be + * called on this driver. Required method for any smart drivers. + * + * @param string $method The name of the called method. + * @param array $params Array of passed parameters + * + * @return bool True if the method can be called with the given parameters, + * false otherwise. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsMethod($method, $params) + { + // Loan history is only available if properly configured + if ($method == 'getMyTransactionHistory') { + return !empty($this->config['TransactionHistory']['enabled']); + } + return is_callable([$this, $method]); + } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/LBS4.php b/module/VuFind/src/VuFind/ILS/Driver/LBS4.php index 84b5cf02ca2d986350be06fafa4940a3f6da2add..4959844d69948b87eedf290f0664c98711c3ea5b 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/LBS4.php +++ b/module/VuFind/src/VuFind/ILS/Driver/LBS4.php @@ -2,7 +2,7 @@ /** * LBS4 ILS Driver (LBS4) * - * PHP version 5 + * PHP version 7 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -24,6 +24,7 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use VuFind\Exception\ILS as ILSException; use VuFind\I18n\Translator\TranslatorAwareInterface; @@ -237,9 +238,9 @@ class LBS4 extends DAIA implements TranslatorAwareInterface ]; if ($row[6] == '81') { $result['group'] = $this->translate('Staff'); - } else if ($row[6] == '1') { + } elseif ($row[6] == '1') { $result['group'] = $this->translate('Student'); - } else if ($row[6] == '30') { + } elseif ($row[6] == '30') { $result['group'] = $this->translate('Residents'); } $row = sybase_fetch_row($sqlStmt); @@ -270,7 +271,7 @@ class LBS4 extends DAIA implements TranslatorAwareInterface * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -286,15 +287,15 @@ class LBS4 extends DAIA implements TranslatorAwareInterface $sqlStmt = sybase_query($sql); while ($row = sybase_fetch_row($sqlStmt)) { $result[$count] = [ - 'id' => $row[0] - ,'duedate' => substr($row[13], 0, 12) - ,'barcode' => $row[31] - ,'renew' => $row[7] - ,'publication_year' => $row[45] - ,'renewable' => $row[61] - ,'message' => $row[60] - ,'title' => $this->picaRecode($row[44]) - ,'item_id' => $row[7] + 'id' => $row[0], + 'duedate' => substr($row[13], 0, 12), + 'barcode' => $row[31], + 'renew' => $row[7], + 'publication_year' => $row[45], + 'renewable' => $row[61], + 'message' => $row[60], + 'title' => $this->picaRecode($row[44]), + 'item_id' => $row[7] ]; $count++; } @@ -312,7 +313,7 @@ class LBS4 extends DAIA implements TranslatorAwareInterface * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -381,7 +382,7 @@ class LBS4 extends DAIA implements TranslatorAwareInterface * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -423,7 +424,7 @@ class LBS4 extends DAIA implements TranslatorAwareInterface $fine = $this->picaRecode($row[5]); $amount = (null == $row[2]) ? 0 : $row[2] * 100; //$balance = (null==$row[3])?0:$row[3]*100; - $checkout = substr($row[3], 0, 12); + $checkout = substr($row[3], 0, 12); $duedate = substr($row[4], 0, 12); $title = $this->picaRecode(substr($row[6], 0, 12)); $result[] = [ @@ -466,7 +467,9 @@ class LBS4 extends DAIA implements TranslatorAwareInterface */ protected function prfz($str) { - $x = 0; $y = 0; $w = 2; + $x = 0; + $y = 0; + $w = 2; $stra = str_split($str); for ($i = strlen($str); $i > 0; $i--) { $c = $stra[$i - 1]; @@ -485,5 +488,4 @@ class LBS4 extends DAIA implements TranslatorAwareInterface } return $ret; } - } diff --git a/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php b/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php index 70c96e49a26167ab446ed939566681ebb3ca4f9f..db2d4bac3ccfcf7d91817885e76a6d73f6a4d0ef 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php +++ b/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php @@ -2,9 +2,9 @@ /** * Multiple Backend Driver. * - * PHP version 5 + * PHP version 7 * - * Copyright (C) The National Library of Finland 2012-2017. + * Copyright (C) The National Library of Finland 2012-2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -192,7 +192,16 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac { $items = []; foreach ($ids as $id) { - $items[] = $this->getStatus($id); + try { + $items[] = $this->getStatus($id); + } catch (ILSException $e) { + $items[] = [ + [ + 'id' => $id, + 'error' => 'An error has occurred' + ] + ]; + } } return $items; } @@ -455,6 +464,30 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac throw new ILSException('No suitable backend driver found'); } + /** + * Get Patron Transaction History + * + * This is responsible for retrieving all historic transactions + * (i.e. checked out items) by a specific patron. + * + * @param array $patron The patron array from patronLogin + * @param array $params Retrieval params + * + * @return array Array of the patron's transactions + */ + public function getMyTransactionHistory($patron, $params) + { + $source = $this->getSource($patron['cat_username']); + $driver = $this->getDriver($source); + if ($driver) { + $transactions = $driver->getMyTransactionHistory( + $this->stripIdPrefixes($patron, $source), $params + ); + return $this->addIdPrefixes($transactions, $source); + } + throw new ILSException('No suitable backend driver found'); + } + /** * Get Renew Details * @@ -469,7 +502,7 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac */ public function getRenewDetails($checkoutDetails) { - $source = $this->getSource($checkoutDetails['id']); + $source = $this->getSource($checkoutDetails['id'] ?? ''); $driver = $this->getDriver($source); if ($driver) { $details = $driver->getRenewDetails( @@ -859,7 +892,7 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac public function getCancelHoldDetails($holdDetails) { $source = $this->getSource( - $holdDetails['id'] ? $holdDetails['id'] : $holdDetails['item_id'] + $holdDetails['id'] ?? $holdDetails['item_id'] ?? '' ); $driver = $this->getDriver($source); if ($driver) { @@ -941,7 +974,7 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac */ public function getCancelStorageRetrievalRequestDetails($details) { - $source = $this->getSource($details['id']); + $source = $this->getSource($details['id'] ?? ''); $driver = $this->getDriver($source); if ($driver && $this->methodSupported( @@ -1148,9 +1181,7 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac */ public function getCancelILLRequestDetails($details) { - $source = $this->getSource( - $details['id'] ? $details['id'] : $details['item_id'] - ); + $source = $this->getSource($details['id'] ?? $details['item_id'] ?? ''); $driver = $this->getDriver($source); if ($driver && $this->methodSupported( @@ -1423,7 +1454,7 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac $this->error("No configuration found for source '$source'"); return null; } - $driverInst = clone($this->driverManager->get($driver)); + $driverInst = clone $this->driverManager->get($driver); $driverInst->setConfig($config); $driverInst->init(); return $driverInst; @@ -1493,17 +1524,17 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac } /** - * Change global ID's to local ID's in the given array - * - * @param mixed $data The data to be modified, normally - * array or array of arrays - * @param string $source Source code - * @param array $modifyFields Fields to be modified in the array - * @param array $ignoreFields Fields to be ignored during recursive processing - * - * @return mixed Modified array or empty/null if that input was - * empty/null - */ + * Change global ID's to local ID's in the given array + * + * @param mixed $data The data to be modified, normally + * array or array of arrays + * @param string $source Source code + * @param array $modifyFields Fields to be modified in the array + * @param array $ignoreFields Fields to be ignored during recursive processing + * + * @return mixed Modified array or empty/null if that input was + * empty/null + */ protected function stripIdPrefixes($data, $source, $modifyFields = ['id', 'cat_username'], $ignoreFields = [] ) { diff --git a/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php b/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f58c05904bd43bfe6dea435352194ca91e08bad1 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for MultiBackend ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for MultiBackend ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class MultiBackendFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager'), + $container->get('VuFind\Auth\ILSAuthenticator'), + $container->get('VuFind\ILS\Driver\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php b/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php index 0f08ced0c6b0bcedff6e515b801399ec0236d184..2d0e9a6738256c0217d861769ca04f78b2cd0e46 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php +++ b/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php @@ -2,7 +2,7 @@ /** * ILS Driver for NewGenLib * - * PHP version 5 + * PHP version 7 * * Copyright (C) Verus Solutions Pvt.Ltd 2010. * @@ -26,7 +26,10 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use PDO, PDOException, VuFind\Exception\ILS as ILSException; + +use PDO; +use PDOException; +use VuFind\Exception\ILS as ILSException; /** * ILS Driver for NewGenLib @@ -82,7 +85,7 @@ class NewGenLib extends AbstractBase * @param string $RecordID The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -126,7 +129,7 @@ class NewGenLib extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -194,7 +197,7 @@ class NewGenLib extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -331,7 +334,7 @@ class NewGenLib extends AbstractBase * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -503,8 +506,7 @@ class NewGenLib extends AbstractBase try { $sqlStmt = $this->db->prepare($sql); $sqlStmt->execute(); - } - catch (PDOException $e) { + } catch (PDOException $e) { throw new ILSException($e->getMessage()); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/NoILS.php b/module/VuFind/src/VuFind/ILS/Driver/NoILS.php index 262ce0bad5929b5ed99bb209ede8c99e385a28b4..f707850f54f41277e70ecc129faf93b76861fae5 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/NoILS.php +++ b/module/VuFind/src/VuFind/ILS/Driver/NoILS.php @@ -2,7 +2,7 @@ /** * Driver for offline/missing ILS. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,8 +27,9 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use VuFind\Exception\ILS as ILSException, - VuFind\I18n\Translator\TranslatorAwareInterface; + +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; /** * Driver for offline/missing ILS. @@ -88,7 +89,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function getConfig($function, $params = null) { - return isset($this->config[$function]) ? $this->config[$function] : false; + return $this->config[$function] ?? false; } /** @@ -98,8 +99,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ protected function getIdPrefix() { - return isset($this->config['settings']['idPrefix']) - ? $this->config['settings']['idPrefix'] : null; + return $this->config['settings']['idPrefix'] ?? null; } /** @@ -134,8 +134,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function getStatus($id) { - $useStatus = isset($this->config['settings']['useStatus']) - ? $this->config['settings']['useStatus'] : 'none'; + $useStatus = $this->config['settings']['useStatus'] ?? 'none'; if ($useStatus == "custom") { $status = $this->translate($this->config['Status']['status']); return [ @@ -155,7 +154,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface ) ] ]; - } else if ($useStatus == "marc") { + } elseif ($useStatus == "marc") { // Retrieve record from index: $recordDriver = $this->getSolrRecord($id); return $this->getFormattedMarcDetails($recordDriver, 'MarcStatus'); @@ -176,8 +175,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function getStatuses($idList) { - $useStatus = isset($this->config['settings']['useStatus']) - ? $this->config['settings']['useStatus'] : 'none'; + $useStatus = $this->config['settings']['useStatus'] ?? 'none'; if ($useStatus == "custom" || $useStatus == "marc") { $status = []; foreach ($idList as $id) { @@ -204,8 +202,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function getHolding($id, array $patron = null) { - $useHoldings = isset($this->config['settings']['useHoldings']) - ? $this->config['settings']['useHoldings'] : 'none'; + $useHoldings = $this->config['settings']['useHoldings'] ?? 'none'; if ($useHoldings == "custom") { return [ @@ -228,10 +225,8 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface $this->config['Holdings']['callnumber'] ), 'barcode' => $this->config['Holdings']['barcode'], - 'notes' => isset($this->config['Holdings']['notes']) - ? $this->config['Holdings']['notes'] : [], - 'summary' => isset($this->config['Holdings']['summary']) - ? $this->config['Holdings']['summary'] : [] + 'notes' => $this->config['Holdings']['notes'] ?? [], + 'summary' => $this->config['Holdings']['summary'] ?? [] ] ]; } elseif ($useHoldings == "marc") { @@ -255,8 +250,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ protected function getFormattedMarcDetails($recordDriver, $configSection) { - $marcStatus = isset($this->config[$configSection]) - ? $this->config[$configSection] : false; + $marcStatus = $this->config[$configSection] ?? false; if ($marcStatus) { $field = $marcStatus['marcField']; unset($marcStatus['marcField']); @@ -290,8 +284,13 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function hasHoldings($id) { - $useHoldings = isset($this->config['settings']['useHoldings']) - ? $this->config['settings']['useHoldings'] : ''; + // If the ILS is disabled, there will never be holdings: + if ($this->getOfflineMode() == 'ils-none') { + return false; + } + + // If the ILS is offline, we should if we can look up details: + $useHoldings = $this->config['settings']['useHoldings'] ?? ''; // "none" will be processed differently in the config depending // on whether it's in or out of quotes; handle both cases. @@ -313,7 +312,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface return []; } - /** + /** * Get New Items * * Retrieve the IDs of items recently added to the catalog. @@ -347,8 +346,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function getOfflineMode() { - return isset($this->config['settings']['mode']) - ? $this->config['settings']['mode'] : "ils-offline"; + return $this->config['settings']['mode'] ?? 'ils-offline'; } /** @@ -360,8 +358,7 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface */ public function loginIsHidden() { - return isset($this->config['settings']['hideLogin']) - ? $this->config['settings']['hideLogin'] : false; + return $this->config['settings']['hideLogin'] ?? false; } /** diff --git a/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php b/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d0db0202e247d15e02b54389c71a4ae468d785c3 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for NoILS ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for NoILS ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class NoILSFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get('VuFind\Record\Loader')); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/PAIA.php b/module/VuFind/src/VuFind/ILS/Driver/PAIA.php index 0e023756b6082cd55fdb7c2f791156710813173f..d1c3a5e09a9128faf762029af2abeef4c30e8a34 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/PAIA.php +++ b/module/VuFind/src/VuFind/ILS/Driver/PAIA.php @@ -2,7 +2,7 @@ /** * PAIA ILS Driver for VuFind to get patron information * - * PHP version 5 + * PHP version 7 * * Copyright (C) Oliver Goldschmidt, Magda Roos, Till Kinstler, André Lahmann 2013, * 2014, 2015. @@ -30,6 +30,7 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use VuFind\Exception\ILS as ILSException; /** @@ -80,7 +81,7 @@ class PAIA extends DAIA /** * SessionManager * - * @var \VuFind\SessionManager + * @var \Zend\Session\SessionManager */ protected $sessionManager; @@ -122,7 +123,7 @@ class PAIA extends DAIA */ protected function getCacheKey($suffix = null) { - return \VuFind\ILS\Driver\AbstractBase::getCacheKey( + return $this->getBaseCacheKey( md5($this->baseUrl . $this->paiaURL) . $suffix ); } @@ -330,10 +331,8 @@ class PAIA extends DAIA 'success' => false, 'status' => $array_response['error'], 'sysMessage' => - isset($array_response['error']) - ? $array_response['error'] : ' ' . - isset($array_response['error_description']) - ? $array_response['error_description'] : ' ' + $array_response['error'] ?? ' ' . + $array_response['error_description'] ?? ' ' ]; } elseif (isset($array_response['patron']) && $array_response['patron'] === $post_data['patron'] @@ -369,7 +368,7 @@ class PAIA extends DAIA */ public function getCancelHoldDetails($checkOutDetails) { - return($checkOutDetails['cancel_details']); + return $checkOutDetails['cancel_details']; } /** @@ -475,6 +474,7 @@ class PAIA extends DAIA // Not yet implemented return false; } + /** * Place ILL Request * @@ -583,7 +583,7 @@ class PAIA extends DAIA $paiaCurrencyPattern = "/^([0-9]+\.[0-9][0-9]) ([A-Z][A-Z][A-Z])$/"; if (preg_match($paiaCurrencyPattern, $fee, $feeMatches)) { // VuFind expects fees in PENNIES - return ($feeMatches[1] * 100); + return $feeMatches[1] * 100; } return $fee; }; @@ -592,18 +592,18 @@ class PAIA extends DAIA if (isset($fees['fee'])) { foreach ($fees['fee'] as $fee) { $result = [ - // fee.amount 1..1 money amount of a single fee + // fee.amount 1..1 money amount of a single fee 'amount' => $feeConverter($fee['amount']), 'checkout' => '', - // fee.feetype 0..1 string textual description of the type + // fee.feetype 0..1 string textual description of the type // of service that caused the fee - 'fine' => (isset($fee['feetype']) ? $fee['feetype'] : null), + 'fine' => ($fee['feetype'] ?? null), 'balance' => $feeConverter($fee['amount']), - // fee.date 0..1 date date when the fee was claimed + // fee.date 0..1 date date when the fee was claimed 'createdate' => (isset($fee['date']) ? $this->convertDate($fee['date']) : null), 'duedate' => '', - // fee.edition 0..1 URI edition that caused the fee + // fee.edition 0..1 URI edition that caused the fee 'id' => (isset($fee['edition']) ? $this->getAlternativeItemId($fee['edition']) : ''), ]; @@ -634,16 +634,13 @@ class PAIA extends DAIA } // custom PAIA fields - // fee.about 0..1 string textual information about the fee - // fee.item 0..1 URI item that caused the fee - // fee.feeid 0..1 URI URI of the type of service that + // fee.about 0..1 string textual information about the fee + // fee.item 0..1 URI item that caused the fee + // fee.feeid 0..1 URI URI of the type of service that // caused the fee - $additionalData['feeid'] = (isset($fee['feeid']) - ? $fee['feeid'] : null); - $additionalData['about'] = (isset($fee['about']) - ? $fee['about'] : null); - $additionalData['item'] = (isset($fee['item']) - ? $fee['item'] : null); + $additionalData['feeid'] = ($fee['feeid'] ?? null); + $additionalData['about'] = ($fee['about'] ?? null); + $additionalData['item'] = ($fee['item'] ?? null); return $additionalData; } @@ -697,7 +694,7 @@ class PAIA extends DAIA // PAIA specific custom values 'expires' => isset($patron['expires']) ? $this->convertDate($patron['expires']) : null, - 'statuscode' => isset($patron['status']) ? $patron['status'] : null, + 'statuscode' => $patron['status'] ?? null, 'canWrite' => in_array('write_items', $this->getScope()), ]; } @@ -802,7 +799,7 @@ class PAIA extends DAIA */ public function getRenewDetails($checkOutDetails) { - return($checkOutDetails['renew_details']); + return $checkOutDetails['renew_details']; } /** @@ -814,7 +811,7 @@ class PAIA extends DAIA */ protected function getCallNumber($doc) { - return isset($doc['label']) ? $doc['label'] : null; + return $doc['label'] ?? null; } /** @@ -1198,8 +1195,7 @@ class PAIA extends DAIA $user['id'] = $patron; $user['firstname'] = $firstname; $user['lastname'] = $lastname; - $user['email'] = (isset($user_response['email']) - ? $user_response['email'] : ''); + $user['email'] = ($user_response['email'] ?? ''); $user['major'] = null; $user['college'] = null; // add other information from PAIA - we don't want anything to get lost @@ -1244,7 +1240,7 @@ class PAIA extends DAIA $result = []; // item (0..1) URI of a particular copy - $result['item_id'] = (isset($doc['item']) ? $doc['item'] : ''); + $result['item_id'] = ($doc['item'] ?? ''); $result['cancel_details'] = (isset($doc['cancancel']) && $doc['cancancel']) @@ -1259,16 +1255,16 @@ class PAIA extends DAIA $result['type'] = $this->paiaStatusString($doc['status']); // storage (0..1) textual description of location of the document - $result['location'] = (isset($doc['storage']) ? $doc['storage'] : null); + $result['location'] = ($doc['storage'] ?? null); // queue (0..1) number of waiting requests for the document or item - $result['position'] = (isset($doc['queue']) ? $doc['queue'] : null); + $result['position'] = ($doc['queue'] ?? null); // only true if status == 4 $result['available'] = false; // about (0..1) textual description of the document - $result['title'] = (isset($doc['about']) ? $doc['about'] : null); + $result['title'] = ($doc['about'] ?? null); // PAIA custom field // label (0..1) call number, shelf mark or similar item label @@ -1338,10 +1334,10 @@ class PAIA extends DAIA $result['available'] = $doc['status'] == 4 ? true : false; $results[] = $result; - } return $results; } + /** * This PAIA helper function allows custom overrides for mapping of PAIA response * to getMyStorageRetrievalRequests data structure. @@ -1358,7 +1354,6 @@ class PAIA extends DAIA $result = $this->getBasicDetails($doc); $results[] = $result; - } return $results; } @@ -1378,11 +1373,10 @@ class PAIA extends DAIA foreach ($items as $doc) { $result = []; // canrenew (0..1) whether a document can be renewed (bool) - $result['renewable'] = (isset($doc['canrenew']) - ? $doc['canrenew'] : false); + $result['renewable'] = ($doc['canrenew'] ?? false); // item (0..1) URI of a particular copy - $result['item_id'] = (isset($doc['item']) ? $doc['item'] : ''); + $result['item_id'] = ($doc['item'] ?? ''); $result['renew_details'] = (isset($doc['canrenew']) && $doc['canrenew']) @@ -1397,17 +1391,17 @@ class PAIA extends DAIA // requested (0..1) URI that was originally requested // about (0..1) textual description of the document - $result['title'] = (isset($doc['about']) ? $doc['about'] : null); + $result['title'] = ($doc['about'] ?? null); // queue (0..1) number of waiting requests for the document or item - $result['request'] = (isset($doc['queue']) ? $doc['queue'] : null); + $result['request'] = ($doc['queue'] ?? null); // renewals (0..1) number of times the document has been renewed - $result['renew'] = (isset($doc['renewals']) ? $doc['renewals'] : null); + $result['renew'] = ($doc['renewals'] ?? null); // reminder (0..1) number of times the patron has been reminded $result['reminder'] = ( - isset($doc['reminder']) ? $doc['reminder'] : null + $doc['reminder'] ?? null ); // custom PAIA field @@ -1427,11 +1421,10 @@ class PAIA extends DAIA // canceled // error (0..1) error message, for instance if a request was rejected - $result['message'] = (isset($doc['error']) ? $doc['error'] : ''); + $result['message'] = ($doc['error'] ?? ''); // storage (0..1) textual description of location of the document - $result['borrowingLocation'] = (isset($doc['storage']) - ? $doc['storage'] : ''); + $result['borrowingLocation'] = ($doc['storage'] ?? ''); // storageid (0..1) location URI @@ -1499,7 +1492,7 @@ class PAIA extends DAIA ); } // return any result as error-handling is done elsewhere - return ($result->getBody()); + return $result->getBody(); } /** @@ -1535,7 +1528,7 @@ class PAIA extends DAIA ); } // return any result as error-handling is done elsewhere - return ($result->getBody()); + return $result->getBody(); } /** @@ -1660,11 +1653,9 @@ class PAIA extends DAIA $session = $this->getSession(); $session->patron - = isset($responseArray['patron']) - ? $responseArray['patron'] : null; + = $responseArray['patron'] ?? null; $session->access_token - = isset($responseArray['access_token']) - ? $responseArray['access_token'] : null; + = $responseArray['access_token'] ?? null; $session->scope = isset($responseArray['scope']) ? explode(' ', $responseArray['scope']) : null; @@ -1735,7 +1726,7 @@ class PAIA extends DAIA public function checkRequestIsValid($id, $data, $patron) { // TODO: make this more configurable - if (isset($patron['status']) && $patron['status'] == 0 + if (isset($patron['status']) && $patron['status'] == 0 && isset($patron['expires']) && $patron['expires'] > date('Y-m-d') && in_array('write_items', $this->getScope()) ) { diff --git a/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php b/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..64db51aab4bc0e4ec660c5b839dff9c28397e232 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for PAIA ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for PAIA ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PAIAFactory extends DriverWithDateConverterFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return parent::__invoke( + $container, $requestedName, + [$container->get('Zend\Session\SessionManager')] + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/PluginFactory.php b/module/VuFind/src/VuFind/ILS/Driver/PluginFactory.php index 9b15b1d223b46a29dc670061c7d6c979ad318cdf..01c8de070f3a8bfae002fc9e17c7126360e33230 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/PluginFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/PluginFactory.php @@ -2,7 +2,7 @@ /** * ILS driver plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php b/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php index 60f16b4b9b97f77761cbaf46e6b682630c7b347a..020df9446d6be9b4faedbb65917ee780841bed14 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php +++ b/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php @@ -2,7 +2,7 @@ /** * ILS driver plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,104 @@ namespace VuFind\ILS\Driver; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'aleph' => 'VuFind\ILS\Driver\Aleph', + 'alma' => 'VuFind\ILS\Driver\Alma', + 'amicus' => 'VuFind\ILS\Driver\Amicus', + 'daia' => 'VuFind\ILS\Driver\DAIA', + 'demo' => 'VuFind\ILS\Driver\Demo', + 'evergreen' => 'VuFind\ILS\Driver\Evergreen', + 'horizon' => 'VuFind\ILS\Driver\Horizon', + 'horizonxmlapi' => 'VuFind\ILS\Driver\HorizonXMLAPI', + 'innovative' => 'VuFind\ILS\Driver\Innovative', + 'koha' => 'VuFind\ILS\Driver\Koha', + 'kohailsdi' => 'VuFind\ILS\Driver\KohaILSDI', + 'lbs4' => 'VuFind\ILS\Driver\LBS4', + 'multibackend' => 'VuFind\ILS\Driver\MultiBackend', + 'newgenlib' => 'VuFind\ILS\Driver\NewGenLib', + 'noils' => 'VuFind\ILS\Driver\NoILS', + 'paia' => 'VuFind\ILS\Driver\PAIA', + 'polaris' => 'VuFind\ILS\Driver\Polaris', + 'sample' => 'VuFind\ILS\Driver\Sample', + 'sierra' => 'VuFind\ILS\Driver\Sierra', + 'sierrarest' => 'VuFind\ILS\Driver\SierraRest', + 'symphony' => 'VuFind\ILS\Driver\Symphony', + 'unicorn' => 'VuFind\ILS\Driver\Unicorn', + 'virtua' => 'VuFind\ILS\Driver\Virtua', + 'voyager' => 'VuFind\ILS\Driver\Voyager', + 'voyagerrestful' => 'VuFind\ILS\Driver\VoyagerRestful', + 'xcncip2' => 'VuFind\ILS\Driver\XCNCIP2', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\ILS\Driver\Aleph' => 'VuFind\ILS\Driver\AlephFactory', + 'VuFind\ILS\Driver\Alma' => 'VuFind\ILS\Driver\AlmaFactory', + 'VuFind\ILS\Driver\Amicus' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\DAIA' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\Demo' => 'VuFind\ILS\Driver\DemoFactory', + 'VuFind\ILS\Driver\Evergreen' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\Horizon' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\HorizonXMLAPI' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\Innovative' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\Koha' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\KohaILSDI' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\LBS4' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\MultiBackend' => 'VuFind\ILS\Driver\MultiBackendFactory', + 'VuFind\ILS\Driver\NewGenLib' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\NoILS' => 'VuFind\ILS\Driver\NoILSFactory', + 'VuFind\ILS\Driver\PAIA' => 'VuFind\ILS\Driver\PAIAFactory', + 'VuFind\ILS\Driver\Polaris' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\Sample' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\Sierra' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\SierraRest' => 'VuFind\ILS\Driver\SierraRestFactory', + 'VuFind\ILS\Driver\Symphony' => 'VuFind\ILS\Driver\SymphonyFactory', + 'VuFind\ILS\Driver\Unicorn' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\Virtua' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\ILS\Driver\Voyager' => + 'VuFind\ILS\Driver\DriverWithDateConverterFactory', + 'VuFind\ILS\Driver\VoyagerRestful' => + 'VuFind\ILS\Driver\VoyagerRestfulFactory', + 'VuFind\ILS\Driver\XCNCIP2' => + 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\ILS\Driver\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/ILS/Driver/Polaris.php b/module/VuFind/src/VuFind/ILS/Driver/Polaris.php index 835d9edf704122c576e22b7e30045cfb60286b0c..ced82a5cba77c08f324f1384c375408f83247ae1 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Polaris.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Polaris.php @@ -2,7 +2,7 @@ /** * Polaris ILS Driver * - * PHP version 5 + * PHP version 7 * * * This program is free software; you can redistribute it and/or modify @@ -25,8 +25,8 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use VuFind\Config\Reader as ConfigReader, - VuFind\Exception\ILS as ILSException; + +use VuFind\Exception\ILS as ILSException; /** * VuFind Connector for Polaris @@ -244,7 +244,6 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf 'position' => $holds_response->QueuePosition, 'title' => $holds_response->Title, ]; - } return $holds; } @@ -302,7 +301,6 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf //'designation' => $designation, 'holdable' => $holdings_response->Holdable, ]; - } return $holding; } @@ -427,17 +425,16 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf "holdrequest/{$response->RequestGUID}", 'PUT', '', $reply_jsonrequest ); - + if ($reply_response->StatusValue == 1) { // auto-reply success - return [ 'success' => true, 'sysMessage' => $response->Message ]; + return [ 'success' => true, 'sysMessage' => $response->Message ]; } else { - return [ 'success' => false, 'sysMessage' => $response->Message ]; + return [ 'success' => false, 'sysMessage' => $response->Message ]; } } else { return [ 'success' => false, 'sysMessage' => $response->Message ]; } - } /** @@ -630,8 +627,8 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf } return $fineList; - } + /** * Get Patron Profile * @@ -752,7 +749,6 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf 'sysMessage' => $response->ItemRenewResult->BlockRows[0]->ErrorDesc, ]; } - } $result = [ 'count' => $count, 'details' => $item_response, @@ -879,7 +875,7 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf . "$items_per_page&page=$page_offset", 'GET', $patron['cat_password'] ); - + $checkout_history_array = $response->PatronReadingHistoryGetRows; foreach ($checkout_history_array as $checkout_response) { $date = $this->formatJSONTime($checkout_response->CheckOutDate); @@ -953,7 +949,7 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf 'UserID' => '1', 'ActivationDate' => "$jsondate" ]; - + $response = $this->makeRequest( "patron/{$patron['cat_username']}/holdrequests/$hold_id/inactive", 'PUT', $patron['cat_password'], $jsonrequest @@ -1019,7 +1015,7 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf 'UserID' => '1', 'ActivationDate' => "$jsondate" ]; - + $response = $this->makeRequest( "patron/{$patron['cat_username']}/holdrequests/$hold_id/active", 'PUT', $patron['cat_password'], $jsonrequest diff --git a/module/VuFind/src/VuFind/ILS/Driver/Sample.php b/module/VuFind/src/VuFind/ILS/Driver/Sample.php index e472b7c0a3ddde5fd4621aef1bcdc12853930c92..ce24eccb7932c6a9b82165d90dac656583d4ebde 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Sample.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Sample.php @@ -2,7 +2,7 @@ /** * Lightweight Dummy ILS Driver -- Always returns hard-coded sample values. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/ILS/Driver/Sierra.php b/module/VuFind/src/VuFind/ILS/Driver/Sierra.php index c14a819ff18e1036de28604a52a0a1b2a3d51d7b..f42e5f3439091bdca1acba6e4d350e3e82daa900 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Sierra.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Sierra.php @@ -3,7 +3,7 @@ /** * Sierra (III) ILS Driver for VuFind * - * PHP version 5 + * PHP version 7 * * Copyright (C) 2013 Julia Bauder * @@ -28,8 +28,8 @@ */ namespace VuFind\ILS\Driver; -use VuFind\Exception\ILS as ILSException, - VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; /** * Sierra (III) ILS Driver for VuFind @@ -569,7 +569,7 @@ class Sierra extends AbstractBase implements TranslatorAwareInterface try { $newItems = []; $offset = $limit * ($page - 1); - $daysOld = (int) $daysOld; + $daysOld = (int)$daysOld; if (is_int($daysOld) == false || $daysOld > 30) { $daysOld = "30"; } @@ -607,7 +607,7 @@ class Sierra extends AbstractBase implements TranslatorAwareInterface $this->db, $query, [$limit, $offset] ); } - $newItems['count'] = (string) pg_num_rows($results); + $newItems['count'] = (string)pg_num_rows($results); if (pg_num_rows($results) != 0) { while ($record = pg_fetch_row($results)) { $bareNumber = $record[0]; diff --git a/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php b/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php index 2d7174af27dfb79bc285da638ec4dcd0a53f5fc0..88a50fde628f49e6d4b786b01ac5499a1897bcfd 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php +++ b/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php @@ -2,7 +2,7 @@ /** * III Sierra REST API driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2016-2017. * @@ -26,11 +26,12 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use VuFind\Exception\ILS as ILSException; +use VuFind\Exception\VuFind\Exception; use VuFind\I18n\Translator\TranslatorAwareInterface; use VuFindHttp\HttpServiceAwareInterface; use Zend\Log\LoggerAwareInterface; -use VuFind\Exception\VuFind\Exception; /** * III Sierra REST API driver @@ -44,6 +45,7 @@ use VuFind\Exception\VuFind\Exception; class SierraRest extends AbstractBase implements TranslatorAwareInterface, HttpServiceAwareInterface, LoggerAwareInterface { + use CacheTrait; use \VuFind\Log\LoggerAwareTrait { logError as error; } @@ -461,12 +463,19 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $zip = ''; $city = ''; if (!empty($result['names'])) { - $name = $result['names'][0]; - list($lastname, $firstname) = explode(', ', $name, 2); + $nameParts = explode(', ', $result['names'][0], 2); + $lastname = $nameParts[0]; + $firstname = $nameParts[1] ?? ''; } if (!empty($result['addresses'][0]['lines'][1])) { $address = $result['addresses'][0]['lines'][0]; - list($zip, $city) = explode(' ', $result['addresses'][0]['lines'][1], 2); + $postalParts = explode(' ', $result['addresses'][0]['lines'][1], 2); + if (isset($postalParts[1])) { + $zip = $postalParts[0]; + $city = $postalParts[1]; + } else { + $city = $postalParts[0]; + } } $expirationDate = !empty($result['expirationDate']) ? $this->dateConverter->convertToDisplayDate( @@ -505,7 +514,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'limit' => 10000, 'offset' => 0, 'fields' => 'item,dueDate,numberOfRenewals,outDate,recallDate' - . ',callNumber' + . ',callNumber,barcode' ], 'GET', $patron @@ -519,6 +528,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'id' => '', 'checkout_id' => $this->extractId($entry['id']), 'item_id' => $this->extractId($entry['item']), + 'barcode' => $entry['barcode'], 'duedate' => $this->dateConverter->convertToDisplayDate( 'Y-m-d', $entry['dueDate'] ), @@ -595,7 +605,9 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $patron ); if (!empty($result['code'])) { - $msg = $this->formatErrorMessage($result['description']); + $msg = $this->formatErrorMessage( + $result['description'] ?? $result['name'] + ); $finalResult['details'][$itemId] = [ 'item_id' => $itemId, 'success' => false, @@ -615,6 +627,85 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return $finalResult; } + /** + * Get Patron Transaction History + * + * This is responsible for retrieving all historic transactions (i.e. checked + * out items) by a specific patron. + * + * @param array $patron The patron array from patronLogin + * @param array $params Parameters + * + * @throws DateException + * @throws ILSException + * @return array Array of the patron's historic transactions on success. + */ + public function getMyTransactionHistory($patron, $params) + { + $pageSize = $params['limit'] ?? 50; + $offset = isset($params['page']) ? ($params['page'] - 1) * $pageSize : 0; + $sortOrder = isset($params['sort']) && 'checkout asc' === $params['sort'] + ? 'asc' : 'desc'; + $result = $this->makeRequest( + ['v3', 'patrons', $patron['id'], 'checkouts', 'history'], + [ + 'limit' => $pageSize, + 'offset' => $offset, + 'sortField' => 'outDate', + 'sortOrder' => $sortOrder, + 'fields' => 'item,outDate' + ], + 'GET', + $patron + ); + if (!empty($result['code'])) { + return [ + 'success' => false, + 'status' => 146 === $result['code'] + ? 'ils_transaction_history_disabled' + : 'ils_connection_failed' + ]; + } + $transactions = []; + foreach ($result['entries'] as $entry) { + $transaction = [ + 'id' => '', + 'item_id' => $this->extractId($entry['item']), + 'checkoutDate' => $this->dateConverter->convertToDisplayDate( + 'Y-m-d', $entry['outDate'] + ) + ]; + // Fetch item information + $item = $this->makeRequest( + ['v3', 'items', $transaction['item_id']], + ['fields' => 'bibIds,varFields'], + 'GET', + $patron + ); + $transaction['volume'] = $this->extractVolume($item); + if (!empty($item['bibIds'])) { + $transaction['id'] = $item['bibIds'][0]; + + // Fetch bib information + $bib = $this->getBibRecord( + $transaction['id'], 'title,publishYear', $patron + ); + if (!empty($bib['title'])) { + $transaction['title'] = $bib['title']; + } + if (!empty($bib['publishYear'])) { + $transaction['publication_year'] = $bib['publishYear']; + } + } + $transactions[] = $transaction; + } + + return [ + 'count' => $result['total'] ?? 0, + 'transactions' => $transactions + ]; + } + /** * Get Patron Holds * @@ -668,9 +759,8 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, if (!empty($bibId)) { // Fetch bib information $bib = $this->getBibRecord($bibId, 'title,publishYear', $patron); - $title = isset($bib['title']) ? $bib['title'] : ''; - $publicationYear = isset($bib['publishYear']) ? $bib['publishYear'] - : ''; + $title = $bib['title'] ?? ''; + $publicationYear = $bib['publishYear'] ?? ''; } $available = in_array($entry['status']['code'], ['b', 'j', 'i']); if ($entry['priority'] >= $entry['priorityQueueLength']) { @@ -737,11 +827,13 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, foreach ($details as $holdId) { $result = $this->makeRequest( - ['v3', 'patrons', 'holds', $holdId], [], 'DELETE', $patron + ['v5', 'patrons', 'holds', $holdId], [], 'DELETE', $patron ); if (!empty($result['code'])) { - $msg = $this->formatErrorMessage($result['description']); + $msg = $this->formatErrorMessage( + $result['description'] ?? $result['name'] + ); $response[$holdId] = [ 'item_id' => $holdId, 'success' => false, @@ -795,7 +887,41 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return $locations; } - return []; + $result = $this->makeRequest( + ['v4', 'branches', 'pickupLocations'], + [ + 'limit' => 10000, + 'offset' => 0, + 'fields' => 'code,name', + 'language' => $this->getTranslatorLocale() + ], + 'GET', + $patron + ); + if (!empty($result['code'])) { + // An error was returned + $this->error( + "Request for pickup locations returned error code: {$result['code']}" + . ", HTTP status: {$result['httpStatus']}, name: {$result['name']}" + ); + throw new ILSException('Problem with Sierra REST API.'); + } + if (empty($result)) { + return []; + } + + $locations = []; + foreach ($result as $entry) { + $locations[] = [ + 'locationID' => $entry['code'], + 'locationDisplay' => $this->translateLocation( + ['code' => $entry['code'], 'name' => $entry['name']] + ) + ]; + } + + usort($locations, [$this, 'pickupLocationSortFunction']); + return $locations; } /** @@ -836,8 +962,8 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, if ($this->getPatronBlocks($patron)) { return false; } - $level = isset($data['level']) ? $data['level'] : 'copy'; - if ('title' == $data['level']) { + $level = $data['level'] ?? 'copy'; + if ('title' === $level) { $bib = $this->getBibRecord($id, 'bibLevel', $patron); if (!isset($bib['bibLevel']['code']) || !in_array($bib['bibLevel']['code'], $this->titleHoldBibLevels) @@ -868,8 +994,8 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, ? $holdDetails['level'] : 'copy'; $pickUpLocation = !empty($holdDetails['pickUpLocation']) ? $holdDetails['pickUpLocation'] : $this->defaultPickUpLocation; - $itemId = isset($holdDetails['item_id']) ? $holdDetails['item_id'] : false; - $comment = isset($holdDetails['comment']) ? $holdDetails['comment'] : ''; + $itemId = $holdDetails['item_id'] ?? false; + $comment = $holdDetails['comment'] ?? ''; $bibId = $holdDetails['id']; // Convert last interest date from Display Format to Sierra's required format @@ -911,20 +1037,21 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'recordType' => $level == 'copy' ? 'i' : 'b', 'recordNumber' => (int)($level == 'copy' ? $itemId : $bibId), 'pickupLocation' => $pickUpLocation, - 'neededBy' => $this->dateConverter->convertFromDisplayDate( - 'Y-m-d', $holdDetails['requiredBy'] - ) + 'neededBy' => $lastInterestDate ]; + if ($comment) { + $request['note'] = $comment; + } $result = $this->makeRequest( - ['v3', 'patrons', $patron['id'], 'holds', 'requests'], + [$comment ? 'v4' : 'v3', 'patrons', $patron['id'], 'holds', 'requests'], json_encode($request), 'POST', $patron ); if (!empty($result['code'])) { - return $this->holdError($result['description']); + return $this->holdError($result['description'] ?? $result['name']); } return ['success' => true]; } @@ -993,7 +1120,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $bibId = $item['bibIds'][0]; // Fetch bib information $bib = $this->getBibRecord($bibId, 'title,publishYear', $patron); - $title = isset($bib['title']) ? $bib['title'] : ''; + $title = $bib['title'] ?? ''; } } @@ -1055,10 +1182,12 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $patron ); - if (isset($result['code']) && $result['code'] != 0) { + if (!empty($result['code'])) { return [ 'success' => false, - 'status' => $this->formatErrorMessage($result['description']) + 'status' => $this->formatErrorMessage( + $result['description'] ?? $result['name'] + ) ]; } return ['success' => true, 'status' => 'change_password_ok']; @@ -1077,6 +1206,19 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, */ public function getConfig($function, $params = null) { + if ('getMyTransactionHistory' === $function) { + if (empty($this->config['TransactionHistory']['enabled'])) { + return false; + } + return [ + 'max_results' => 100, + 'sort' => [ + 'checkout desc' => 'sort_checkout_date_desc', + 'checkout asc' => 'sort_checkout_date_asc' + ], + 'default_sort' => 'checkout desc' + ]; + } return isset($this->config[$function]) ? $this->config[$function] : false; } @@ -1095,10 +1237,14 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, */ public function supportsMethod($method, $params) { - // Special case: change password is only available if properly configured. + // Changing password is only available if properly configured. if ($method == 'changePassword') { return isset($this->config['changePassword']); } + // Loan history is only available if properly configured + if ($method == 'getMyTransactionHistory') { + return !empty($this->config['TransactionHistory']['enabled']); + } return is_callable([$this, $method]); } @@ -1615,7 +1761,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, protected function isHoldable($item) { if (!empty($this->validHoldStatuses)) { - list($status, $duedate, $notes) = $this->getItemStatus($item); + list($status) = $this->getItemStatus($item); if (!in_array($status, $this->validHoldStatuses)) { return false; } @@ -1701,6 +1847,23 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return $result; } + /** + * Pickup location sort function + * + * @param array $a First pickup location record to compare + * @param array $b Second pickup location record to compare + * + * @return int + */ + protected function pickupLocationSortFunction($a, $b) + { + $result = strcmp($a['locationDisplay'], $b['locationDisplay']); + if ($result == 0) { + $result = $a['locationID'] - $b['locationID']; + } + return $result; + } + /** * Is the selected pickup location valid for the hold? * diff --git a/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php b/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dd0ebd9993174421ccb8c8116ec2b82453109143 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for SierraRest ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for SierraRest ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SierraRestFactory extends DriverWithDateConverterFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $sessionFactory = function ($namespace) use ($container) { + $manager = $container->get('Zend\Session\SessionManager'); + return new \Zend\Session\Container("SierraRest_$namespace", $manager); + }; + return parent::__invoke($container, $requestedName, [$sessionFactory]); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/Symphony.php b/module/VuFind/src/VuFind/ILS/Driver/Symphony.php index 50235a8eba16e2a6c5c59868a728ea1ada6b1f5d..153fb4691b4b3d6324f988adb538677880aaf8ef 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Symphony.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Symphony.php @@ -2,7 +2,7 @@ /** * Symphony Web Services (symws) ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use SoapClient; use SoapFault; use SoapHeader; @@ -206,7 +207,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface $reset = false ) { $data = ['clientID' => $this->config['WebServices']['clientID']]; - if (!is_null($login)) { + if (null !== $login) { $data['sessionToken'] = $this->getSessionToken($login, $password, $reset); } @@ -301,9 +302,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface */ if (isset($options['login'])) { $login = $options['login']; - $password = isset($options['password']) - ? $options['password'] - : null; + $password = $options['password'] ?? null; } elseif (isset($options['WebServices']['login']) && !in_array( $operation, @@ -545,9 +544,9 @@ class Symphony extends AbstractBase implements LoggerAwareInterface } $library = $this->translatePolicyID('LIBR', $libraryID); - $copyNumber = 0; // ItemInfo does not include copy numbers, - // so we generate them under the assumption - // that items are being listed in order. + // ItemInfo does not include copy numbers, so we generate them under + // the assumption that items are being listed in order. + $copyNumber = 0; $itemInfos = is_array($callInfo->ItemInfo) ? $callInfo->ItemInfo @@ -977,8 +976,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface $policyID = strtoupper($policyID); $policyList = $this->getPolicyList($policyType); - return isset($policyList[$policyID]) ? - $policyList[$policyID] : $policyID; + return $policyList[$policyID] ?? $policyID; } /** @@ -996,7 +994,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface public function getStatus($id) { $statuses = $this->getStatuses([$id]); - return isset($statuses[$id]) ? $statuses[$id] : []; + return $statuses[$id] ?? []; } /** @@ -1054,7 +1052,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface return []; } - /** + /** * Patron Login * * This is responsible for authenticating a patron against the catalog. @@ -1129,7 +1127,6 @@ class Symphony extends AbstractBase implements LoggerAwareInterface break; } } - } } @@ -1201,7 +1198,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface $group = null; } - list($lastname,$firstname) + list($lastname, $firstname) = explode(', ', $result->patronInfo->displayName); $profile = [ @@ -1331,7 +1328,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface ]; } return $holdList; - } catch(SoapFault $e) { + } catch (SoapFault $e) { return null; } catch (\Exception $e) { throw new ILSException($e->getMessage()); @@ -1372,18 +1369,12 @@ class Symphony extends AbstractBase implements LoggerAwareInterface foreach ($fees as $fee) { $fineList[] = [ 'amount' => $fee->amount->_ * 100, - 'checkout' => - isset($fee->feeItemInfo->checkoutDate) ? - $fee->feeItemInfo->checkoutDate : null, + 'checkout' => $fee->feeItemInfo->checkoutDate ?? null, 'fine' => $fee->billReasonDescription, 'balance' => $fee->amountOutstanding->_ * 100, - 'createdate' => - isset($fee->dateBilled) ? $fee->dateBilled : null, - 'duedate' => - isset($fee->feeItemInfo->dueDate) ? - $fee->feeItemInfo->dueDate : null, - 'id' => isset($fee->feeItemInfo->titleKey) ? - $fee->feeItemInfo->titleKey : null + 'createdate' => $fee->dateBilled ?? null, + 'duedate' => $fee->feeItemInfo->dueDate ?? null, + 'id' => $fee->feeItemInfo->titleKey ?? null ]; } } @@ -1410,7 +1401,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface return $holdDetails['reqnum']; } - /** + /** * Cancel Holds * * Attempts to Cancel a hold on a particular item @@ -1458,7 +1449,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface return $result; } - /** + /** * Public Function which retrieves renew, hold and cancel settings from the * driver ini file. * @@ -1466,7 +1457,7 @@ class Symphony extends AbstractBase implements LoggerAwareInterface * @param array $params Optional feature-specific parameters (array) * * @return array An array with key-value pairs. - * + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getConfig($function, $params = null) diff --git a/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php b/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..52ae28982d8df2728a2717027f3f8cb594a19286 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for Symphony ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Symphony ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SymphonyFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Record\Loader'), + $container->get('VuFind\Cache\Manager') + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php b/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php index 4e1153cc2df47ee08f3a94297f8068205e968dd5..b51e8ea1cc2115f2c41b2255b51b19e75bd68329 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php @@ -2,7 +2,7 @@ /** * SirsiDynix Unicorn ILS Driver (VuFind side) * - * PHP version 5 + * PHP version 7 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -25,7 +25,9 @@ * @link http://code.google.com/p/vufind-unicorn/ vufind-unicorn project */ namespace VuFind\ILS\Driver; -use File_MARC, VuFind\Exception\ILS as ILSException; + +use File_MARC; +use VuFind\Exception\ILS as ILSException; /** * SirsiDynix Unicorn ILS Driver (VuFind side) @@ -394,7 +396,7 @@ class Unicorn extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -573,7 +575,7 @@ class Unicorn extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -631,7 +633,7 @@ class Unicorn extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -758,7 +760,7 @@ class Unicorn extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ diff --git a/module/VuFind/src/VuFind/ILS/Driver/Virtua.php b/module/VuFind/src/VuFind/ILS/Driver/Virtua.php index a9377aca46d088ab1630c4feaa2c810fe03f7bb9..c71de284dc6f58f0e91e2e8bd2b7f486bb9e1a06 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Virtua.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Virtua.php @@ -2,7 +2,7 @@ /** * VTLS Virtua Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) University of Southern Queensland 2008. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; + use VuFind\Exception\ILS as ILSException; /** @@ -155,7 +156,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa ]; switch ($result[0]['CALL_NUMBER']) { - case 'ELECTRONIC RESOURCE' : + case 'ELECTRONIC RESOURCE': $new_holding['availability'] = true; $new_holding['status'] = null; $new_holding['location'] = "Online"; @@ -163,19 +164,19 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa $holding[] = $new_holding; return $holding; break; - case 'ON ORDER' : + case 'ON ORDER': $new_holding['status'] = "ON ORDER"; $new_holding['location'] = "Pending..."; $holding[] = $new_holding; return $holding; break; - case 'ORDER CANCELLED' : + case 'ORDER CANCELLED': $new_holding['status'] = "ORDER CANCELLED"; $new_holding['location'] = "None"; $holding[] = $new_holding; return $holding; break; - case 'MISSING' : + case 'MISSING': $new_holding['status'] = "MISSING"; $new_holding['location'] = "Unknown"; $holding[] = $new_holding; @@ -325,7 +326,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -718,7 +719,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa $end_time = strtotime("01-" . $months[1] . "-" . $years[0]); break; // January 2000 - case "11"; + case "11": $start_string = "F Y"; $end_time = null; break; @@ -985,7 +986,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa $tag = $subfield['tag']; $sort = explode('.', $subfield['data']); $sort_rule = $sort[0]; - $sort_order = isset($sort[1]) ? $sort[1] : 0; + $sort_order = $sort[1] ?? 0; $sort_order = sprintf("%05d", $sort_order); } else { // Everything else goes in the data bucket @@ -1239,7 +1240,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -1280,7 +1281,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -1319,7 +1320,7 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -1873,14 +1874,14 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa */ protected function httpRequest($url, $postParams = null, $rawPost = null) { - $method = (is_null($postParams) && is_null($rawPost)) ? 'GET' : 'POST'; + $method = (null === $postParams && null === $rawPost) ? 'GET' : 'POST'; try { $client = $this->httpService->createClient($url); if (is_array($postParams)) { $client->setParameterPost($postParams); } - if (!is_null($rawPost)) { + if (null !== $rawPost) { $client->setRawBody($rawPost); $client->setEncType('application/x-www-form-urlencoded'); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Voyager.php b/module/VuFind/src/VuFind/ILS/Driver/Voyager.php index 4f915b741354db7099eb4ccf36ad8759889c4deb..409095d5755cd250d992196b74e87f894891ad6e 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Voyager.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Voyager.php @@ -2,7 +2,7 @@ /** * Voyager ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * Copyright (C) The National Library of Finland 2014-2016. @@ -29,11 +29,15 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use File_MARC, Yajra\Pdo\Oci8, PDO, PDOException, - VuFind\Exception\Date as DateException, - VuFind\Exception\ILS as ILSException, - VuFind\I18n\Translator\TranslatorAwareInterface, - Zend\Validator\EmailAddress as EmailAddressValidator; + +use File_MARC; +use PDO; +use PDOException; +use VuFind\Date\DateException; +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use Yajra\Pdo\Oci8; +use Zend\Validator\EmailAddress as EmailAddressValidator; /** * Voyager ILS Driver @@ -90,6 +94,13 @@ class Voyager extends AbstractBase */ protected $useHoldingsSortGroups; + /** + * Loan interval types for which to display the due time (empty = all) + * + * @var array + */ + protected $displayDueTimeIntervals; + /** * Constructor * @@ -141,6 +152,12 @@ class Voyager extends AbstractBase $this->useHoldingsSortGroups = isset($this->config['Holdings']['use_sort_groups']) ? $this->config['Holdings']['use_sort_groups'] : true; + + $this->displayDueTimeIntervals + = isset($this->config['Loans']['display_due_time_only_for_intervals']) + ? explode( + ':', $this->config['Loans']['display_due_time_only_for_intervals'] + ) : []; } /** @@ -227,8 +244,10 @@ class Voyager extends AbstractBase $status = $statusArray[0]; $rank = $this->getStatusRanking($status); for ($x = 1; $x < count($statusArray); $x++) { - if ($this->getStatusRanking($statusArray[$x]) < $rank) { + $thisRank = $this->getStatusRanking($statusArray[$x]); + if ($thisRank < $rank) { $status = $statusArray[$x]; + $rank = $thisRank; } } @@ -471,9 +490,7 @@ class Voyager extends AbstractBase 'reserve' => $row['ON_RESERVE'], 'callnumber' => $row['CALLNUMBER'], 'item_sort_seq' => $row['ITEM_SEQUENCE_NUMBER'], - 'sort_seq' => isset($row['SORT_SEQ']) - ? $row['SORT_SEQ'] - : PHP_INT_MAX + 'sort_seq' => $row['SORT_SEQ'] ?? PHP_INT_MAX ]; } else { $statusFound = in_array( @@ -590,8 +607,10 @@ class Voyager extends AbstractBase public function getStatuses($idList) { $status = []; - foreach ($idList as $id) { - $status[] = $this->getStatus($id); + if (is_array($idList)) { + foreach ($idList as $id) { + $status[] = $this->getStatus($id); + } } return $status; } @@ -747,17 +766,12 @@ EOT; $data = []; foreach ($sqlRows as $row) { - // Determine Copy Number (always use sequence number; append volume - // when available) + // Determine Copy Number $number = $row['ITEM_SEQUENCE_NUMBER']; - if (isset($row['ITEM_ENUM'])) { - $number .= ' (' . utf8_encode($row['ITEM_ENUM']) . ')'; - } // Concat wrapped rows (MARC data more than 300 bytes gets split // into multiple rows) - $rowId = isset($row['ITEM_ID']) - ? $row['ITEM_ID'] : 'MFHD' . $row['MFHD_ID']; + $rowId = $row['ITEM_ID'] ?? 'MFHD' . $row['MFHD_ID']; if (isset($data[$rowId][$number])) { // We don't want to concatenate the same MARC information to // itself over and over due to a record with multiple status @@ -1000,9 +1014,7 @@ EOT; 'use_unknown_message' => in_array('No information available', $sqlRow['STATUS_ARRAY']), 'item_sort_seq' => $sqlRow['ITEM_SEQUENCE_NUMBER'], - 'sort_seq' => isset($sqlRow['SORT_SEQ']) - ? $sqlRow['SORT_SEQ'] - : PHP_INT_MAX + 'sort_seq' => $sqlRow['SORT_SEQ'] ?? PHP_INT_MAX ]; } @@ -1043,7 +1055,7 @@ EOT; = $this->pickStatus($availability['otherStatuses']); } - // Convert Voyager Format to display format + // Convert Voyager Format to display format $dueDate = false; if (!empty($row['DUEDATE'])) { $dueDate = $this->dateFormat->convertToDisplayDate( @@ -1057,8 +1069,7 @@ EOT; ); } - $requests_placed = isset($row['HOLDS_PLACED']) - ? $row['HOLDS_PLACED'] : 0; + $requests_placed = $row['HOLDS_PLACED'] ?? 0; if (isset($row['RECALLS_PLACED'])) { $requests_placed += $row['RECALLS_PLACED']; } @@ -1072,6 +1083,8 @@ EOT; } $holding[$i] += [ 'availability' => $availability['available'], + 'enumchron' => isset($row['ITEM_ENUM']) + ? utf8_encode($row['ITEM_ENUM']) : null, 'duedate' => $dueDate, 'number' => $number, 'requests_placed' => $requests_placed, @@ -1254,16 +1267,16 @@ EOT; // For some reason barcode is not unique, so evaluate all resulting // rows just to be safe while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { - $primary = !is_null($row['LOGIN']) + $primary = null !== $row['LOGIN'] ? mb_strtolower(utf8_encode($row['LOGIN']), 'UTF-8') : null; - $fallback = $fallback_login_field && is_null($row['LOGIN']) + $fallback = $fallback_login_field && null === $row['LOGIN'] ? mb_strtolower(utf8_encode($row['FALLBACK_LOGIN']), 'UTF-8') : null; - if ((!is_null($primary) && ($primary == $compareLogin + if ((null !== $primary && ($primary == $compareLogin || $primary == $this->sanitizePIN($compareLogin))) - || ($fallback_login_field && is_null($primary) + || ($fallback_login_field && null === $primary && $fallback == $compareLogin) ) { return [ @@ -1304,13 +1317,15 @@ EOT; "MAX(CIRC_TRANSACTIONS.ITEM_ID) as ITEM_ID", "MAX(MFHD_ITEM.ITEM_ENUM) AS ITEM_ENUM", "MAX(MFHD_ITEM.YEAR) AS YEAR", + "MAX(ITEM_BARCODE.ITEM_BARCODE) AS ITEM_BARCODE", "MAX(BIB_TEXT.TITLE_BRIEF) AS TITLE_BRIEF", "MAX(BIB_TEXT.TITLE) AS TITLE", "LISTAGG(ITEM_STATUS_DESC, CHR(9)) " . "WITHIN GROUP (ORDER BY ITEM_STATUS_DESC) as status", "MAX(CIRC_TRANSACTIONS.RENEWAL_COUNT) AS RENEWAL_COUNT", "MAX(CIRC_POLICY_MATRIX.RENEWAL_COUNT) as RENEWAL_LIMIT", - "MAX(LOCATION.LOCATION_DISPLAY_NAME) as BORROWING_LOCATION" + "MAX(LOCATION.LOCATION_DISPLAY_NAME) as BORROWING_LOCATION", + "MAX(CIRC_POLICY_MATRIX.LOAN_INTERVAL) as LOAN_INTERVAL" ]; // From @@ -1320,6 +1335,7 @@ EOT; $this->dbName . ".ITEM", $this->dbName . ".ITEM_STATUS", $this->dbName . ".ITEM_STATUS_TYPE", + $this->dbName . ".ITEM_BARCODE", $this->dbName . ".MFHD_ITEM", $this->dbName . ".BIB_TEXT", $this->dbName . ".CIRC_POLICY_MATRIX", @@ -1338,6 +1354,11 @@ EOT; "BIB_ITEM.ITEM_ID = ITEM.ITEM_ID", "ITEM.ITEM_ID = ITEM_STATUS.ITEM_ID", "ITEM_STATUS.ITEM_STATUS = ITEM_STATUS_TYPE.ITEM_STATUS_TYPE", + "ITEM.ITEM_ID = ITEM_BARCODE.ITEM_ID(+)", + "(ITEM_BARCODE.BARCODE_STATUS IS NULL OR " . + "ITEM_BARCODE.BARCODE_STATUS IN (SELECT BARCODE_STATUS_TYPE FROM " . + "$this->dbName.ITEM_BARCODE_STATUS " . + " WHERE BARCODE_STATUS_DESC = 'Active'))" ]; // Order @@ -1410,7 +1431,7 @@ EOT; if (is_numeric($dueTimeStamp)) { if ($now > $dueTimeStamp) { $dueStatus = "overdue"; - } else if ($now > $dueTimeStamp - (1 * 24 * 60 * 60)) { + } elseif ($now > $dueTimeStamp - (1 * 24 * 60 * 60)) { $dueStatus = "due"; } } @@ -1419,8 +1440,8 @@ EOT; $transaction = [ 'id' => $sqlRow['BIB_ID'], 'item_id' => $sqlRow['ITEM_ID'], + 'barcode' => utf8_encode($sqlRow['ITEM_BARCODE']), 'duedate' => $dueDate, - 'dueTime' => $dueTime, 'dueStatus' => $dueStatus, 'volume' => str_replace("v.", "", utf8_encode($sqlRow['ITEM_ENUM'])), 'publication_year' => $sqlRow['YEAR'], @@ -1431,9 +1452,13 @@ EOT; 'message' => $this->pickTransactionStatus(explode(chr(9), $sqlRow['STATUS'])), ]; - if (isset($this->config['Loans']['display_borrowing_location']) - && $this->config['Loans']['display_borrowing_location'] + // Display due time only if loan interval is not in days if configured + if (empty($this->displayDueTimeIntervals) + || in_array($sqlRow['LOAN_INTERVAL'], $this->displayDueTimeIntervals) ) { + $transaction['dueTime'] = $dueTime; + } + if (!empty($this->config['Loans']['display_borrowing_location'])) { $transaction['borrowingLocation'] = utf8_encode($sqlRow['BORROWING_LOCATION']); } @@ -1722,7 +1747,6 @@ EOT; $returnList = []; if (!empty($holdList)) { - $sortHoldList = []; // Get a unique List of Bib Ids foreach ($holdList as $holdItem) { @@ -1954,16 +1978,24 @@ EOT; "PATRON.HISTORICAL_CHARGES, PATRON_ADDRESS.ADDRESS_LINE1, " . "PATRON_ADDRESS.ADDRESS_LINE2, PATRON_ADDRESS.ZIP_POSTAL, " . "PATRON_ADDRESS.CITY, PATRON_ADDRESS.COUNTRY, " . - "PATRON_PHONE.PHONE_NUMBER, PATRON_GROUP.PATRON_GROUP_NAME " . + "PATRON_PHONE.PHONE_NUMBER, PHONE_TYPE.PHONE_DESC, " . + "PATRON_GROUP.PATRON_GROUP_NAME " . "FROM $this->dbName.PATRON, $this->dbName.PATRON_ADDRESS, " . - "$this->dbName.PATRON_PHONE, $this->dbName.PATRON_BARCODE, " . - "$this->dbName.PATRON_GROUP " . + "$this->dbName.PATRON_PHONE, $this->dbName.PHONE_TYPE, " . + "$this->dbName.PATRON_BARCODE, $this->dbName.PATRON_GROUP " . "WHERE PATRON.PATRON_ID = PATRON_ADDRESS.PATRON_ID (+) " . "AND PATRON_ADDRESS.ADDRESS_ID = PATRON_PHONE.ADDRESS_ID (+) " . "AND PATRON.PATRON_ID = PATRON_BARCODE.PATRON_ID (+) " . "AND PATRON_BARCODE.PATRON_GROUP_ID = " . "PATRON_GROUP.PATRON_GROUP_ID (+) " . + "AND PATRON_PHONE.PHONE_TYPE = PHONE_TYPE.PHONE_TYPE (+) " . "AND PATRON.PATRON_ID = :id"; + $primaryPhoneType = isset($this->config['Profile']['primary_phone']) + ? $this->config['Profile']['primary_phone'] + : 'Primary'; + $mobilePhoneType = isset($this->config['Profile']['mobile_phone']) + ? $this->config['Profile']['mobile_phone'] + : 'Mobile'; try { $sqlStmt = $this->executeSQL($sql, [':id' => $patron['id']]); $patron = []; @@ -1975,7 +2007,11 @@ EOT; $patron['lastname'] = utf8_encode($row['LAST_NAME']); } if (!empty($row['PHONE_NUMBER'])) { - $patron['phone'] = utf8_encode($row['PHONE_NUMBER']); + if ($primaryPhoneType === $row['PHONE_DESC']) { + $patron['phone'] = utf8_encode($row['PHONE_NUMBER']); + } elseif ($mobilePhoneType === $row['PHONE_DESC']) { + $patron['mobile_phone'] = utf8_encode($row['PHONE_NUMBER']); + } } if (!empty($row['PATRON_GROUP_NAME'])) { $patron['group'] = utf8_encode($row['PATRON_GROUP_NAME']); @@ -1984,7 +2020,7 @@ EOT; $addr1 = utf8_encode($row['ADDRESS_LINE1']); if ($validator->isValid($addr1)) { $patron['email'] = $addr1; - } else if (!isset($patron['address1'])) { + } elseif (!isset($patron['address1'])) { if (!empty($addr1)) { $patron['address1'] = $addr1; } @@ -2002,7 +2038,7 @@ EOT; } } } - return (empty($patron) ? null : $patron); + return empty($patron) ? null : $patron; } catch (PDOException $e) { throw new ILSException($e->getMessage()); } @@ -2418,6 +2454,107 @@ EOT; return $recordList; } + /** + * Get bib records for recently returned items. + * + * @param int $limit Maximum number of records to retrieve (default = 30) + * @param int $maxage The maximum number of days to consider "recently + * returned." + * @param array $patron Patron Data + * + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getRecentlyReturnedBibs($limit = 30, $maxage = 30, + $patron = null + ) { + $recordList = []; + + // Oracle does not support the SQL LIMIT clause before version 12, so + // instead we need to provide an optimizer hint, which requires us to + // ensure that $limit is a valid integer. + $intLimit = intval($limit); + $safeLimit = $intLimit < 1 ? 30 : $intLimit; + + $sql = "select /*+ FIRST_ROWS($safeLimit) */ BIB_MFHD.BIB_ID, " + . "max(CIRC_TRANS_ARCHIVE.DISCHARGE_DATE) as RETURNED " + . "from $this->dbName.CIRC_TRANS_ARCHIVE " + . "join $this->dbName.MFHD_ITEM " + . "on CIRC_TRANS_ARCHIVE.ITEM_ID = MFHD_ITEM.ITEM_ID " + . "join $this->dbName.BIB_MFHD " + . "on BIB_MFHD.MFHD_ID = MFHD_ITEM.MFHD_ID " + . "join $this->dbName.BIB_MASTER " + . "on BIB_MASTER.BIB_ID = BIB_MFHD.BIB_ID " + . "where CIRC_TRANS_ARCHIVE.DISCHARGE_DATE is not null " + . "and CIRC_TRANS_ARCHIVE.DISCHARGE_DATE > SYSDATE - :maxage " + . "and BIB_MASTER.SUPPRESS_IN_OPAC='N' " + . "group by BIB_MFHD.BIB_ID " + . "order by RETURNED desc"; + try { + $sqlStmt = $this->executeSQL($sql, [':maxage' => $maxage]); + while (count($recordList) < $limit + && $row = $sqlStmt->fetch(PDO::FETCH_ASSOC) + ) { + $recordList[] = ['id' => $row['BIB_ID']]; + } + } catch (PDOException $e) { + throw new ILSException($e->getMessage()); + } + return $recordList; + } + + /** + * Get bib records for "trending" items (recently returned with high usage). + * + * @param int $limit Maximum number of records to retrieve (default = 30) + * @param int $maxage The maximum number of days' worth of data to examine. + * @param array $patron Patron Data + * + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getTrendingBibs($limit = 30, $maxage = 30, $patron = null) + { + $recordList = []; + + // Oracle does not support the SQL LIMIT clause before version 12, so + // instead we need to provide an optimizer hint, which requires us to + // ensure that $limit is a valid integer. + $intLimit = intval($limit); + $safeLimit = $intLimit < 1 ? 30 : $intLimit; + + $sql = "select /*+ FIRST_ROWS($safeLimit) */ BIB_MFHD.BIB_ID, " + . "count(CIRC_TRANS_ARCHIVE.DISCHARGE_DATE) as RECENT, " + . "sum(ITEM.HISTORICAL_CHARGES) as OVERALL " + . "from $this->dbName.CIRC_TRANS_ARCHIVE " + . "join $this->dbName.MFHD_ITEM " + . "on CIRC_TRANS_ARCHIVE.ITEM_ID = MFHD_ITEM.ITEM_ID " + . "join $this->dbName.BIB_MFHD " + . "on BIB_MFHD.MFHD_ID = MFHD_ITEM.MFHD_ID " + . "join $this->dbName.ITEM " + . "on CIRC_TRANS_ARCHIVE.ITEM_ID = ITEM.ITEM_ID " + . "join $this->dbName.BIB_MASTER " + . "on BIB_MASTER.BIB_ID = BIB_MFHD.BIB_ID " + . "where CIRC_TRANS_ARCHIVE.DISCHARGE_DATE is not null " + . "and CIRC_TRANS_ARCHIVE.DISCHARGE_DATE > SYSDATE - :maxage " + . "and BIB_MASTER.SUPPRESS_IN_OPAC='N' " + . "group by BIB_MFHD.BIB_ID " + . "order by RECENT desc, OVERALL desc"; + try { + $sqlStmt = $this->executeSQL($sql, [':maxage' => $maxage]); + while (count($recordList) < $limit + && $row = $sqlStmt->fetch(PDO::FETCH_ASSOC) + ) { + $recordList[] = ['id' => $row['BIB_ID']]; + } + } catch (PDOException $e) { + throw new ILSException($e->getMessage()); + } + return $recordList; + } + /** * Get suppressed records. * diff --git a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php index bbae414af17e8c82dd0eb4d0ceee10c42de20c16..a338cd0293d4fda728230f62a5e7fa5dffff53b3 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php +++ b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php @@ -2,7 +2,7 @@ /** * Voyager ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * Copyright (C) The National Library of Finland 2014-2016. @@ -30,8 +30,11 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use PDO, PDOException, VuFind\Exception\Date as DateException, - VuFind\Exception\ILS as ILSException; + +use PDO; +use PDOException; +use VuFind\Date\DateException; +use VuFind\Exception\ILS as ILSException; /** * Voyager Restful ILS Driver @@ -47,6 +50,9 @@ use PDO, PDOException, VuFind\Exception\Date as DateException, */ class VoyagerRestful extends Voyager implements \VuFindHttp\HttpServiceAwareInterface { + use CacheTrait { + getCacheKey as protected getBaseCacheKey; + } use \VuFindHttp\HttpServiceAwareTrait; /** @@ -356,7 +362,7 @@ class VoyagerRestful extends Voyager implements \VuFindHttp\HttpServiceAwareInte $valid_hold_statuses_array = explode(':', $this->config['Holds']['valid_hold_statuses']); - if (count($valid_hold_statuses_array > 0)) { + if (!empty($valid_hold_statuses_array)) { foreach ($statusArray as $status) { if (!in_array($status, $valid_hold_statuses_array)) { $is_holdable = false; @@ -585,11 +591,11 @@ class VoyagerRestful extends Voyager implements \VuFindHttp\HttpServiceAwareInte */ public function checkRequestIsValid($id, $data, $patron) { - $holdType = isset($data['holdtype']) ? $data['holdtype'] : 'auto'; - $level = isset($data['level']) ? $data['level'] : 'copy'; + $holdType = $data['holdtype'] ?? 'auto'; + $level = $data['level'] ?? 'copy'; $mode = ('title' == $level) ? $this->titleHoldsMode : $this->holdsMode; if ('driver' == $mode && 'auto' == $holdType) { - $itemID = isset($data['item_id']) ? $data['item_id'] : false; + $itemID = $data['item_id'] ?? false; $result = $this->determineHoldType($patron['id'], $id, $itemID); if (!$result) { return false; @@ -625,7 +631,7 @@ class VoyagerRestful extends Voyager implements \VuFindHttp\HttpServiceAwareInte return false; } - $level = isset($data['level']) ? $data['level'] : 'copy'; + $level = $data['level'] ?? 'copy'; $itemID = ($level != 'title' && isset($data['item_id'])) ? $data['item_id'] : false; @@ -1253,6 +1259,7 @@ EOT; * * @return array An array of renewal information keyed by item ID */ + /** * Renew My Items * @@ -1426,7 +1433,6 @@ EOT; $itemId = false ) { if (!empty($bibId) && !empty($patronId) && !empty($request)) { - $hierarchy = []; // Build Hierarchy @@ -1911,8 +1917,7 @@ EOT; continue 2; } foreach ($copyFields as $field) { - $hold[$field] = isset($apiHold[$field]) - ? $apiHold[$field] : ''; + $hold[$field] = $apiHold[$field] ?? ''; } break; } @@ -1944,8 +1949,8 @@ EOT; ? $holdDetails['level'] : 'copy'; $pickUpLocation = !empty($holdDetails['pickUpLocation']) ? $holdDetails['pickUpLocation'] : $this->defaultPickUpLocation; - $itemId = isset($holdDetails['item_id']) ? $holdDetails['item_id'] : false; - $comment = isset($holdDetails['comment']) ? $holdDetails['comment'] : ''; + $itemId = $holdDetails['item_id'] ?? false; + $comment = $holdDetails['comment'] ?? ''; $bibId = $holdDetails['id']; // Request was initiated before patron was logged in - @@ -1994,12 +1999,11 @@ EOT; return $this->holdError('hold_invalid_request_group'); } - // Optional check that the bib has items + // Optional check that the bib has items if ($this->checkItemsExist) { $exist = $this->itemsExist( $bibId, - isset($holdDetails['requestGroupId']) - ? $holdDetails['requestGroupId'] : null + $holdDetails['requestGroupId'] ?? null ); if (!$exist) { return $this->holdError('hold_no_items'); @@ -2018,8 +2022,7 @@ EOT; ) { $available = $this->itemsAvailable( $bibId, - isset($holdDetails['requestGroupId']) - ? $holdDetails['requestGroupId'] : null + $holdDetails['requestGroupId'] ?? null ); if ($available) { return $this->holdError('hold_items_available'); @@ -2079,7 +2082,7 @@ EOT; foreach ($details as $cancelDetails) { list($itemId, $cancelCode) = explode('|', $cancelDetails); - // Create Rest API Cancel Key + // Create Rest API Cancel Key $cancelID = $this->ws_dbKey . '|' . $cancelCode; // Build Hierarchy @@ -2111,7 +2114,6 @@ EOT; ? 'hold_cancel_success' : 'hold_cancel_fail', 'sysMessage' => ($reply == 'ok') ? false : $reply, ]; - } else { $response[$itemId] = [ 'success' => false, 'status' => 'hold_cancel_fail' @@ -2156,9 +2158,7 @@ EOT; */ public function getRenewDetails($checkOutDetails) { - $renewDetails = (isset($checkOutDetails['institution_dbkey']) - ? $checkOutDetails['institution_dbkey'] - : '') + $renewDetails = ($checkOutDetails['institution_dbkey'] ?? '') . '|' . $checkOutDetails['item_id']; return $renewDetails; } @@ -2229,7 +2229,7 @@ EOT; if ($dueTimeStamp !== false && is_numeric($dueTimeStamp)) { if ($now > $dueTimeStamp) { $dueStatus = 'overdue'; - } else if ($now > $dueTimeStamp - (1 * 24 * 60 * 60)) { + } elseif ($now > $dueTimeStamp - (1 * 24 * 60 * 60)) { $dueStatus = 'due'; } } @@ -2466,9 +2466,9 @@ EOT; $patron = $details['patron']; $level = isset($details['level']) && !empty($details['level']) ? $details['level'] : 'copy'; - $itemId = isset($details['item_id']) ? $details['item_id'] : false; - $mfhdId = isset($details['holdings_id']) ? $details['holdings_id'] : false; - $comment = isset($details['comment']) ? $details['comment'] : ''; + $itemId = $details['item_id'] ?? false; + $mfhdId = $details['holdings_id'] ?? false; + $comment = $details['comment'] ?? ''; $bibId = $details['id']; // Make Sure Pick Up Location is Valid @@ -2576,7 +2576,7 @@ EOT; foreach ($details as $cancelDetails) { list($dbKey, $itemId, $cancelCode) = explode('|', $cancelDetails); - // Create Rest API Cancel Key + // Create Rest API Cancel Key $cancelID = ($dbKey ? $dbKey : $this->ws_dbKey) . '|' . $cancelCode; // Build Hierarchy @@ -2608,7 +2608,6 @@ EOT; : 'storage_retrieval_request_cancel_fail', 'sysMessage' => ($reply == 'ok') ? false : $reply, ]; - } else { $response[$itemId] = [ 'success' => false, @@ -2635,10 +2634,7 @@ EOT; public function getCancelStorageRetrievalRequestDetails($details) { $details - = (isset($details['institution_dbkey']) - ? $details['institution_dbkey'] - : '' - ) + = ($details['institution_dbkey'] ?? '') . '|' . $details['item_id'] . '|' . $details['reqnum']; return $details; @@ -2854,7 +2850,7 @@ EOT; return false; } - $level = isset($data['level']) ? $data['level'] : 'copy'; + $level = $data['level'] ?? 'copy'; $itemID = ($level != 'title' && isset($data['item_id'])) ? $data['item_id'] : false; @@ -3023,7 +3019,7 @@ EOT; $pickupLibrary = $this->encodeXML($details['pickUpLibrary']); $itemId = $this->encodeXML($details['item_id'] . '.' . $details['id']); $comment = $this->encodeXML( - isset($details['comment']) ? $details['comment'] : '' + $details['comment'] ?? '' ); $bibId = $this->encodeXML($details['id']); $bibDbName = $this->encodeXML($this->config['Catalog']['database']); @@ -3175,7 +3171,7 @@ EOT; foreach ($details as $cancelDetails) { list($dbKey, $itemId, $type, $cancelCode) = explode('|', $cancelDetails); - // Create Rest API Cancel Key + // Create Rest API Cancel Key $cancelID = ($dbKey ? $dbKey : $this->ws_dbKey) . '|' . $cancelCode; // Build Hierarchy @@ -3213,7 +3209,6 @@ EOT; ? 'ill_request_cancel_success' : 'ill_request_cancel_fail', 'sysMessage' => ($reply == 'ok') ? false : $reply, ]; - } else { $response[$itemId] = [ 'success' => false, @@ -3240,9 +3235,7 @@ EOT; */ public function getCancelILLRequestDetails($details) { - $details = (isset($details['institution_dbkey']) - ? $details['institution_dbkey'] - : '') + $details = ($details['institution_dbkey'] ?? '') . '|' . $details['item_id'] . '|' . $details['type'] . '|' . $details['reqnum']; diff --git a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..56e525226c9e4c15c18fcbc9b6c971208d063ca2 --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for VoyagerRestful ILS driver. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Driver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for VoyagerRestful ILS driver. + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class VoyagerRestfulFactory extends DriverWithDateConverterFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $ils = $container->get('VuFind\ILS\HoldSettings'); + $extraParams = [$ils->getHoldsMode(), $ils->getTitleHoldsMode()]; + return parent::__invoke($container, $requestedName, $extraParams); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php index 2b9624664f81b20c691af9920fea42af5894027a..e7c7d2101f69f118cee5dbad82698e16c04ebb05 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php +++ b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php @@ -2,7 +2,7 @@ /** * XC NCIP Toolkit (v2) ILS Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ namespace VuFind\ILS\Driver; -use VuFind\Exception\ILS as ILSException, - VuFind\Config\Locator as ConfigLocator; + +use VuFind\Config\Locator as ConfigLocator; +use VuFind\Exception\ILS as ILSException; /** * XC NCIP Toolkit (v2) ILS Driver @@ -347,7 +348,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf { $agencyList = []; - if (is_null($agency)) { + if (null === $agency) { $keys = array_keys($this->agency); $agencyList[] = $keys[0]; } @@ -445,7 +446,6 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf $holdings = $current->xpath('ns1:HoldingsSet'); foreach ($holdings as $current) { - $holdCallNo = $current->xpath('ns1:CallNumber'); $holdCallNo = (string)$holdCallNo[0]; @@ -489,7 +489,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * @param array $patron Patron data * @param array $ids The (consortial) source records for the record id * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -504,7 +504,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf $agencyList = []; $idList = []; - if (! is_null($ids)) { + if (null !== $ids) { foreach ($ids as $_id) { // Need to parse out the 035$a format, e.g., "(Agency) 123" if (preg_match('/\(([^\)]+)\)\s*([0-9]+)/', $_id, $matches)) { @@ -568,7 +568,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, @@ -672,7 +672,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's transactions on success. */ @@ -732,7 +732,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return mixed Array of the patron's fines on success. */ @@ -753,7 +753,6 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf $fines = []; $balance = 0; foreach ($list as $current) { - $current->registerXPathNamespace('ns1', 'http://www.niso.org/2008/ncip'); $tmp = $current->xpath( @@ -795,7 +794,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * * @param array $patron The patron array from patronLogin * - * @throws \VuFind\Exception\Date + * @throws VuFind\Date\DateException; * @throws ILSException * @return array Array of the patron's holds on success. */ @@ -897,10 +896,10 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf return [ 'firstname' => (string)$first[0], 'lastname' => (string)$last[0], - 'address1' => isset($address[0]) ? $address[0] : '', - 'address2' => (isset($address[1]) ? $address[1] : '') . + 'address1' => $address[0] ?? '', + 'address2' => ($address[1] ?? '') . (isset($address[2]) ? ', ' . $address[2] : ''), - 'zip' => isset($address[3]) ? $address[3] : '', + 'zip' => $address[3] ?? '', 'phone' => '', // TODO: phone number support 'group' => '' ]; @@ -1486,7 +1485,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf if ($dueDate) { $tmp = $dueDate; $newDueDate = (string)$tmp[0]; - $tmp = split("T", $newDueDate); + $tmp = explode('T', $newDueDate); $splitDate = $tmp[0]; $splitTime = $tmp[1]; $details[$renewId] = [ @@ -1495,7 +1494,6 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf "new_time" => rtrim($splitTime, "Z"), "item_id" => $renewId, ]; - } else { $details[$renewId] = [ "success" => false, @@ -1526,7 +1524,6 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf $requestId, $type ) { - return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' . 'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/' . @@ -1687,7 +1684,6 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf $itemAgencyId, $patronAgencyId ) { - return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . '<ns1:NCIPMessage xmlns:ns1="http://www.niso.org/2008/ncip" ' . 'ns1:version="http://www.niso.org/schemas/ncip/v2_0/imp1/' . @@ -1754,7 +1750,7 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf 'xsd/ncip_v2_0.xsd">' . '<ns1:LookupUser>'; - if (!is_null($patron_agency_id)) { + if (null !== $patron_agency_id) { $ret .= '<ns1:InitiationHeader>' . '<ns1:FromAgencyId>' . diff --git a/module/VuFind/src/VuFind/ILS/HoldSettings.php b/module/VuFind/src/VuFind/ILS/HoldSettings.php index 7051528179931f558eb992e01c175fb0675f0e2b..2ea4fa44326220f5bff2058eab6bc9913e36e227 100644 --- a/module/VuFind/src/VuFind/ILS/HoldSettings.php +++ b/module/VuFind/src/VuFind/ILS/HoldSettings.php @@ -5,7 +5,7 @@ * This class is responsible for determining hold settings for VuFind based * on configuration and defaults. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php b/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..00dc7e2988902d644c7639c5670b75f62f93e8fb --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * ILS hold settings factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ILS hold settings factory + * + * @category VuFind + * @package ILS_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HoldSettingsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('config')->Catalog + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Logic/Holds.php b/module/VuFind/src/VuFind/ILS/Logic/Holds.php index 88ebd73c4fa1cd319e4d7c8bed84a9c064f48393..c5d4c6bc1f3f92826ac49a472bfe59eedadffc74 100644 --- a/module/VuFind/src/VuFind/ILS/Logic/Holds.php +++ b/module/VuFind/src/VuFind/ILS/Logic/Holds.php @@ -2,7 +2,7 @@ /** * Hold Logic Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,8 +27,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ILS\Logic; -use VuFind\ILS\Connection as ILSConnection, - VuFind\Exception\ILS as ILSException; + +use VuFind\Exception\ILS as ILSException; +use VuFind\ILS\Connection as ILSConnection; /** * Hold Logic Class @@ -120,10 +121,8 @@ class Holds foreach ($holdings as $groupKey => $items) { $retVal[$groupKey] = [ 'items' => $items, - 'location' => isset($items[0]['location']) - ? $items[0]['location'] : '', - 'locationhref' => isset($items[0]['locationhref']) - ? $items[0]['locationhref'] : '' + 'location' => $items[0]['location'] ?? '', + 'locationhref' => $items[0]['locationhref'] ?? '' ]; // Copy all text fields from the item to the holdings level foreach ($items as $item) { @@ -217,7 +216,7 @@ class Holds if ($mode == "disabled") { $holdings = $this->standardHoldings($result); - } else if ($mode == "driver") { + } elseif ($mode == "driver") { $holdings = $this->driverHoldings($result, $config, !empty($blocks)); } else { $holdings = $this->generateHoldings($result, $mode, $config); @@ -340,7 +339,7 @@ class Holds = ($holds_override && isset($copy['holdOverride'])) ? $copy['holdOverride'] : $type; - switch($currentType) { + switch ($currentType) { case "all": $addlink = true; // always provide link break; @@ -522,8 +521,7 @@ class Holds // Build Params return [ 'action' => $action, 'record' => $details['id'], - 'source' => isset($details['source']) - ? $details['source'] : DEFAULT_SEARCH_BACKEND, + 'source' => $details['source'] ?? DEFAULT_SEARCH_BACKEND, 'query' => $queryString, 'anchor' => "#tabnav" ]; } diff --git a/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php b/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0ceae43623bfb71875a8ee815f72bf3929a0096d --- /dev/null +++ b/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Shared factory for ILS logic classes. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Logic + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ILS\Logic; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Shared factory for ILS logic classes. + * + * @category VuFind + * @package ILS_Logic + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class LogicFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Auth\ILSAuthenticator'), + $container->get('VuFind\ILS\Connection'), + $container->get('VuFind\Crypt\HMAC'), + $container->get('VuFind\Config\PluginManager')->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php b/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php index 3d8ed1c8a40803ddd887582a174b134448d849df..00f60f67224165e4afa53877fb9eabaed99cb7ca 100644 --- a/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php +++ b/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php @@ -2,7 +2,7 @@ /** * Title Hold Logic Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -27,8 +27,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ILS\Logic; -use VuFind\ILS\Connection as ILSConnection, - VuFind\Exception\ILS as ILSException; + +use VuFind\Exception\ILS as ILSException; +use VuFind\ILS\Connection as ILSConnection; /** * Title Hold Logic Class @@ -116,8 +117,8 @@ class TitleHolds if ($this->catalog) { $mode = $this->catalog->getTitleHoldsMode(); if ($mode == 'disabled') { - return false; - } else if ($mode == 'driver') { + return false; + } elseif ($mode == 'driver') { try { $patron = $this->ilsAuth->storedCatalogLogin(); if (!$patron) { @@ -245,9 +246,8 @@ class TitleHolds if ($checkHolds != false) { if ($type == 'always') { - $addlink = true; + $addlink = true; } elseif ($type == 'availability') { - $holdings = $this->getHoldings($id); foreach ($holdings as $holding) { if ($holding['availability'] diff --git a/module/VuFind/src/VuFind/ImageLoader.php b/module/VuFind/src/VuFind/ImageLoader.php index 1c5ddb3fae20a9715ea318b773e1f3b37dfe80b3..228622ead3ed404968a34caf828dcd3fc4650e5a 100644 --- a/module/VuFind/src/VuFind/ImageLoader.php +++ b/module/VuFind/src/VuFind/ImageLoader.php @@ -2,7 +2,7 @@ /** * Base class for loading images (shared by Cover\Loader and QRCode\Loader) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/Log/Logger.php b/module/VuFind/src/VuFind/Log/Logger.php index c3b3e40a4bb33cd287c6f5addace7ece9af47993..1ce144ba9d3253e0d700e5cdcbc7daea0ac3b7b7 100644 --- a/module/VuFind/src/VuFind/Log/Logger.php +++ b/module/VuFind/src/VuFind/Log/Logger.php @@ -2,7 +2,7 @@ /** * VuFind Logger * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Log; + use Zend\Log\Logger as BaseLogger; /** @@ -82,7 +83,7 @@ class Logger extends BaseLogger $writer->write( [ 'timestamp' => $timestamp, - 'priority' => (int) $priority, + 'priority' => (int)$priority, 'priorityName' => $this->priorities[$priority], 'message' => $message, 'extra' => $extra diff --git a/module/VuFind/src/VuFind/Log/LoggerAwareTrait.php b/module/VuFind/src/VuFind/Log/LoggerAwareTrait.php index 7bb4ed59bcefba8fff11f7a1c9924d5fcf4155c2..c5326f297a8bac71e802b754d2581a154adb164e 100644 --- a/module/VuFind/src/VuFind/Log/LoggerAwareTrait.php +++ b/module/VuFind/src/VuFind/Log/LoggerAwareTrait.php @@ -2,7 +2,7 @@ /** * Extension of \Zend\Log\LoggerAwareTrait with some convenience methods. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Log/LoggerFactory.php b/module/VuFind/src/VuFind/Log/LoggerFactory.php index 807a04a4649e3f3d1e0e3a56a4b0bd146f64f36d..d35bb6f83c367fbf9f8a342c944454a0a0bae76d 100644 --- a/module/VuFind/src/VuFind/Log/LoggerFactory.php +++ b/module/VuFind/src/VuFind/Log/LoggerFactory.php @@ -2,7 +2,7 @@ /** * Factory for instantiating Logger * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -26,9 +26,11 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Log; + +use Interop\Container\ContainerInterface; use Zend\Config\Config; use Zend\Log\Writer\WriterInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for instantiating Logger @@ -41,23 +43,23 @@ use Zend\ServiceManager\ServiceLocatorInterface; * * @codeCoverageIgnore */ -class LoggerFactory implements \Zend\ServiceManager\FactoryInterface +class LoggerFactory implements FactoryInterface { /** * Configure database writers. * - * @param Logger $logger Logger object - * @param ServiceLocatorInterface $sm Service locator - * @param string $config Configuration + * @param Logger $logger Logger object + * @param ContainerInterface $container Service manager + * @param string $config Configuration * * @return void */ - protected function addDbWriters(Logger $logger, ServiceLocatorInterface $sm, + protected function addDbWriters(Logger $logger, ContainerInterface $container, $config ) { $parts = explode(':', $config); $table_name = $parts[0]; - $error_types = isset($parts[1]) ? $parts[1] : ''; + $error_types = $parts[1] ?? ''; $columnMapping = [ 'priority' => 'priority', @@ -69,7 +71,7 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface // Make Writers $filters = explode(',', $error_types); $writer = new Writer\Db( - $sm->get('VuFind\DbAdapter'), + $container->get('Zend\Db\Adapter\Adapter'), $table_name, $columnMapping ); $this->addWriters($logger, $writer, $filters); @@ -78,23 +80,23 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface /** * Configure email writers. * - * @param Logger $logger Logger object - * @param ServiceLocatorInterface $sm Service locator - * @param Config $config Configuration + * @param Logger $logger Logger object + * @param ContainerInterface $container Service manager + * @param Config $config Configuration * * @return void */ - protected function addEmailWriters(Logger $logger, ServiceLocatorInterface $sm, + protected function addEmailWriters(Logger $logger, ContainerInterface $container, Config $config ) { // Set up the logger's mailer to behave consistently with VuFind's // general mailer: $parts = explode(':', $config->Logging->email); $email = $parts[0]; - $error_types = isset($parts[1]) ? $parts[1] : ''; + $error_types = $parts[1] ?? ''; // use smtp - $mailer = $sm->get('VuFind\Mailer'); + $mailer = $container->get('VuFind\Mailer\Mailer'); $msg = $mailer->getNewMessage() ->addFrom($config->Site->email) ->addTo($email) @@ -136,13 +138,13 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface /** * Configure Slack writers. * - * @param Logger $logger Logger object - * @param ServiceLocatorInterface $sm Service locator - * @param Config $config Configuration + * @param Logger $logger Logger object + * @param ContainerInterface $container Service manager + * @param Config $config Configuration * * @return void */ - protected function addSlackWriters(Logger $logger, ServiceLocatorInterface $sm, + protected function addSlackWriters(Logger $logger, ContainerInterface $container, Config $config ) { $options = []; @@ -162,7 +164,7 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface // Make Writers $writer = new Writer\Slack( $config->Logging->slackurl, - $sm->get('VuFind\Http')->createClient(), + $container->get('VuFindHttp\HttpService')->createClient(), $options ); $writer->setContentType('application/json'); @@ -176,14 +178,14 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface /** * Set configuration * - * @param ServiceLocatorInterface $sm Service manager - * @param Logger $logger Logger to configure + * @param ContainerInterface $container Service manager + * @param Logger $logger Logger to configure * * @return void */ - protected function configureLogger(ServiceLocatorInterface $sm, Logger $logger) + protected function configureLogger(ContainerInterface $container, Logger $logger) { - $config = $sm->get('VuFind\Config')->get('config'); + $config = $container->get('VuFind\Config\PluginManager')->get('config'); $hasWriter = false; @@ -196,7 +198,7 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface // Activate database logging, if applicable: if (isset($config->Logging->database)) { $hasWriter = true; - $this->addDbWriters($logger, $sm, $config->Logging->database); + $this->addDbWriters($logger, $container, $config->Logging->database); } // Activate file logging, if applicable: @@ -208,13 +210,13 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface // Activate email logging, if applicable: if (isset($config->Logging->email)) { $hasWriter = true; - $this->addEmailWriters($logger, $sm, $config); + $this->addEmailWriters($logger, $container, $config); } // Activate slack logging, if applicable: if (isset($config->Logging->slack) && isset($config->Logging->slackurl)) { $hasWriter = true; - $this->addSlackWriters($logger, $sm, $config); + $this->addSlackWriters($logger, $container, $config); } // Null (no-op) writer to avoid errors @@ -273,11 +275,11 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface foreach ($filters as $filter) { $parts = explode('-', $filter); $priority = $parts[0]; - $verbosity = isset($parts[1]) ? $parts[1] : false; + $verbosity = $parts[1] ?? false; // VuFind's configuration provides four priority options, each // combining two of the standard Zend levels. - switch(trim($priority)) { + switch (trim($priority)) { case 'debug': // Set static flag indicating that debug is turned on: $logger->debugNeeded(true); @@ -303,7 +305,7 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface // Clone the submitted writer since we'll need a separate instance of the // writer for each selected priority level. - $newWriter = clone($writer); + $newWriter = clone $writer; // verbosity if ($verbosity) { @@ -328,16 +330,27 @@ class LoggerFactory implements \Zend\ServiceManager\FactoryInterface } /** - * Create service + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * - * @param ServiceLocatorInterface $sm Service manager + * @return object * - * @return mixed + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs */ - public function createService(ServiceLocatorInterface $sm) - { - $logger = new Logger(); - $this->configureLogger($sm, $logger); + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $logger = new $requestedName(); + $this->configureLogger($container, $logger); return $logger; } } diff --git a/module/VuFind/src/VuFind/Log/Writer/Db.php b/module/VuFind/src/VuFind/Log/Writer/Db.php index c467468935fcd5dfb0bb70754c691767e5079a52..06799e9e725a5c2c192572755aa00f0ab72ec99d 100644 --- a/module/VuFind/src/VuFind/Log/Writer/Db.php +++ b/module/VuFind/src/VuFind/Log/Writer/Db.php @@ -2,7 +2,7 @@ /** * DB log writer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Log/Writer/Mail.php b/module/VuFind/src/VuFind/Log/Writer/Mail.php index 853fbc03ae607357f6dd5075ce775af5535c0629..7df3132b3037beb48fa0dcbd8b395d33bb7b78af 100644 --- a/module/VuFind/src/VuFind/Log/Writer/Mail.php +++ b/module/VuFind/src/VuFind/Log/Writer/Mail.php @@ -2,7 +2,7 @@ /** * Mail log writer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -39,7 +39,7 @@ namespace VuFind\Log\Writer; class Mail extends \Zend\Log\Writer\Mail { use VerbosityTrait; - + /** * Write a message to the log. * diff --git a/module/VuFind/src/VuFind/Log/Writer/Post.php b/module/VuFind/src/VuFind/Log/Writer/Post.php index ab1cc838b03ecef472d4e2c0b5dcd42b8980762c..e638eadc7f9cc0cbd2ebdc19faea9f63c1b7cd5d 100644 --- a/module/VuFind/src/VuFind/Log/Writer/Post.php +++ b/module/VuFind/src/VuFind/Log/Writer/Post.php @@ -2,7 +2,7 @@ /** * HTTP POST log writer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Log\Writer; + use Zend\Http\Client; /** diff --git a/module/VuFind/src/VuFind/Log/Writer/Slack.php b/module/VuFind/src/VuFind/Log/Writer/Slack.php index 26284522ac531f036b80f59d411c7afa74fa0e6f..f6dda44f6cf5e95c2624c42a63cb44495614e875 100644 --- a/module/VuFind/src/VuFind/Log/Writer/Slack.php +++ b/module/VuFind/src/VuFind/Log/Writer/Slack.php @@ -2,7 +2,7 @@ /** * HTTP POST log writer for Slack * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Log\Writer; + use Zend\Http\Client; /** diff --git a/module/VuFind/src/VuFind/Log/Writer/Stream.php b/module/VuFind/src/VuFind/Log/Writer/Stream.php index eca9096fb4947bfadc454342d7290591a1250231..8567fa3088f42c0e78d386f9cf586e4d3f7527c5 100644 --- a/module/VuFind/src/VuFind/Log/Writer/Stream.php +++ b/module/VuFind/src/VuFind/Log/Writer/Stream.php @@ -2,7 +2,7 @@ /** * Stream writer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Log/Writer/VerbosityTrait.php b/module/VuFind/src/VuFind/Log/Writer/VerbosityTrait.php index 8343155ceadce9f324d18bb7109a7e2bd9f17813..f55b0e21ff76cd80644bb677e2f094c848efe212 100644 --- a/module/VuFind/src/VuFind/Log/Writer/VerbosityTrait.php +++ b/module/VuFind/src/VuFind/Log/Writer/VerbosityTrait.php @@ -2,7 +2,7 @@ /** * Trait to add configurable verbosity settings to loggers * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFind/src/VuFind/Mailer/Factory.php b/module/VuFind/src/VuFind/Mailer/Factory.php index 996c3e4d9d0ee6078e754607cc15f72f2cf8ee07..6aa5dba410ca2e4d21b2386279f222b9abc29d89 100644 --- a/module/VuFind/src/VuFind/Mailer/Factory.php +++ b/module/VuFind/src/VuFind/Mailer/Factory.php @@ -2,7 +2,7 @@ /** * Factory for instantiating Mailer objects * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,9 +26,12 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Mailer; + +use Interop\Container\ContainerInterface; use Zend\Mail\Transport\InMemory; -use Zend\Mail\Transport\Smtp, Zend\Mail\Transport\SmtpOptions; -use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Mail\Transport\Smtp; +use Zend\Mail\Transport\SmtpOptions; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for instantiating Mailer objects @@ -41,7 +44,7 @@ use Zend\ServiceManager\ServiceLocatorInterface; * * @codeCoverageIgnore */ -class Factory implements \Zend\ServiceManager\FactoryInterface +class Factory implements FactoryInterface { /** * Build the mail transport object. @@ -83,18 +86,34 @@ class Factory implements \Zend\ServiceManager\FactoryInterface } /** - * Create service + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * - * @param ServiceLocatorInterface $sm Service manager + * @return object * - * @return mixed + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs */ - public function createService(ServiceLocatorInterface $sm) - { + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + // Load configurations: - $config = $sm->get('VuFind\Config')->get('config'); + $config = $container->get('VuFind\Config\PluginManager')->get('config'); // Create service: - return new \VuFind\Mailer\Mailer($this->getTransport($config)); + $class = new $requestedName($this->getTransport($config)); + if (!empty($config->Mail->override_from)) { + $class->setFromAddressOverride($config->Mail->override_from); + } + return $class; } } diff --git a/module/VuFind/src/VuFind/Mailer/Mailer.php b/module/VuFind/src/VuFind/Mailer/Mailer.php index 333250be1ec84b54d49c9a58d7cf6d804cd41ec3..193cb5823cac9a835d16c8ae789d60cc6f8fe74c 100644 --- a/module/VuFind/src/VuFind/Mailer/Mailer.php +++ b/module/VuFind/src/VuFind/Mailer/Mailer.php @@ -2,7 +2,7 @@ /** * VuFind Mailer Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,11 +26,12 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Mailer; -use VuFind\Exception\Mail as MailException, - Zend\Mail\Address, - Zend\Mail\AddressList, - Zend\Mail\Message, - Zend\Mail\Header\ContentType; + +use VuFind\Exception\Mail as MailException; +use Zend\Mail\Address; +use Zend\Mail\AddressList; +use Zend\Mail\Header\ContentType; +use Zend\Mail\Message; /** * VuFind Mailer Class @@ -59,6 +60,13 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface */ protected $maxRecipients = 1; + /** + * "From" address override + * + * @var string + */ + protected $fromAddressOverride = ''; + /** * Constructor * @@ -138,26 +146,22 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface * @param string $subject Subject line for message * @param string $body Message body * @param string $cc CC recipient (null for none) + * @param string|Address|AddressList $replyTo Reply-To address (or delimited + * list, null for none) * * @throws MailException * @return void */ - public function send($to, $from, $subject, $body, $cc = null) + public function send($to, $from, $subject, $body, $cc = null, $replyTo = null) { - if ($to instanceof AddressList) { - $recipients = $to; - } else if ($to instanceof Address) { - $recipients = new AddressList(); - $recipients->add($to); - } else { - $recipients = $this->stringToAddressList($to); - } + $recipients = $this->convertToAddressList($to); + $replyTo = $this->convertToAddressList($replyTo); // Validate email addresses: - if ($this->maxRecipients > 0 - && $this->maxRecipients < count($recipients) - ) { - throw new MailException('Too Many Email Recipients'); + if ($this->maxRecipients > 0) { + if ($this->maxRecipients < count($recipients)) { + throw new MailException('Too Many Email Recipients'); + } } $validator = new \Zend\Validator\EmailAddress(); if (count($recipients) == 0) { @@ -168,11 +172,32 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface throw new MailException('Invalid Recipient Email Address'); } } + foreach ($replyTo as $current) { + if (!$validator->isValid($current->getEmail())) { + throw new MailException('Invalid Reply-To Email Address'); + } + } $fromEmail = ($from instanceof Address) ? $from->getEmail() : $from; if (!$validator->isValid($fromEmail)) { throw new MailException('Invalid Sender Email Address'); } + + if (!empty($this->fromAddressOverride) + && $this->fromAddressOverride != $fromEmail + ) { + $replyTo->add($fromEmail); + if (!($from instanceof Address)) { + $from = new Address($from); + } + $name = $from->getName(); + if (!$name) { + list($fromPre) = explode('@', $from->getEmail()); + $name = $fromPre ? $fromPre : null; + } + $from = new Address($this->fromAddressOverride, $name); + } + // Convert all exceptions thrown by mailer into MailException objects: try { // Send message @@ -180,11 +205,13 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface ->addFrom($from) ->addTo($recipients) ->setBody($body) - ->setSubject($subject) - ->setReplyTo($from); + ->setSubject($subject); if ($cc !== null) { $message->addCc($cc); } + if ($replyTo) { + $message->addReplyTo($replyTo); + } $this->getTransport()->send($message); } catch (\Exception $e) { throw new MailException($e->getMessage()); @@ -203,12 +230,14 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface * email templates) * @param string $subject Subject for email (optional) * @param string $cc CC recipient (null for none) + * @param string|Address|AddressList $replyTo Reply-To address (or delimited + * list, null for none) * * @throws MailException * @return void */ public function sendLink($to, $from, $msg, $url, $view, $subject = null, - $cc = null + $cc = null, $replyTo = null ) { if (null === $subject) { $subject = $this->getDefaultLinkSubject(); @@ -219,7 +248,7 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface 'msgUrl' => $url, 'to' => $to, 'from' => $from, 'message' => $msg ] ); - return $this->send($to, $from, $subject, $body, $cc); + return $this->send($to, $from, $subject, $body, $cc, $replyTo); } /** @@ -245,12 +274,14 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface * email templates) * @param string $subject Subject for email (optional) * @param string $cc CC recipient (null for none) + * @param string|Address|AddressList $replyTo Reply-To address (or + * delimited list, null for none) * * @throws MailException * @return void */ public function sendRecord($to, $from, $msg, $record, $view, $subject = null, - $cc = null + $cc = null, $replyTo = null ) { if (null === $subject) { $subject = $this->getDefaultRecordSubject($record); @@ -261,7 +292,7 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface 'driver' => $record, 'to' => $to, 'from' => $from, 'message' => $msg ] ); - return $this->send($to, $from, $subject, $body, $cc); + return $this->send($to, $from, $subject, $body, $cc, $replyTo); } /** @@ -288,4 +319,46 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface return $this->translate('Library Catalog Record') . ': ' . $record->getBreadcrumb(); } + + /** + * Get the "From" address override value + * + * @return string + */ + public function getFromAddressOverride() + { + return $this->fromAddressOverride; + } + + /** + * Set the "From" address override + * + * @param string $address "From" address + * + * @return void + */ + public function setFromAddressOverride($address) + { + $this->fromAddressOverride = $address; + } + + /** + * Convert the given addresses to an AddressList object + * + * @param string|Address|AddressList $addresses Addresses + * + * @return AddressList + */ + protected function convertToAddressList($addresses) + { + if ($addresses instanceof AddressList) { + $result = $addresses; + } elseif ($addresses instanceof Address) { + $result = new AddressList(); + $result->add($addresses); + } else { + $result = $this->stringToAddressList($addresses ? $addresses : ''); + } + return $result; + } } diff --git a/module/VuFind/src/VuFind/Net/IpAddressUtils.php b/module/VuFind/src/VuFind/Net/IpAddressUtils.php index 5c997e6158b2eb9e523eb8b8d02c1be68714b021..2bbb411b6f1f03cb5f623345b9f5be28f38f39d4 100644 --- a/module/VuFind/src/VuFind/Net/IpAddressUtils.php +++ b/module/VuFind/src/VuFind/Net/IpAddressUtils.php @@ -2,7 +2,7 @@ /** * IP address utility functions. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * diff --git a/module/VuFind/src/VuFind/OAI/Server.php b/module/VuFind/src/VuFind/OAI/Server.php index 539cec5baa1d223a99cebcdc97007aceba64e923..db808589a5fe22ab4db0bf16b4317f43a1ca12aa 100644 --- a/module/VuFind/src/VuFind/OAI/Server.php +++ b/module/VuFind/src/VuFind/OAI/Server.php @@ -2,7 +2,7 @@ /** * OAI Server class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\OAI; -use SimpleXMLElement, - VuFind\Exception\RecordMissing as RecordMissingException, VuFind\SimpleXML; + +use SimpleXMLElement; +use VuFind\Exception\RecordMissing as RecordMissingException; +use VuFind\SimpleXML; /** * OAI Server class @@ -222,7 +224,7 @@ class Server if (!$this->hasParam('verb')) { return $this->showError('badVerb', 'Missing Verb Argument'); } else { - switch($this->params['verb']) { + switch ($this->params['verb']) { case 'GetRecord': return $this->getRecord(); case 'Identify': @@ -295,11 +297,12 @@ class Server * @param object $record A record driver object * @param string $format Metadata format to obtain (false for none) * @param bool $headerOnly Only attach the header? + * @param string $set Currently active set * * @return bool */ protected function attachNonDeleted($container, $record, $format, - $headerOnly = false + $headerOnly = false, $set = '' ) { // Get the XML (and display an error if it is unsupported): if ($format === false) { @@ -321,11 +324,14 @@ class Server // Check for sets: $fields = $record->getRawData(); - if (!is_null($this->setField) && !empty($fields[$this->setField])) { - $sets = $fields[$this->setField]; + if (null !== $this->setField && !empty($fields[$this->setField])) { + $sets = (array)$fields[$this->setField]; } else { $sets = []; } + if (!empty($set)) { + $sets = array_unique(array_merge($sets, [$set])); + } // Get modification date: $date = $record->getLastIndexed(); @@ -402,7 +408,7 @@ class Server */ protected function hasParam($param) { - return (isset($this->params[$param]) && !empty($this->params[$param])); + return isset($this->params[$param]) && !empty($this->params[$param]); } /** @@ -596,14 +602,14 @@ class Server $solrLimit = ($params['cursor'] + $this->pageSize) - $currentCursor; // Get non-deleted records from the Solr index: + $set = $params['set'] ?? ''; $result = $this->listRecordsGetNonDeleted( - $from, $until, $solrOffset, $solrLimit, - isset($params['set']) ? $params['set'] : '' + $from, $until, $solrOffset, $solrLimit, $set ); $nonDeletedCount = $result->getResultTotal(); $format = $params['metadataPrefix']; foreach ($result->getResults() as $doc) { - if (!$this->attachNonDeleted($xml, $doc, $format, $headersOnly)) { + if (!$this->attachNonDeleted($xml, $doc, $format, $headersOnly, $set)) { $this->unexpectedError('Cannot load document'); } $currentCursor++; @@ -613,7 +619,7 @@ class Server $listSize = $deletedCount + $nonDeletedCount; if ($listSize > $currentCursor) { $this->saveResumptionToken($xml, $params, $currentCursor, $listSize); - } else if ($solrOffset > 0) { + } elseif ($solrOffset > 0) { // If we reached the end of the list but there is more than one page, we // still need to display an empty <resumptionToken> tag: $token = $xml->addChild('resumptionToken'); @@ -730,10 +736,10 @@ class Server // Apply filters as needed. if (!empty($set)) { if (isset($this->setQueries[$set])) { - // use hidden filter here to allow for complex queries; - // plain old addFilter expects simple field:value queries. - $params->addHiddenFilter($this->setQueries[$set]); - } else if (null !== $this->setField) { + // Put parentheses around the query so that it does not get + // parsed as a simple field:value filter. + $params->addFilter('(' . $this->setQueries[$set] . ')'); + } elseif (null !== $this->setField) { $params->addFilter( $this->setField . ':"' . addcslashes($set, '"') . '"' ); @@ -841,7 +847,7 @@ class Server } else { return true; } - } else if (strpos($until, 'T') && strpos($until, 'Z')) { + } elseif (strpos($until, 'T') && strpos($until, 'Z')) { return true; } diff --git a/module/VuFind/src/VuFind/OAI/Server/Auth.php b/module/VuFind/src/VuFind/OAI/Server/Auth.php index 826bf57f8b5853ce41a2f2340cd5246a01af6899..4211191e688c809df0e0ce6b1909db1a1a1c3142 100644 --- a/module/VuFind/src/VuFind/OAI/Server/Auth.php +++ b/module/VuFind/src/VuFind/OAI/Server/Auth.php @@ -2,7 +2,7 @@ /** * OAI Server class for Authority core * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\OAI\Server; + use VuFind\OAI\Server as Base; /** diff --git a/module/VuFind/src/VuFind/QRCode/Loader.php b/module/VuFind/src/VuFind/QRCode/Loader.php index b6dd7667e353dfbbad61bd8c44dac6fd00d7ee5c..56a5b6d2d81870e838b1af2f5425fdddf5113d18 100644 --- a/module/VuFind/src/VuFind/QRCode/Loader.php +++ b/module/VuFind/src/VuFind/QRCode/Loader.php @@ -2,7 +2,7 @@ /** * QR Code Generator * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -28,7 +28,9 @@ * @link https://vufind.org/wiki/configuration:external_content Wiki */ namespace VuFind\QRCode; -use PHPQRCode; + +use Endroid\QrCode\ErrorCorrectionLevel; +use Endroid\QrCode\QrCode; /** * QR Code Generator @@ -44,27 +46,11 @@ use PHPQRCode; class Loader extends \VuFind\ImageLoader { /** - * VuFind configuration settings - * - * @var \Zend\Config\Config - */ - protected $config; - - /** - * The text used to generate the QRCode - * - * @var string - */ - protected $text = null; - - /** - * The params used to generate the QRCode + * The default params used to generate the QRCode * * @var string */ - protected $params = [ - 'level' => "L", 'size' => "3", 'margin' => "4" - ]; + protected $defaultParams = ['level' => 'L', 'size' => '3', 'margin' => '4']; /** * Constructor @@ -74,48 +60,123 @@ class Loader extends \VuFind\ImageLoader */ public function __construct($config, \VuFindTheme\ThemeInfo $theme) { - $this->config = $config; $this->setThemeInfo($theme); $this->configuredFailImage - = isset($this->config->QRCode->noQRCodeAvailableImage) - ? $this->config->QRCode->noQRCodeAvailableImage : null; + = $config->QRCode->noQRCodeAvailableImage ?? null; $this->defaultFailImage = 'images/noQRCode.gif'; } + /** + * Get default parameters. + * + * @return array + */ + public function getDefaults() + { + return $this->defaultParams; + } + /** * Set up a QR code image * - * @param string $text The QR code text - * @param array $params QR code parameters (level/size/margin) + * @param string $text The QR code text + * @param array $rawParams QR code parameters (level/size/margin) * * @return void */ - public function loadQRCode($text, - $params = ['level' => "L", 'size' => "3", 'margin' => "4"] - ) { + public function loadQRCode($text, $rawParams = []) + { + // Fill in defaults: + $params = $rawParams + $this->defaultParams; + + // Normalize parameters; when the size setting is less than 30 pixels, + // do some math to try to map old PHPQRCode-style settings to new + // Endroid\QrCode equivalents. When the size setting is 30 or higher, + // treat 'size' and 'margin' as literal pixel sizes. + $margin = $params['margin']; + $level = $this->mapErrorLevel($params['level']); + if ($params['size'] < 30) { + // In the old system, the margin was multiplied by the size.... + $margin *= $params['size']; + + // Do some magic math to adjust the QR code size to accommodate the + // length of the text and the quality level. This is probably not the + // smartest way to do this, but it seems good enough for VuFind's + // limited needs. + $sizeIncrement = ceil(ceil(sqrt(strlen($text))) / 10); + if ($level === ErrorCorrectionLevel::HIGH) { + $sizeIncrement *= 38; + } elseif ($level === ErrorCorrectionLevel::QUARTILE) { + $sizeIncrement *= 34; + } else { + $sizeIncrement *= 30; + } + + // Put it all together: + $size = $params['size'] * $sizeIncrement - $params['margin']; + } else { + $size = $params['size']; + } + // Sanitize parameters: - $this->text = $text; - $this->params = $params; - if (!$this->fetchQRCode()) { + if (!$this->fetchQRCode($text, $size, $margin, $level)) { $this->loadUnavailable(); } } + /** + * Map an incoming error correction level parameter to a valid constant. + * + * @param string $level Error correction level parameter + * + * @return string + */ + protected function mapErrorLevel($level) + { + switch (strtoupper(substr($level, 0, 1))) { + case '3': + case 'H': + return ErrorCorrectionLevel::HIGH; + case '2': + case 'Q': + return ErrorCorrectionLevel::QUARTILE; + case '1': + case 'M': + return ErrorCorrectionLevel::MEDIUM; + case '0': + case 'L': + default: + return ErrorCorrectionLevel::LOW; + } + } + /** * Generate a QR code image * + * @param string $text The QR code text + * @param int $size QR code width / height (in pixels) + * @param int $margin QR code margin (in pixels) + * @param string $level Error correction level constant + * * @return bool True if image displayed, false on failure. */ - protected function fetchQRCode() + protected function fetchQRCode($text, $size, $margin, $level) { - if (empty($this->text)) { + if (strlen(trim($text)) == 0) { return false; } - $this->contentType = 'image/png'; - $this->image = PHPQRCode\QRcode::PNG( - $this->text, false, - $this->params['level'], $this->params['size'], $this->params['margin'] - ); + + // Build the code: + $code = new QrCode($text); + $code->setWriterByName('png'); + $code->setMargin($margin); + $code->setErrorCorrectionLevel($level); + $code->setSize($size); + $code->setEncoding('UTF-8'); + + // Save the values. + $this->contentType = $code->getContentType(); + $this->image = $code->writeString(); return true; } } diff --git a/module/VuFind/src/VuFind/QRCode/LoaderFactory.php b/module/VuFind/src/VuFind/QRCode/LoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..67d5db90f84b7016fd88f6a0da36adb24524365f --- /dev/null +++ b/module/VuFind/src/VuFind/QRCode/LoaderFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for QR Code Generator + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package QRCode_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\QRCode; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for QR Code Generator + * + * @category VuFind + * @package QRCode_Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class LoaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('config'), + $container->get('VuFindTheme\ThemeInfo') + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/AbstractFacets.php b/module/VuFind/src/VuFind/Recommend/AbstractFacets.php index 4dbca413a88096adeeb483e8a825d455c77a7a11..200378721ed2936236b56386b04b2cfa2e4e3ce8 100644 --- a/module/VuFind/src/VuFind/Recommend/AbstractFacets.php +++ b/module/VuFind/src/VuFind/Recommend/AbstractFacets.php @@ -2,7 +2,7 @@ /** * SideFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use Zend\Config\Config; /** diff --git a/module/VuFind/src/VuFind/Recommend/AbstractResultsPassthrough.php b/module/VuFind/src/VuFind/Recommend/AbstractResultsPassthrough.php index 96ad5c783bddb29ecdac0678d4964b0a830bf4d2..a199edf2b06773db6fe3f058d73b028c3b048ddb 100644 --- a/module/VuFind/src/VuFind/Recommend/AbstractResultsPassthrough.php +++ b/module/VuFind/src/VuFind/Recommend/AbstractResultsPassthrough.php @@ -3,7 +3,7 @@ * Simple abstract recommendation module that simply passes the Results object * through to the template. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommend.php b/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommend.php index 8afb149ca35d470390e634c47a1e5dc0c550097f..28447eb245d092719e9b64d9a813e785ef1462cc 100644 --- a/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommend.php +++ b/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommend.php @@ -2,7 +2,7 @@ /** * Abstract base class for pulling Summon-specific recommendations. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php b/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php index b40561eafbfc3beefcfcd9b48acd0cfc5b8d817a..b71764cce7bbe947a7e5429953369efb94fafb25 100644 --- a/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php @@ -2,7 +2,7 @@ /** * Abstract base for deferred-load Summon recommendations modules * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -113,7 +113,7 @@ class AbstractSummonRecommendDeferred implements RecommendInterface // with a blank string, so we can rebuild the parameters to pass through // AJAX later on! for ($i = 0; $i < $this->paramCount; $i++) { - $settings[$i] = isset($settings[$i]) ? $settings[$i] : ''; + $settings[$i] = $settings[$i] ?? ''; } // Collect the best possible search term(s): diff --git a/module/VuFind/src/VuFind/Recommend/AlphaBrowseLink.php b/module/VuFind/src/VuFind/Recommend/AlphaBrowseLink.php index f6dcb1532061a49ecda3931b1b38269ac7ed88d4..8d7278fbeaa20d8ebcfe31c20652b0feb6d560bd 100644 --- a/module/VuFind/src/VuFind/Recommend/AlphaBrowseLink.php +++ b/module/VuFind/src/VuFind/Recommend/AlphaBrowseLink.php @@ -2,7 +2,7 @@ /** * AlphaBrowseLink Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/AuthorFacets.php b/module/VuFind/src/VuFind/Recommend/AuthorFacets.php index 7fee194aa87c8fd669e075cce3418ba23131770f..ffee275ea4e97cc4305ba552afddd28b921307d7 100644 --- a/module/VuFind/src/VuFind/Recommend/AuthorFacets.php +++ b/module/VuFind/src/VuFind/Recommend/AuthorFacets.php @@ -2,7 +2,7 @@ /** * AuthorFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,8 +27,10 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use VuFindSearch\Query\Query; -use Zend\Http\Request, Zend\StdLib\Parameters; +use Zend\Http\Request; +use Zend\StdLib\Parameters; /** * AuthorFacets Recommendations Module diff --git a/module/VuFind/src/VuFind/Recommend/AuthorInfo.php b/module/VuFind/src/VuFind/Recommend/AuthorInfo.php index 020823d06c8d9871e9fe609fed77432a984ccdd0..779f100b84a0675869ca40162427f72f90d85b94 100644 --- a/module/VuFind/src/VuFind/Recommend/AuthorInfo.php +++ b/module/VuFind/src/VuFind/Recommend/AuthorInfo.php @@ -2,7 +2,7 @@ /** * AuthorInfo Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use VuFind\Connection\Wikipedia; use VuFind\I18n\Translator\TranslatorAwareInterface; use VuFindSearch\Query\Query; diff --git a/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php b/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php index 8f9b57e403ad9732d1a5c7b1951dda83abfc2fcf..9928edea8552ec7cccaaec258c4e909ad4ade236 100644 --- a/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php +++ b/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php @@ -2,7 +2,7 @@ /** * AuthorityRecommend Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2012. * @@ -27,8 +27,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Recommend; -use VuFindSearch\Backend\Exception\RequestErrorException, - Zend\Http\Request, Zend\StdLib\Parameters; + +use VuFindSearch\Backend\Exception\RequestErrorException; +use Zend\StdLib\Parameters; /** * AuthorityRecommend Module @@ -92,6 +93,13 @@ class AuthorityRecommend implements RecommendInterface */ protected $resultsManager; + /** + * Which lookup mode(s) to use. + * + * @var string + */ + protected $mode = '*'; + /** * Constructor * @@ -116,6 +124,8 @@ class AuthorityRecommend implements RecommendInterface if (isset($params[$i + 1])) { if ($params[$i] == '__resultlimit__') { $this->resultLimit = intval($params[$i + 1]); + } elseif ($params[$i] == '__mode__') { + $this->mode = strtolower($params[$i + 1]); } else { $this->filters[] = $params[$i] . ':' . $params[$i + 1]; } @@ -134,6 +144,8 @@ class AuthorityRecommend implements RecommendInterface * request. * * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function init($params, $request) { @@ -141,6 +153,112 @@ class AuthorityRecommend implements RecommendInterface $this->lookfor = $request->get('lookfor'); } + /** + * Perform a search of the authority index. + * + * @param array $params Array of request parameters. + * + * @return array + */ + protected function performSearch($params) + { + // Initialise and process search (ignore Solr errors -- no reason to fail + // just because search syntax is not compatible with Authority core): + try { + $authResults = $this->resultsManager->get('SolrAuth'); + $authParams = $authResults->getParams(); + $authParams->initFromRequest(new Parameters($params)); + foreach ($this->filters as $filter) { + $authParams->addHiddenFilter($filter); + } + return $authResults->getResults(); + } catch (RequestErrorException $e) { + return []; + } + } + + /** + * Return true if $a and $b are similar enough to represent the same heading. + * + * @param string $a First string to compare + * @param string $b Second string to compare + * + * @return bool + */ + protected function fuzzyCompare($a, $b) + { + $normalize = function ($str) { + return trim(strtolower(preg_replace('/\W/', '', $str))); + }; + return $normalize($a) == $normalize($b); + } + + /** + * Add main headings from records that match search terms on use_for/see_also. + * + * @return void + */ + protected function addUseForHeadings() + { + // Build an advanced search request that prevents Solr from retrieving + // records that would already have been retrieved by a search of the biblio + // core, i.e. it only returns results where $lookfor IS found in in the + // "Heading" search and IS NOT found in the "MainHeading" search defined + // in authsearchspecs.yaml. + $params = [ + 'join' => 'AND', + 'bool0' => ['AND'], + 'lookfor0' => [$this->lookfor], + 'type0' => ['Heading'], + 'bool1' => ['NOT'], + 'lookfor1' => [$this->lookfor], + 'type1' => ['MainHeading'] + ]; + + // loop through records and assign id and headings to separate arrays defined + // above + foreach ($this->performSearch($params) as $result) { + $this->recommendations[] = $result->getBreadcrumb(); + } + } + + /** + * Add "see also" headings from records that match search terms on main heading. + * + * @return void + */ + protected function addSeeAlsoReferences() + { + // Build a simple "MainHeading" search. + $params = [ + 'lookfor' => [$this->lookfor], + 'type' => ['MainHeading'] + ]; + + // loop through records and assign id and headings to separate arrays defined + // above + foreach ($this->performSearch($params) as $result) { + foreach ($result->getSeeAlso() as $seeAlso) { + // check for duplicates before adding record to recordSet + if (!$this->fuzzyCompare($seeAlso, $this->lookfor)) { + $this->recommendations[] = $seeAlso; + } + } + } + } + + /** + * Is the specified mode configured to be active? + * + * @param string $mode Mode to check + * + * @return bool + */ + protected function isModeActive($mode) + { + return $this->mode === '*' || strpos($this->mode, $mode) !== false; + } + /** * Called after the Search Results object has performed its main search. This * may be used to extract necessary information from the Search Results object @@ -166,52 +284,14 @@ class AuthorityRecommend implements RecommendInterface return; } - // Build an advanced search request that prevents Solr from retrieving - // records that would already have been retrieved by a search of the biblio - // core, i.e. it only returns results where $lookfor IS found in in the - // "Heading" search and IS NOT found in the "MainHeading" search defined - // in authsearchspecs.yaml. - $request = new Parameters( - [ - 'join' => 'AND', - 'bool0' => ['AND'], - 'lookfor0' => [$this->lookfor], - 'type0' => ['Heading'], - 'bool1' => ['NOT'], - 'lookfor1' => [$this->lookfor], - 'type1' => ['MainHeading'] - ] - ); - - // Initialise and process search (ignore Solr errors -- no reason to fail - // just because search syntax is not compatible with Authority core): - try { - $authResults = $this->resultsManager->get('SolrAuth'); - $authParams = $authResults->getParams(); - $authParams->initFromRequest($request); - foreach ($this->filters as $filter) { - $authParams->addHiddenFilter($filter); - } - $results = $authResults->getResults(); - } catch (RequestErrorException $e) { - return; + // see if we can add main headings matching use_for/see_also fields... + if ($this->isModeActive('usefor')) { + $this->addUseForHeadings(); } - // loop through records and assign id and headings to separate arrays defined - // above - foreach ($results as $result) { - // Extract relevant details: - $recordArray = [ - 'id' => $result->getUniqueID(), - 'heading' => $result->getBreadcrumb() - ]; - - // check for duplicates before adding record to recordSet - if (!$this->inArrayR($recordArray['heading'], $this->recommendations)) { - array_push($this->recommendations, $recordArray); - } else { - continue; - } + // see if we can add see-also references associated with main headings... + if ($this->isModeActive('seealso')) { + $this->addSeeAlsoReferences(); } } @@ -222,7 +302,7 @@ class AuthorityRecommend implements RecommendInterface */ public function getRecommendations() { - return $this->recommendations; + return array_unique($this->recommendations); } /** @@ -234,26 +314,4 @@ class AuthorityRecommend implements RecommendInterface { return $this->results; } - - /** - * Helper function to do recursive searches of multi-dimensional arrays. - * - * @param string $needle Search term - * @param array $haystack Multi-dimensional array - * - * @return bool - */ - protected function inArrayR($needle, $haystack) - { - foreach ($haystack as $v) { - if ($needle == $v) { - return true; - } elseif (is_array($v)) { - if ($this->inArrayR($needle, $v)) { - return true; - } - } - } - return false; - } } diff --git a/module/VuFind/src/VuFind/Recommend/CatalogResults.php b/module/VuFind/src/VuFind/Recommend/CatalogResults.php index 30db19bfbbe590e956e03f800981a7e5a60a5f49..ca7f1dce24b7ffa827b452ebd7e5e2c80bf3b62c 100644 --- a/module/VuFind/src/VuFind/Recommend/CatalogResults.php +++ b/module/VuFind/src/VuFind/Recommend/CatalogResults.php @@ -2,7 +2,7 @@ /** * CatalogResults Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/Channels.php b/module/VuFind/src/VuFind/Recommend/Channels.php index 264f09c216f3a21802243c3b15fda6ec3f544631..8836a5bb55a1ecc295d836b85b82fc639b0379c9 100644 --- a/module/VuFind/src/VuFind/Recommend/Channels.php +++ b/module/VuFind/src/VuFind/Recommend/Channels.php @@ -2,7 +2,7 @@ /** * Channels Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFind/src/VuFind/Recommend/CollectionSideFacets.php b/module/VuFind/src/VuFind/Recommend/CollectionSideFacets.php index b92fdcc1200825ffef89174bbbe6dec097a856fe..e17bc7f1aa0d32ab10c65be2e51ea2b92c710dc1 100644 --- a/module/VuFind/src/VuFind/Recommend/CollectionSideFacets.php +++ b/module/VuFind/src/VuFind/Recommend/CollectionSideFacets.php @@ -2,7 +2,7 @@ /** * CollectionSideFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/DOI.php b/module/VuFind/src/VuFind/Recommend/DOI.php index f282a25dc5584a14710c368a8d32d901104af85f..4c72b06375e5a30c8fa2892294965a45f1d5e075 100644 --- a/module/VuFind/src/VuFind/Recommend/DOI.php +++ b/module/VuFind/src/VuFind/Recommend/DOI.php @@ -2,7 +2,7 @@ /** * DOI Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -104,7 +104,7 @@ class DOI implements RecommendInterface { $query = $results->getParams()->getDisplayQuery(); preg_match('|10\.[^\s/]{4,}/[^\s]{1,}|', $query, $matches); - $this->match = isset($matches[0]) ? $matches[0] : null; + $this->match = $matches[0] ?? null; $this->exact = (null !== $this->match) && ($this->match === $query); } diff --git a/module/VuFind/src/VuFind/Recommend/DPLATerms.php b/module/VuFind/src/VuFind/Recommend/DPLATerms.php index 0e89abae4232f0c4807cc9f9121e72119d182324..34404018ee61c3fca05d519e9a30d98f0fbdd2a8 100644 --- a/module/VuFind/src/VuFind/Recommend/DPLATerms.php +++ b/module/VuFind/src/VuFind/Recommend/DPLATerms.php @@ -2,7 +2,7 @@ /** * DPLATerms Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; -use Zend\Http\Client as HttpClient, - Zend\Http\Client\Adapter\Exception\TimeoutException; + +use Zend\Http\Client\Adapter\Exception\TimeoutException; +use Zend\Http\Client as HttpClient; /** * DPLATerms Recommendations Module diff --git a/module/VuFind/src/VuFind/Recommend/Deprecated.php b/module/VuFind/src/VuFind/Recommend/Deprecated.php index 0fa9eb5b6e562d391834636c7eea059a7aa8b772..e8a98a3fd4b86eeebf462dffb173f35a5d4fa9af 100644 --- a/module/VuFind/src/VuFind/Recommend/Deprecated.php +++ b/module/VuFind/src/VuFind/Recommend/Deprecated.php @@ -3,7 +3,7 @@ * Deprecated Recommendations Module - used to replace legacy modules that no * longer function due to, for example, external APIs that have been shut down. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * diff --git a/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php b/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php index c906684f38e42b5f353b557449857a24959f3ac5..f163c352dd59c5e01411561b7cd7ae83ea956f41 100644 --- a/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php +++ b/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php @@ -2,7 +2,7 @@ /** * EuropeanaResults Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use Zend\Feed\Reader\Reader as FeedReader; /** diff --git a/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php b/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php index 768335db2a7462775525d4d87a2c9e3629b4a89d..9be50bdf5fa6af55677cdd3ab85c60b964951c64 100644 --- a/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php @@ -2,7 +2,7 @@ /** * EuropeanaResultsDeferred Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -96,7 +96,7 @@ class EuropeanaResultsDeferred implements RecommendInterface // with a blank string, so we can rebuild the parameters to pass through // AJAX later on! for ($i = 0; $i < 4; $i++) { - $settings[$i] = isset($settings[$i]) ? $settings[$i] : ''; + $settings[$i] = $settings[$i] ?? ''; } // Collect the best possible search term(s): diff --git a/module/VuFind/src/VuFind/Recommend/ExpandFacets.php b/module/VuFind/src/VuFind/Recommend/ExpandFacets.php index 19c310a24852a2802674ea85bad19d1a044f91eb..aaa091ffa374997931be57ee6fdba50788505c91 100644 --- a/module/VuFind/src/VuFind/Recommend/ExpandFacets.php +++ b/module/VuFind/src/VuFind/Recommend/ExpandFacets.php @@ -2,7 +2,7 @@ /** * ExpandFacets Module Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -102,7 +102,7 @@ class ExpandFacets implements RecommendInterface // Parse the additional settings: $settings = explode(':', $settings); $mainSection = empty($settings[0]) ? 'Results' : $settings[0]; - $iniName = isset($settings[1]) ? $settings[1] : 'facets'; + $iniName = $settings[1] ?? 'facets'; // Load the desired facet information... $config = $this->configLoader->get($iniName); diff --git a/module/VuFind/src/VuFind/Recommend/FacetCloud.php b/module/VuFind/src/VuFind/Recommend/FacetCloud.php index 7f99a2de256aa04c63594653e955dded81505236..4693f7eaa5cec2362379be56765edb81dbd4be21 100644 --- a/module/VuFind/src/VuFind/Recommend/FacetCloud.php +++ b/module/VuFind/src/VuFind/Recommend/FacetCloud.php @@ -2,7 +2,7 @@ /** * FacetCloud Recommendations Module * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Recommend/Factory.php b/module/VuFind/src/VuFind/Recommend/Factory.php index 908e115fc76a9e5d54334b611615dd92b5a5aa75..8027246e0c331978c840b02fe5b659e43022e45a 100644 --- a/module/VuFind/src/VuFind/Recommend/Factory.php +++ b/module/VuFind/src/VuFind/Recommend/Factory.php @@ -2,7 +2,7 @@ /** * Recommendation Module Factory Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ namespace VuFind\Recommend; + use Zend\ServiceManager\ServiceManager; /** @@ -51,7 +52,7 @@ class Factory public static function getAuthorFacets(ServiceManager $sm) { return new AuthorFacets( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Search\Results\PluginManager') ); } @@ -64,10 +65,10 @@ class Factory */ public static function getAuthorInfo(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); return new AuthorInfo( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager'), - $sm->getServiceLocator()->get('VuFind\Http')->createClient(), + $sm->get('VuFind\Search\Results\PluginManager'), + $sm->get('VuFindHttp\HttpService')->createClient(), isset($config->Content->authors) ? $config->Content->authors : '' ); } @@ -82,7 +83,7 @@ class Factory public static function getAuthorityRecommend(ServiceManager $sm) { return new AuthorityRecommend( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Search\Results\PluginManager') ); } @@ -95,9 +96,7 @@ class Factory */ public static function getCatalogResults(ServiceManager $sm) { - return new CatalogResults( - $sm->getServiceLocator()->get('VuFind\SearchRunner') - ); + return new CatalogResults($sm->get('VuFind\Search\SearchRunner')); } /** @@ -110,8 +109,8 @@ class Factory public static function getCollectionSideFacets(ServiceManager $sm) { return new CollectionSideFacets( - $sm->getServiceLocator()->get('VuFind\Config'), - $sm->getServiceLocator()->get('VuFind\HierarchicalFacetHelper') + $sm->get('VuFind\Config\PluginManager'), + $sm->get('VuFind\Search\Solr\HierarchicalFacetHelper') ); } @@ -124,13 +123,13 @@ class Factory */ public static function getDPLATerms(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); if (!isset($config->DPLA->apiKey)) { throw new \Exception('DPLA API key missing from configuration.'); } return new DPLATerms( $config->DPLA->apiKey, - $sm->getServiceLocator()->get('VuFind\Http')->createClient() + $sm->get('VuFindHttp\HttpService')->createClient() ); } @@ -143,7 +142,7 @@ class Factory */ public static function getEuropeanaResults(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); return new EuropeanaResults( $config->Content->europeanaAPI ); @@ -159,8 +158,8 @@ class Factory public static function getExpandFacets(ServiceManager $sm) { return new ExpandFacets( - $sm->getServiceLocator()->get('VuFind\Config'), - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Config\PluginManager'), + $sm->get('VuFind\Search\Results\PluginManager') ->get('Solr') ); } @@ -174,11 +173,10 @@ class Factory */ public static function getFavoriteFacets(ServiceManager $sm) { - $parentSm = $sm->getServiceLocator(); return new FavoriteFacets( - $parentSm->get('VuFind\Config'), + $sm->get('VuFind\Config\PluginManager'), null, - $parentSm->get('VuFind\AccountCapabilities')->getTagSetting() + $sm->get('VuFind\Config\AccountCapabilities')->getTagSetting() ); } @@ -191,10 +189,18 @@ class Factory */ public function getMapSelection(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('Vufind\Config'); - $backend = $sm->getServiceLocator()->get('VuFind\Search\BackendManager'); + $backend = $sm->get('VuFind\Search\BackendManager'); $solr = $backend->get('Solr'); - return new MapSelection($config, $solr); + + // add basemap options + $basemapConfig = $sm->get('VuFind\GeoFeatures\BasemapConfig'); + $basemapOptions = $basemapConfig->getBasemap('MapSelection'); + + // get MapSelection options + $mapSelectionConfig = $sm->get('VuFind\GeoFeatures\MapSelectionConfig'); + $mapSelectionOptions = $mapSelectionConfig->getMapSelectionOptions(); + + return new MapSelection($solr, $basemapOptions, $mapSelectionOptions); } /** @@ -207,8 +213,8 @@ class Factory public static function getRandomRecommend(ServiceManager $sm) { return new RandomRecommend( - $sm->getServiceLocator()->get('VuFind\Search'), - $sm->getServiceLocator()->get('VuFind\SearchParamsPluginManager') + $sm->get('VuFindSearch\Service'), + $sm->get('VuFind\Search\Params\PluginManager') ); } @@ -222,8 +228,8 @@ class Factory public static function getSideFacets(ServiceManager $sm) { return new SideFacets( - $sm->getServiceLocator()->get('VuFind\Config'), - $sm->getServiceLocator()->get('VuFind\HierarchicalFacetHelper') + $sm->get('VuFind\Config\PluginManager'), + $sm->get('VuFind\Search\Solr\HierarchicalFacetHelper') ); } @@ -237,7 +243,7 @@ class Factory public static function getSummonBestBets(ServiceManager $sm) { return new SummonBestBets( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Search\Results\PluginManager') ); } @@ -251,7 +257,7 @@ class Factory public static function getSummonDatabases(ServiceManager $sm) { return new SummonDatabases( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Search\Results\PluginManager') ); } @@ -264,9 +270,7 @@ class Factory */ public static function getSummonResults(ServiceManager $sm) { - return new SummonResults( - $sm->getServiceLocator()->get('VuFind\SearchRunner') - ); + return new SummonResults($sm->get('VuFind\Search\SearchRunner')); } /** @@ -279,7 +283,7 @@ class Factory public static function getSummonTopics(ServiceManager $sm) { return new SummonTopics( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') + $sm->get('VuFind\Search\Results\PluginManager') ); } @@ -293,7 +297,7 @@ class Factory public static function getSwitchQuery(ServiceManager $sm) { return new SwitchQuery( - $sm->getServiceLocator()->get('VuFind\Search\BackendManager') + $sm->get('VuFind\Search\BackendManager') ); } @@ -307,7 +311,7 @@ class Factory public static function getTopFacets(ServiceManager $sm) { return new TopFacets( - $sm->getServiceLocator()->get('VuFind\Config') + $sm->get('VuFind\Config\PluginManager') ); } @@ -321,7 +325,7 @@ class Factory public static function getVisualFacets(ServiceManager $sm) { return new VisualFacets( - $sm->getServiceLocator()->get('VuFind\Config') + $sm->get('VuFind\Config\PluginManager') ); } @@ -334,7 +338,7 @@ class Factory */ public static function getWebResults(ServiceManager $sm) { - return new WebResults($sm->getServiceLocator()->get('VuFind\SearchRunner')); + return new WebResults($sm->get('VuFind\Search\SearchRunner')); } /** @@ -346,8 +350,6 @@ class Factory */ public static function getWorldCatIdentities(ServiceManager $sm) { - return new WorldCatIdentities( - $sm->getServiceLocator()->get('VuFind\WorldCatUtils') - ); + return new WorldCatIdentities($sm->get('VuFind\Connection\WorldCatUtils')); } } diff --git a/module/VuFind/src/VuFind/Recommend/FavoriteFacets.php b/module/VuFind/src/VuFind/Recommend/FavoriteFacets.php index 525264622d453569f63bb669f9b0157e26fa56ed..4d37fda26214cb8933c7c604c391ef5cee8d52d3 100644 --- a/module/VuFind/src/VuFind/Recommend/FavoriteFacets.php +++ b/module/VuFind/src/VuFind/Recommend/FavoriteFacets.php @@ -2,7 +2,7 @@ /** * FavoriteFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use VuFind\Search\Solr\HierarchicalFacetHelper; /** diff --git a/module/VuFind/src/VuFind/Recommend/Libraryh3lp.php b/module/VuFind/src/VuFind/Recommend/Libraryh3lp.php index f42319204cadbb5e4235bde2eddefab51687f17f..9d4c1faa8430b74cdd14589df7d663f4566ede4f 100644 --- a/module/VuFind/src/VuFind/Recommend/Libraryh3lp.php +++ b/module/VuFind/src/VuFind/Recommend/Libraryh3lp.php @@ -2,7 +2,7 @@ /** * Libraryh3lp Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -69,7 +69,7 @@ class Libraryh3lp implements RecommendInterface $this->chatId = $params[0] === 'user' ? $params[1] . '@libraryh3lp.com' // user : $params[1] . '@chat.libraryh3lp.com'; // queue - $this->skin = isset($params[2]) ? $params[2] : 11977; + $this->skin = $params[2] ?? 11977; } /** diff --git a/module/VuFind/src/VuFind/Recommend/MapSelection.php b/module/VuFind/src/VuFind/Recommend/MapSelection.php index a882f8b13556dd483dfda328707d44f54d68b5a3..219907c3b446c14188dea9b0a12fcb7954b98be1 100644 --- a/module/VuFind/src/VuFind/Recommend/MapSelection.php +++ b/module/VuFind/src/VuFind/Recommend/MapSelection.php @@ -2,7 +2,7 @@ /** * MapSelection Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,8 +38,18 @@ namespace VuFind\Recommend; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ -class MapSelection implements \VuFind\Recommend\RecommendInterface +class MapSelection implements \VuFind\Recommend\RecommendInterface, + \VuFind\I18n\Translator\TranslatorAwareInterface { + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Basemap configuration parameters + * + * @var array + */ + protected $basemapOptions = []; + /** * Default coordinates. Order is WENS * @@ -61,6 +71,13 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface */ protected $height; + /** + * Map Selection configuration options + * + * @var array + */ + protected $mapSelectionOptions = []; + /** * Selected coordinates * @@ -96,13 +113,6 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface */ protected $bboxSearchCoords = []; - /** - * Configuration loader - * - * @var \VuFind\Config\PluginManager - */ - protected $configLoader; - /** * Solr search loader * @@ -141,15 +151,17 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface /** * Constructor * - * @param \VuFind\Config\PluginManager $configLoader Configuration loader - * @param \VuFind\Search\BackendManager $solr Search interface + * @param \VuFind\Search\BackendManager $solr Search interface + * @param array $basemapOptions Basemap Options + * @param array $mapSelectionOptions Map Options */ - public function __construct(\VuFind\Config\PluginManager $configLoader, $solr) + public function __construct($solr, $basemapOptions, $mapSelectionOptions) { - $this->configLoader = $configLoader; $this->solr = $solr; $this->queryBuilder = $solr->getQueryBuilder(); $this->solrConnector = $solr->getConnector(); + $this->basemapOptions = $basemapOptions; + $this->mapSelectionOptions = $mapSelectionOptions; } /** @@ -163,21 +175,15 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface */ public function setConfig($settings) { - $settings = explode(':', $settings); - $mainSection = empty($settings[0]) ? 'MapSelection' : $settings[0]; - $iniFile = empty($settings[1]) ? 'searches' : $settings[1]; - $config = $this->configLoader->get($iniFile); - if (isset($config->$mainSection)) { - $entries = $config->$mainSection; - if (isset($entries->default_coordinates)) { - $this->defaultCoordinates = explode( - ',', $entries->default_coordinates - ); - } - if (isset($entries->height)) { - $this->height = $entries->height; - } - } + $basemapOptions = $this->basemapOptions; + $mapSelectionOptions = $this->mapSelectionOptions; + $this->defaultCoordinates = explode( + ',', + $mapSelectionOptions['default_coordinates'] + ); + $this->height = $mapSelectionOptions['height']; + $this->basemapUrl = $basemapOptions['basemap_url']; + $this->basemapAttribution = $basemapOptions['basemap_attribution']; } /** @@ -269,6 +275,19 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface return $this->defaultCoordinates; } + /** + * Get the basemap configuration settings. + * + * @return array + */ + public function getBasemap() + { + return [ + $this->basemapOptions['basemap_url'], + $this->basemapOptions['basemap_attribution'] + ]; + } + /** * GetHeight * @@ -322,6 +341,9 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface $params->set('rows', '10000000'); // set to return all results $response = json_decode($this->solrConnector->search($params)); foreach ($response->response->docs as $current) { + if (!isset($current->title)) { + $current->title = $this->translate('Title not available'); + } $result[] = [ $current->id, $current->{$this->geoField}, $current->title ]; @@ -330,289 +352,33 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface return $result; } - /** - * Convert coordinates to 360 degree grid - * - * @param array $coordinates coordinates for conversion - * - * @return array - */ - public function coordinatesToGrid($coordinates) - { - $gridCoords = []; - list($coordW, $coordE, $coordN, $coordS) = $coordinates; - if ($coordE == (float)-0) { - $coordE = (float)0; - } - // Convert coordinates to 360 degree grid - if ($coordE < $coordW && $coordE < 0) { - $coordE = 360 + $coordE; - } - if ($coordW < 0 && $coordW >= -180) { - $coordW = 360 + $coordW; - $coordE = 360 + $coordE; - } - $gridCoords = [$coordW, $coordE, $coordN, $coordS]; - return $gridCoords; - } - - /** - * Convert coordinates to longitude latitude grid - * - * @param array $centerPt coordinates for conversion - * - * @return array - */ - public function centerToLongLat($centerPt) - { - $LongLatCoords = []; - list($coordWE, $coordSN) = $centerPt; - // convert coordinate to 180 degree grid - if ($coordWE > 180) { - $coordWE = $coordWE - 360; - } - $LongLatCoords = [$coordWE, $coordSN]; - return $LongLatCoords; - } - - /** - * Calculated the center of search box and coordinate overlap - * - * @param array $bboxCoords search box coordinates - * @param array $coordinates coordinates for conversion - * - * @return array - */ - public function getCenterFromBboxCoordIntersect($bboxCoords, $coordinates) - { - $centerCoordBbox = []; - list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; - list($coordW, $coordE, $coordN, $coordS) = $coordinates; - $bboxLon = range(floor($bboxW), ceil($bboxE)); - $bboxLat = range(floor($bboxS), ceil($bboxN)); - $coordLon = range(floor($coordW), ceil($coordE)); - $coordLat = range(floor($coordS), ceil($coordN)); - $cbLon = array_intersect($coordLon, $bboxLon); - $cbLat = array_intersect($coordLat, $bboxLat); - $centerCoordBbox = [ - min($cbLon), max($cbLon), min($cbLat), max($cbLat) - ]; - return $centerCoordBbox; - } - - /** - * Check to see if coordinate and bbox intersect - * - * @param array $bboxCoords searchbox coordinates - * @param array $coordinate result record coordinates - * - * @return bool - */ - public function coordBboxIntersect($bboxCoords, $coordinate) - { - $coordIntersect = false; - list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; - list($coordW, $coordE, $coordN, $coordS) = $coordinate; - //Does coordinate fall within search box - if ((($coordW >= $bboxW && $coordW <= $bboxE) - || ($coordE >= $bboxW && $coordE <= $bboxE)) - && (($coordS >= $bboxS && $coordS <= $bboxN) - || ($coordN >= $bboxS && $coordN <= $bboxN)) - ) { - $coordIntersect = true; - } - // Does searchbox fall within coordinate - if ((($bboxW >= $coordW && $bboxW <= $coordE) - || ($bboxE >= $coordW && $bboxE <= $coordE)) - && (($bboxS >= $coordS && $bboxS <= $coordN) - || ($bboxN >= $coordS && $bboxN <= $coordN)) - ) { - $coordIntersect = true; - } - // Does searchbox span coordinate - if ((($coordE >= $bboxW && $coordE <= $bboxE) - && ($coordW >= $bboxW && $coordW <= $bboxE)) - && ($coordN > $bboxN && $coordS < $bboxS) - ) { - $coordIntersect = true; - } - // Does coordinate span searchbox - if (($coordW < $bboxW && $coordE > $bboxE) - && (($coordS >= $bboxS && $coordS <= $bboxN) - && ($coordN >= $bboxS && $coordN <= $bboxN)) - ) { - $coordIntersect = true; - } - return $coordIntersect; - } - - /** - * Calculate center point of coordinate set - * - * @param array $coordinate centerPoint coordinate - * - * @return array - */ - public function calculateCenterPoint($coordinate) - { - $centerCoord = []; - list($coordW, $coordE, $coordN, $coordS) = $coordinate; - // Calculate center point - $centerWE = (($coordW - $coordE) / 2) + $coordE; - $centerSN = (($coordN - $coordS) / 2) + $coordS; - // Return WENS coordinates even though W=E and N=S - $centerCoord = [$centerWE, $centerWE, $centerSN, $centerSN]; - return $centerCoord; - } - - /** - * Create array of geo features that are in search box - * - * Return search results record data - * - * @param string $recordId record ID - * @param string $recordCoord record coordinates - * @param string $title record title - * @param array $bboxCoords search box coordinates - * - * @return array - */ - public function createGeoFeature($recordId, $recordCoord, $title, $bboxCoords) - { - $recId = $recordId; - $recCoord = $recordCoord; - $recTitle = $title; - list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; - $centerData = []; - $match = []; - if (preg_match('/ENVELOPE\((.*),(.*),(.*),(.*)\)/', $recCoord, $match)) { - // Convert coordinates to 360 degree grid - $floats = array_map('floatval', $match); - $matchCoords = [$floats[1], $floats[2], $floats[3], $floats[4]]; - $gridCoords = $this->coordinatesToGrid($matchCoords); - list($coordW, $coordE, $coordN, $coordS) = $gridCoords; - - // Adjust coordinates on grid if necessary based on search box - if ($bboxW > 180 && ($coordW > 0 && $coordW < 180)) { - $coordW = 360 + $coordW; - $coordE = 360 + $coordE; - } - if ($bboxE > 180 && ($coordE > 0 && $coordE < 180)) { - $coordE = 360 + $coordE; - } - //Does coordinate fall within search box - if ($this->coordBboxIntersect( - $bboxCoords, [$coordW, $coordE, $coordN, $coordS] - ) - ) { - // Calculate center point - $centerPt = $this->calculateCenterPoint( - [$coordW, $coordE, $coordN, $coordS] - ); - // Does center point intersect search box? - if ($this->coordBboxIntersect($bboxCoords, $centerPt)) { - // Convert center point to long lat cooridnate - $ctrLongLat = $this->centerToLongLat( - [$centerPt[0], $centerPt[2]] - ); - $centerData = [ - $recId, $ctrLongLat[0], $ctrLongLat[1], $recTitle - ]; - } else { - // Recalculate center point - $centerCoordBbox = $this->getCenterFromBboxCoordIntersect( - [$bboxW, $bboxE, $bboxN, $bboxS], - [$coordW, $coordE, $coordN, $coordS] - ); - // Calculate new center point - $newCtr = $this->calculateCenterPoint($centerCoordBbox); - // Does new center point intersect search box? - if ($this->coordBboxIntersect($bboxCoords, $newCtr)) { - // Convert new center point to long lat cooridnate - $ctrLongLat = $this->centerToLongLat( - [$newCtr[0], $newCtr[2]] - ); - $centerData = [ - $recId, $ctrLongLat[0], $ctrLongLat[1], $recTitle - ]; - } else { - // Make center point center of search box - $bboxCtr = $this->calculateCenterPoint( - [$bboxW, $bboxE, $bboxN, $bboxS] - ); - $ctrLongLat = $this->centerToLongLat( - [$bboxCtr[0],$bboxCtr[2]] - ); - $centerData = [ - $recId, $ctrLongLat[0], $ctrLongLat[1], $recTitle - ]; - } - - } - } - } - return $centerData; - } - /** * Process search result record coordinate values - * - * Return search results record coordinates and process for - * display on search map. + * for Leaflet mapping platform. * * @return array */ public function getMapResultCoordinates() { - $centerCoords = []; - $rawCoordIds = []; - $centerCoordIds = []; - // Both coordinate variables are in WENS order // + $results = []; $rawCoords = $this->getSearchResultCoordinates(); - // Convert bbox coords to 360 grid // - $bboxCoords = $this->coordinatesToGrid($this->bboxSearchCoords); - list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; foreach ($rawCoords as $idCoords) { foreach ($idCoords[1] as $coord) { + $recCoords = []; $recId = $idCoords[0]; - $rawCoordIds[] = $recId; $title = $idCoords[2]; - $centerPoint = $this->createGeoFeature( - $recId, $coord, $title, $bboxCoords - ); - if ($centerPoint) { - $centerCoordIds[] = $centerPoint[0]; - $centerCoords[] = $centerPoint; - break; - } - } - } - //Solr search includes close-by geo features - //Check and add these if there are any - $addIds = array_merge( - array_diff($rawCoordIds, $centerCoordIds), - array_diff($centerCoordIds, $rawCoordIds) - ); - //Remove duplicate ids - $addIds = array_unique($addIds); - if (count($addIds) > 0) { - $bboxCenter = $this->calculateCenterPoint( - [$bboxW, $bboxE, $bboxN, $bboxS] - ); - $centerLongLat = $this->centerToLongLat([$bboxCenter[0],$bboxCenter[2]]); - foreach ($addIds as $coordId) { - foreach ($rawCoords as $idCoords) { - if ($coordId == $idCoords[0]) { - $title = $idCoords[2]; - $centerCoords[] = [$coordId, - $centerLongLat[0], - $centerLongLat[1], - $title - ]; - } + // convert title to UTF-8 + $title = mb_convert_encoding($title, 'UTF-8'); + $patternStr = '/ENVELOPE\((.*),(.*),(.*),(.*)\)/'; + if (preg_match($patternStr, $coord, $match)) { + $floats = array_map('floatval', $match); + $recCoords = [$floats[1], $floats[2], $floats[3], $floats[4]]; } + $results[] = [$recId, $title, $recCoords[0], + $recCoords[1], $recCoords[2], $recCoords[3] + ]; } } - return $centerCoords; + return $results; } } diff --git a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php index 7a62aa79bd05ca85ba6b9ac569cc42660ee9ae59..12b144da0232a186ba2847227599ddb1c2ad17dd 100644 --- a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php +++ b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php @@ -2,7 +2,7 @@ /** * OpenLibrarySubjects Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; -use VuFind\Connection\OpenLibrary, VuFind\Solr\Utils as SolrUtils; + +use VuFind\Connection\OpenLibrary; +use VuFind\Solr\Utils as SolrUtils; /** * OpenLibrarySubjects Recommendations Module @@ -200,9 +202,9 @@ class OpenLibrarySubjects implements RecommendInterface, // Try to extract range details from request parameters or SearchObject: $from = $request->get($field . 'from'); $to = $request->get($field . 'to'); - if (!is_null($from) && !is_null($to)) { + if (null !== $from && null !== $to) { $range = ['from' => $from, 'to' => $to]; - } else if (is_object($params)) { + } elseif (is_object($params)) { $currentFilters = $params->getFilters(); if (isset($currentFilters[$field][0])) { $range = SolrUtils::parseRange($currentFilters[$field][0]); diff --git a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php index c3a6121f946dabb411756ab82c9813cdbafd08dd..081460d039baf0909867ffaea56613bb7b05a24e 100644 --- a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php @@ -2,7 +2,7 @@ /** * OpenLibrarySubjects Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -91,7 +91,7 @@ class OpenLibrarySubjectsDeferred extends OpenLibrarySubjects // Make sure all elements of the params array are filled in, even if just // with a blank string, so we can rebuild the parameters to pass through // AJAX later on! - $settings[1] = isset($settings[1]) ? $settings[1] : ''; + $settings[1] = $settings[1] ?? ''; // If Publication Date filter is to be applied, get the range and add it to // $settings since the $searchObject will not be available after the AJAX diff --git a/module/VuFind/src/VuFind/Recommend/PluginFactory.php b/module/VuFind/src/VuFind/Recommend/PluginFactory.php index 098b64fb81b201150c5199b9e0ad626f022aab27..49041f580c8b57d138566e2cfd2e1c0554b067e9 100644 --- a/module/VuFind/src/VuFind/Recommend/PluginFactory.php +++ b/module/VuFind/src/VuFind/Recommend/PluginFactory.php @@ -2,7 +2,7 @@ /** * Recommendation module plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/PluginManager.php b/module/VuFind/src/VuFind/Recommend/PluginManager.php index e340311211c8f9659a2c6f23587a13b6ff4737b7..c357e7c906e21f64c5188c6d2a2c395cc6441db0 100644 --- a/module/VuFind/src/VuFind/Recommend/PluginManager.php +++ b/module/VuFind/src/VuFind/Recommend/PluginManager.php @@ -2,7 +2,7 @@ /** * Recommendation module plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,128 @@ namespace VuFind\Recommend; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'alphabrowselink' => 'VuFind\Recommend\AlphaBrowseLink', + 'authorfacets' => 'VuFind\Recommend\AuthorFacets', + 'authorinfo' => 'VuFind\Recommend\AuthorInfo', + 'authorityrecommend' => 'VuFind\Recommend\AuthorityRecommend', + 'catalogresults' => 'VuFind\Recommend\CatalogResults', + 'channels' => 'VuFind\Recommend\Channels', + 'collectionsidefacets' => 'VuFind\Recommend\CollectionSideFacets', + 'doi' => 'VuFind\Recommend\DOI', + 'dplaterms' => 'VuFind\Recommend\DPLATerms', + 'europeanaresults' => 'VuFind\Recommend\EuropeanaResults', + 'europeanaresultsdeferred' => 'VuFind\Recommend\EuropeanaResultsDeferred', + 'expandfacets' => 'VuFind\Recommend\ExpandFacets', + 'facetcloud' => 'VuFind\Recommend\FacetCloud', + 'favoritefacets' => 'VuFind\Recommend\FavoriteFacets', + 'libraryh3lp' => 'VuFind\Recommend\Libraryh3lp', + 'mapselection' => 'VuFind\Recommend\MapSelection', + 'sidefacets' => 'VuFind\Recommend\SideFacets', + 'openlibrarysubjects' => 'VuFind\Recommend\OpenLibrarySubjects', + 'openlibrarysubjectsdeferred' => + 'VuFind\Recommend\OpenLibrarySubjectsDeferred', + 'pubdatevisajax' => 'VuFind\Recommend\PubDateVisAjax', + 'randomrecommend' => 'VuFind\Recommend\RandomRecommend', + 'removefilters' => 'VuFind\Recommend\RemoveFilters', + 'resultgooglemapajax' => 'VuFind\Recommend\Deprecated', + 'spellingsuggestions' => 'VuFind\Recommend\SpellingSuggestions', + 'summonbestbets' => 'VuFind\Recommend\SummonBestBets', + 'summonbestbetsdeferred' => 'VuFind\Recommend\SummonBestBetsDeferred', + 'summondatabases' => 'VuFind\Recommend\SummonDatabases', + 'summondatabasesdeferred' => 'VuFind\Recommend\SummonDatabasesDeferred', + 'summonresults' => 'VuFind\Recommend\SummonResults', + 'summonresultsdeferred' => 'VuFind\Recommend\SummonResultsDeferred', + 'summontopics' => 'VuFind\Recommend\SummonTopics', + 'switchquery' => 'VuFind\Recommend\SwitchQuery', + 'switchtype' => 'VuFind\Recommend\SwitchType', + 'topfacets' => 'VuFind\Recommend\TopFacets', + 'visualfacets' => 'VuFind\Recommend\VisualFacets', + 'webresults' => 'VuFind\Recommend\WebResults', + 'worldcatidentities' => 'VuFind\Recommend\WorldCatIdentities', + 'worldcatterms' => 'VuFind\Recommend\Deprecated', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Recommend\AlphaBrowseLink' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\AuthorFacets' => + 'VuFind\Recommend\Factory::getAuthorFacets', + 'VuFind\Recommend\AuthorInfo' => 'VuFind\Recommend\Factory::getAuthorInfo', + 'VuFind\Recommend\AuthorityRecommend' => + 'VuFind\Recommend\Factory::getAuthorityRecommend', + 'VuFind\Recommend\CatalogResults' => + 'VuFind\Recommend\Factory::getCatalogResults', + 'VuFind\Recommend\Channels' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\CollectionSideFacets' => + 'VuFind\Recommend\Factory::getCollectionSideFacets', + 'VuFind\Recommend\Deprecated' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\DOI' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\DPLATerms' => 'VuFind\Recommend\Factory::getDPLATerms', + 'VuFind\Recommend\EuropeanaResults' => + 'VuFind\Recommend\Factory::getEuropeanaResults', + 'VuFind\Recommend\EuropeanaResultsDeferred' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\ExpandFacets' => + 'VuFind\Recommend\Factory::getExpandFacets', + 'VuFind\Recommend\FacetCloud' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\FavoriteFacets' => + 'VuFind\Recommend\Factory::getFavoriteFacets', + 'VuFind\Recommend\Libraryh3lp' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\MapSelection' => + 'VuFind\Recommend\Factory::getMapSelection', + 'VuFind\Recommend\SideFacets' => 'VuFind\Recommend\Factory::getSideFacets', + 'VuFind\Recommend\OpenLibrarySubjects' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\OpenLibrarySubjectsDeferred' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\PubDateVisAjax' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\RandomRecommend' => + 'VuFind\Recommend\Factory::getRandomRecommend', + 'VuFind\Recommend\RemoveFilters' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\SpellingSuggestions' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\SummonBestBets' => + 'VuFind\Recommend\Factory::getSummonBestBets', + 'VuFind\Recommend\SummonBestBetsDeferred' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\SummonDatabases' => + 'VuFind\Recommend\Factory::getSummonDatabases', + 'VuFind\Recommend\SummonDatabasesDeferred' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\SummonResults' => + 'VuFind\Recommend\Factory::getSummonResults', + 'VuFind\Recommend\SummonResultsDeferred' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\SummonTopics' => + 'VuFind\Recommend\Factory::getSummonTopics', + 'VuFind\Recommend\SwitchQuery' => 'VuFind\Recommend\Factory::getSwitchQuery', + 'VuFind\Recommend\SwitchType' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Recommend\TopFacets' => 'VuFind\Recommend\Factory::getTopFacets', + 'VuFind\Recommend\VisualFacets' => + 'VuFind\Recommend\Factory::getVisualFacets', + 'VuFind\Recommend\WebResults' => 'VuFind\Recommend\Factory::getWebResults', + 'VuFind\Recommend\WorldCatIdentities' => + 'VuFind\Recommend\Factory::getWorldCatIdentities', + ]; + /** * Constructor * @@ -52,8 +174,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ) { // These objects are not meant to be shared -- every time we retrieve one, // we are building a brand new object. - $this->setShareByDefault(false); + $this->sharedByDefault = false; + $this->addAbstractFactory('VuFind\Recommend\PluginFactory'); parent::__construct($configOrContainerInstance, $v3config); } diff --git a/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php b/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php index c8967c35fdfe304df16416da15c034d91a98e8f2..bf4c1da89a8efb356c29c96715547600840a24a3 100644 --- a/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php +++ b/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php @@ -2,7 +2,7 @@ /** * PubDateVisAjax Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Till Kinstler 2011. * diff --git a/module/VuFind/src/VuFind/Recommend/RandomRecommend.php b/module/VuFind/src/VuFind/Recommend/RandomRecommend.php index f5211f96fa779af7c6adc668b91385acfe591624..7041b96a9a12d8a45550990fe2a6a809cb07196b 100644 --- a/module/VuFind/src/VuFind/Recommend/RandomRecommend.php +++ b/module/VuFind/src/VuFind/Recommend/RandomRecommend.php @@ -2,7 +2,7 @@ /** * RandomRecommend Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2012. * @@ -27,9 +27,6 @@ */ namespace VuFind\Recommend; -use VuFindSearch\Query\Query, - VuFindSearch\ParamBag; - /** * RandomRecommend Module * @@ -168,7 +165,7 @@ class RandomRecommend implements RecommendInterface * request. * * @return void - */ + */ public function init($params, $request) { if ("retain" !== $this->mode) { @@ -194,7 +191,7 @@ class RandomRecommend implements RecommendInterface * @param \VuFind\Search\Base\Results $results Search results object * * @return void - */ + */ public function process($results) { } diff --git a/module/VuFind/src/VuFind/Recommend/RecommendInterface.php b/module/VuFind/src/VuFind/Recommend/RecommendInterface.php index 8d63bf12c52d6e86cd49f2017bfdbdc945affeec..59a0d735b0d231bbb1bfd98dd844f75800db79c0 100644 --- a/module/VuFind/src/VuFind/Recommend/RecommendInterface.php +++ b/module/VuFind/src/VuFind/Recommend/RecommendInterface.php @@ -2,7 +2,7 @@ /** * Search Recommendations Interface * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/module/VuFind/src/VuFind/Recommend/RemoveFilters.php b/module/VuFind/src/VuFind/Recommend/RemoveFilters.php index 8f921e9badf8c88ef44aeae8f446476a32c986f7..6f29b61133bbd7ed549251309ba0693fcd373e7d 100644 --- a/module/VuFind/src/VuFind/Recommend/RemoveFilters.php +++ b/module/VuFind/src/VuFind/Recommend/RemoveFilters.php @@ -3,7 +3,7 @@ * RemoveFilters Recommendations Module * Recommends to remove filters * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -95,12 +95,8 @@ class RemoveFilters implements RecommendInterface public function process($results) { $filters = $results->getParams()->getFilterList(false); - if (count($filters) > 0) { - foreach ($filters as $filter) { - foreach ($filter as $f) { - $this->activeFacetsCount++; - } - } + foreach ($filters as $filter) { + $this->activeFacetsCount += count($filter); } $this->results = $results; } @@ -112,7 +108,7 @@ class RemoveFilters implements RecommendInterface */ public function hasFilters() { - return ($this->activeFacetsCount > 0); + return $this->activeFacetsCount > 0; } /** diff --git a/module/VuFind/src/VuFind/Recommend/SearchObject.php b/module/VuFind/src/VuFind/Recommend/SearchObject.php index c4d6520ad1e5c16c045df807f226e8495c4ea2bc..7abbd7d3149ac07b0623ba2a30dd5f380fbcfac0 100644 --- a/module/VuFind/src/VuFind/Recommend/SearchObject.php +++ b/module/VuFind/src/VuFind/Recommend/SearchObject.php @@ -3,7 +3,7 @@ * Abstract SearchObject Recommendations Module (needs to be extended to use * a particular search object). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use VuFind\Search\SearchRunner; /** diff --git a/module/VuFind/src/VuFind/Recommend/SideFacets.php b/module/VuFind/src/VuFind/Recommend/SideFacets.php index 30f96a99ef5054c82d9707bf98919006c37df404..f6cca916b799a9fe2be1a7ff02aea615ca47cc99 100644 --- a/module/VuFind/src/VuFind/Recommend/SideFacets.php +++ b/module/VuFind/src/VuFind/Recommend/SideFacets.php @@ -2,7 +2,7 @@ /** * SideFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; -use VuFind\Solr\Utils as SolrUtils; + use VuFind\Search\Solr\HierarchicalFacetHelper; +use VuFind\Solr\Utils as SolrUtils; /** * SideFacets Recommendations Module @@ -153,8 +154,8 @@ class SideFacets extends AbstractFacets // Parse the additional settings: $settings = explode(':', $settings); $mainSection = empty($settings[0]) ? 'Results' : $settings[0]; - $checkboxSection = isset($settings[1]) ? $settings[1] : false; - $iniName = isset($settings[2]) ? $settings[2] : 'facets'; + $checkboxSection = $settings[1] ?? false; + $iniName = $settings[2] ?? 'facets'; // Load the desired facet information... $config = $this->configLoader->get($iniName); diff --git a/module/VuFind/src/VuFind/Recommend/SpellingSuggestions.php b/module/VuFind/src/VuFind/Recommend/SpellingSuggestions.php index bd343d2a160e9717f38ad8ecd4aad949d3826735..75f2560fa3a93cdd3abbae1c81255d363679d520 100644 --- a/module/VuFind/src/VuFind/Recommend/SpellingSuggestions.php +++ b/module/VuFind/src/VuFind/Recommend/SpellingSuggestions.php @@ -2,7 +2,7 @@ /** * SpellingSuggestions Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonBestBets.php b/module/VuFind/src/VuFind/Recommend/SummonBestBets.php index a82ff34d0f579b58701ba2c750482705b8c28ee6..9b55f2903ab51695a03e6c0be1ac8fe94612c6e0 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonBestBets.php +++ b/module/VuFind/src/VuFind/Recommend/SummonBestBets.php @@ -2,7 +2,7 @@ /** * SummonBestBets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonBestBetsDeferred.php b/module/VuFind/src/VuFind/Recommend/SummonBestBetsDeferred.php index 7ef745c020f309d1af9bde39a7bbdf28fa50d8e1..4928517f69ce21438247bae551d2321fb6ab223c 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonBestBetsDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/SummonBestBetsDeferred.php @@ -2,7 +2,7 @@ /** * SummonBestBetsDeferred Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonDatabases.php b/module/VuFind/src/VuFind/Recommend/SummonDatabases.php index f36eb697c0d66559f9ebb1c643c6d9af84665291..12d157fc63b2b9749c8621cfb855bf85ae072a91 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonDatabases.php +++ b/module/VuFind/src/VuFind/Recommend/SummonDatabases.php @@ -2,7 +2,7 @@ /** * SummonDatabases Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonDatabasesDeferred.php b/module/VuFind/src/VuFind/Recommend/SummonDatabasesDeferred.php index a25f9a92dec618cf5b0232aa1c79fcbf53594b0b..ddcce2227c56bee6add3f1509ea32accec09d861 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonDatabasesDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/SummonDatabasesDeferred.php @@ -2,7 +2,7 @@ /** * SummonDatabasesDeferred Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonResults.php b/module/VuFind/src/VuFind/Recommend/SummonResults.php index 9ee4ea7abde1c2fc0d8fe909610cdbbebf884ede..0cf03cf5bdaaba847eb77296f0b8687a42e0237d 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonResults.php +++ b/module/VuFind/src/VuFind/Recommend/SummonResults.php @@ -2,7 +2,7 @@ /** * SummonResults Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonResultsDeferred.php b/module/VuFind/src/VuFind/Recommend/SummonResultsDeferred.php index bc42487418f77c5d8bb1c82c25ea0bd5c06282fa..b60cabe17b70f2f0b8e9c70fa00e2dd475c058bf 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonResultsDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/SummonResultsDeferred.php @@ -2,7 +2,7 @@ /** * SummonResultsDeferred Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SummonTopics.php b/module/VuFind/src/VuFind/Recommend/SummonTopics.php index 8c815cb28bb83cb57675e0990f61fd06ff1c8917..e4debda91b83a3291495c22d220727b894c5910f 100644 --- a/module/VuFind/src/VuFind/Recommend/SummonTopics.php +++ b/module/VuFind/src/VuFind/Recommend/SummonTopics.php @@ -2,7 +2,7 @@ /** * SummonTopics Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SwitchQuery.php b/module/VuFind/src/VuFind/Recommend/SwitchQuery.php index 41d3b91bf627d93949f8e7996d97e2d42bbbdfbd..7630b5c09b92d6c7b2f1ba485d88f97cffb6bc43 100644 --- a/module/VuFind/src/VuFind/Recommend/SwitchQuery.php +++ b/module/VuFind/src/VuFind/Recommend/SwitchQuery.php @@ -2,7 +2,7 @@ /** * SwitchQuery Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use VuFind\Search\BackendManager; /** diff --git a/module/VuFind/src/VuFind/Recommend/SwitchTab.php b/module/VuFind/src/VuFind/Recommend/SwitchTab.php index ef705b54024d2563f8e4346c5bfed26be24cc332..42c22b5469f9a8513483f30fde89fab5088cf1c1 100644 --- a/module/VuFind/src/VuFind/Recommend/SwitchTab.php +++ b/module/VuFind/src/VuFind/Recommend/SwitchTab.php @@ -3,7 +3,7 @@ * SwitchTab Recommendations Module * Recommends to use another SearchTab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/SwitchType.php b/module/VuFind/src/VuFind/Recommend/SwitchType.php index b448d68db5c8db0513ae78902ef372a702ac94d8..dbc1fb0e2a67117d60a582c156e8ab00659815e2 100644 --- a/module/VuFind/src/VuFind/Recommend/SwitchType.php +++ b/module/VuFind/src/VuFind/Recommend/SwitchType.php @@ -2,7 +2,7 @@ /** * SwitchType Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -81,7 +81,7 @@ class SwitchType implements RecommendInterface { $params = explode(':', $settings); $this->newHandler = !empty($params[0]) ? $params[0] : 'AllFields'; - $this->newHandlerName = isset($params[1]) ? $params[1] : 'All Fields'; + $this->newHandlerName = $params[1] ?? 'All Fields'; } /** @@ -119,7 +119,7 @@ class SwitchType implements RecommendInterface // anything! We should only show recommendations if we know what handler is // being used and can determine that it is not the same as the new handler // that we want to recommend. - $this->active = (!is_null($handler) && $handler != $this->newHandler); + $this->active = (null !== $handler && $handler != $this->newHandler); } /** diff --git a/module/VuFind/src/VuFind/Recommend/TopFacets.php b/module/VuFind/src/VuFind/Recommend/TopFacets.php index ba055512c759e9a5f7e2de2be601063c30dc7ddf..0498957e9fe8ad4c368603936e2caaef5073b6c1 100644 --- a/module/VuFind/src/VuFind/Recommend/TopFacets.php +++ b/module/VuFind/src/VuFind/Recommend/TopFacets.php @@ -2,7 +2,7 @@ /** * SideFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -71,7 +71,7 @@ class TopFacets extends AbstractFacets { $settings = explode(':', $settings); $mainSection = empty($settings[0]) ? 'ResultsTop' : $settings[0]; - $iniName = isset($settings[1]) ? $settings[1] : 'facets'; + $iniName = $settings[1] ?? 'facets'; // Load the desired facet information: $config = $this->configLoader->get($iniName); diff --git a/module/VuFind/src/VuFind/Recommend/VisualFacets.php b/module/VuFind/src/VuFind/Recommend/VisualFacets.php index 9e119facc6ffae616f15b599ed59e6b21f937be8..35e44f21a18ecf70a1be8891b8917a9a1e6b06d9 100644 --- a/module/VuFind/src/VuFind/Recommend/VisualFacets.php +++ b/module/VuFind/src/VuFind/Recommend/VisualFacets.php @@ -2,7 +2,7 @@ /** * VisualFacets Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Julia Bauder 2014. * @@ -66,7 +66,7 @@ class VisualFacets extends AbstractFacets { $settings = explode(':', $settings); $mainSection = empty($settings[0]) ? 'Visual_Settings' : $settings[0]; - $iniName = isset($settings[1]) ? $settings[1] : 'facets'; + $iniName = $settings[1] ?? 'facets'; // Load the desired facet information: $config = $this->configLoader->get($iniName); diff --git a/module/VuFind/src/VuFind/Recommend/WebResults.php b/module/VuFind/src/VuFind/Recommend/WebResults.php index 296100943b58a2902519a3409adecd1c008dd15c..cdfbbdc98c3e3217f610f90917abf3ed9853007b 100644 --- a/module/VuFind/src/VuFind/Recommend/WebResults.php +++ b/module/VuFind/src/VuFind/Recommend/WebResults.php @@ -2,7 +2,7 @@ /** * WebResults Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Recommend/WorldCatIdentities.php b/module/VuFind/src/VuFind/Recommend/WorldCatIdentities.php index 49f51b835d92196e67ad579401771de67ab82c02..0b8dd688357563ec0e4ddfcc22577a021a389901 100644 --- a/module/VuFind/src/VuFind/Recommend/WorldCatIdentities.php +++ b/module/VuFind/src/VuFind/Recommend/WorldCatIdentities.php @@ -2,7 +2,7 @@ /** * WorldCatIdentities Recommendations Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki */ namespace VuFind\Recommend; + use VuFind\Connection\WorldCatUtils; use VuFindSearch\Query\Query; diff --git a/module/VuFind/src/VuFind/Record/Cache.php b/module/VuFind/src/VuFind/Record/Cache.php index 154dc0d30b395cdc824712e527b36e8e7182ef93..76b0ddaf24a3a8836a0335d9b76d7ea82d8d5f01 100644 --- a/module/VuFind/src/VuFind/Record/Cache.php +++ b/module/VuFind/src/VuFind/Record/Cache.php @@ -2,7 +2,7 @@ /** * Record Cache * - * PHP version 5 + * PHP version 7 * * Copyright (C) University of Freiburg 2014. * Copyright (C) The National Library of Finland 2015. @@ -28,9 +28,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Record; -use VuFind\Db\Table\Record as Record, - VuFind\RecordDriver\PluginManager as RecordFactory, - Zend\Config\Config as Config; + +use VuFind\Db\Table\Record as Record; +use VuFind\RecordDriver\PluginManager as RecordFactory; +use Zend\Config\Config as Config; /** * Record Cache diff --git a/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareInterface.php b/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareInterface.php index a68c5d89f54607c3b775937bee964312d830751d..8188cec2237aec9096567d95df950ef928cb22d8 100644 --- a/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareInterface.php +++ b/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareInterface.php @@ -2,7 +2,7 @@ /** * Record cache aware marker interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) 2014 University of Freiburg. * diff --git a/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareTrait.php b/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareTrait.php index 57f3150ead406cabc1771057a5d19f9956c31933..ac8d39ff97ba3b1efa4adf6514bba6ef71bd5ccb 100644 --- a/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareTrait.php +++ b/module/VuFind/src/VuFind/Record/Cache/RecordCacheAwareTrait.php @@ -2,7 +2,7 @@ /** * Default implementation of RecordCacheAwareInterface * - * PHP version 5 + * PHP version 7 * * Copyright (C) 2014 University of Freiburg. * @@ -66,5 +66,4 @@ trait RecordCacheAwareTrait { return $this->recordCache; } - } diff --git a/module/VuFind/src/VuFind/Record/CacheFactory.php b/module/VuFind/src/VuFind/Record/CacheFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c63a9bc9a64103d4be50d650d70f9d940b2d65a9 --- /dev/null +++ b/module/VuFind/src/VuFind/Record/CacheFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Record cache factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Record + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Record; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Record cache factory. + * + * @category VuFind + * @package Record + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CacheFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\RecordDriver\PluginManager'), + $container->get('VuFind\Config\PluginManager')->get('RecordCache'), + $container->get('VuFind\Db\Table\PluginManager')->get('Record') + ); + } +} diff --git a/module/VuFind/src/VuFind/Record/Loader.php b/module/VuFind/src/VuFind/Record/Loader.php index 3cda3c8d728b852465f53108e8edf1bf876d16e9..40ac9e504ab95fc329532bcb32ad86e9de0fd36b 100644 --- a/module/VuFind/src/VuFind/Record/Loader.php +++ b/module/VuFind/src/VuFind/Record/Loader.php @@ -2,7 +2,7 @@ /** * Record loader * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015. @@ -28,10 +28,10 @@ * @link https://vufind.org Main Site */ namespace VuFind\Record; -use VuFind\Exception\RecordMissing as RecordMissingException, - VuFind\RecordDriver\PluginManager as RecordFactory, - VuFindSearch\Service as SearchService, - VuFind\Record\Cache; + +use VuFind\Exception\RecordMissing as RecordMissingException; +use VuFind\RecordDriver\PluginManager as RecordFactory; +use VuFindSearch\Service as SearchService; /** * Record loader @@ -254,8 +254,7 @@ class Loader implements \Zend\Log\LoggerAwareInterface // objects: foreach ($ids as $i => $details) { if (!isset($retVal[$i]) || !is_object($retVal[$i])) { - $fields = isset($details['extra_fields']) - ? $details['extra_fields'] : []; + $fields = $details['extra_fields'] ?? []; $fields['id'] = $details['id']; $retVal[$i] = $this->recordFactory->get('Missing'); $retVal[$i]->setRawData($fields); diff --git a/module/VuFind/src/VuFind/Record/LoaderFactory.php b/module/VuFind/src/VuFind/Record/LoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7d508f7f72d6b225f885da11eaed61ba76bb2f35 --- /dev/null +++ b/module/VuFind/src/VuFind/Record/LoaderFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Record loader factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Record + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Record; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Record loader factory. + * + * @category VuFind + * @package Record + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class LoaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFindSearch\Service'), + $container->get('VuFind\RecordDriver\PluginManager'), + $container->get('VuFind\Record\Cache') + ); + } +} diff --git a/module/VuFind/src/VuFind/Record/Router.php b/module/VuFind/src/VuFind/Record/Router.php index da967271bf3f3694432d505fd8a000973004404e..8102c57459c7d1598690616f31b90515100c6be9 100644 --- a/module/VuFind/src/VuFind/Record/Router.php +++ b/module/VuFind/src/VuFind/Record/Router.php @@ -2,7 +2,7 @@ /** * Record route generator * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Record/RouterFactory.php b/module/VuFind/src/VuFind/Record/RouterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b9e7f79b055e5d24b15fd8283d5aa5782eac18f9 --- /dev/null +++ b/module/VuFind/src/VuFind/Record/RouterFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Record router factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Record + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Record; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Record router factory. + * + * @category VuFind + * @package Record + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RouterFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('VuFind\Record\Loader'), + $container->get('VuFind\Config\PluginManager')->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php b/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php index 3daa678495d598ab8927fdbcab3dfea058ff8c76..cd0d9da2f0799937ec91f636a67922c9bc937dba 100644 --- a/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php +++ b/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php @@ -2,7 +2,7 @@ /** * Abstract base record model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,8 @@ * @link https://vufind.org Main Page */ namespace VuFind\RecordDriver; -use VuFind\Exception\LoginRequired as LoginRequiredException, - VuFind\XSLT\Import\VuFind as ArticleStripper; + +use VuFind\XSLT\Import\VuFind as ArticleStripper; /** * Abstract base record model. @@ -296,41 +296,6 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface, return $this->sourceIdentifier; } - /** - * Return an array of related record suggestion objects (implementing the - * \VuFind\Related\RelatedInterface) based on the current record. - * - * @param \VuFind\Related\PluginManager $factory Related module plugin factory - * @param array $types Array of relationship types to - * load; each entry should be a service name (i.e. 'Similar' or 'Editions') - * optionally followed by a colon-separated list of parameters to pass to the - * constructor. If the parameter is set to null instead of an array, default - * settings will be loaded from config.ini. - * - * @return array - */ - public function getRelated(\VuFind\Related\PluginManager $factory, $types = null) - { - if (is_null($types)) { - $types = isset($this->recordConfig->Record->related) ? - $this->recordConfig->Record->related : []; - } - $retVal = []; - foreach ($types as $current) { - $parts = explode(':', $current); - $type = $parts[0]; - $params = isset($parts[1]) ? $parts[1] : null; - if ($factory->has($type)) { - $plugin = $factory->get($type); - $plugin->init($params, $this); - $retVal[] = $plugin; - } else { - throw new \Exception("Related module {$type} does not exist."); - } - } - return $retVal; - } - /** * Returns true if the record supports real-time AJAX status lookups. * diff --git a/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php b/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..da058162ec3194c91b2b9a19785b09da16ed6f2d --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php @@ -0,0 +1,64 @@ +<?php +/** + * Default factory for record drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Default factory for record drivers. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractBaseFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $mainConfig = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($mainConfig, ...($options ?: [])); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/BrowZine.php b/module/VuFind/src/VuFind/RecordDriver/BrowZine.php index de1d6b4483b1c77f5dab3d092a4f973f8cb5d7a7..ce3da8e02463fc2695869368bfa2d4b9c6a415f1 100644 --- a/module/VuFind/src/VuFind/RecordDriver/BrowZine.php +++ b/module/VuFind/src/VuFind/RecordDriver/BrowZine.php @@ -2,7 +2,7 @@ /** * Model for BrowZine records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class BrowZine extends SolrDefault +class BrowZine extends DefaultRecord { /** * Get the short (pre-subtitle) title of the record. @@ -69,6 +69,7 @@ class BrowZine extends SolrDefault ? $this->fields['name'] : parent::getTitle(); } + /** * Returns one of three things: a full URL to a thumbnail preview of the record * if an image is available in an external system; an array of parameters to diff --git a/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php b/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php new file mode 100644 index 0000000000000000000000000000000000000000..716647b2217944880744fb0468e47bc7bce28f10 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php @@ -0,0 +1,1695 @@ +<?php +/** + * Default model for records + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +use VuFind\View\Helper\Root\RecordLink; +use VuFindCode\ISBN; + +/** + * Default model for records + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + * + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +class DefaultRecord extends AbstractBase +{ + /** + * Should we highlight fields in search results? + * + * @var bool + */ + protected $highlight = false; + + /** + * Constructor + * + * @param \Zend\Config\Config $mainConfig VuFind main configuration (omit for + * built-in defaults) + * @param \Zend\Config\Config $recordConfig Record-specific configuration file + * (omit to use $mainConfig as $recordConfig) + * @param \Zend\Config\Config $searchSettings Search-specific configuration file + */ + public function __construct($mainConfig = null, $recordConfig = null, + $searchSettings = null + ) { + // Turn on highlighting as needed: + $this->highlight = !isset($searchSettings->General->highlighting) + ? false : $searchSettings->General->highlighting; + + parent::__construct($mainConfig, $recordConfig); + } + + /** + * Get access restriction notes for the record. + * + * @return array + */ + public function getAccessRestrictions() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get all subject headings associated with this record. Each heading is + * returned as an array of chunks, increasing from least specific to most + * specific. + * + * @param bool $extended Whether to return a keyed array with the following + * keys: + * - heading: the actual subject heading chunks + * - type: heading type + * - source: source vocabulary + * + * @return array + */ + public function getAllSubjectHeadings($extended = false) + { + $headings = []; + foreach (['topic', 'geographic', 'genre', 'era'] as $field) { + if (isset($this->fields[$field])) { + $headings = array_merge($headings, $this->fields[$field]); + } + } + + // The default index schema doesn't currently store subject headings in a + // broken-down format, so we'll just send each value as a single chunk. + // Other record drivers (i.e. SolrMarc) can offer this data in a more + // granular format. + $callback = function ($i) use ($extended) { + return $extended + ? ['heading' => [$i], 'type' => '', 'source' => ''] + : [$i]; + }; + return array_map($callback, array_unique($headings)); + } + + /** + * Get all record links related to the current record. Each link is returned as + * array. + * NB: to use this method you must override it. + * Format: + * <code> + * array( + * array( + * 'title' => label_for_title + * 'value' => link_name + * 'link' => link_URI + * ), + * ... + * ) + * </code> + * + * @return null|array + */ + public function getAllRecordLinks() + { + return null; + } + + /** + * Get Author Information with Associated Data Fields + * + * @param string $index The author index [primary, corporate, or secondary] + * used to construct a method name for retrieving author data (e.g. + * getPrimaryAuthors). + * @param array $dataFields An array of fields to used to construct method + * names for retrieving author-related data (e.g., if you pass 'role' the + * data method will be similar to getPrimaryAuthorsRoles). This value will also + * be used as a key associated with each author in the resulting data array. + * + * @return array + */ + public function getAuthorDataFields($index, $dataFields = []) + { + $data = $dataFieldValues = []; + + // Collect author data + $authorMethod = sprintf('get%sAuthors', ucfirst($index)); + $authors = $this->tryMethod($authorMethod, [], []); + + // Collect attribute data + foreach ($dataFields as $field) { + $fieldMethod = $authorMethod . ucfirst($field) . 's'; + $dataFieldValues[$field] = $this->tryMethod($fieldMethod, [], []); + } + + // Match up author and attribute data (this assumes that the attribute + // arrays have the same indices as the author array; i.e. $author[$i] + // has $dataFieldValues[$attribute][$i]. + foreach ($authors as $i => $author) { + if (!isset($data[$author])) { + $data[$author] = []; + } + + foreach ($dataFieldValues as $field => $dataFieldValue) { + if (!empty($dataFieldValue[$i])) { + $data[$author][$field][] = $dataFieldValue[$i]; + } + } + } + + return $data; + } + + /** + * Get award notes for the record. + * + * @return array + */ + public function getAwards() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get notes on bibliography content. + * + * @return array + */ + public function getBibliographyNotes() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get text that can be displayed to represent this record in + * breadcrumbs. + * + * @return string Breadcrumb text to represent this record. + */ + public function getBreadcrumb() + { + return $this->getShortTitle(); + } + + /** + * Get the first call number associated with the record (empty string if none). + * + * @return string + */ + public function getCallNumber() + { + $all = $this->getCallNumbers(); + return $all[0] ?? ''; + } + + /** + * Get all call numbers associated with the record (empty string if none). + * + * @return array + */ + public function getCallNumbers() + { + return isset($this->fields['callnumber-raw']) + ? $this->fields['callnumber-raw'] : []; + } + + /** + * Return the first valid DOI found in the record (false if none). + * + * @return mixed + */ + public function getCleanDOI() + { + $field = 'doi_str_mv'; + return (isset($this->fields[$field][0]) && !empty($this->fields[$field][0])) + ? $this->fields[$field][0] : false; + } + + /** + * Return the first valid ISBN found in the record (favoring ISBN-10 over + * ISBN-13 when possible). + * + * @return mixed + */ + public function getCleanISBN() + { + // Get all the ISBNs and initialize the return value: + $isbns = $this->getISBNs(); + $isbn13 = false; + + // Loop through the ISBNs: + foreach ($isbns as $isbn) { + // Strip off any unwanted notes: + if ($pos = strpos($isbn, ' ')) { + $isbn = substr($isbn, 0, $pos); + } + + // If we find an ISBN-10, return it immediately; otherwise, if we find + // an ISBN-13, save it if it is the first one encountered. + $isbnObj = new ISBN($isbn); + if ($isbn10 = $isbnObj->get10()) { + return $isbn10; + } + if (!$isbn13) { + $isbn13 = $isbnObj->get13(); + } + } + return $isbn13; + } + + /** + * Get just the base portion of the first listed ISSN (or false if no ISSNs). + * + * @return mixed + */ + public function getCleanISSN() + { + $issns = $this->getISSNs(); + if (empty($issns)) { + return false; + } + $issn = $issns[0]; + if ($pos = strpos($issn, ' ')) { + $issn = substr($issn, 0, $pos); + } + return $issn; + } + + /** + * Get just the first listed OCLC Number (or false if none available). + * + * @return mixed + */ + public function getCleanOCLCNum() + { + $nums = $this->getOCLC(); + return empty($nums) ? false : $nums[0]; + } + + /** + * Get just the first listed UPC Number (or false if none available). + * + * @return mixed + */ + public function getCleanUPC() + { + $nums = $this->getUPC(); + return empty($nums) ? false : $nums[0]; + } + + /** + * Get the main corporate authors (if any) for the record. + * + * @return array + */ + public function getCorporateAuthors() + { + return isset($this->fields['author_corporate']) ? + $this->fields['author_corporate'] : []; + } + + /** + * Get an array of all main corporate authors roles. + * + * @return array + */ + public function getCorporateAuthorsRoles() + { + return isset($this->fields['author_corporate_role']) ? + $this->fields['author_corporate_role'] : []; + } + + /** + * Get the date coverage for a record which spans a period of time (i.e. a + * journal). Use getPublicationDates for publication dates of particular + * monographic items. + * + * @return array + */ + public function getDateSpan() + { + return isset($this->fields['dateSpan']) ? + $this->fields['dateSpan'] : []; + } + + /** + * Deduplicate author information into associative array with main/corporate/ + * secondary keys. + * + * @param array $dataFields An array of extra data fields to retrieve (see + * getAuthorDataFields) + * + * @return array + */ + public function getDeduplicatedAuthors($dataFields = ['role']) + { + $authors = []; + foreach (['primary', 'secondary', 'corporate'] as $type) { + $authors[$type] = $this->getAuthorDataFields($type, $dataFields); + } + + // deduplicate + $dedup = function (&$array1, &$array2) { + if (!empty($array1) && !empty($array2)) { + $keys = array_keys($array1); + foreach ($keys as $author) { + if (isset($array2[$author])) { + $array1[$author] = array_merge( + $array1[$author], + $array2[$author] + ); + unset($array2[$author]); + } + } + } + }; + + $dedup($authors['primary'], $authors['corporate']); + $dedup($authors['secondary'], $authors['corporate']); + $dedup($authors['primary'], $authors['secondary']); + + $dedup_data = function (&$array) { + foreach ($array as $author => $data) { + foreach ($data as $field => $values) { + if (is_array($values)) { + $array[$author][$field] = array_unique($values); + } + } + } + }; + + $dedup_data($authors['primary']); + $dedup_data($authors['secondary']); + $dedup_data($authors['corporate']); + + return $authors; + } + + /** + * Get the edition of the current record. + * + * @return string + */ + public function getEdition() + { + return isset($this->fields['edition']) ? + $this->fields['edition'] : ''; + } + + /** + * Get notes on finding aids related to the record. + * + * @return array + */ + public function getFindingAids() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get an array of all the formats associated with the record. + * + * @return array + */ + public function getFormats() + { + return isset($this->fields['format']) ? $this->fields['format'] : []; + } + + /** + * Get general notes on the record. + * + * @return array + */ + public function getGeneralNotes() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get highlighted author data, if available. + * + * @return array + */ + public function getRawAuthorHighlights() + { + // Not supported by default. + return []; + } + + /** + * Get primary author information with highlights applied (if applicable) + * + * @return array + */ + public function getPrimaryAuthorsWithHighlighting() + { + $highlights = []; + // Create a map of de-highlighted valeus => highlighted values. + foreach ($this->getRawAuthorHighlights() as $current) { + $dehighlighted = str_replace( + ['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $current + ); + $highlights[$dehighlighted] = $current; + } + + // replace unhighlighted authors with highlighted versions where + // applicable: + $authors = []; + foreach ($this->getPrimaryAuthors() as $author) { + $authors[] = $highlights[$author] ?? $author; + } + return $authors; + } + + /** + * Get a string representing the last date that the record was indexed. + * + * @return string + */ + public function getLastIndexed() + { + return isset($this->fields['last_indexed']) + ? $this->fields['last_indexed'] : ''; + } + + /** + * Given a field name, return an appropriate caption. + * + * @param string $field Field name + * + * @return mixed Caption if found, false if none available. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getSnippetCaption($field) + { + // Not supported by default. + return false; + } + + /** + * Pick one line from the highlighted text (if any) to use as a snippet. + * + * @return mixed False if no snippet found, otherwise associative array + * with 'snippet' and 'caption' keys. + */ + public function getHighlightedSnippet() + { + // Not supported by default. + return false; + } + + /** + * Get a highlighted title string, if available. + * + * @return string + */ + public function getHighlightedTitle() + { + // Not supported by default. + return ''; + } + + /** + * Get the institutions holding the record. + * + * @return array + */ + public function getInstitutions() + { + return isset($this->fields['institution']) + ? $this->fields['institution'] : []; + } + + /** + * Get an array of all ISBNs associated with the record (may be empty). + * + * @return array + */ + public function getISBNs() + { + // If ISBN is in the index, it should automatically be an array... but if + // it's not set at all, we should normalize the value to an empty array. + return isset($this->fields['isbn']) && is_array($this->fields['isbn']) ? + $this->fields['isbn'] : []; + } + + /** + * Get an array of all ISSNs associated with the record (may be empty). + * + * @return array + */ + public function getISSNs() + { + // If ISSN is in the index, it should automatically be an array... but if + // it's not set at all, we should normalize the value to an empty array. + return isset($this->fields['issn']) && is_array($this->fields['issn']) ? + $this->fields['issn'] : []; + } + + /** + * Get an array of all the languages associated with the record. + * + * @return array + */ + public function getLanguages() + { + return isset($this->fields['language']) ? + $this->fields['language'] : []; + } + + /** + * Get a raw, unnormalized LCCN. (See getLCCN for normalization). + * + * @return string + */ + protected function getRawLCCN() + { + // Get LCCN from Index + return isset($this->fields['lccn']) ? $this->fields['lccn'] : ''; + } + + /** + * Get a LCCN, normalised according to info:lccn + * + * @return string + */ + public function getLCCN() + { + // Remove all blanks. + $raw = preg_replace('{[ \t]+}', '', $this->getRawLCCN()); + + // If there is a forward slash (/) in the string, remove it, and remove all + // characters to the right of the forward slash. + if (strpos($raw, '/') > 0) { + $tmpArray = explode("/", $raw); + $raw = $tmpArray[0]; + } + /* If there is a hyphen in the string: + a. Remove it. + b. Inspect the substring following (to the right of) the (removed) + hyphen. Then (and assuming that steps 1 and 2 have been carried out): + i. All these characters should be digits, and there should be + six or less. + ii. If the length of the substring is less than 6, left-fill the + substring with zeros until the length is six. + */ + if (strpos($raw, '-') > 0) { + // haven't checked for i. above. If they aren't all digits, there is + // nothing that can be done, so might as well leave it. + $tmpArray = explode("-", $raw); + $raw = $tmpArray[0] . str_pad($tmpArray[1], 6, "0", STR_PAD_LEFT); + } + return $raw; + } + + /** + * Get an array of newer titles for the record. + * + * @return array + */ + public function getNewerTitles() + { + return isset($this->fields['title_new']) ? + $this->fields['title_new'] : []; + } + + /** + * Get the OCLC number(s) of the record. + * + * @return array + */ + public function getOCLC() + { + return isset($this->fields['oclc_num']) ? + $this->fields['oclc_num'] : []; + } + + /** + * Support method for getOpenUrl() -- pick the OpenURL format. + * + * @return string + */ + protected function getOpenUrlFormat() + { + // If we have multiple formats, Book, Journal and Article are most + // important... + $formats = $this->getFormats(); + if (in_array('Book', $formats)) { + return 'Book'; + } elseif (in_array('Article', $formats)) { + return 'Article'; + } elseif (in_array('Journal', $formats)) { + return 'Journal'; + } elseif (isset($formats[0])) { + return $formats[0]; + } elseif (strlen($this->getCleanISSN()) > 0) { + return 'Journal'; + } elseif (strlen($this->getCleanISBN()) > 0) { + return 'Book'; + } + return 'UnknownFormat'; + } + + /** + * Get the COinS identifier. + * + * @return string + */ + protected function getCoinsID() + { + // Get the COinS ID -- it should be in the OpenURL section of config.ini, + // but we'll also check the COinS section for compatibility with legacy + // configurations (this moved between the RC2 and 1.0 releases). + if (isset($this->mainConfig->OpenURL->rfr_id) + && !empty($this->mainConfig->OpenURL->rfr_id) + ) { + return $this->mainConfig->OpenURL->rfr_id; + } + if (isset($this->mainConfig->COinS->identifier) + && !empty($this->mainConfig->COinS->identifier) + ) { + return $this->mainConfig->COinS->identifier; + } + return 'vufind.svn.sourceforge.net'; + } + + /** + * Get default OpenURL parameters. + * + * @return array + */ + protected function getDefaultOpenUrlParams() + { + // Get a representative publication date: + $pubDate = $this->getPublicationDates(); + $pubDate = empty($pubDate) ? '' : $pubDate[0]; + + // Start an array of OpenURL parameters: + return [ + 'url_ver' => 'Z39.88-2004', + 'ctx_ver' => 'Z39.88-2004', + 'ctx_enc' => 'info:ofi/enc:UTF-8', + 'rfr_id' => 'info:sid/' . $this->getCoinsID() . ':generator', + 'rft.title' => $this->getTitle(), + 'rft.date' => $pubDate + ]; + } + + /** + * Get OpenURL parameters for a book. + * + * @return array + */ + protected function getBookOpenUrlParams() + { + $params = $this->getDefaultOpenUrlParams(); + $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:book'; + $params['rft.genre'] = 'book'; + $params['rft.btitle'] = $params['rft.title']; + $series = $this->getSeries(); + if (count($series) > 0) { + // Handle both possible return formats of getSeries: + $params['rft.series'] = is_array($series[0]) ? + $series[0]['name'] : $series[0]; + } + $params['rft.au'] = $this->getPrimaryAuthor(); + $publishers = $this->getPublishers(); + if (count($publishers) > 0) { + $params['rft.pub'] = $publishers[0]; + } + $params['rft.edition'] = $this->getEdition(); + $params['rft.isbn'] = (string)$this->getCleanISBN(); + return $params; + } + + /** + * Get OpenURL parameters for an article. + * + * @return array + */ + protected function getArticleOpenUrlParams() + { + $params = $this->getDefaultOpenUrlParams(); + $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:journal'; + $params['rft.genre'] = 'article'; + $params['rft.issn'] = (string)$this->getCleanISSN(); + // an article may have also an ISBN: + $params['rft.isbn'] = (string)$this->getCleanISBN(); + $params['rft.volume'] = $this->getContainerVolume(); + $params['rft.issue'] = $this->getContainerIssue(); + $params['rft.spage'] = $this->getContainerStartPage(); + // unset default title -- we only want jtitle/atitle here: + unset($params['rft.title']); + $params['rft.jtitle'] = $this->getContainerTitle(); + $params['rft.atitle'] = $this->getTitle(); + $params['rft.au'] = $this->getPrimaryAuthor(); + + $params['rft.format'] = 'Article'; + $langs = $this->getLanguages(); + if (count($langs) > 0) { + $params['rft.language'] = $langs[0]; + } + return $params; + } + + /** + * Get OpenURL parameters for an unknown format. + * + * @param string $format Name of format + * + * @return array + */ + protected function getUnknownFormatOpenUrlParams($format = 'UnknownFormat') + { + $params = $this->getDefaultOpenUrlParams(); + $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:dc'; + $params['rft.creator'] = $this->getPrimaryAuthor(); + $publishers = $this->getPublishers(); + if (count($publishers) > 0) { + $params['rft.pub'] = $publishers[0]; + } + $params['rft.format'] = $format; + $langs = $this->getLanguages(); + if (count($langs) > 0) { + $params['rft.language'] = $langs[0]; + } + return $params; + } + + /** + * Get OpenURL parameters for a journal. + * + * @return array + */ + protected function getJournalOpenUrlParams() + { + $params = $this->getUnknownFormatOpenUrlParams('Journal'); + /* This is probably the most technically correct way to represent + * a journal run as an OpenURL; however, it doesn't work well with + * Zotero, so it is currently commented out -- instead, we just add + * some extra fields and to the "unknown format" case. + $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:journal'; + $params['rft.genre'] = 'journal'; + $params['rft.jtitle'] = $params['rft.title']; + $params['rft.issn'] = $this->getCleanISSN(); + $params['rft.au'] = $this->getPrimaryAuthor(); + */ + $params['rft.issn'] = (string)$this->getCleanISSN(); + + // Including a date in a title-level Journal OpenURL may be too + // limiting -- in some link resolvers, it may cause the exclusion + // of databases if they do not cover the exact date provided! + unset($params['rft.date']); + + // If we're working with the SFX resolver, we should add a + // special parameter to ensure that electronic holdings links + // are shown even though no specific date or issue is specified: + if (isset($this->mainConfig->OpenURL->resolver) + && strtolower($this->mainConfig->OpenURL->resolver) == 'sfx' + ) { + $params['sfx.ignore_date_threshold'] = 1; + } + return $params; + } + + /** + * Get the OpenURL parameters to represent this record (useful for the + * title attribute of a COinS span tag). + * + * @param bool $overrideSupportsOpenUrl Flag to override checking + * supportsOpenUrl() (default is false) + * + * @return string OpenURL parameters. + */ + public function getOpenUrl($overrideSupportsOpenUrl = false) + { + // stop here if this record does not support OpenURLs + if (!$overrideSupportsOpenUrl && !$this->supportsOpenUrl()) { + return false; + } + + // Set up parameters based on the format of the record: + $format = $this->getOpenUrlFormat(); + $method = "get{$format}OpenUrlParams"; + if (method_exists($this, $method)) { + $params = $this->$method(); + } else { + $params = $this->getUnknownFormatOpenUrlParams($format); + } + + // Assemble the URL: + return http_build_query($params); + } + + /** + * Get the OpenURL parameters to represent this record for COinS even if + * supportsOpenUrl() is false for this RecordDriver. + * + * @return string OpenURL parameters. + */ + public function getCoinsOpenUrl() + { + return $this->getOpenUrl($this->supportsCoinsOpenUrl()); + } + + /** + * Get an array of physical descriptions of the item. + * + * @return array + */ + public function getPhysicalDescriptions() + { + return isset($this->fields['physical']) ? + $this->fields['physical'] : []; + } + + /** + * Get the item's place of publication. + * + * @return array + */ + public function getPlacesOfPublication() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get an array of playing times for the record (if applicable). + * + * @return array + */ + public function getPlayingTimes() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get an array of previous titles for the record. + * + * @return array + */ + public function getPreviousTitles() + { + return isset($this->fields['title_old']) ? + $this->fields['title_old'] : []; + } + + /** + * Get the main author of the record. + * + * @return string + */ + public function getPrimaryAuthor() + { + $authors = $this->getPrimaryAuthors(); + return $authors[0] ?? ''; + } + + /** + * Get the main authors of the record. + * + * @return array + */ + public function getPrimaryAuthors() + { + return isset($this->fields['author']) + ? (array)$this->fields['author'] : []; + } + + /** + * Get an array of all main authors roles (complementing + * getSecondaryAuthorsRoles()). + * + * @return array + */ + public function getPrimaryAuthorsRoles() + { + return isset($this->fields['author_role']) ? + $this->fields['author_role'] : []; + } + + /** + * Get credits of people involved in production of the item. + * + * @return array + */ + public function getProductionCredits() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get the publication dates of the record. See also getDateSpan(). + * + * @return array + */ + public function getPublicationDates() + { + return isset($this->fields['publishDate']) ? + $this->fields['publishDate'] : []; + } + + /** + * Get human readable publication dates for display purposes (may not be suitable + * for computer processing -- use getPublicationDates() for that). + * + * @return array + */ + public function getHumanReadablePublicationDates() + { + return $this->getPublicationDates(); + } + + /** + * Get an array of publication detail lines combining information from + * getPublicationDates(), getPublishers() and getPlacesOfPublication(). + * + * @return array + */ + public function getPublicationDetails() + { + $places = $this->getPlacesOfPublication(); + $names = $this->getPublishers(); + $dates = $this->getHumanReadablePublicationDates(); + + $i = 0; + $retval = []; + while (isset($places[$i]) || isset($names[$i]) || isset($dates[$i])) { + // Build objects to represent each set of data; these will + // transform seamlessly into strings in the view layer. + $retval[] = new Response\PublicationDetails( + $places[$i] ?? '', + $names[$i] ?? '', + $dates[$i] ?? '' + ); + $i++; + } + + return $retval; + } + + /** + * Get an array of publication frequency information. + * + * @return array + */ + public function getPublicationFrequency() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get the publishers of the record. + * + * @return array + */ + public function getPublishers() + { + return isset($this->fields['publisher']) ? + $this->fields['publisher'] : []; + } + + /** + * Get an array of information about record history, obtained in real-time + * from the ILS. + * + * @return array + */ + public function getRealTimeHistory() + { + // Not supported by the default index schema -- implement in child classes. + return []; + } + + /** + * Get an array of information about record holdings, obtained in real-time + * from the ILS. + * + * @return array + */ + public function getRealTimeHoldings() + { + // Not supported by the default index schema -- implement in child classes. + return ['holdings' => []]; + } + + /** + * Get an array of strings describing relationships to other items. + * + * @return array + */ + public function getRelationshipNotes() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get an array of all secondary authors (complementing getPrimaryAuthors()). + * + * @return array + */ + public function getSecondaryAuthors() + { + return isset($this->fields['author2']) ? + $this->fields['author2'] : []; + } + + /** + * Get an array of all secondary authors roles (complementing + * getPrimaryAuthorsRoles()). + * + * @return array + */ + public function getSecondaryAuthorsRoles() + { + return isset($this->fields['author2_role']) ? + $this->fields['author2_role'] : []; + } + + /** + * Get an array of all series names containing the record. Array entries may + * be either the name string, or an associative array with 'name' and 'number' + * keys. + * + * @return array + */ + public function getSeries() + { + // Only use the contents of the series2 field if the series field is empty + if (isset($this->fields['series']) && !empty($this->fields['series'])) { + return $this->fields['series']; + } + return isset($this->fields['series2']) ? + $this->fields['series2'] : []; + } + + /** + * Get the short (pre-subtitle) title of the record. + * + * @return string + */ + public function getShortTitle() + { + return isset($this->fields['title_short']) ? + $this->fields['title_short'] : ''; + } + + /** + * Get the item's source. + * + * @return string + */ + public function getSource() + { + // Not supported in base class: + return ''; + } + + /** + * Get the subtitle of the record. + * + * @return string + */ + public function getSubtitle() + { + return isset($this->fields['title_sub']) ? + $this->fields['title_sub'] : ''; + } + + /** + * Get an array of technical details on the item represented by the record. + * + * @return array + */ + public function getSystemDetails() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get an array of summary strings for the record. + * + * @return array + */ + public function getSummary() + { + // We need to return an array, so if we have a description, turn it into an + // array as needed (it should be a flat string according to the default + // schema, but we might as well support the array case just to be on the safe + // side: + if (isset($this->fields['description']) + && !empty($this->fields['description']) + ) { + return is_array($this->fields['description']) + ? $this->fields['description'] : [$this->fields['description']]; + } + + // If we got this far, no description was found: + return []; + } + + /** + * Get an array of note about the record's target audience. + * + * @return array + */ + public function getTargetAudienceNotes() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Returns one of three things: a full URL to a thumbnail preview of the record + * if an image is available in an external system; an array of parameters to + * send to VuFind's internal cover generator if no fixed URL exists; or false + * if no thumbnail can be generated. + * + * @param string $size Size of thumbnail (small, medium or large -- small is + * default). + * + * @return string|array|bool + */ + public function getThumbnail($size = 'small') + { + if (isset($this->fields['thumbnail']) && $this->fields['thumbnail']) { + return $this->fields['thumbnail']; + } + $arr = [ + 'author' => mb_substr($this->getPrimaryAuthor(), 0, 300, 'utf-8'), + 'callnumber' => $this->getCallNumber(), + 'size' => $size, + 'title' => mb_substr($this->getTitle(), 0, 300, 'utf-8'), + 'recordid' => $this->getUniqueID(), + 'source' => $this->getSourceIdentifier(), + ]; + if ($isbn = $this->getCleanISBN()) { + $arr['isbn'] = $isbn; + } + if ($issn = $this->getCleanISSN()) { + $arr['issn'] = $issn; + } + if ($oclc = $this->getCleanOCLCNum()) { + $arr['oclc'] = $oclc; + } + if ($upc = $this->getCleanUPC()) { + $arr['upc'] = $upc; + } + // If an ILS driver has injected extra details, check for IDs in there + // to fill gaps: + if ($ilsDetails = $this->getExtraDetail('ils_details')) { + foreach (['isbn', 'issn', 'oclc', 'upc'] as $key) { + if (!isset($arr[$key]) && isset($ilsDetails[$key])) { + $arr[$key] = $ilsDetails[$key]; + } + } + } + return $arr; + } + + /** + * Get the full title of the record. + * + * @return string + */ + public function getTitle() + { + return isset($this->fields['title']) ? + $this->fields['title'] : ''; + } + + /** + * Get the text of the part/section portion of the title. + * + * @return string + */ + public function getTitleSection() + { + // Not currently stored in the default index schema + return null; + } + + /** + * Get the statement of responsibility that goes with the title (i.e. "by John + * Smith"). + * + * @return string + */ + public function getTitleStatement() + { + // Not currently stored in the default index schema + return null; + } + + /** + * Get an array of lines from the table of contents. + * + * @return array + */ + public function getTOC() + { + return isset($this->fields['contents']) + ? $this->fields['contents'] : []; + } + + /** + * Get hierarchical place names + * + * @return array + */ + public function getHierarchicalPlaceNames() + { + // Not currently stored in the default index schema + return []; + } + + /** + * Get the UPC number(s) of the record. + * + * @return array + */ + public function getUPC() + { + return isset($this->fields['upc_str_mv']) ? + $this->fields['upc_str_mv'] : []; + } + + /** + * Return an array of associative URL arrays with one or more of the following + * keys: + * + * <li> + * <ul>desc: URL description text to display (optional)</ul> + * <ul>url: fully-formed URL (required if 'route' is absent)</ul> + * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> + * <ul>routeParams: Parameters for route (optional)</ul> + * <ul>queryString: Query params to append after building route (optional)</ul> + * </li> + * + * @return array + */ + public function getURLs() + { + // If non-empty, map internal URL array to expected return format; + // otherwise, return empty array: + if (isset($this->fields['url']) && is_array($this->fields['url'])) { + $filter = function ($url) { + return ['url' => $url]; + }; + return array_map($filter, $this->fields['url']); + } + return []; + } + + /** + * Get the hierarchy_top_id(s) associated with this item (empty if none). + * + * @return array + */ + public function getHierarchyTopID() + { + // Unsupported by default: + return []; + } + + /** + * Get the absolute parent title(s) associated with this item (empty if none). + * + * @return array + */ + public function getHierarchyTopTitle() + { + // Unsupported by default: + return []; + } + + /** + * Get an associative array (id => title) of collections containing this record. + * + * @return array + */ + public function getContainingCollections() + { + // Unsupported by default: + return []; + } + + /** + * Get the value of whether or not this is a collection level record + * + * NOTE: \VuFind\Hierarchy\TreeDataFormatter\AbstractBase::isCollection() + * duplicates some of this logic. + * + * @return bool + */ + public function isCollection() + { + // Unsupported by default: + return false; + } + + /** + * Get a list of hierarchy trees containing this record. + * + * @param string $hierarchyID The hierarchy to get the tree for + * + * @return mixed An associative array of hierarchy trees on success + * (id => title), false if no hierarchies found + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getHierarchyTrees($hierarchyID = false) + { + // Unsupported by default: + return false; + } + + /** + * Get the Hierarchy Type (false if none) + * + * @return string|bool + */ + public function getHierarchyType() + { + // Unsupported by default: + return false; + } + + /** + * Return the unique identifier of this record within the index; + * useful for retrieving additional information (like tags and user + * comments) from the external MySQL database. + * + * @return string Unique identifier. + */ + public function getUniqueID() + { + if (!isset($this->fields['id'])) { + throw new \Exception('ID not set!'); + } + return $this->fields['id']; + } + + /** + * Return an XML representation of the record using the specified format. + * Return false if the format is unsupported. + * + * @param string $format Name of format to use (corresponds with OAI-PMH + * metadataPrefix parameter). + * @param string $baseUrl Base URL of host containing VuFind (optional; + * may be used to inject record URLs into XML when appropriate). + * @param RecordLink $recordLink Record link helper (optional; may be used to + * inject record URLs into XML when appropriate). + * + * @return mixed XML, or false if format unsupported. + */ + public function getXML($format, $baseUrl = null, $recordLink = null) + { + // For OAI-PMH Dublin Core, produce the necessary XML: + if ($format == 'oai_dc') { + $dc = 'http://purl.org/dc/elements/1.1/'; + $xml = new \SimpleXMLElement( + '<oai_dc:dc ' + . 'xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" ' + . 'xmlns:dc="' . $dc . '" ' + . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + . 'xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ ' + . 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd" />' + ); + $xml->addChild('title', htmlspecialchars($this->getTitle()), $dc); + $authors = $this->getDeduplicatedAuthors(); + foreach ($authors as $list) { + foreach (array_keys($list) as $author) { + $xml->addChild('creator', htmlspecialchars($author), $dc); + } + } + foreach ($this->getLanguages() as $lang) { + $xml->addChild('language', htmlspecialchars($lang), $dc); + } + foreach ($this->getPublishers() as $pub) { + $xml->addChild('publisher', htmlspecialchars($pub), $dc); + } + foreach ($this->getPublicationDates() as $date) { + $xml->addChild('date', htmlspecialchars($date), $dc); + } + foreach ($this->getAllSubjectHeadings() as $subj) { + $xml->addChild( + 'subject', htmlspecialchars(implode(' -- ', $subj)), $dc + ); + } + if (null !== $baseUrl && null !== $recordLink) { + $url = $baseUrl . $recordLink->getUrl($this); + $xml->addChild('identifier', $url, $dc); + } + + return $xml->asXml(); + } + + // Unsupported format: + return false; + } + + /** + * Get an array of strings representing citation formats supported + * by this record's data (empty if none). For possible legal values, + * see /application/themes/root/helpers/Citation.php, getCitation() + * method. + * + * @return array Strings representing citation formats. + */ + protected function getSupportedCitationFormats() + { + return ['APA', 'Chicago', 'MLA']; + } + + /** + * Get the title of the item that contains this record (i.e. MARC 773s of a + * journal). + * + * @return string + */ + public function getContainerTitle() + { + return isset($this->fields['container_title']) + ? $this->fields['container_title'] : ''; + } + + /** + * Get the volume of the item that contains this record (i.e. MARC 773v of a + * journal). + * + * @return string + */ + public function getContainerVolume() + { + return isset($this->fields['container_volume']) + ? $this->fields['container_volume'] : ''; + } + + /** + * Get the issue of the item that contains this record (i.e. MARC 773l of a + * journal). + * + * @return string + */ + public function getContainerIssue() + { + return isset($this->fields['container_issue']) + ? $this->fields['container_issue'] : ''; + } + + /** + * Get the start page of the item that contains this record (i.e. MARC 773q of a + * journal). + * + * @return string + */ + public function getContainerStartPage() + { + return isset($this->fields['container_start_page']) + ? $this->fields['container_start_page'] : ''; + } + + /** + * Get the end page of the item that contains this record. + * + * @return string + */ + public function getContainerEndPage() + { + // Not supported by the default index schema -- implement in child classes. + return ''; + } + + /** + * Get a full, free-form reference to the context of the item that contains this + * record (i.e. volume, year, issue, pages). + * + * @return string + */ + public function getContainerReference() + { + return isset($this->fields['container_reference']) + ? $this->fields['container_reference'] : ''; + } + + /** + * Get a sortable title for the record (i.e. no leading articles). + * + * @return string + */ + public function getSortTitle() + { + return isset($this->fields['title_sort']) + ? $this->fields['title_sort'] : parent::getSortTitle(); + } + + /** + * Get schema.org type mapping, an array of sub-types of + * http://schema.org/CreativeWork, defaulting to CreativeWork + * itself if nothing else matches. + * + * @return array + */ + public function getSchemaOrgFormatsArray() + { + $types = []; + foreach ($this->getFormats() as $format) { + switch ($format) { + case 'Book': + case 'eBook': + $types['Book'] = 1; + break; + case 'Video': + case 'VHS': + $types['Movie'] = 1; + break; + case 'Photo': + $types['Photograph'] = 1; + break; + case 'Map': + $types['Map'] = 1; + break; + case 'Audio': + $types['MusicAlbum'] = 1; + break; + default: + $types['CreativeWork'] = 1; + } + } + return array_keys($types); + } + + /** + * Get schema.org type mapping, expected to be a space-delimited string of + * sub-types of http://schema.org/CreativeWork, defaulting to CreativeWork + * itself if nothing else matches. + * + * @return string + */ + public function getSchemaOrgFormats() + { + return implode(' ', $this->getSchemaOrgFormatsArray()); + } + + /** + * Get information on records deduplicated with this one + * + * @return array Array keyed by source id containing record id + */ + public function getDedupData() + { + return isset($this->fields['dedup_data']) + ? $this->fields['dedup_data'] + : []; + } + + /** + * Get the number of child records belonging to this record + * + * @return int Number of records + */ + public function getChildRecordCount() + { + // Unsupported by default + return 0; + } + + /** + * Get the container record id. + * + * @return string Container record id (empty string if none) + */ + public function getContainerRecordID() + { + // Unsupported by default + return ''; + } + + /** + * Get the bbox-geo variable. + * + * @return array + */ + public function getGeoLocation() + { + return isset($this->fields['long_lat']) + ? $this->fields['long_lat'] : []; + } + + /** + * Get the map display (lat/lon) coordinates + * + * @return array + */ + public function getDisplayCoordinates() + { + return isset($this->fields['long_lat_display']) + ? $this->fields['long_lat_display'] : []; + } + + /** + * Get the map display (lat/lon) labels + * + * @return array + */ + public function getCoordinateLabels() + { + return isset($this->fields['long_lat_label']) + ? $this->fields['long_lat_label'] : []; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/EDS.php b/module/VuFind/src/VuFind/RecordDriver/EDS.php index 6a77afe19cf3f66f3664095ce7a2e9afac312020..bd3b2e9a7b0369cbca5e99fda48dd44d66151f6c 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EDS.php +++ b/module/VuFind/src/VuFind/RecordDriver/EDS.php @@ -2,7 +2,7 @@ /** * Model for EDS records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class EDS extends SolrDefault +class EDS extends DefaultRecord { /** * Document types that are treated as PDF links. @@ -199,8 +199,8 @@ class EDS extends SolrDefault if (isset($this->fields['Items']) && !empty($this->fields['Items'])) { foreach ($this->fields['Items'] as $item) { $items[] = [ - 'Label' => isset($item['Label']) ? $item['Label'] : '', - 'Group' => isset($item['Group']) ? $item['Group'] : '', + 'Label' => $item['Label'] ?? '', + 'Group' => $item['Group'] ?? '', 'Data' => isset($item['Data']) ? $this->toHTML($item['Data'], $item['Group']) : '' ]; @@ -391,7 +391,6 @@ class EDS extends SolrDefault return $this->toHTML($item['Data']); } } - } return ''; } diff --git a/module/VuFind/src/VuFind/RecordDriver/EIT.php b/module/VuFind/src/VuFind/RecordDriver/EIT.php index 64b471f86924d224ce140fe4d1d6afd1121c56b4..25f15985817280647a2719a3043382c6c4ef9bc8 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EIT.php +++ b/module/VuFind/src/VuFind/RecordDriver/EIT.php @@ -2,7 +2,7 @@ /** * Model for records retrieved via EBSCO's EIT API. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Julia Bauder 2013. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class EIT extends SolrDefault +class EIT extends DefaultRecord { /** * Used for identifying search backends @@ -66,7 +66,7 @@ class EIT extends SolrDefault public function setRawData($data) { // Easy way to recursively convert a SimpleXML Object to an array - $data = json_decode(json_encode((array) $data), 1); + $data = json_decode(json_encode((array)$data), 1); if (isset($data['fields'])) { $this->fields = $data['fields']; } else { @@ -150,7 +150,7 @@ class EIT extends SolrDefault $this->controlInfo['jinfo']['issn'] : false; } - /** + /** * Get the date coverage for a record which spans a period of time (i.e. a * journal). Use getPublicationDates for publication dates of particular * monographic items. @@ -203,7 +203,6 @@ class EIT extends SolrDefault return isset($this->controlInfo['artinfo']['aug']['au']) ? [$this->controlInfo['artinfo']['aug']['au']] : []; } - } /** @@ -217,7 +216,7 @@ class EIT extends SolrDefault return [ $this->controlInfo['pubinfo']['dt']['@attributes']['year'] ]; - } else if (isset($this->controlInfo['pubinfo']['dt'])) { + } elseif (isset($this->controlInfo['pubinfo']['dt'])) { return [$this->controlInfo['pubinfo']['dt']]; } else { return []; @@ -246,7 +245,7 @@ class EIT extends SolrDefault ? $this->controlInfo['artinfo']['tig']['atl'] : ''; } - /** + /** * Get an array of summary strings for the record. * * @return array @@ -269,7 +268,7 @@ class EIT extends SolrDefault return []; } - /** + /** * Get the full title of the record. * * @return string @@ -280,7 +279,7 @@ class EIT extends SolrDefault ? $this->controlInfo['artinfo']['tig']['atl'] : ''; } - /** + /** * Return an array of associative URL arrays with one or more of the following * keys: * @@ -327,7 +326,7 @@ class EIT extends SolrDefault return $this->fields['fields']['header']['@attributes']['uiTerm']; } - /** + /** * Get the title of the item that contains this record (i.e. MARC 773s of a * journal). * @@ -397,13 +396,13 @@ class EIT extends SolrDefault $pagecount = $this->getContainerPageCount(); $endpage = $startpage + $pagecount; if ($endpage != 0) { - return $endpage; + return $endpage; } else { return null; } } - /** + /** * Get a sortable title for the record (i.e. no leading articles). * * @return string @@ -426,9 +425,9 @@ class EIT extends SolrDefault $formats = $this->getFormats(); if (in_array('Book', $formats)) { return 'Book'; - } else if (in_array('Article', $formats)) { + } elseif (in_array('Article', $formats)) { return 'Article'; - } else if (in_array('Journal', $formats)) { + } elseif (in_array('Journal', $formats)) { return 'Journal'; } // Defaulting to "Article" because many EBSCO databases have things like diff --git a/module/VuFind/src/VuFind/RecordDriver/Factory.php b/module/VuFind/src/VuFind/RecordDriver/Factory.php deleted file mode 100644 index f31badeefcfb274c2a27723349d11aa6f22e05e1..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/RecordDriver/Factory.php +++ /dev/null @@ -1,265 +0,0 @@ -<?php -/** - * Record Driver Factory Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package RecordDrivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\RecordDriver; -use Zend\ServiceManager\ServiceManager; - -/** - * Record Driver Factory Class - * - * @category VuFind - * @package RecordDrivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for EDS record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return EDS - */ - public static function getEDS(ServiceManager $sm) - { - $eds = $sm->getServiceLocator()->get('VuFind\Config')->get('EDS'); - return new EDS( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), $eds, $eds - ); - } - - /** - * Factory for EIT record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return EIT - */ - public static function getEIT(ServiceManager $sm) - { - $eit = $sm->getServiceLocator()->get('VuFind\Config')->get('EIT'); - return new EIT( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), $eit, $eit - ); - } - - /** - * Factory for Missing record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Missing - */ - public static function getMissing(ServiceManager $sm) - { - return new Missing( - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Factory for Pazpar2 record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Pazpar2 - */ - public static function getPazpar2(ServiceManager $sm) - { - $pp2 = $sm->getServiceLocator()->get('VuFind\Config')->get('Pazpar2'); - return new Pazpar2( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), $pp2, $pp2 - ); - } - - /** - * Factory for Primo record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Primo - */ - public static function getPrimo(ServiceManager $sm) - { - $primo = $sm->getServiceLocator()->get('VuFind\Config')->get('Primo'); - $driver = new Primo( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - $primo, $primo - ); - return $driver; - } - - /** - * Factory for SolrAuth record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrAuth - */ - public static function getSolrAuth(ServiceManager $sm) - { - return new SolrAuth( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - null, - $sm->getServiceLocator()->get('VuFind\Config')->get('searches') - ); - } - - /** - * Factory for SolrDefault record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrDefault - */ - public static function getSolrDefault(ServiceManager $sm) - { - $driver = new SolrDefault( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - null, - $sm->getServiceLocator()->get('VuFind\Config')->get('searches') - ); - $driver->attachSearchService($sm->getServiceLocator()->get('VuFind\Search')); - return $driver; - } - - /** - * Factory for SolrMarc record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrMarc - */ - public static function getSolrMarc(ServiceManager $sm) - { - $driver = new SolrMarc( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - null, - $sm->getServiceLocator()->get('VuFind\Config')->get('searches') - ); - $driver->attachILS( - $sm->getServiceLocator()->get('VuFind\ILSConnection'), - $sm->getServiceLocator()->get('VuFind\ILSHoldLogic'), - $sm->getServiceLocator()->get('VuFind\ILSTitleHoldLogic') - ); - $driver->attachSearchService($sm->getServiceLocator()->get('VuFind\Search')); - return $driver; - } - - /** - * Factory for SolrMarcRemote record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrMarc - */ - public static function getSolrMarcRemote(ServiceManager $sm) - { - $driver = new SolrMarcRemote( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - null, - $sm->getServiceLocator()->get('VuFind\Config')->get('searches') - ); - $driver->attachILS( - $sm->getServiceLocator()->get('VuFind\ILSConnection'), - $sm->getServiceLocator()->get('VuFind\ILSHoldLogic'), - $sm->getServiceLocator()->get('VuFind\ILSTitleHoldLogic') - ); - $driver->attachSearchService($sm->getServiceLocator()->get('VuFind\Search')); - return $driver; - } - - /** - * Factory for SolrReserves record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrReserves - */ - public static function getSolrReserves(ServiceManager $sm) - { - return new SolrReserves( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - null, - $sm->getServiceLocator()->get('VuFind\Config')->get('searches') - ); - } - - /** - * Factory for SolrWeb record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return SolrWeb - */ - public static function getSolrWeb(ServiceManager $sm) - { - $web = $sm->getServiceLocator()->get('VuFind\Config')->get('website'); - return new SolrWeb( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), $web, $web - ); - } - - /** - * Factory for Summon record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Summon - */ - public static function getSummon(ServiceManager $sm) - { - $summon = $sm->getServiceLocator()->get('VuFind\Config')->get('Summon'); - $driver = new Summon( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - $summon, $summon - ); - $driver->setDateConverter( - $sm->getServiceLocator()->get('VuFind\DateConverter') - ); - return $driver; - } - - /** - * Factory for WorldCat record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return WorldCat - */ - public static function getWorldCat(ServiceManager $sm) - { - $wc = $sm->getServiceLocator()->get('VuFind\Config')->get('WorldCat'); - return new WorldCat( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), $wc, $wc - ); - } -} diff --git a/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php b/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..2804c99cde64443a1a26f19ef442c6f3a31eb577 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php @@ -0,0 +1,237 @@ +<?php +/** + * Hierarchy support for record drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +/** + * Hierarchy support for record drivers. + * + * Assumption: Hierarchy fields found in $this->fields. + * Assumption: Config object found in $this->mainConfig. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +trait HierarchyAwareTrait +{ + /** + * Hierarchy driver plugin manager + * + * @var \VuFind\Hierarchy\Driver\PluginManager + */ + protected $hierarchyDriverManager = null; + + /** + * Hierarchy driver for current object + * + * @var \VuFind\Hierarchy\Driver\AbstractBase + */ + protected $hierarchyDriver = null; + + /** + * Get a hierarchy driver appropriate to the current object. (May be false if + * disabled/unavailable). + * + * @return \VuFind\Hierarchy\Driver\AbstractBase|bool + */ + public function getHierarchyDriver() + { + if (null === $this->hierarchyDriver + && null !== $this->hierarchyDriverManager + ) { + $type = $this->getHierarchyType(); + $this->hierarchyDriver = $type + ? $this->hierarchyDriverManager->get($type) : false; + } + return $this->hierarchyDriver; + } + + /** + * Inject a hierarchy driver plugin manager. + * + * @param \VuFind\Hierarchy\Driver\PluginManager $pm Hierarchy driver manager + * + * @return object + */ + public function setHierarchyDriverManager( + \VuFind\Hierarchy\Driver\PluginManager $pm + ) { + $this->hierarchyDriverManager = $pm; + return $this; + } + + /** + * Get the hierarchy_top_id(s) associated with this item (empty if none). + * + * @return array + */ + public function getHierarchyTopID() + { + return isset($this->fields['hierarchy_top_id']) + ? $this->fields['hierarchy_top_id'] : []; + } + + /** + * Get the absolute parent title(s) associated with this item (empty if none). + * + * @return array + */ + public function getHierarchyTopTitle() + { + return isset($this->fields['hierarchy_top_title']) + ? $this->fields['hierarchy_top_title'] : []; + } + + /** + * Get an associative array (id => title) of collections containing this record. + * + * @return array + */ + public function getContainingCollections() + { + // If collections are disabled or this record is not part of a hierarchy, go + // no further.... + if (!isset($this->mainConfig->Collections->collections) + || !$this->mainConfig->Collections->collections + || !($hierarchyDriver = $this->getHierarchyDriver()) + ) { + return false; + } + + // Initialize some variables needed within the switch below: + $isCollection = $this->isCollection(); + $titles = $ids = []; + + // Check config setting for what constitutes a collection, act accordingly: + switch ($hierarchyDriver->getCollectionLinkType()) { + case 'All': + if (isset($this->fields['hierarchy_parent_title']) + && isset($this->fields['hierarchy_parent_id']) + ) { + $titles = $this->fields['hierarchy_parent_title']; + $ids = $this->fields['hierarchy_parent_id']; + } + break; + case 'Top': + if (isset($this->fields['hierarchy_top_title']) + && isset($this->fields['hierarchy_top_id']) + ) { + foreach ($this->fields['hierarchy_top_id'] as $i => $topId) { + // Don't mark an item as its own parent -- filter out parent + // collections whose IDs match that of the current collection. + if (!$isCollection + || $topId !== $this->fields['is_hierarchy_id'] + ) { + $ids[] = $topId; + $titles[] = $this->fields['hierarchy_top_title'][$i]; + } + } + } + break; + } + + // Map the titles and IDs to a useful format: + $c = count($ids); + $retVal = []; + for ($i = 0; $i < $c; $i++) { + $retVal[$ids[$i]] = $titles[$i]; + } + return $retVal; + } + + /** + * Get the value of whether or not this is a collection level record + * + * NOTE: \VuFind\Hierarchy\TreeDataFormatter\AbstractBase::isCollection() + * duplicates some of this logic. + * + * @return bool + */ + public function isCollection() + { + if (!($hierarchyDriver = $this->getHierarchyDriver())) { + // Not a hierarchy type record + return false; + } + + // Check config setting for what constitutes a collection + switch ($hierarchyDriver->getCollectionLinkType()) { + case 'All': + return isset($this->fields['is_hierarchy_id']); + case 'Top': + return isset($this->fields['is_hierarchy_title']) + && isset($this->fields['is_hierarchy_id']) + && in_array( + $this->fields['is_hierarchy_id'], + $this->fields['hierarchy_top_id'] + ); + default: + // Default to not be a collection level record + return false; + } + } + + /** + * Get a list of hierarchy trees containing this record. + * + * @param string $hierarchyID The hierarchy to get the tree for + * + * @return mixed An associative array of hierarchy trees on success + * (id => title), false if no hierarchies found + */ + public function getHierarchyTrees($hierarchyID = false) + { + $hierarchyDriver = $this->getHierarchyDriver(); + if ($hierarchyDriver && $hierarchyDriver->showTree()) { + return $hierarchyDriver->getTreeRenderer($this) + ->getTreeList($hierarchyID); + } + return false; + } + + /** + * Get the Hierarchy Type (false if none) + * + * @return string|bool + */ + public function getHierarchyType() + { + if (isset($this->fields['hierarchy_top_id'])) { + $hierarchyType = isset($this->fields['hierarchytype']) + ? $this->fields['hierarchytype'] : false; + if (!$hierarchyType) { + $hierarchyType = isset($this->mainConfig->Hierarchy->driver) + ? $this->mainConfig->Hierarchy->driver : false; + } + return $hierarchyType; + } + return false; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php b/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..de9a28fca9b2ae089d4ce095e0f0bf3aab004e91 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php @@ -0,0 +1,97 @@ +<?php +/** + * ILS aware delegator factory + * + * Copyright (C) Villanova University 2018. + * + * PHP version 7 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\DelegatorFactoryInterface; + +/** + * ILS aware delegator factory + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +class IlsAwareDelegatorFactory implements DelegatorFactoryInterface +{ + /** + * Invokes this factory. + * + * @param ContainerInterface $container Service container + * @param string $name Service name + * @param callable $callback Service callback + * @param array|null $options Service options + * + * @return AbstractBase + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $name, + callable $callback, array $options = null + ) { + $driver = call_user_func($callback); + + // Attach the ILS if at least one backend supports it: + $ilsBackends = $this->getIlsBackends($container); + if (!empty($ilsBackends) && $container->has('VuFind\ILS\Connection')) { + $driver->attachILS( + $container->get('VuFind\ILS\Connection'), + $container->get('VuFind\ILS\Logic\Holds'), + $container->get('VuFind\ILS\Logic\TitleHolds') + ); + $driver->setIlsBackends($ilsBackends); + } + + return $driver; + } + + /** + * Get the ILS backend configuration. + * + * @param ContainerInterface $container Service container + * + * @return string[] + */ + protected function getIlsBackends(ContainerInterface $container) + { + // Get a list of ILS-compatible backends. + static $ilsBackends = null; + if (!is_array($ilsBackends)) { + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $settings = isset($config->Catalog) ? $config->Catalog->toArray() : []; + + // If the setting is missing, default to the default backend; if it + // is present but empty, don't put an empty string in the final array! + $rawSetting = $settings['ilsBackends'] ?? [DEFAULT_SEARCH_BACKEND]; + $ilsBackends = empty($rawSetting) ? [] : (array)$rawSetting; + } + return $ilsBackends; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/IlsAwareTrait.php b/module/VuFind/src/VuFind/RecordDriver/IlsAwareTrait.php index a7d95a0036dc4257986342bfdf0c271d0039d236..323ff5cbee483f4f2c2d25bc8fc1f9a7cda576d5 100644 --- a/module/VuFind/src/VuFind/RecordDriver/IlsAwareTrait.php +++ b/module/VuFind/src/VuFind/RecordDriver/IlsAwareTrait.php @@ -2,7 +2,7 @@ /** * ILS support for MARC and other types of records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015. @@ -48,19 +48,26 @@ trait IlsAwareTrait */ protected $ils = null; + /** + * Backends with ILS integration. + * + * @var string[] + */ + protected $ilsBackends = []; + /** * Hold logic * * @var \VuFind\ILS\Logic\Holds */ - protected $holdLogic; + protected $holdLogic = null; /** * Title hold logic * * @var \VuFind\ILS\Logic\TitleHolds */ - protected $titleHoldLogic; + protected $titleHoldLogic = null; /** * Attach an ILS connection and related logic to the driver @@ -87,7 +94,8 @@ trait IlsAwareTrait */ protected function hasILS() { - return null !== $this->ils; + return null !== $this->ils + && in_array($this->getSourceIdentifier(), $this->ilsBackends); } /** @@ -141,6 +149,18 @@ trait IlsAwareTrait return false; } + /** + * Set the list of backends that support ILS integration. + * + * @param array $backends List of backends that support ILS integration + * + * @return string[] + */ + public function setIlsBackends($backends) + { + $this->ilsBackends = $backends; + } + /** * Returns true if the record supports real-time AJAX status lookups. * diff --git a/module/VuFind/src/VuFind/RecordDriver/LibGuides.php b/module/VuFind/src/VuFind/RecordDriver/LibGuides.php index c7cfecc561c461ba1a2bad46a072a576d1835687..6a050a528798a1762e63f0fd20a4de341bee4016 100644 --- a/module/VuFind/src/VuFind/RecordDriver/LibGuides.php +++ b/module/VuFind/src/VuFind/RecordDriver/LibGuides.php @@ -2,7 +2,7 @@ /** * Model for LibGuides records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class LibGuides extends SolrDefault +class LibGuides extends DefaultRecord { /** * Get the short (pre-subtitle) title of the record. diff --git a/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php b/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..d6afe45a08b9090d5dff1ae03c988f850d29f6f9 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php @@ -0,0 +1,902 @@ +<?php +/** + * Functions to add advanced MARC-driven functionality to a record driver already + * powered by the standard index spec. Depends upon MarcReaderTrait. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +use VuFind\View\Helper\Root\RecordLink; +use VuFind\XSLT\Processor as XSLTProcessor; + +/** + * Functions to add advanced MARC-driven functionality to a record driver already + * powered by the standard index spec. Depends upon MarcReaderTrait. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +trait MarcAdvancedTrait +{ + /** + * Fields that may contain subject headings, and their descriptions + * + * @var array + */ + protected $subjectFields = [ + '600' => 'personal name', + '610' => 'corporate name', + '611' => 'meeting name', + '630' => 'uniform title', + '648' => 'chronological', + '650' => 'topic', + '651' => 'geographic', + '653' => '', + '655' => 'genre/form', + '656' => 'occupation' + ]; + + /** + * Mappings from subject source indicators (2nd indicator of subject fields in + * MARC 21) to the their codes. + * + * @var array + * @link https://www.loc.gov/marc/bibliographic/bd6xx.html Subject field docs + * @link https://www.loc.gov/standards/sourcelist/subject.html Code list + */ + protected $subjectSources = [ + '0' => 'lcsh', + '1' => 'lcshac', + '2' => 'mesh', + '3' => 'nal', + '4' => 'unknown', + '5' => 'cash', + '6' => 'rvm' + ]; + + /** + * Get access restriction notes for the record. + * + * @return array + */ + public function getAccessRestrictions() + { + return $this->getFieldArray('506'); + } + + /** + * Get all subject headings associated with this record. Each heading is + * returned as an array of chunks, increasing from least specific to most + * specific. + * + * @param bool $extended Whether to return a keyed array with the following + * keys: + * - heading: the actual subject heading chunks + * - type: heading type + * - source: source vocabulary + * + * @return array + */ + public function getAllSubjectHeadings($extended = false) + { + // This is all the collected data: + $retval = []; + + // Try each MARC field one at a time: + foreach ($this->subjectFields as $field => $fieldType) { + // Do we have any results for the current field? If not, try the next. + $results = $this->getMarcRecord()->getFields($field); + if (!$results) { + continue; + } + + // If we got here, we found results -- let's loop through them. + foreach ($results as $result) { + // Start an array for holding the chunks of the current heading: + $current = []; + + // Get all the chunks and collect them together: + $subfields = $result->getSubfields(); + if ($subfields) { + foreach ($subfields as $subfield) { + // Numeric subfields are for control purposes and should not + // be displayed: + if (!is_numeric($subfield->getCode())) { + $current[] = $subfield->getData(); + } + } + // If we found at least one chunk, add a heading to our result: + if (!empty($current)) { + if ($extended) { + $sourceIndicator = $result->getIndicator(2); + $source = ''; + if (isset($this->subjectSources[$sourceIndicator])) { + $source = $this->subjectSources[$sourceIndicator]; + } else { + $source = $result->getSubfield('2'); + if ($source) { + $source = $source->getData(); + } + } + $retval[] = [ + 'heading' => $current, + 'type' => $fieldType, + 'source' => $source ?: '' + ]; + } else { + $retval[] = $current; + } + } + } + } + } + + // Remove duplicates and then send back everything we collected: + return array_map( + 'unserialize', array_unique(array_map('serialize', $retval)) + ); + } + + /** + * Get award notes for the record. + * + * @return array + */ + public function getAwards() + { + return $this->getFieldArray('586'); + } + + /** + * Get the bibliographic level of the current record. + * + * @return string + */ + public function getBibliographicLevel() + { + $leader = $this->getMarcRecord()->getLeader(); + $biblioLevel = strtoupper($leader[7]); + + switch ($biblioLevel) { + case 'M': // Monograph + return "Monograph"; + case 'S': // Serial + return "Serial"; + case 'A': // Monograph Part + return "MonographPart"; + case 'B': // Serial Part + return "SerialPart"; + case 'C': // Collection + return "Collection"; + case 'D': // Collection Part + return "CollectionPart"; + default: + return "Unknown"; + } + } + + /** + * Get notes on bibliography content. + * + * @return array + */ + public function getBibliographyNotes() + { + return $this->getFieldArray('504'); + } + + /** + * Return full record as filtered XML for public APIs. + * + * @return string + */ + public function getFilteredXML() + { + $record = clone $this->getMarcRecord(); + // The default implementation does not filter out any fields + // $record->deleteFields('9', true); + return $record->toXML(); + } + + /** + * Get notes on finding aids related to the record. + * + * @return array + */ + public function getFindingAids() + { + return $this->getFieldArray('555'); + } + + /** + * Get general notes on the record. + * + * @return array + */ + public function getGeneralNotes() + { + return $this->getFieldArray('500'); + } + + /** + * Get human readable publication dates for display purposes (may not be suitable + * for computer processing -- use getPublicationDates() for that). + * + * @return array + */ + public function getHumanReadablePublicationDates() + { + return $this->getPublicationInfo('c'); + } + + /** + * Get an array of newer titles for the record. + * + * @return array + */ + public function getNewerTitles() + { + // If the MARC links are being used, return blank array + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) + : []; + return in_array('785', $fieldsNames) ? [] : parent::getNewerTitles(); + } + + /** + * Get the item's places of publication. + * + * @return array + */ + public function getPlacesOfPublication() + { + return $this->getPublicationInfo(); + } + + /** + * Get an array of playing times for the record (if applicable). + * + * @return array + */ + public function getPlayingTimes() + { + $times = $this->getFieldArray('306', ['a'], false); + + // Format the times to include colons ("HH:MM:SS" format). + for ($x = 0; $x < count($times); $x++) { + $times[$x] = substr($times[$x], 0, 2) . ':' . + substr($times[$x], 2, 2) . ':' . + substr($times[$x], 4, 2); + } + + return $times; + } + + /** + * Get an array of previous titles for the record. + * + * @return array + */ + public function getPreviousTitles() + { + // If the MARC links are being used, return blank array + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) + : []; + return in_array('780', $fieldsNames) ? [] : parent::getPreviousTitles(); + } + + /** + * Get credits of people involved in production of the item. + * + * @return array + */ + public function getProductionCredits() + { + return $this->getFieldArray('508'); + } + + /** + * Get an array of publication frequency information. + * + * @return array + */ + public function getPublicationFrequency() + { + return $this->getFieldArray('310', ['a', 'b']); + } + + /** + * Get an array of strings describing relationships to other items. + * + * @return array + */ + public function getRelationshipNotes() + { + return $this->getFieldArray('580'); + } + + /** + * Get an array of all series names containing the record. Array entries may + * be either the name string, or an associative array with 'name' and 'number' + * keys. + * + * @return array + */ + public function getSeries() + { + $matches = []; + + // First check the 440, 800 and 830 fields for series information: + $primaryFields = [ + '440' => ['a', 'p'], + '800' => ['a', 'b', 'c', 'd', 'f', 'p', 'q', 't'], + '830' => ['a', 'p']]; + $matches = $this->getSeriesFromMARC($primaryFields); + if (!empty($matches)) { + return $matches; + } + + // Now check 490 and display it only if 440/800/830 were empty: + $secondaryFields = ['490' => ['a']]; + $matches = $this->getSeriesFromMARC($secondaryFields); + if (!empty($matches)) { + return $matches; + } + + // Still no results found? Resort to the Solr-based method just in case! + return parent::getSeries(); + } + + /** + * Support method for getSeries() -- given a field specification, look for + * series information in the MARC record. + * + * @param array $fieldInfo Associative array of field => subfield information + * (used to find series name) + * + * @return array + */ + protected function getSeriesFromMARC($fieldInfo) + { + $matches = []; + + // Loop through the field specification.... + foreach ($fieldInfo as $field => $subfields) { + // Did we find any matching fields? + $series = $this->getMarcRecord()->getFields($field); + if (is_array($series)) { + foreach ($series as $currentField) { + // Can we find a name using the specified subfield list? + $name = $this->getSubfieldArray($currentField, $subfields); + if (isset($name[0])) { + $currentArray = ['name' => $name[0]]; + + // Can we find a number in subfield v? (Note that number is + // always in subfield v regardless of whether we are dealing + // with 440, 490, 800 or 830 -- hence the hard-coded array + // rather than another parameter in $fieldInfo). + $number + = $this->getSubfieldArray($currentField, ['v']); + if (isset($number[0])) { + $currentArray['number'] = $number[0]; + } + + // Save the current match: + $matches[] = $currentArray; + } + } + } + } + + return $matches; + } + + /** + * Get an array of summary strings for the record. + * + * @return array + */ + public function getSummary() + { + return $this->getFieldArray('520'); + } + + /** + * Get an array of technical details on the item represented by the record. + * + * @return array + */ + public function getSystemDetails() + { + return $this->getFieldArray('538'); + } + + /** + * Get an array of note about the record's target audience. + * + * @return array + */ + public function getTargetAudienceNotes() + { + return $this->getFieldArray('521'); + } + + /** + * Get the text of the part/section portion of the title. + * + * @return string + */ + public function getTitleSection() + { + return $this->getFirstFieldValue('245', ['n', 'p']); + } + + /** + * Get the statement of responsibility that goes with the title (i.e. "by John + * Smith"). + * + * @return string + */ + public function getTitleStatement() + { + return $this->getFirstFieldValue('245', ['c']); + } + + /** + * Get an array of lines from the table of contents. + * + * @return array + */ + public function getTOC() + { + // Return empty array if we have no table of contents: + $fields = $this->getMarcRecord()->getFields('505'); + if (!$fields) { + return []; + } + + // If we got this far, we have a table -- collect it as a string: + $toc = []; + foreach ($fields as $field) { + $subfields = $field->getSubfields(); + foreach ($subfields as $subfield) { + // Break the string into appropriate chunks, filtering empty strings, + // and merge them into return array: + $toc = array_merge( + $toc, + array_filter(explode('--', $subfield->getData()), 'trim') + ); + } + } + return $toc; + } + + /** + * Get hierarchical place names (MARC field 752) + * + * Returns an array of formatted hierarchical place names, consisting of all + * alpha-subfields, concatenated for display + * + * @return array + */ + public function getHierarchicalPlaceNames() + { + $placeNames = []; + if ($fields = $this->getMarcRecord()->getFields('752')) { + foreach ($fields as $field) { + $subfields = $field->getSubfields(); + $current = []; + foreach ($subfields as $subfield) { + if (!is_numeric($subfield->getCode())) { + $current[] = $subfield->getData(); + } + } + $placeNames[] = implode(' -- ', $current); + } + } + return $placeNames; + } + + /** + * Return an array of associative URL arrays with one or more of the following + * keys: + * + * <li> + * <ul>desc: URL description text to display (optional)</ul> + * <ul>url: fully-formed URL (required if 'route' is absent)</ul> + * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> + * <ul>routeParams: Parameters for route (optional)</ul> + * <ul>queryString: Query params to append after building route (optional)</ul> + * </li> + * + * @return array + */ + public function getURLs() + { + $retVal = []; + + // Which fields/subfields should we check for URLs? + $fieldsToCheck = [ + '856' => ['y', 'z', '3'], // Standard URL + '555' => ['a'] // Cumulative index/finding aids + ]; + + foreach ($fieldsToCheck as $field => $subfields) { + $urls = $this->getMarcRecord()->getFields($field); + if ($urls) { + foreach ($urls as $url) { + // Is there an address in the current field? + $address = $url->getSubfield('u'); + if ($address) { + $address = $address->getData(); + + // Is there a description? If not, just use the URL itself. + foreach ($subfields as $current) { + $desc = $url->getSubfield($current); + if ($desc) { + break; + } + } + if ($desc) { + $desc = $desc->getData(); + } else { + $desc = $address; + } + + $retVal[] = ['url' => $address, 'desc' => $desc]; + } + } + } + } + + return $retVal; + } + + /** + * Get all record links related to the current record. Each link is returned as + * array. + * Format: + * array( + * array( + * 'title' => label_for_title + * 'value' => link_name + * 'link' => link_URI + * ), + * ... + * ) + * + * @return null|array + */ + public function getAllRecordLinks() + { + // Load configurations: + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? explode(',', $this->mainConfig->Record->marc_links) : []; + $useVisibilityIndicator + = isset($this->mainConfig->Record->marc_links_use_visibility_indicator) + ? $this->mainConfig->Record->marc_links_use_visibility_indicator : true; + + $retVal = []; + foreach ($fieldsNames as $value) { + $value = trim($value); + $fields = $this->getMarcRecord()->getFields($value); + if (!empty($fields)) { + foreach ($fields as $field) { + // Check to see if we should display at all + if ($useVisibilityIndicator) { + $visibilityIndicator = $field->getIndicator('1'); + if ($visibilityIndicator == '1') { + continue; + } + } + + // Get data for field + $tmp = $this->getFieldData($field); + if (is_array($tmp)) { + $retVal[] = $tmp; + } + } + } + } + return empty($retVal) ? null : $retVal; + } + + /** + * Support method for getFieldData() -- factor the relationship indicator + * into the field number where relevant to generate a note to associate + * with a record link. + * + * @param File_MARC_Data_Field $field Field to examine + * + * @return string + */ + protected function getRecordLinkNote($field) + { + // If set, use relationship information from subfield i + if ($subfieldI = $field->getSubfield('i')) { + $data = trim($subfieldI->getData()); + if (!empty($data)) { + return $data; + } + } + + // Normalize blank relationship indicator to 0: + $relationshipIndicator = $field->getIndicator('2'); + if ($relationshipIndicator == ' ') { + $relationshipIndicator = '0'; + } + + // Assign notes based on the relationship type + $value = $field->getTag(); + switch ($value) { + case '780': + if (in_array($relationshipIndicator, range('0', '7'))) { + $value .= '_' . $relationshipIndicator; + } + break; + case '785': + if (in_array($relationshipIndicator, range('0', '8'))) { + $value .= '_' . $relationshipIndicator; + } + break; + } + + return 'note_' . $value; + } + + /** + * Returns the array element for the 'getAllRecordLinks' method + * + * @param File_MARC_Data_Field $field Field to examine + * + * @return array|bool Array on success, boolean false if no + * valid link could be found in the data. + */ + protected function getFieldData($field) + { + // Make sure that there is a t field to be displayed: + if ($title = $field->getSubfield('t')) { + $title = $title->getData(); + } else { + return false; + } + + $linkTypeSetting = isset($this->mainConfig->Record->marc_links_link_types) + ? $this->mainConfig->Record->marc_links_link_types + : 'id,oclc,dlc,isbn,issn,title'; + $linkTypes = explode(',', $linkTypeSetting); + $linkFields = $field->getSubfields('w'); + + // Run through the link types specified in the config. + // For each type, check field for reference + // If reference found, exit loop and go straight to end + // If no reference found, check the next link type instead + foreach ($linkTypes as $linkType) { + switch (trim($linkType)) { + case 'oclc': + foreach ($linkFields as $current) { + if ($oclc = $this->getIdFromLinkingField($current, 'OCoLC')) { + $link = ['type' => 'oclc', 'value' => $oclc]; + } + } + break; + case 'dlc': + foreach ($linkFields as $current) { + if ($dlc = $this->getIdFromLinkingField($current, 'DLC', true)) { + $link = ['type' => 'dlc', 'value' => $dlc]; + } + } + break; + case 'id': + foreach ($linkFields as $current) { + if ($bibLink = $this->getIdFromLinkingField($current)) { + $link = ['type' => 'bib', 'value' => $bibLink]; + } + } + break; + case 'isbn': + if ($isbn = $field->getSubfield('z')) { + $link = [ + 'type' => 'isn', 'value' => trim($isbn->getData()), + 'exclude' => $this->getUniqueId() + ]; + } + break; + case 'issn': + if ($issn = $field->getSubfield('x')) { + $link = [ + 'type' => 'isn', 'value' => trim($issn->getData()), + 'exclude' => $this->getUniqueId() + ]; + } + break; + case 'title': + $link = ['type' => 'title', 'value' => $title]; + break; + } + // Exit loop if we have a link + if (isset($link)) { + break; + } + } + // Make sure we have something to display: + return !isset($link) ? false : [ + 'title' => $this->getRecordLinkNote($field), + 'value' => $title, + 'link' => $link + ]; + } + + /** + * Returns an id extracted from the identifier subfield passed in + * + * @param \File_MARC_Subfield $idField MARC field containing id information + * @param string $prefix Prefix to search for in id field + * @param bool $raw Return raw match, or normalize? + * + * @return string|bool ID on success, false on failure + */ + protected function getIdFromLinkingField($idField, $prefix = null, $raw = false) + { + $text = $idField->getData(); + if (preg_match('/\(([^)]+)\)(.+)/', $text, $matches)) { + // If prefix matches, return ID: + if ($matches[1] == $prefix) { + // Special case -- LCCN should not be stripped: + return $raw + ? $matches[2] + : trim(str_replace(range('a', 'z'), '', ($matches[2]))); + } + } elseif ($prefix == null) { + // If no prefix was given or found, we presume it is a raw bib record + return $text; + } + return false; + } + + /** + * Get Status/Holdings Information from the internally stored MARC Record + * (support method used by the NoILS driver). + * + * @param array $field The MARC Field to retrieve + * @param array $data A keyed array of data to retrieve from subfields + * + * @return array + */ + public function getFormattedMarcDetails($field, $data) + { + // Initialize return array + $matches = []; + $i = 0; + + // Try to look up the specified field, return empty array if it doesn't + // exist. + $fields = $this->getMarcRecord()->getFields($field); + if (!is_array($fields)) { + return $matches; + } + + // Extract all the requested subfields, if applicable. + foreach ($fields as $currentField) { + foreach ($data as $key => $info) { + $split = explode("|", $info); + if ($split[0] == "msg") { + if ($split[1] == "true") { + $result = true; + } elseif ($split[1] == "false") { + $result = false; + } else { + $result = $split[1]; + } + $matches[$i][$key] = $result; + } else { + // Default to subfield a if nothing is specified. + if (count($split) < 2) { + $subfields = ['a']; + } else { + $subfields = str_split($split[1]); + } + $result = $this->getSubfieldArray( + $currentField, $subfields, true + ); + $matches[$i][$key] = count($result) > 0 + ? (string)$result[0] : ''; + } + } + $matches[$i]['id'] = $this->getUniqueID(); + $i++; + } + return $matches; + } + + /** + * Return an XML representation of the record using the specified format. + * Return false if the format is unsupported. + * + * @param string $format Name of format to use (corresponds with OAI-PMH + * metadataPrefix parameter). + * @param string $baseUrl Base URL of host containing VuFind (optional; + * may be used to inject record URLs into XML when appropriate). + * @param RecordLink $recordLink Record link helper (optional; may be used to + * inject record URLs into XML when appropriate). + * + * @return mixed XML, or false if format unsupported. + */ + public function getXML($format, $baseUrl = null, $recordLink = null) + { + // Special case for MARC: + if ($format == 'marc21') { + $xml = $this->getMarcRecord()->toXML(); + $xml = str_replace( + [chr(27), chr(28), chr(29), chr(30), chr(31)], ' ', $xml + ); + $xml = simplexml_load_string($xml); + if (!$xml || !isset($xml->record)) { + return false; + } + + // Set up proper namespacing and extract just the <record> tag: + $xml->record->addAttribute('xmlns', "http://www.loc.gov/MARC21/slim"); + $xml->record->addAttribute( + 'xsi:schemaLocation', + 'http://www.loc.gov/MARC21/slim ' . + 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd', + 'http://www.w3.org/2001/XMLSchema-instance' + ); + $xml->record->addAttribute('type', 'Bibliographic'); + return $xml->record->asXML(); + } + + // Try the parent method: + return parent::getXML($format, $baseUrl, $recordLink); + } + + /** + * Get an XML RDF representation of the data in this record. + * + * @return mixed XML RDF data (empty if unsupported or error). + */ + public function getRDFXML() + { + return XSLTProcessor::process( + 'record-rdf-mods.xsl', trim($this->getMarcRecord()->toXML()) + ); + } + + /** + * Return the list of "source records" for this consortial record. + * + * @return array + */ + public function getConsortialIDs() + { + return $this->getFieldArray('035', 'a', true); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/MarcBasicTrait.php b/module/VuFind/src/VuFind/RecordDriver/MarcBasicTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..82b99c8f97e7185369d5e669ea7dab6f48fabbd5 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/MarcBasicTrait.php @@ -0,0 +1,323 @@ +<?php +/** + * Functions to add basic MARC-driven functionality to a record driver not already + * powered by the standard index spec. Depends upon MarcReaderTrait. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +/** + * Functions to add basic MARC-driven functionality to a record driver not already + * powered by the standard index spec. Depends upon MarcReaderTrait. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +trait MarcBasicTrait +{ + /** + * Get an array of all ISBNs associated with the record (may be empty). + * + * @return array + */ + public function getISBNs() + { + $isbn = array_merge( + $this->getFieldArray('020', ['a', 'z', '9'], false), + $this->getFieldArray('773', ['z']) + ); + foreach ($isbn as $key => $num) { + $isbn[$key] = str_replace("-", "", $num); + } + $isbn = array_unique($isbn); + return $isbn; + } + + /** + * Get an array of all ISSNs associated with the record (may be empty). + * + * @return array + */ + public function getISSNs() + { + $issn = array_merge( + $this->getFieldArray('022', ['a']), $this->getFieldArray('440', ['x']), + $this->getFieldArray('490', ['x']), $this->getFieldArray('730', ['x']), + $this->getFieldArray('773', ['x']), $this->getFieldArray('776', ['x']), + $this->getFieldArray('780', ['x']), $this->getFieldArray('785', ['x']) + ); + $issn = array_unique($issn); + return $issn; + } + + /** + * Get an array of all the formats associated with the record. + * + * @return array + */ + public function getFormats() + { + return $this->getFieldArray('245', ['h']); + } + + /** + * Return the unique identifier of this record within the Solr index; + * useful for retrieving additional information (like tags and user + * comments) from the external MySQL database. + * + * @return string Unique identifier. + */ + public function getUniqueID() + { + return (string)$this->getMarcRecord()->getField('001')->getData(); + } + + /** + * Get the call numbers associated with the record (empty string if none). + * + * @return array + */ + public function getCallNumbers() + { + $retVal = []; + foreach (['090', '050'] as $field) { + $callNo = $this->getFirstFieldValue($field, ['a', 'b']); + if (!empty($callNo)) { + $retVal[] = $callNo; + } + } + $dewey = $this->getDeweyCallNumber(); + if (!empty($dewey)) { + $retVal[] = $dewey; + } + return $retVal; + } + + /** + * Get the Dewey call number associated with this record (empty string if none). + * + * @return string + */ + public function getDeweyCallNumber() + { + return $this->getFirstFieldValue('082', ['a']); + } + + /** + * Get the main authors of the record. + * + * @return array + */ + public function getPrimaryAuthors() + { + $primary = $this->getFirstFieldValue('100', ['a', 'b', 'c', 'd']); + return empty($primary) ? [] : [$primary]; + } + + /** + * Get an array of all the languages associated with the record. + * + * @return array + */ + public function getLanguages() + { + $retVal = []; + $field = $this->getMarcRecord()->getField('008'); + if ($field) { + $content = $field->getData(); + if (strlen($content) >= 38) { + $retVal[] = substr($content, 35, 3); + } + } + $fields = $this->getMarcRecord()->getFields('041'); + foreach ($fields as $field) { + if (strcmp($field->getIndicator(2), '7') !== 0) { + foreach ($field->getSubFields('a') as $sf) { + $retVal[] = $sf->getData(); + } + } + } + return array_unique($retVal); + } + + /** + * Get the full title of the record. + * + * @return string + */ + public function getTitle() + { + return $this->getFirstFieldValue('245', ['a', 'b']); + } + + /** + * Get a sortable title for the record (i.e. no leading articles). + * + * @return string + */ + public function getSortTitle() + { + $field = $this->getMarcRecord()->getField('245'); + if ($field) { + $title = $field->getSubfield('a'); + if ($title) { + $skip = $field->getIndicator(2); + return substr($title->getData(), $skip); + } + } + return parent::getSortTitle(); + } + + /** + * Get the short (pre-subtitle) title of the record. + * + * @return string + */ + public function getShortTitle() + { + return $this->getFirstFieldValue('245', ['a']); + } + + /** + * Get the subtitle of the record. + * + * @return string + */ + public function getSubtitle() + { + return $this->getFirstFieldValue('245', ['b']); + } + + /** + * Get the publishers of the record. + * + * @return array + */ + public function getPublishers() + { + return $this->getPublicationInfo('b'); + } + + /** + * Get the date coverage for a record which spans a period of time (i.e. a + * journal). Use getPublicationDates for publication dates of particular + * monographic items. + * + * @return array + */ + public function getDateSpan() + { + return $this->getFieldArray('362', ['a']); + } + + /** + * Get the publication dates of the record. See also getDateSpan(). + * + * @return array + */ + public function getPublicationDates() + { + return $this->getPublicationInfo('c'); + } + + /** + * Get an array of all corporate authors (complementing getPrimaryAuthor()). + * + * @return array + */ + public function getCorporateAuthors() + { + return array_merge( + $this->getFieldArray('110', ['a', 'b']), + $this->getFieldArray('111', ['a', 'b']), + $this->getFieldArray('710', ['a', 'b']), + $this->getFieldArray('711', ['a', 'b']) + ); + } + + /** + * Get an array of all secondary authors (complementing getPrimaryAuthors()). + * + * @return array + */ + public function getSecondaryAuthors() + { + return $this->getFieldArray('700', ['a', 'b', 'c', 'd']); + } + + /** + * Get an array of newer titles for the record. + * + * @return array + */ + public function getNewerTitles() + { + return $this->getFieldArray('785', ['a', 's', 't']); + } + + /** + * Get an array of previous titles for the record. + * + * @return array + */ + public function getPreviousTitles() + { + return $this->getFieldArray('780', ['a', 's', 't']); + } + + /** + * Get the edition of the current record. + * + * @return string + */ + public function getEdition() + { + return $this->getFirstFieldValue('250', ['a']); + } + + /** + * Get a raw, unnormalized LCCN. (See DefaultRecord::getLCCN for normalization). + * + * @return string + */ + protected function getRawLCCN() + { + return $this->getFirstFieldValue('010', ['a']); + } + + /** + * Get an array of physical descriptions of the item. + * + * @return array + */ + public function getPhysicalDescriptions() + { + return $this->getFieldArray('300', ['a', 'b', 'c', 'e', 'f', 'g'], true); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/MarcReaderTrait.php b/module/VuFind/src/VuFind/RecordDriver/MarcReaderTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..57c06afaac0743c1ba7b40a440cbc908adc1f642 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/MarcReaderTrait.php @@ -0,0 +1,231 @@ +<?php +/** + * Functions for reading MARC records. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +/** + * Functions for reading MARC records. + * + * Assumption: raw MARC data can be found in $this->fields['fullrecord']. + * + * Assumption: VuFind config available as $this->mainConfig + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +trait MarcReaderTrait +{ + /** + * MARC record. Access only via getMarcRecord() as this is initialized lazily. + * + * @var \File_MARC_Record + */ + protected $lazyMarcRecord = null; + + /** + * Get access to the raw File_MARC object. + * + * @return \File_MARCBASE + */ + public function getMarcRecord() + { + if (null === $this->lazyMarcRecord) { + $marc = trim($this->fields['fullrecord']); + + // check if we are dealing with MARCXML + if (substr($marc, 0, 1) == '<') { + $marc = new \File_MARCXML($marc, \File_MARCXML::SOURCE_STRING); + } else { + // When indexing over HTTP, SolrMarc may use entities instead of + // certain control characters; we should normalize these: + $marc = str_replace( + ['#29;', '#30;', '#31;'], ["\x1D", "\x1E", "\x1F"], $marc + ); + $marc = new \File_MARC($marc, \File_MARC::SOURCE_STRING); + } + + $this->lazyMarcRecord = $marc->next(); + if (!$this->lazyMarcRecord) { + throw new \File_MARC_Exception('Cannot Process MARC Record'); + } + } + + return $this->lazyMarcRecord; + } + + /** + * Return an array of all values extracted from the specified field/subfield + * combination. If multiple subfields are specified and $concat is true, they + * will be concatenated together in the order listed -- each entry in the array + * will correspond with a single MARC field. If $concat is false, the return + * array will contain separate entries for separate subfields. + * + * @param string $field The MARC field number to read + * @param array $subfields The MARC subfield codes to read + * @param bool $concat Should we concatenate subfields? + * @param string $separator Separator string (used only when $concat === true) + * + * @return array + */ + protected function getFieldArray($field, $subfields = null, $concat = true, + $separator = ' ' + ) { + // Default to subfield a if nothing is specified. + if (!is_array($subfields)) { + $subfields = ['a']; + } + + // Initialize return array + $matches = []; + + // Try to look up the specified field, return empty array if it doesn't + // exist. + $fields = $this->getMarcRecord()->getFields($field); + if (!is_array($fields)) { + return $matches; + } + + // Extract all the requested subfields, if applicable. + foreach ($fields as $currentField) { + $next = $this + ->getSubfieldArray($currentField, $subfields, $concat, $separator); + $matches = array_merge($matches, $next); + } + + return $matches; + } + + /** + * Get the first value matching the specified MARC field and subfields. + * If multiple subfields are specified, they will be concatenated together. + * + * @param string $field The MARC field to read + * @param array $subfields The MARC subfield codes to read + * + * @return string + */ + protected function getFirstFieldValue($field, $subfields = null) + { + $matches = $this->getFieldArray($field, $subfields); + return (is_array($matches) && count($matches) > 0) ? + $matches[0] : null; + } + + /** + * Get the item's publication information + * + * @param string $subfield The subfield to retrieve ('a' = location, 'c' = date) + * + * @return array + */ + protected function getPublicationInfo($subfield = 'a') + { + // Get string separator for publication information: + $separator = isset($this->mainConfig->Record->marcPublicationInfoSeparator) + ? $this->mainConfig->Record->marcPublicationInfoSeparator : ' '; + + // First check old-style 260 field: + $results = $this->getFieldArray('260', [$subfield], true, $separator); + + // Now track down relevant RDA-style 264 fields; we only care about + // copyright and publication places (and ignore copyright places if + // publication places are present). This behavior is designed to be + // consistent with default SolrMarc handling of names/dates. + $pubResults = $copyResults = []; + + $fields = $this->getMarcRecord()->getFields('264'); + if (is_array($fields)) { + foreach ($fields as $currentField) { + $currentVal = $this + ->getSubfieldArray($currentField, [$subfield], true, $separator); + if (!empty($currentVal)) { + switch ($currentField->getIndicator('2')) { + case '1': + $pubResults = array_merge($pubResults, $currentVal); + break; + case '4': + $copyResults = array_merge($copyResults, $currentVal); + break; + } + } + } + } + $replace260 = isset($this->mainConfig->Record->replaceMarc260) + ? $this->mainConfig->Record->replaceMarc260 : false; + if (count($pubResults) > 0) { + return $replace260 ? $pubResults : array_merge($results, $pubResults); + } elseif (count($copyResults) > 0) { + return $replace260 ? $copyResults : array_merge($results, $copyResults); + } + + return $results; + } + + /** + * Return an array of non-empty subfield values found in the provided MARC + * field. If $concat is true, the array will contain either zero or one + * entries (empty array if no subfields found, subfield values concatenated + * together in specified order if found). If concat is false, the array + * will contain a separate entry for each subfield value found. + * + * @param object $currentField Result from File_MARC::getFields. + * @param array $subfields The MARC subfield codes to read + * @param bool $concat Should we concatenate subfields? + * @param string $separator Separator string (used only when $concat === true) + * + * @return array + */ + protected function getSubfieldArray($currentField, $subfields, $concat = true, + $separator = ' ' + ) { + // Start building a line of text for the current field + $matches = []; + + // Loop through all subfields, collecting results that match the whitelist; + // note that it is important to retain the original MARC order here! + $allSubfields = $currentField->getSubfields(); + if (!empty($allSubfields)) { + foreach ($allSubfields as $currentSubfield) { + if (in_array($currentSubfield->getCode(), $subfields)) { + // Grab the current subfield value and act on it if it is + // non-empty: + $data = trim($currentSubfield->getData()); + if (!empty($data)) { + $matches[] = $data; + } + } + } + } + + // Send back the data in a different format depending on $concat mode: + return $concat && $matches ? [implode($separator, $matches)] : $matches; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/Missing.php b/module/VuFind/src/VuFind/RecordDriver/Missing.php index dc4477e207adf5041d2473f1e99b188c3c9ab51e..e13102d53d4dd32d3970a94e7a6ab1870b37fb82 100644 --- a/module/VuFind/src/VuFind/RecordDriver/Missing.php +++ b/module/VuFind/src/VuFind/RecordDriver/Missing.php @@ -3,7 +3,7 @@ * Model for missing records -- used for saved favorites that have been deleted * from the index. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,7 +38,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class Missing extends SolrDefault +class Missing extends DefaultRecord { /** * Constructor diff --git a/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php b/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..845b40741d5c8d51ccf4db8591d09c7c265b5b06 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for record drivers that uses the class name to look up config files. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for record drivers that uses the class name to look up config files. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class NameBasedConfigFactory extends AbstractBaseFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $parts = explode('\\', $requestedName); + $configName = array_pop($parts); + $config = $container->get('VuFind\Config\PluginManager')->get($configName); + $finalOptions = [$config, $config]; + return parent::__invoke($container, $requestedName, $finalOptions); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php b/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php index c630c4b8d8609721f551a5edd1eb60efd4bcf4e1..6954566eb20b50081512b12bff8a728e233d69e7 100644 --- a/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php +++ b/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php @@ -2,7 +2,7 @@ /** * Model for Pazpar2 records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class Pazpar2 extends SolrDefault +class Pazpar2 extends DefaultRecord { /** * Pazpar2 fields @@ -77,15 +77,15 @@ class Pazpar2 extends SolrDefault if (count($data->attributes()) > 0) { $children['_attr_'] = []; foreach ($data->attributes() as $name => $attr) { - $children['_attr_'][$name] = (string) $attr; + $children['_attr_'][$name] = (string)$attr; } } // If there's no children, we're at data if ($data->count() == 0) { if (!isset($children['_attr_'])) { - $children = (string) $data; // Flatten + $children = (string)$data; // Flatten } else { - $children[$key] = (string) $data; + $children[$key] = (string)$data; } } else { // If there's children, recurse on this XML @@ -112,7 +112,7 @@ class Pazpar2 extends SolrDefault if (count($xml->attributes()) > 0) { $array['_attr_'] = []; foreach ($xml->attributes() as $key => $attr) { - $array['_attr_'][$key] = (string) $attr; + $array['_attr_'][$key] = (string)$attr; } } return $array; @@ -222,7 +222,7 @@ class Pazpar2 extends SolrDefault function ($url) { return ['url' => $url]; }, - (array) $this->pz2fields['location']['md-electronic-url'] + (array)$this->pz2fields['location']['md-electronic-url'] ); } return []; diff --git a/module/VuFind/src/VuFind/RecordDriver/PluginFactory.php b/module/VuFind/src/VuFind/RecordDriver/PluginFactory.php index f42e7968c4a4fec1b9166b27ce76cfa504841b5f..10a5c4f20db164cfdc9d1d39536ce59c4eb7baac 100644 --- a/module/VuFind/src/VuFind/RecordDriver/PluginFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/PluginFactory.php @@ -2,7 +2,7 @@ /** * Record driver plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php index e55ce0ab33d9e06691d99dec1b28235463b6eee0..deb4d000bb714ac016c4727874a8622ea135baff 100644 --- a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php @@ -2,7 +2,7 @@ /** * Record driver plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,76 @@ namespace VuFind\RecordDriver; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'browzine' => 'VuFind\RecordDriver\BrowZine', + 'eds' => 'VuFind\RecordDriver\EDS', + 'eit' => 'VuFind\RecordDriver\EIT', + 'libguides' => 'VuFind\RecordDriver\LibGuides', + 'missing' => 'VuFind\RecordDriver\Missing', + 'pazpar2' => 'VuFind\RecordDriver\Pazpar2', + 'primo' => 'VuFind\RecordDriver\Primo', + 'solrauth' => 'VuFind\RecordDriver\SolrAuthMarc', // legacy name + 'solrauthdefault' => 'VuFind\RecordDriver\SolrAuthDefault', + 'solrauthmarc' => 'VuFind\RecordDriver\SolrAuthMarc', + 'solrdefault' => 'VuFind\RecordDriver\SolrDefault', + 'solrmarc' => 'VuFind\RecordDriver\SolrMarc', + 'solrmarcremote' => 'VuFind\RecordDriver\SolrMarcRemote', + 'solrreserves' => 'VuFind\RecordDriver\SolrReserves', + 'solrweb' => 'VuFind\RecordDriver\SolrWeb', + 'summon' => 'VuFind\RecordDriver\Summon', + 'worldcat' => 'VuFind\RecordDriver\WorldCat', + ]; + + /** + * Default delegator factories. + * + * @var string[][]|\Zend\ServiceManager\Factory\DelegatorFactoryInterface[][] + */ + protected $delegators = [ + 'VuFind\RecordDriver\SolrMarc' => + ['VuFind\RecordDriver\IlsAwareDelegatorFactory'], + 'VuFind\RecordDriver\SolrMarcRemote' => + ['VuFind\RecordDriver\IlsAwareDelegatorFactory'], + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\RecordDriver\BrowZine' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\RecordDriver\EDS' => 'VuFind\RecordDriver\NameBasedConfigFactory', + 'VuFind\RecordDriver\EIT' => 'VuFind\RecordDriver\NameBasedConfigFactory', + 'VuFind\RecordDriver\LibGuides' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\RecordDriver\Missing' => 'VuFind\RecordDriver\AbstractBaseFactory', + 'VuFind\RecordDriver\Pazpar2' => + 'VuFind\RecordDriver\NameBasedConfigFactory', + 'VuFind\RecordDriver\Primo' => 'VuFind\RecordDriver\NameBasedConfigFactory', + 'VuFind\RecordDriver\SolrAuthDefault' => + 'VuFind\RecordDriver\SolrDefaultWithoutSearchServiceFactory', + 'VuFind\RecordDriver\SolrAuthMarc' => + 'VuFind\RecordDriver\SolrDefaultWithoutSearchServiceFactory', + 'VuFind\RecordDriver\SolrDefault' => + 'VuFind\RecordDriver\SolrDefaultFactory', + 'VuFind\RecordDriver\SolrMarc' => 'VuFind\RecordDriver\SolrDefaultFactory', + 'VuFind\RecordDriver\SolrMarcRemote' => + 'VuFind\RecordDriver\SolrDefaultFactory', + 'VuFind\RecordDriver\SolrReserves' => + 'VuFind\RecordDriver\SolrDefaultWithoutSearchServiceFactory', + 'VuFind\RecordDriver\SolrWeb' => 'VuFind\RecordDriver\SolrWebFactory', + 'VuFind\RecordDriver\Summon' => 'VuFind\RecordDriver\SummonFactory', + 'VuFind\RecordDriver\WorldCat' => + 'VuFind\RecordDriver\NameBasedConfigFactory', + ]; + /** * Constructor * @@ -52,20 +122,21 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ) { // These objects are not meant to be shared -- every time we retrieve one, // we are building a brand new object. - $this->setShareByDefault(false); + $this->sharedByDefault = false; + + $this->addAbstractFactory('VuFind\RecordDriver\PluginFactory'); parent::__construct($configOrContainerInstance, $v3config); // Add an initializer for setting up hierarchies - $initializer = function ($instance, $manager) { + $initializer = function ($sm, $instance) { $hasHierarchyType = is_callable([$instance, 'getHierarchyType']); if ($hasHierarchyType && is_callable([$instance, 'setHierarchyDriverManager']) ) { - $sm = $manager->getServiceLocator(); - if ($sm && $sm->has('VuFind\HierarchyDriverPluginManager')) { + if ($sm && $sm->has('VuFind\Hierarchy\Driver\PluginManager')) { $instance->setHierarchyDriverManager( - $sm->get('VuFind\HierarchyDriverPluginManager') + $sm->get('VuFind\Hierarchy\Driver\PluginManager') ); } } @@ -87,22 +158,35 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager /** * Convenience method to retrieve a populated Solr record driver. * - * @param array $data Raw Solr data + * @param array $data Raw Solr data + * @param string $keyPrefix Record class name prefix + * @param string $defaultKeySuffix Default key suffix * * @return AbstractBase */ - public function getSolrRecord($data) - { - if (isset($data['recordtype'])) { - $key = 'Solr' . ucwords($data['recordtype']); - $recordType = $this->has($key) ? $key : 'SolrDefault'; - } else { - $recordType = 'SolrDefault'; - } + public function getSolrRecord($data, $keyPrefix = 'Solr', + $defaultKeySuffix = 'Default' + ) { + $key = $keyPrefix . ucwords( + $data['record_format'] ?? $data['recordtype'] ?? $defaultKeySuffix + ); + $recordType = $this->has($key) ? $key : $keyPrefix . $defaultKeySuffix; // Build the object: $driver = $this->get($recordType); $driver->setRawData($data); return $driver; } + + /** + * Convenience method to retrieve a populated Solr authority record driver. + * + * @param array $data Raw Solr data + * + * @return AbstractBase + */ + public function getSolrAuthRecord($data) + { + return $this->getSolrRecord($data, 'SolrAuth'); + } } diff --git a/module/VuFind/src/VuFind/RecordDriver/Primo.php b/module/VuFind/src/VuFind/RecordDriver/Primo.php index fee58726efcf9589f245028bf0e25f587dbb5f5d..300241b951ec92d8c17aef4244df16051f419568 100644 --- a/module/VuFind/src/VuFind/RecordDriver/Primo.php +++ b/module/VuFind/src/VuFind/RecordDriver/Primo.php @@ -2,7 +2,7 @@ /** * Model for Primo Central records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,15 +36,8 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class Primo extends SolrDefault +class Primo extends DefaultRecord { - /** - * Date converter - * - * @var \VuFind\Date\Converter - */ - protected $dateConverter = null; - /** * Get the short (pre-subtitle) title of the record. * diff --git a/module/VuFind/src/VuFind/RecordDriver/Response/PublicationDetails.php b/module/VuFind/src/VuFind/RecordDriver/Response/PublicationDetails.php index 4f3c2bdcfe767f2a99784f4e97aa10e660cf28d6..5cf970d837aece33dcd0f96e4218182ae6cce688 100644 --- a/module/VuFind/src/VuFind/RecordDriver/Response/PublicationDetails.php +++ b/module/VuFind/src/VuFind/RecordDriver/Response/PublicationDetails.php @@ -2,7 +2,7 @@ /** * Class encapsulating publication details. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrAuth.php b/module/VuFind/src/VuFind/RecordDriver/SolrAuthDefault.php similarity index 81% rename from module/VuFind/src/VuFind/RecordDriver/SolrAuth.php rename to module/VuFind/src/VuFind/RecordDriver/SolrAuthDefault.php index bcce5575dfcd19311fc827917bb9dfb179d1564a..412ec121ac1b15178faf0d7c6de7232791999df7 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrAuth.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrAuthDefault.php @@ -2,7 +2,7 @@ /** * Model for Solr authority records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class SolrAuth extends SolrMarc +class SolrAuthDefault extends SolrDefault { /** * Get the short (pre-subtitle) title of the record. @@ -82,24 +82,4 @@ class SolrAuth extends SolrMarc && is_array($this->fields['use_for']) ? $this->fields['use_for'] : []; } - - /** - * Get a raw LCCN (not normalized). Returns false if none available. - * - * @return string|bool - */ - public function getRawLCCN() - { - $lccn = $this->getFirstFieldValue('010'); - if (!empty($lccn)) { - return $lccn; - } - $lccns = $this->getFieldArray('700', ['0']); - foreach ($lccns as $lccn) { - if (substr($lccn, 0, '5') == '(DLC)') { - return substr($lccn, 5); - } - } - return false; - } } diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrAuthMarc.php b/module/VuFind/src/VuFind/RecordDriver/SolrAuthMarc.php new file mode 100644 index 0000000000000000000000000000000000000000..e8cb8035b25fdfcbc2207839d38db9dabedfdb61 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SolrAuthMarc.php @@ -0,0 +1,66 @@ +<?php +/** + * Model for MARC authority records in Solr. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * Copyright (C) The National Library of Finland 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +/** + * Model for MARC authority records in Solr. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class SolrAuthMarc extends SolrAuthDefault +{ + use MarcReaderTrait; + use MarcAdvancedTrait; + + /** + * Get a raw LCCN (not normalized). Returns false if none available. + * + * @return string|bool + */ + public function getRawLCCN() + { + $lccn = $this->getFirstFieldValue('010'); + if (!empty($lccn)) { + return $lccn; + } + $lccns = $this->getFieldArray('700', ['0']); + foreach ($lccns as $lccn) { + if (substr($lccn, 0, '5') == '(DLC)') { + return substr($lccn, 5); + } + } + return false; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php index 48e079eb59ae935c6d6252b01905851f26d957d8..8ac6607393a41011dfc47771b22285edcad1e49c 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php @@ -3,7 +3,7 @@ * Default model for Solr records -- used when a more specific model based on * the recordtype field cannot be found. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,6 @@ * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ namespace VuFind\RecordDriver; -use VuFindCode\ISBN, VuFind\View\Helper\Root\RecordLink; /** * Default model for Solr records -- used when a more specific model based on @@ -43,8 +42,10 @@ use VuFindCode\ISBN, VuFind\View\Helper\Root\RecordLink; * * @SuppressWarnings(PHPMD.ExcessivePublicCount) */ -class SolrDefault extends AbstractBase +class SolrDefault extends DefaultRecord { + use HierarchyAwareTrait; + /** * These Solr fields should be used for snippets if available (listed in order * of preference). @@ -77,13 +78,6 @@ class SolrDefault extends AbstractBase */ protected $snippetCaptions = []; - /** - * Should we highlight fields in search results? - * - * @var bool - */ - protected $highlight = false; - /** * Should we include snippets in search results? * @@ -91,20 +85,6 @@ class SolrDefault extends AbstractBase */ protected $snippet = false; - /** - * Hierarchy driver plugin manager - * - * @var \VuFind\Hierarchy\Driver\PluginManager - */ - protected $hierarchyDriverManager = null; - - /** - * Hierarchy driver for current object - * - * @var \VuFind\Hierarchy\Driver\AbstractBase - */ - protected $hierarchyDriver = null; - /** * Highlighting details * @@ -113,18 +93,18 @@ class SolrDefault extends AbstractBase protected $highlightDetails = []; /** - * Search results plugin manager + * Should we use hierarchy fields for simple container-child records linking? * - * @var \VuFindSearch\Service + * @var bool */ - protected $searchService = null; + protected $containerLinking = false; /** - * Should we use hierarchy fields for simple container-child records linking? + * Search results plugin manager * - * @var bool + * @var \VuFindSearch\Service */ - protected $containerLinking = false; + protected $searchService = null; /** * Constructor @@ -138,13 +118,9 @@ class SolrDefault extends AbstractBase public function __construct($mainConfig = null, $recordConfig = null, $searchSettings = null ) { - // Turn on highlighting/snippets as needed: - $this->highlight = !isset($searchSettings->General->highlighting) - ? false : $searchSettings->General->highlighting; + // Load snippet settings: $this->snippet = !isset($searchSettings->General->snippets) ? false : $searchSettings->General->snippets; - - // Load snippet caption settings: if (isset($searchSettings->Snippet_Captions) && count($searchSettings->Snippet_Captions) > 0 ) { @@ -152,13 +128,11 @@ class SolrDefault extends AbstractBase $this->snippetCaptions[$key] = $value; } } - // Container-contents linking $this->containerLinking = !isset($mainConfig->Hierarchy->simpleContainerLinks) ? false : $mainConfig->Hierarchy->simpleContainerLinks; - - parent::__construct($mainConfig, $recordConfig); + parent::__construct($mainConfig, $recordConfig, $searchSettings); } /** @@ -183,388 +157,6 @@ class SolrDefault extends AbstractBase $this->highlightDetails = $details; } - /** - * Get access restriction notes for the record. - * - * @return array - */ - public function getAccessRestrictions() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get all subject headings associated with this record. Each heading is - * returned as an array of chunks, increasing from least specific to most - * specific. - * - * @param bool $extended Whether to return a keyed array with the following - * keys: - * - heading: the actual subject heading chunks - * - type: heading type - * - source: source vocabulary - * - * @return array - */ - public function getAllSubjectHeadings($extended = false) - { - $headings = []; - foreach (['topic', 'geographic', 'genre', 'era'] as $field) { - if (isset($this->fields[$field])) { - $headings = array_merge($headings, $this->fields[$field]); - } - } - - // The Solr index doesn't currently store subject headings in a broken-down - // format, so we'll just send each value as a single chunk. Other record - // drivers (i.e. MARC) can offer this data in a more granular format. - $callback = function ($i) use ($extended) { - return $extended - ? ['heading' => [$i], 'type' => '', 'source' => ''] - : [$i]; - }; - return array_map($callback, array_unique($headings)); - } - - /** - * Get all record links related to the current record. Each link is returned as - * array. - * NB: to use this method you must override it. - * Format: - * <code> - * array( - * array( - * 'title' => label_for_title - * 'value' => link_name - * 'link' => link_URI - * ), - * ... - * ) - * </code> - * - * @return null|array - */ - public function getAllRecordLinks() - { - return null; - } - - /** - * Get Author Information with Associated Data Fields - * - * @param string $index The author index [primary, corporate, or secondary] - * used to construct a method name for retrieving author data (e.g. - * getPrimaryAuthors). - * @param array $dataFields An array of fields to used to construct method - * names for retrieving author-related data (e.g., if you pass 'role' the - * data method will be similar to getPrimaryAuthorsRoles). This value will also - * be used as a key associated with each author in the resulting data array. - * - * @return array - */ - public function getAuthorDataFields($index, $dataFields = []) - { - $data = $dataFieldValues = []; - - // Collect author data - $authorMethod = sprintf('get%sAuthors', ucfirst($index)); - $authors = $this->tryMethod($authorMethod, [], []); - - // Collect attribute data - foreach ($dataFields as $field) { - $fieldMethod = $authorMethod . ucfirst($field) . 's'; - $dataFieldValues[$field] = $this->tryMethod($fieldMethod, [], []); - } - - // Match up author and attribute data (this assumes that the attribute - // arrays have the same indices as the author array; i.e. $author[$i] - // has $dataFieldValues[$attribute][$i]. - foreach ($authors as $i => $author) { - if (!isset($data[$author])) { - $data[$author] = []; - } - - foreach ($dataFieldValues as $field => $dataFieldValue) { - if (!empty($dataFieldValue[$i])) { - $data[$author][$field][] = $dataFieldValue[$i]; - } - } - } - - return $data; - } - - /** - * Get award notes for the record. - * - * @return array - */ - public function getAwards() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get notes on bibliography content. - * - * @return array - */ - public function getBibliographyNotes() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get text that can be displayed to represent this record in - * breadcrumbs. - * - * @return string Breadcrumb text to represent this record. - */ - public function getBreadcrumb() - { - return $this->getShortTitle(); - } - - /** - * Get the first call number associated with the record (empty string if none). - * - * @return string - */ - public function getCallNumber() - { - $all = $this->getCallNumbers(); - return isset($all[0]) ? $all[0] : ''; - } - - /** - * Get all call numbers associated with the record (empty string if none). - * - * @return array - */ - public function getCallNumbers() - { - return isset($this->fields['callnumber-raw']) - ? $this->fields['callnumber-raw'] : []; - } - - /** - * Return the first valid DOI found in the record (false if none). - * - * @return mixed - */ - public function getCleanDOI() - { - $field = 'doi_str_mv'; - return (isset($this->fields[$field][0]) && !empty($this->fields[$field][0])) - ? $this->fields[$field][0] : false; - } - - /** - * Return the first valid ISBN found in the record (favoring ISBN-10 over - * ISBN-13 when possible). - * - * @return mixed - */ - public function getCleanISBN() - { - // Get all the ISBNs and initialize the return value: - $isbns = $this->getISBNs(); - $isbn13 = false; - - // Loop through the ISBNs: - foreach ($isbns as $isbn) { - // Strip off any unwanted notes: - if ($pos = strpos($isbn, ' ')) { - $isbn = substr($isbn, 0, $pos); - } - - // If we find an ISBN-10, return it immediately; otherwise, if we find - // an ISBN-13, save it if it is the first one encountered. - $isbnObj = new ISBN($isbn); - if ($isbn10 = $isbnObj->get10()) { - return $isbn10; - } - if (!$isbn13) { - $isbn13 = $isbnObj->get13(); - } - } - return $isbn13; - } - - /** - * Get just the base portion of the first listed ISSN (or false if no ISSNs). - * - * @return mixed - */ - public function getCleanISSN() - { - $issns = $this->getISSNs(); - if (empty($issns)) { - return false; - } - $issn = $issns[0]; - if ($pos = strpos($issn, ' ')) { - $issn = substr($issn, 0, $pos); - } - return $issn; - } - - /** - * Get just the first listed OCLC Number (or false if none available). - * - * @return mixed - */ - public function getCleanOCLCNum() - { - $nums = $this->getOCLC(); - return empty($nums) ? false : $nums[0]; - } - - /** - * Get just the first listed UPC Number (or false if none available). - * - * @return mixed - */ - public function getCleanUPC() - { - $nums = $this->getUPC(); - return empty($nums) ? false : $nums[0]; - } - - /** - * Get the main corporate authors (if any) for the record. - * - * @return array - */ - public function getCorporateAuthors() - { - return isset($this->fields['author_corporate']) ? - $this->fields['author_corporate'] : []; - } - - /** - * Get an array of all main corporate authors roles. - * - * @return array - */ - public function getCorporateAuthorsRoles() - { - return isset($this->fields['author_corporate_role']) ? - $this->fields['author_corporate_role'] : []; - } - - /** - * Get the date coverage for a record which spans a period of time (i.e. a - * journal). Use getPublicationDates for publication dates of particular - * monographic items. - * - * @return array - */ - public function getDateSpan() - { - return isset($this->fields['dateSpan']) ? - $this->fields['dateSpan'] : []; - } - - /** - * Deduplicate author information into associative array with main/corporate/ - * secondary keys. - * - * @param array $dataFields An array of extra data fields to retrieve (see - * getAuthorDataFields) - * - * @return array - */ - public function getDeduplicatedAuthors($dataFields = ['role']) - { - $authors = []; - foreach (['primary', 'secondary', 'corporate'] as $type) { - $authors[$type] = $this->getAuthorDataFields($type, $dataFields); - } - - // deduplicate - $dedup = function (&$array1, &$array2) { - if (!empty($array1) && !empty($array2)) { - $keys = array_keys($array1); - foreach ($keys as $author) { - if (isset($array2[$author])) { - $array1[$author] = array_merge( - $array1[$author], - $array2[$author] - ); - unset($array2[$author]); - } - } - } - }; - - $dedup($authors['primary'], $authors['corporate']); - $dedup($authors['secondary'], $authors['corporate']); - $dedup($authors['primary'], $authors['secondary']); - - $dedup_data = function (&$array) { - foreach ($array as $author => $data) { - foreach ($data as $field => $values) { - if (is_array($values)) { - $array[$author][$field] = array_unique($values); - } - } - } - }; - - $dedup_data($authors['primary']); - $dedup_data($authors['secondary']); - $dedup_data($authors['corporate']); - - return $authors; - } - - /** - * Get the edition of the current record. - * - * @return string - */ - public function getEdition() - { - return isset($this->fields['edition']) ? - $this->fields['edition'] : ''; - } - - /** - * Get notes on finding aids related to the record. - * - * @return array - */ - public function getFindingAids() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get an array of all the formats associated with the record. - * - * @return array - */ - public function getFormats() - { - return isset($this->fields['format']) ? $this->fields['format'] : []; - } - - /** - * Get general notes on the record. - * - * @return array - */ - public function getGeneralNotes() - { - // Not currently stored in the Solr index - return []; - } - /** * Get highlighted author data, if available. * @@ -577,43 +169,6 @@ class SolrDefault extends AbstractBase ? $this->highlightDetails['author'] : []; } - /** - * Get primary author information with highlights applied (if applicable) - * - * @return array - */ - public function getPrimaryAuthorsWithHighlighting() - { - $highlights = []; - // Create a map of de-highlighted valeus => highlighted values. - foreach ($this->getRawAuthorHighlights() as $current) { - $dehighlighted = str_replace( - ['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $current - ); - $highlights[$dehighlighted] = $current; - } - - // replace unhighlighted authors with highlighted versions where - // applicable: - $authors = []; - foreach ($this->getPrimaryAuthors() as $author) { - $authors[] = isset($highlights[$author]) - ? $highlights[$author] : $author; - } - return $authors; - } - - /** - * Get a string representing the last date that the record was indexed. - * - * @return string - */ - public function getLastIndexed() - { - return isset($this->fields['last_indexed']) - ? $this->fields['last_indexed'] : ''; - } - /** * Given a Solr field name, return an appropriate caption. * @@ -682,1232 +237,33 @@ class SolrDefault extends AbstractBase } /** - * Get the institutions holding the record. + * Attach a Search Results Plugin Manager connection and related logic to + * the driver * - * @return array - */ - public function getInstitutions() - { - return isset($this->fields['institution']) - ? $this->fields['institution'] : []; - } - - /** - * Get an array of all ISBNs associated with the record (may be empty). + * @param \VuFindSearch\Service $service Search Service Manager * - * @return array + * @return void */ - public function getISBNs() + public function attachSearchService(\VuFindSearch\Service $service) { - // If ISBN is in the index, it should automatically be an array... but if - // it's not set at all, we should normalize the value to an empty array. - return isset($this->fields['isbn']) && is_array($this->fields['isbn']) ? - $this->fields['isbn'] : []; + $this->searchService = $service; } /** - * Get an array of all ISSNs associated with the record (may be empty). + * Get the number of child records belonging to this record * - * @return array + * @return int Number of records */ - public function getISSNs() + public function getChildRecordCount() { - // If ISSN is in the index, it should automatically be an array... but if - // it's not set at all, we should normalize the value to an empty array. - return isset($this->fields['issn']) && is_array($this->fields['issn']) ? - $this->fields['issn'] : []; - } - - /** - * Get an array of all the languages associated with the record. - * - * @return array - */ - public function getLanguages() - { - return isset($this->fields['language']) ? - $this->fields['language'] : []; - } - - /** - * Get a LCCN, normalised according to info:lccn - * - * @return string - */ - public function getLCCN() - { - // Get LCCN from Index - $raw = isset($this->fields['lccn']) ? $this->fields['lccn'] : ''; - - // Remove all blanks. - $raw = preg_replace('{[ \t]+}', '', $raw); - - // If there is a forward slash (/) in the string, remove it, and remove all - // characters to the right of the forward slash. - if (strpos($raw, '/') > 0) { - $tmpArray = explode("/", $raw); - $raw = $tmpArray[0]; - } - /* If there is a hyphen in the string: - a. Remove it. - b. Inspect the substring following (to the right of) the (removed) - hyphen. Then (and assuming that steps 1 and 2 have been carried out): - i. All these characters should be digits, and there should be - six or less. - ii. If the length of the substring is less than 6, left-fill the - substring with zeros until the length is six. - */ - if (strpos($raw, '-') > 0) { - // haven't checked for i. above. If they aren't all digits, there is - // nothing that can be done, so might as well leave it. - $tmpArray = explode("-", $raw); - $raw = $tmpArray[0] . str_pad($tmpArray[1], 6, "0", STR_PAD_LEFT); - } - return $raw; - } - - /** - * Get an array of newer titles for the record. - * - * @return array - */ - public function getNewerTitles() - { - return isset($this->fields['title_new']) ? - $this->fields['title_new'] : []; - } - - /** - * Get the OCLC number(s) of the record. - * - * @return array - */ - public function getOCLC() - { - return isset($this->fields['oclc_num']) ? - $this->fields['oclc_num'] : []; - } - - /** - * Support method for getOpenUrl() -- pick the OpenURL format. - * - * @return string - */ - protected function getOpenUrlFormat() - { - // If we have multiple formats, Book, Journal and Article are most - // important... - $formats = $this->getFormats(); - if (in_array('Book', $formats)) { - return 'Book'; - } else if (in_array('Article', $formats)) { - return 'Article'; - } else if (in_array('Journal', $formats)) { - return 'Journal'; - } else if (isset($formats[0])) { - return $formats[0]; - } else if (strlen($this->getCleanISSN()) > 0) { - return 'Journal'; - } else if (strlen($this->getCleanISBN()) > 0) { - return 'Book'; - } - return 'UnknownFormat'; - } - - /** - * Get the COinS identifier. - * - * @return string - */ - protected function getCoinsID() - { - // Get the COinS ID -- it should be in the OpenURL section of config.ini, - // but we'll also check the COinS section for compatibility with legacy - // configurations (this moved between the RC2 and 1.0 releases). - if (isset($this->mainConfig->OpenURL->rfr_id) - && !empty($this->mainConfig->OpenURL->rfr_id) - ) { - return $this->mainConfig->OpenURL->rfr_id; - } - if (isset($this->mainConfig->COinS->identifier) - && !empty($this->mainConfig->COinS->identifier) - ) { - return $this->mainConfig->COinS->identifier; - } - return 'vufind.svn.sourceforge.net'; - } - - /** - * Get default OpenURL parameters. - * - * @return array - */ - protected function getDefaultOpenUrlParams() - { - // Get a representative publication date: - $pubDate = $this->getPublicationDates(); - $pubDate = empty($pubDate) ? '' : $pubDate[0]; - - // Start an array of OpenURL parameters: - return [ - 'url_ver' => 'Z39.88-2004', - 'ctx_ver' => 'Z39.88-2004', - 'ctx_enc' => 'info:ofi/enc:UTF-8', - 'rfr_id' => 'info:sid/' . $this->getCoinsID() . ':generator', - 'rft.title' => $this->getTitle(), - 'rft.date' => $pubDate - ]; - } - - /** - * Get OpenURL parameters for a book. - * - * @return array - */ - protected function getBookOpenUrlParams() - { - $params = $this->getDefaultOpenUrlParams(); - $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:book'; - $params['rft.genre'] = 'book'; - $params['rft.btitle'] = $params['rft.title']; - $series = $this->getSeries(); - if (count($series) > 0) { - // Handle both possible return formats of getSeries: - $params['rft.series'] = is_array($series[0]) ? - $series[0]['name'] : $series[0]; - } - $params['rft.au'] = $this->getPrimaryAuthor(); - $publishers = $this->getPublishers(); - if (count($publishers) > 0) { - $params['rft.pub'] = $publishers[0]; - } - $params['rft.edition'] = $this->getEdition(); - $params['rft.isbn'] = (string)$this->getCleanISBN(); - return $params; - } - - /** - * Get OpenURL parameters for an article. - * - * @return array - */ - protected function getArticleOpenUrlParams() - { - $params = $this->getDefaultOpenUrlParams(); - $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:journal'; - $params['rft.genre'] = 'article'; - $params['rft.issn'] = (string)$this->getCleanISSN(); - // an article may have also an ISBN: - $params['rft.isbn'] = (string)$this->getCleanISBN(); - $params['rft.volume'] = $this->getContainerVolume(); - $params['rft.issue'] = $this->getContainerIssue(); - $params['rft.spage'] = $this->getContainerStartPage(); - // unset default title -- we only want jtitle/atitle here: - unset($params['rft.title']); - $params['rft.jtitle'] = $this->getContainerTitle(); - $params['rft.atitle'] = $this->getTitle(); - $params['rft.au'] = $this->getPrimaryAuthor(); - - $params['rft.format'] = 'Article'; - $langs = $this->getLanguages(); - if (count($langs) > 0) { - $params['rft.language'] = $langs[0]; - } - return $params; - } - - /** - * Get OpenURL parameters for an unknown format. - * - * @param string $format Name of format - * - * @return array - */ - protected function getUnknownFormatOpenUrlParams($format = 'UnknownFormat') - { - $params = $this->getDefaultOpenUrlParams(); - $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:dc'; - $params['rft.creator'] = $this->getPrimaryAuthor(); - $publishers = $this->getPublishers(); - if (count($publishers) > 0) { - $params['rft.pub'] = $publishers[0]; - } - $params['rft.format'] = $format; - $langs = $this->getLanguages(); - if (count($langs) > 0) { - $params['rft.language'] = $langs[0]; - } - return $params; - } - - /** - * Get OpenURL parameters for a journal. - * - * @return array - */ - protected function getJournalOpenUrlParams() - { - $params = $this->getUnknownFormatOpenUrlParams('Journal'); - /* This is probably the most technically correct way to represent - * a journal run as an OpenURL; however, it doesn't work well with - * Zotero, so it is currently commented out -- instead, we just add - * some extra fields and to the "unknown format" case. - $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:journal'; - $params['rft.genre'] = 'journal'; - $params['rft.jtitle'] = $params['rft.title']; - $params['rft.issn'] = $this->getCleanISSN(); - $params['rft.au'] = $this->getPrimaryAuthor(); - */ - $params['rft.issn'] = (string)$this->getCleanISSN(); - - // Including a date in a title-level Journal OpenURL may be too - // limiting -- in some link resolvers, it may cause the exclusion - // of databases if they do not cover the exact date provided! - unset($params['rft.date']); - - // If we're working with the SFX resolver, we should add a - // special parameter to ensure that electronic holdings links - // are shown even though no specific date or issue is specified: - if (isset($this->mainConfig->OpenURL->resolver) - && strtolower($this->mainConfig->OpenURL->resolver) == 'sfx' - ) { - $params['sfx.ignore_date_threshold'] = 1; - } - return $params; - } - - /** - * Get the OpenURL parameters to represent this record (useful for the - * title attribute of a COinS span tag). - * - * @param bool $overrideSupportsOpenUrl Flag to override checking - * supportsOpenUrl() (default is false) - * - * @return string OpenURL parameters. - */ - public function getOpenUrl($overrideSupportsOpenUrl = false) - { - // stop here if this record does not support OpenURLs - if (!$overrideSupportsOpenUrl && !$this->supportsOpenUrl()) { - return false; - } - - // Set up parameters based on the format of the record: - $format = $this->getOpenUrlFormat(); - $method = "get{$format}OpenUrlParams"; - if (method_exists($this, $method)) { - $params = $this->$method(); - } else { - $params = $this->getUnknownFormatOpenUrlParams($format); - } - - // Assemble the URL: - return http_build_query($params); - } - - /** - * Get the OpenURL parameters to represent this record for COinS even if - * supportsOpenUrl() is false for this RecordDriver. - * - * @return string OpenURL parameters. - */ - public function getCoinsOpenUrl() - { - return $this->getOpenUrl($this->supportsCoinsOpenUrl()); - } - - /** - * Get an array of physical descriptions of the item. - * - * @return array - */ - public function getPhysicalDescriptions() - { - return isset($this->fields['physical']) ? - $this->fields['physical'] : []; - } - - /** - * Get the item's place of publication. - * - * @return array - */ - public function getPlacesOfPublication() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get an array of playing times for the record (if applicable). - * - * @return array - */ - public function getPlayingTimes() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get an array of previous titles for the record. - * - * @return array - */ - public function getPreviousTitles() - { - return isset($this->fields['title_old']) ? - $this->fields['title_old'] : []; - } - - /** - * Get the main author of the record. - * - * @return string - */ - public function getPrimaryAuthor() - { - $authors = $this->getPrimaryAuthors(); - return isset($authors[0]) ? $authors[0] : ''; - } - - /** - * Get the main authors of the record. - * - * @return array - */ - public function getPrimaryAuthors() - { - return isset($this->fields['author']) - ? (array) $this->fields['author'] : []; - } - - /** - * Get an array of all main authors roles (complementing - * getSecondaryAuthorsRoles()). - * - * @return array - */ - public function getPrimaryAuthorsRoles() - { - return isset($this->fields['author_role']) ? - $this->fields['author_role'] : []; - } - - /** - * Get credits of people involved in production of the item. - * - * @return array - */ - public function getProductionCredits() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get the publication dates of the record. See also getDateSpan(). - * - * @return array - */ - public function getPublicationDates() - { - return isset($this->fields['publishDate']) ? - $this->fields['publishDate'] : []; - } - - /** - * Get human readable publication dates for display purposes (may not be suitable - * for computer processing -- use getPublicationDates() for that). - * - * @return array - */ - public function getHumanReadablePublicationDates() - { - return $this->getPublicationDates(); - } - - /** - * Get an array of publication detail lines combining information from - * getPublicationDates(), getPublishers() and getPlacesOfPublication(). - * - * @return array - */ - public function getPublicationDetails() - { - $places = $this->getPlacesOfPublication(); - $names = $this->getPublishers(); - $dates = $this->getHumanReadablePublicationDates(); - - $i = 0; - $retval = []; - while (isset($places[$i]) || isset($names[$i]) || isset($dates[$i])) { - // Build objects to represent each set of data; these will - // transform seamlessly into strings in the view layer. - $retval[] = new Response\PublicationDetails( - isset($places[$i]) ? $places[$i] : '', - isset($names[$i]) ? $names[$i] : '', - isset($dates[$i]) ? $dates[$i] : '' - ); - $i++; - } - - return $retval; - } - - /** - * Get an array of publication frequency information. - * - * @return array - */ - public function getPublicationFrequency() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get the publishers of the record. - * - * @return array - */ - public function getPublishers() - { - return isset($this->fields['publisher']) ? - $this->fields['publisher'] : []; - } - - /** - * Get an array of information about record history, obtained in real-time - * from the ILS. - * - * @return array - */ - public function getRealTimeHistory() - { - // Not supported by the Solr index -- implement in child classes. - return []; - } - - /** - * Get an array of information about record holdings, obtained in real-time - * from the ILS. - * - * @return array - */ - public function getRealTimeHoldings() - { - // Not supported by the Solr index -- implement in child classes. - return ['holdings' => []]; - } - - /** - * Get an array of strings describing relationships to other items. - * - * @return array - */ - public function getRelationshipNotes() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get an array of all secondary authors (complementing getPrimaryAuthors()). - * - * @return array - */ - public function getSecondaryAuthors() - { - return isset($this->fields['author2']) ? - $this->fields['author2'] : []; - } - - /** - * Get an array of all secondary authors roles (complementing - * getPrimaryAuthorsRoles()). - * - * @return array - */ - public function getSecondaryAuthorsRoles() - { - return isset($this->fields['author2_role']) ? - $this->fields['author2_role'] : []; - } - - /** - * Get an array of all series names containing the record. Array entries may - * be either the name string, or an associative array with 'name' and 'number' - * keys. - * - * @return array - */ - public function getSeries() - { - // Only use the contents of the series2 field if the series field is empty - if (isset($this->fields['series']) && !empty($this->fields['series'])) { - return $this->fields['series']; - } - return isset($this->fields['series2']) ? - $this->fields['series2'] : []; - } - - /** - * Get the short (pre-subtitle) title of the record. - * - * @return string - */ - public function getShortTitle() - { - return isset($this->fields['title_short']) ? - $this->fields['title_short'] : ''; - } - - /** - * Get the item's source. - * - * @return string - */ - public function getSource() - { - // Not supported in base class: - return ''; - } - - /** - * Get the subtitle of the record. - * - * @return string - */ - public function getSubtitle() - { - return isset($this->fields['title_sub']) ? - $this->fields['title_sub'] : ''; - } - - /** - * Get an array of technical details on the item represented by the record. - * - * @return array - */ - public function getSystemDetails() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get an array of summary strings for the record. - * - * @return array - */ - public function getSummary() - { - // We need to return an array, so if we have a description, turn it into an - // array as needed (it should be a flat string according to the default - // schema, but we might as well support the array case just to be on the safe - // side: - if (isset($this->fields['description']) - && !empty($this->fields['description']) - ) { - return is_array($this->fields['description']) - ? $this->fields['description'] : [$this->fields['description']]; - } - - // If we got this far, no description was found: - return []; - } - - /** - * Get an array of note about the record's target audience. - * - * @return array - */ - public function getTargetAudienceNotes() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Returns one of three things: a full URL to a thumbnail preview of the record - * if an image is available in an external system; an array of parameters to - * send to VuFind's internal cover generator if no fixed URL exists; or false - * if no thumbnail can be generated. - * - * @param string $size Size of thumbnail (small, medium or large -- small is - * default). - * - * @return string|array|bool - */ - public function getThumbnail($size = 'small') - { - if (isset($this->fields['thumbnail']) && $this->fields['thumbnail']) { - return $this->fields['thumbnail']; - } - $arr = [ - 'author' => mb_substr($this->getPrimaryAuthor(), 0, 300, 'utf-8'), - 'callnumber' => $this->getCallNumber(), - 'size' => $size, - 'title' => mb_substr($this->getTitle(), 0, 300, 'utf-8'), - 'recordid' => $this->getUniqueID(), - 'source' => $this->getSourceIdentifier(), - ]; - if ($isbn = $this->getCleanISBN()) { - $arr['isbn'] = $isbn; - } - if ($issn = $this->getCleanISSN()) { - $arr['issn'] = $issn; - } - if ($oclc = $this->getCleanOCLCNum()) { - $arr['oclc'] = $oclc; - } - if ($upc = $this->getCleanUPC()) { - $arr['upc'] = $upc; - } - // If an ILS driver has injected extra details, check for IDs in there - // to fill gaps: - if ($ilsDetails = $this->getExtraDetail('ils_details')) { - foreach (['isbn', 'issn', 'oclc', 'upc'] as $key) { - if (!isset($arr[$key]) && isset($ilsDetails[$key])) { - $arr[$key] = $ilsDetails[$key]; - } - } - } - return $arr; - } - - /** - * Get the full title of the record. - * - * @return string - */ - public function getTitle() - { - return isset($this->fields['title']) ? - $this->fields['title'] : ''; - } - - /** - * Get the text of the part/section portion of the title. - * - * @return string - */ - public function getTitleSection() - { - // Not currently stored in the Solr index - return null; - } - - /** - * Get the statement of responsibility that goes with the title (i.e. "by John - * Smith"). - * - * @return string - */ - public function getTitleStatement() - { - // Not currently stored in the Solr index - return null; - } - - /** - * Get an array of lines from the table of contents. - * - * @return array - */ - public function getTOC() - { - return isset($this->fields['contents']) - ? $this->fields['contents'] : []; - } - - /** - * Get hierarchical place names - * - * @return array - */ - public function getHierarchicalPlaceNames() - { - // Not currently stored in the Solr index - return []; - } - - /** - * Get the UPC number(s) of the record. - * - * @return array - */ - public function getUPC() - { - return isset($this->fields['upc_str_mv']) ? - $this->fields['upc_str_mv'] : []; - } - - /** - * Return an array of associative URL arrays with one or more of the following - * keys: - * - * <li> - * <ul>desc: URL description text to display (optional)</ul> - * <ul>url: fully-formed URL (required if 'route' is absent)</ul> - * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> - * <ul>routeParams: Parameters for route (optional)</ul> - * <ul>queryString: Query params to append after building route (optional)</ul> - * </li> - * - * @return array - */ - public function getURLs() - { - // If non-empty, map internal URL array to expected return format; - // otherwise, return empty array: - if (isset($this->fields['url']) && is_array($this->fields['url'])) { - $filter = function ($url) { - return ['url' => $url]; - }; - return array_map($filter, $this->fields['url']); - } - return []; - } - - /** - * Get a hierarchy driver appropriate to the current object. (May be false if - * disabled/unavailable). - * - * @return \VuFind\Hierarchy\Driver\AbstractBase|bool - */ - public function getHierarchyDriver() - { - if (null === $this->hierarchyDriver - && null !== $this->hierarchyDriverManager - ) { - $type = $this->getHierarchyType(); - $this->hierarchyDriver = $type - ? $this->hierarchyDriverManager->get($type) : false; - } - return $this->hierarchyDriver; - } - - /** - * Inject a hierarchy driver plugin manager. - * - * @param \VuFind\Hierarchy\Driver\PluginManager $pm Hierarchy driver manager - * - * @return SolrDefault - */ - public function setHierarchyDriverManager( - \VuFind\Hierarchy\Driver\PluginManager $pm - ) { - $this->hierarchyDriverManager = $pm; - return $this; - } - - /** - * Get the hierarchy_top_id(s) associated with this item (empty if none). - * - * @return array - */ - public function getHierarchyTopID() - { - return isset($this->fields['hierarchy_top_id']) - ? $this->fields['hierarchy_top_id'] : []; - } - - /** - * Get the absolute parent title(s) associated with this item (empty if none). - * - * @return array - */ - public function getHierarchyTopTitle() - { - return isset($this->fields['hierarchy_top_title']) - ? $this->fields['hierarchy_top_title'] : []; - } - - /** - * Get an associative array (id => title) of collections containing this record. - * - * @return array - */ - public function getContainingCollections() - { - // If collections are disabled or this record is not part of a hierarchy, go - // no further.... - if (!isset($this->mainConfig->Collections->collections) - || !$this->mainConfig->Collections->collections - || !($hierarchyDriver = $this->getHierarchyDriver()) - ) { - return false; - } - - // Initialize some variables needed within the switch below: - $isCollection = $this->isCollection(); - $titles = $ids = []; - - // Check config setting for what constitutes a collection, act accordingly: - switch ($hierarchyDriver->getCollectionLinkType()) { - case 'All': - if (isset($this->fields['hierarchy_parent_title']) - && isset($this->fields['hierarchy_parent_id']) - ) { - $titles = $this->fields['hierarchy_parent_title']; - $ids = $this->fields['hierarchy_parent_id']; - } - break; - case 'Top': - if (isset($this->fields['hierarchy_top_title']) - && isset($this->fields['hierarchy_top_id']) - ) { - foreach ($this->fields['hierarchy_top_id'] as $i => $topId) { - // Don't mark an item as its own parent -- filter out parent - // collections whose IDs match that of the current collection. - if (!$isCollection - || $topId !== $this->fields['is_hierarchy_id'] - ) { - $ids[] = $topId; - $titles[] = $this->fields['hierarchy_top_title'][$i]; - } - } - } - break; - } - - // Map the titles and IDs to a useful format: - $c = count($ids); - $retVal = []; - for ($i = 0; $i < $c; $i++) { - $retVal[$ids[$i]] = $titles[$i]; - } - return $retVal; - } - - /** - * Get the value of whether or not this is a collection level record - * - * NOTE: \VuFind\Hierarchy\TreeDataFormatter\AbstractBase::isCollection() - * duplicates some of this logic. - * - * @return bool - */ - public function isCollection() - { - if (!($hierarchyDriver = $this->getHierarchyDriver())) { - // Not a hierarchy type record - return false; - } - - // Check config setting for what constitutes a collection - switch ($hierarchyDriver->getCollectionLinkType()) { - case 'All': - return (isset($this->fields['is_hierarchy_id'])); - case 'Top': - return isset($this->fields['is_hierarchy_title']) - && isset($this->fields['is_hierarchy_id']) - && in_array( - $this->fields['is_hierarchy_id'], - $this->fields['hierarchy_top_id'] - ); - default: - // Default to not be a collection level record - return false; - } - } - - /** - * Get a list of hierarchy trees containing this record. - * - * @param string $hierarchyID The hierarchy to get the tree for - * - * @return mixed An associative array of hierarchy trees on success - * (id => title), false if no hierarchies found - */ - public function getHierarchyTrees($hierarchyID = false) - { - $hierarchyDriver = $this->getHierarchyDriver(); - if ($hierarchyDriver && $hierarchyDriver->showTree()) { - return $hierarchyDriver->getTreeRenderer($this) - ->getTreeList($hierarchyID); - } - return false; - } - - /** - * Get the Hierarchy Type (false if none) - * - * @return string|bool - */ - public function getHierarchyType() - { - if (isset($this->fields['hierarchy_top_id'])) { - $hierarchyType = isset($this->fields['hierarchytype']) - ? $this->fields['hierarchytype'] : false; - if (!$hierarchyType) { - $hierarchyType = isset($this->mainConfig->Hierarchy->driver) - ? $this->mainConfig->Hierarchy->driver : false; - } - return $hierarchyType; - } - return false; - } - - /** - * Return the unique identifier of this record within the Solr index; - * useful for retrieving additional information (like tags and user - * comments) from the external MySQL database. - * - * @return string Unique identifier. - */ - public function getUniqueID() - { - if (!isset($this->fields['id'])) { - throw new \Exception('ID not set!'); - } - return $this->fields['id']; - } - - /** - * Return an XML representation of the record using the specified format. - * Return false if the format is unsupported. - * - * @param string $format Name of format to use (corresponds with OAI-PMH - * metadataPrefix parameter). - * @param string $baseUrl Base URL of host containing VuFind (optional; - * may be used to inject record URLs into XML when appropriate). - * @param RecordLink $recordLink Record link helper (optional; may be used to - * inject record URLs into XML when appropriate). - * - * @return mixed XML, or false if format unsupported. - */ - public function getXML($format, $baseUrl = null, $recordLink = null) - { - // For OAI-PMH Dublin Core, produce the necessary XML: - if ($format == 'oai_dc') { - $dc = 'http://purl.org/dc/elements/1.1/'; - $xml = new \SimpleXMLElement( - '<oai_dc:dc ' - . 'xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" ' - . 'xmlns:dc="' . $dc . '" ' - . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' - . 'xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ ' - . 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd" />' - ); - $xml->addChild('title', htmlspecialchars($this->getTitle()), $dc); - $authors = $this->getDeduplicatedAuthors(); - foreach ($authors as $list) { - foreach ((array)$list as $author) { - $xml->addChild('creator', htmlspecialchars($author), $dc); - } - } - foreach ($this->getLanguages() as $lang) { - $xml->addChild('language', htmlspecialchars($lang), $dc); - } - foreach ($this->getPublishers() as $pub) { - $xml->addChild('publisher', htmlspecialchars($pub), $dc); - } - foreach ($this->getPublicationDates() as $date) { - $xml->addChild('date', htmlspecialchars($date), $dc); - } - foreach ($this->getAllSubjectHeadings() as $subj) { - $xml->addChild( - 'subject', htmlspecialchars(implode(' -- ', $subj)), $dc - ); - } - if (null !== $baseUrl && null !== $recordLink) { - $url = $baseUrl . $recordLink->getUrl($this); - $xml->addChild('identifier', $url, $dc); - } - - return $xml->asXml(); - } - - // Unsupported format: - return false; - } - - /** - * Get an array of strings representing citation formats supported - * by this record's data (empty if none). For possible legal values, - * see /application/themes/root/helpers/Citation.php, getCitation() - * method. - * - * @return array Strings representing citation formats. - */ - protected function getSupportedCitationFormats() - { - return ['APA', 'Chicago', 'MLA']; - } - - /** - * Get the title of the item that contains this record (i.e. MARC 773s of a - * journal). - * - * @return string - */ - public function getContainerTitle() - { - return isset($this->fields['container_title']) - ? $this->fields['container_title'] : ''; - } - - /** - * Get the volume of the item that contains this record (i.e. MARC 773v of a - * journal). - * - * @return string - */ - public function getContainerVolume() - { - return isset($this->fields['container_volume']) - ? $this->fields['container_volume'] : ''; - } - - /** - * Get the issue of the item that contains this record (i.e. MARC 773l of a - * journal). - * - * @return string - */ - public function getContainerIssue() - { - return isset($this->fields['container_issue']) - ? $this->fields['container_issue'] : ''; - } - - /** - * Get the start page of the item that contains this record (i.e. MARC 773q of a - * journal). - * - * @return string - */ - public function getContainerStartPage() - { - return isset($this->fields['container_start_page']) - ? $this->fields['container_start_page'] : ''; - } - - /** - * Get the end page of the item that contains this record. - * - * @return string - */ - public function getContainerEndPage() - { - // not currently supported by Solr index: - return ''; - } - - /** - * Get a full, free-form reference to the context of the item that contains this - * record (i.e. volume, year, issue, pages). - * - * @return string - */ - public function getContainerReference() - { - return isset($this->fields['container_reference']) - ? $this->fields['container_reference'] : ''; - } - - /** - * Get a sortable title for the record (i.e. no leading articles). - * - * @return string - */ - public function getSortTitle() - { - return isset($this->fields['title_sort']) - ? $this->fields['title_sort'] : parent::getSortTitle(); - } - - /** - * Get schema.org type mapping, an array of sub-types of - * http://schema.org/CreativeWork, defaulting to CreativeWork - * itself if nothing else matches. - * - * @return array - */ - public function getSchemaOrgFormatsArray() - { - $types = []; - foreach ($this->getFormats() as $format) { - switch ($format) { - case 'Book': - case 'eBook': - $types['Book'] = 1; - break; - case 'Video': - case 'VHS': - $types['Movie'] = 1; - break; - case 'Photo': - $types['Photograph'] = 1; - break; - case 'Map': - $types['Map'] = 1; - break; - case 'Audio': - $types['MusicAlbum'] = 1; - break; - default: - $types['CreativeWork'] = 1; - } - } - return array_keys($types); - } - - /** - * Get schema.org type mapping, expected to be a space-delimited string of - * sub-types of http://schema.org/CreativeWork, defaulting to CreativeWork - * itself if nothing else matches. - * - * @return string - */ - public function getSchemaOrgFormats() - { - return implode(' ', $this->getSchemaOrgFormatsArray()); - } - - /** - * Get information on records deduplicated with this one - * - * @return array Array keyed by source id containing record id - */ - public function getDedupData() - { - return isset($this->fields['dedup_data']) - ? $this->fields['dedup_data'] - : []; - } - - /** - * Attach a Search Results Plugin Manager connection and related logic to - * the driver - * - * @param \VuFindSearch\Service $service Search Service Manager - * - * @return void - */ - public function attachSearchService(\VuFindSearch\Service $service) - { - $this->searchService = $service; - } - - /** - * Get the number of child records belonging to this record - * - * @return int Number of records - */ - public function getChildRecordCount() - { - // Shortcut: if this record is not the top record, let's not find out the - // count. This assumes that contained records cannot contain more records. - if (!$this->containerLinking - || empty($this->fields['is_hierarchy_id']) - || null === $this->searchService - ) { - return 0; - } + // Shortcut: if this record is not the top record, let's not find out the + // count. This assumes that contained records cannot contain more records. + if (!$this->containerLinking + || empty($this->fields['is_hierarchy_id']) + || null === $this->searchService + ) { + return 0; + } $safeId = addcslashes($this->fields['is_hierarchy_id'], '"'); $query = new \VuFindSearch\Query\Query( @@ -1915,7 +271,8 @@ class SolrDefault extends AbstractBase ); // Disable highlighting for efficiency; not needed here: $params = new \VuFindSearch\ParamBag(['hl' => ['false']]); - return $this->searchService->search('Solr', $query, 0, 0, $params) + return $this->searchService + ->search($this->sourceIdentifier, $query, 0, 0, $params) ->getTotal(); } @@ -1930,37 +287,4 @@ class SolrDefault extends AbstractBase && !empty($this->fields['hierarchy_parent_id']) ? $this->fields['hierarchy_parent_id'][0] : ''; } - - /** - * Get the bbox-geo variable. - * - * @return array - */ - public function getGeoLocation() - { - return isset($this->fields['long_lat']) - ? $this->fields['long_lat'] : []; - } - - /** - * Get the map display (lat/lon) coordinates - * - * @return array - */ - public function getDisplayCoordinates() - { - return isset($this->fields['long_lat_display']) - ? $this->fields['long_lat_display'] : []; - } - - /** - * Get the map display (lat/lon) labels - * - * @return array - */ - public function getCoordinateLabels() - { - return isset($this->fields['long_lat_label']) - ? $this->fields['long_lat_label'] : []; - } } diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..80dca9410831c0abdedfc7c830672f5456743932 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php @@ -0,0 +1,64 @@ +<?php +/** + * Factory for SolrDefault record drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for SolrDefault record drivers. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SolrDefaultFactory extends SolrDefaultWithoutSearchServiceFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $driver = parent::__invoke($container, $requestedName, $options); + $driver->attachSearchService($container->get('VuFindSearch\Service')); + return $driver; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c24019b7dab7ba03784b5b4b60dd0492386cdbe1 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for SolrDefault-based record drivers that do not need a search service. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for SolrDefault-based record drivers that do not need a search service. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SolrDefaultWithoutSearchServiceFactory extends AbstractBaseFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('searches'); + $finalOptions = [null, $config]; + return parent::__invoke($container, $requestedName, $finalOptions); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrMarc.php b/module/VuFind/src/VuFind/RecordDriver/SolrMarc.php index 1e9d69951794405c1a67d3fccb93d57ce12419b4..dd95449e228bf6ba9b7aed5f091581d869478a2c 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrMarc.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrMarc.php @@ -2,7 +2,7 @@ /** * Model for MARC records in Solr. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015. @@ -28,9 +28,6 @@ * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ namespace VuFind\RecordDriver; -use VuFind\Exception\ILS as ILSException, - VuFind\View\Helper\Root\RecordLink, - VuFind\XSLT\Processor as XSLTProcessor; /** * Model for MARC records in Solr. @@ -45,1066 +42,6 @@ use VuFind\Exception\ILS as ILSException, class SolrMarc extends SolrDefault { use IlsAwareTrait; - - /** - * MARC record. Access only via getMarcRecord() as this is initialized lazily. - * - * @var \File_MARC_Record - */ - protected $lazyMarcRecord = null; - - /** - * Fields that may contain subject headings, and their descriptions - * - * @var array - */ - protected $subjectFields = [ - '600' => 'personal name', - '610' => 'corporate name', - '611' => 'meeting name', - '630' => 'uniform title', - '648' => 'chronological', - '650' => 'topic', - '651' => 'geographic', - '653' => '', - '655' => 'genre/form', - '656' => 'occupation' - ]; - - /** - * Mappings from subject source indicators (2nd indicator of subject fields in - * MARC 21) to the their codes. - * - * @var array - * @link https://www.loc.gov/marc/bibliographic/bd6xx.html Subject field docs - * @link https://www.loc.gov/standards/sourcelist/subject.html Code list - */ - protected $subjectSources = [ - '0' => 'lcsh', - '1' => 'lcshac', - '2' => 'mesh', - '3' => 'nal', - '4' => 'unknown', - '5' => 'cash', - '6' => 'rvm' - ]; - - /** - * Get access restriction notes for the record. - * - * @return array - */ - public function getAccessRestrictions() - { - return $this->getFieldArray('506'); - } - - /** - * Get all subject headings associated with this record. Each heading is - * returned as an array of chunks, increasing from least specific to most - * specific. - * - * @param bool $extended Whether to return a keyed array with the following - * keys: - * - heading: the actual subject heading chunks - * - type: heading type - * - source: source vocabulary - * - * @return array - */ - public function getAllSubjectHeadings($extended = false) - { - // This is all the collected data: - $retval = []; - - // Try each MARC field one at a time: - foreach ($this->subjectFields as $field => $fieldType) { - // Do we have any results for the current field? If not, try the next. - $results = $this->getMarcRecord()->getFields($field); - if (!$results) { - continue; - } - - // If we got here, we found results -- let's loop through them. - foreach ($results as $result) { - // Start an array for holding the chunks of the current heading: - $current = []; - - // Get all the chunks and collect them together: - $subfields = $result->getSubfields(); - if ($subfields) { - foreach ($subfields as $subfield) { - // Numeric subfields are for control purposes and should not - // be displayed: - if (!is_numeric($subfield->getCode())) { - $current[] = $subfield->getData(); - } - } - // If we found at least one chunk, add a heading to our result: - if (!empty($current)) { - if ($extended) { - $sourceIndicator = $result->getIndicator(2); - $source = ''; - if (isset($this->subjectSources[$sourceIndicator])) { - $source = $this->subjectSources[$sourceIndicator]; - } else { - $source = $result->getSubfield('2'); - if ($source) { - $source = $source->getData(); - } - } - $retval[] = [ - 'heading' => $current, - 'type' => $fieldType, - 'source' => $source ?: '' - ]; - } else { - $retval[] = $current; - } - } - } - } - } - - // Remove duplicates and then send back everything we collected: - return array_map( - 'unserialize', array_unique(array_map('serialize', $retval)) - ); - } - - /** - * Get award notes for the record. - * - * @return array - */ - public function getAwards() - { - return $this->getFieldArray('586'); - } - - /** - * Get the bibliographic level of the current record. - * - * @return string - */ - public function getBibliographicLevel() - { - $leader = $this->getMarcRecord()->getLeader(); - $biblioLevel = strtoupper($leader[7]); - - switch ($biblioLevel) { - case 'M': // Monograph - return "Monograph"; - case 'S': // Serial - return "Serial"; - case 'A': // Monograph Part - return "MonographPart"; - case 'B': // Serial Part - return "SerialPart"; - case 'C': // Collection - return "Collection"; - case 'D': // Collection Part - return "CollectionPart"; - default: - return "Unknown"; - } - } - - /** - * Get notes on bibliography content. - * - * @return array - */ - public function getBibliographyNotes() - { - return $this->getFieldArray('504'); - } - - /** - * Return an array of all values extracted from the specified field/subfield - * combination. If multiple subfields are specified and $concat is true, they - * will be concatenated together in the order listed -- each entry in the array - * will correspond with a single MARC field. If $concat is false, the return - * array will contain separate entries for separate subfields. - * - * @param string $field The MARC field number to read - * @param array $subfields The MARC subfield codes to read - * @param bool $concat Should we concatenate subfields? - * @param string $separator Separator string (used only when $concat === true) - * - * @return array - */ - protected function getFieldArray($field, $subfields = null, $concat = true, - $separator = ' ' - ) { - // Default to subfield a if nothing is specified. - if (!is_array($subfields)) { - $subfields = ['a']; - } - - // Initialize return array - $matches = []; - - // Try to look up the specified field, return empty array if it doesn't - // exist. - $fields = $this->getMarcRecord()->getFields($field); - if (!is_array($fields)) { - return $matches; - } - - // Extract all the requested subfields, if applicable. - foreach ($fields as $currentField) { - $next = $this - ->getSubfieldArray($currentField, $subfields, $concat, $separator); - $matches = array_merge($matches, $next); - } - - return $matches; - } - - /** - * Return full record as filtered XML for public APIs. - * - * @return string - */ - public function getFilteredXML() - { - $record = clone($this->getMarcRecord()); - // The default implementation does not filter out any fields - // $record->deleteFields('9', true); - return $record->toXML(); - } - - /** - * Get notes on finding aids related to the record. - * - * @return array - */ - public function getFindingAids() - { - return $this->getFieldArray('555'); - } - - /** - * Get the first value matching the specified MARC field and subfields. - * If multiple subfields are specified, they will be concatenated together. - * - * @param string $field The MARC field to read - * @param array $subfields The MARC subfield codes to read - * - * @return string - */ - protected function getFirstFieldValue($field, $subfields = null) - { - $matches = $this->getFieldArray($field, $subfields); - return (is_array($matches) && count($matches) > 0) ? - $matches[0] : null; - } - - /** - * Get general notes on the record. - * - * @return array - */ - public function getGeneralNotes() - { - return $this->getFieldArray('500'); - } - - /** - * Get human readable publication dates for display purposes (may not be suitable - * for computer processing -- use getPublicationDates() for that). - * - * @return array - */ - public function getHumanReadablePublicationDates() - { - return $this->getPublicationInfo('c'); - } - - /** - * Get an array of newer titles for the record. - * - * @return array - */ - public function getNewerTitles() - { - // If the MARC links are being used, return blank array - $fieldsNames = isset($this->mainConfig->Record->marc_links) - ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) - : []; - return in_array('785', $fieldsNames) ? [] : parent::getNewerTitles(); - } - - /** - * Get the item's publication information - * - * @param string $subfield The subfield to retrieve ('a' = location, 'c' = date) - * - * @return array - */ - protected function getPublicationInfo($subfield = 'a') - { - // Get string separator for publication information: - $separator = isset($this->mainConfig->Record->marcPublicationInfoSeparator) - ? $this->mainConfig->Record->marcPublicationInfoSeparator : ' '; - - // First check old-style 260 field: - $results = $this->getFieldArray('260', [$subfield], true, $separator); - - // Now track down relevant RDA-style 264 fields; we only care about - // copyright and publication places (and ignore copyright places if - // publication places are present). This behavior is designed to be - // consistent with default SolrMarc handling of names/dates. - $pubResults = $copyResults = []; - - $fields = $this->getMarcRecord()->getFields('264'); - if (is_array($fields)) { - foreach ($fields as $currentField) { - $currentVal = $this - ->getSubfieldArray($currentField, [$subfield], true, $separator); - if (!empty($currentVal)) { - switch ($currentField->getIndicator('2')) { - case '1': - $pubResults = array_merge($pubResults, $currentVal); - break; - case '4': - $copyResults = array_merge($copyResults, $currentVal); - break; - } - } - } - } - $replace260 = isset($this->mainConfig->Record->replaceMarc260) - ? $this->mainConfig->Record->replaceMarc260 : false; - if (count($pubResults) > 0) { - return $replace260 ? $pubResults : array_merge($results, $pubResults); - } else if (count($copyResults) > 0) { - return $replace260 ? $copyResults : array_merge($results, $copyResults); - } - - return $results; - } - - /** - * Get the item's places of publication. - * - * @return array - */ - public function getPlacesOfPublication() - { - return $this->getPublicationInfo(); - } - - /** - * Get an array of playing times for the record (if applicable). - * - * @return array - */ - public function getPlayingTimes() - { - $times = $this->getFieldArray('306', ['a'], false); - - // Format the times to include colons ("HH:MM:SS" format). - for ($x = 0; $x < count($times); $x++) { - $times[$x] = substr($times[$x], 0, 2) . ':' . - substr($times[$x], 2, 2) . ':' . - substr($times[$x], 4, 2); - } - - return $times; - } - - /** - * Get an array of previous titles for the record. - * - * @return array - */ - public function getPreviousTitles() - { - // If the MARC links are being used, return blank array - $fieldsNames = isset($this->mainConfig->Record->marc_links) - ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) - : []; - return in_array('780', $fieldsNames) ? [] : parent::getPreviousTitles(); - } - - /** - * Get credits of people involved in production of the item. - * - * @return array - */ - public function getProductionCredits() - { - return $this->getFieldArray('508'); - } - - /** - * Get an array of publication frequency information. - * - * @return array - */ - public function getPublicationFrequency() - { - return $this->getFieldArray('310', ['a', 'b']); - } - - /** - * Get an array of strings describing relationships to other items. - * - * @return array - */ - public function getRelationshipNotes() - { - return $this->getFieldArray('580'); - } - - /** - * Get an array of all series names containing the record. Array entries may - * be either the name string, or an associative array with 'name' and 'number' - * keys. - * - * @return array - */ - public function getSeries() - { - $matches = []; - - // First check the 440, 800 and 830 fields for series information: - $primaryFields = [ - '440' => ['a', 'p'], - '800' => ['a', 'b', 'c', 'd', 'f', 'p', 'q', 't'], - '830' => ['a', 'p']]; - $matches = $this->getSeriesFromMARC($primaryFields); - if (!empty($matches)) { - return $matches; - } - - // Now check 490 and display it only if 440/800/830 were empty: - $secondaryFields = ['490' => ['a']]; - $matches = $this->getSeriesFromMARC($secondaryFields); - if (!empty($matches)) { - return $matches; - } - - // Still no results found? Resort to the Solr-based method just in case! - return parent::getSeries(); - } - - /** - * Support method for getSeries() -- given a field specification, look for - * series information in the MARC record. - * - * @param array $fieldInfo Associative array of field => subfield information - * (used to find series name) - * - * @return array - */ - protected function getSeriesFromMARC($fieldInfo) - { - $matches = []; - - // Loop through the field specification.... - foreach ($fieldInfo as $field => $subfields) { - // Did we find any matching fields? - $series = $this->getMarcRecord()->getFields($field); - if (is_array($series)) { - foreach ($series as $currentField) { - // Can we find a name using the specified subfield list? - $name = $this->getSubfieldArray($currentField, $subfields); - if (isset($name[0])) { - $currentArray = ['name' => $name[0]]; - - // Can we find a number in subfield v? (Note that number is - // always in subfield v regardless of whether we are dealing - // with 440, 490, 800 or 830 -- hence the hard-coded array - // rather than another parameter in $fieldInfo). - $number - = $this->getSubfieldArray($currentField, ['v']); - if (isset($number[0])) { - $currentArray['number'] = $number[0]; - } - - // Save the current match: - $matches[] = $currentArray; - } - } - } - } - - return $matches; - } - - /** - * Return an array of non-empty subfield values found in the provided MARC - * field. If $concat is true, the array will contain either zero or one - * entries (empty array if no subfields found, subfield values concatenated - * together in specified order if found). If concat is false, the array - * will contain a separate entry for each subfield value found. - * - * @param object $currentField Result from File_MARC::getFields. - * @param array $subfields The MARC subfield codes to read - * @param bool $concat Should we concatenate subfields? - * @param string $separator Separator string (used only when $concat === true) - * - * @return array - */ - protected function getSubfieldArray($currentField, $subfields, $concat = true, - $separator = ' ' - ) { - // Start building a line of text for the current field - $matches = []; - - // Loop through all subfields, collecting results that match the whitelist; - // note that it is important to retain the original MARC order here! - $allSubfields = $currentField->getSubfields(); - if (!empty($allSubfields)) { - foreach ($allSubfields as $currentSubfield) { - if (in_array($currentSubfield->getCode(), $subfields)) { - // Grab the current subfield value and act on it if it is - // non-empty: - $data = trim($currentSubfield->getData()); - if (!empty($data)) { - $matches[] = $data; - } - } - } - } - - // Send back the data in a different format depending on $concat mode: - return $concat && $matches ? [implode($separator, $matches)] : $matches; - } - - /** - * Get an array of summary strings for the record. - * - * @return array - */ - public function getSummary() - { - return $this->getFieldArray('520'); - } - - /** - * Get an array of technical details on the item represented by the record. - * - * @return array - */ - public function getSystemDetails() - { - return $this->getFieldArray('538'); - } - - /** - * Get an array of note about the record's target audience. - * - * @return array - */ - public function getTargetAudienceNotes() - { - return $this->getFieldArray('521'); - } - - /** - * Get the text of the part/section portion of the title. - * - * @return string - */ - public function getTitleSection() - { - return $this->getFirstFieldValue('245', ['n', 'p']); - } - - /** - * Get the statement of responsibility that goes with the title (i.e. "by John - * Smith"). - * - * @return string - */ - public function getTitleStatement() - { - return $this->getFirstFieldValue('245', ['c']); - } - - /** - * Get an array of lines from the table of contents. - * - * @return array - */ - public function getTOC() - { - // Return empty array if we have no table of contents: - $fields = $this->getMarcRecord()->getFields('505'); - if (!$fields) { - return []; - } - - // If we got this far, we have a table -- collect it as a string: - $toc = []; - foreach ($fields as $field) { - $subfields = $field->getSubfields(); - foreach ($subfields as $subfield) { - // Break the string into appropriate chunks, filtering empty strings, - // and merge them into return array: - $toc = array_merge( - $toc, - array_filter(explode('--', $subfield->getData()), 'trim') - ); - } - } - return $toc; - } - - /** - * Get hierarchical place names (MARC field 752) - * - * Returns an array of formatted hierarchical place names, consisting of all - * alpha-subfields, concatenated for display - * - * @return array - */ - public function getHierarchicalPlaceNames() - { - $placeNames = []; - if ($fields = $this->getMarcRecord()->getFields('752')) { - foreach ($fields as $field) { - $subfields = $field->getSubfields(); - $current = []; - foreach ($subfields as $subfield) { - if (!is_numeric($subfield->getCode())) { - $current[] = $subfield->getData(); - } - } - $placeNames[] = implode(' -- ', $current); - } - } - return $placeNames; - } - - /** - * Return an array of associative URL arrays with one or more of the following - * keys: - * - * <li> - * <ul>desc: URL description text to display (optional)</ul> - * <ul>url: fully-formed URL (required if 'route' is absent)</ul> - * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> - * <ul>routeParams: Parameters for route (optional)</ul> - * <ul>queryString: Query params to append after building route (optional)</ul> - * </li> - * - * @return array - */ - public function getURLs() - { - $retVal = []; - - // Which fields/subfields should we check for URLs? - $fieldsToCheck = [ - '856' => ['y', 'z', '3'], // Standard URL - '555' => ['a'] // Cumulative index/finding aids - ]; - - foreach ($fieldsToCheck as $field => $subfields) { - $urls = $this->getMarcRecord()->getFields($field); - if ($urls) { - foreach ($urls as $url) { - // Is there an address in the current field? - $address = $url->getSubfield('u'); - if ($address) { - $address = $address->getData(); - - // Is there a description? If not, just use the URL itself. - foreach ($subfields as $current) { - $desc = $url->getSubfield($current); - if ($desc) { - break; - } - } - if ($desc) { - $desc = $desc->getData(); - } else { - $desc = $address; - } - - $retVal[] = ['url' => $address, 'desc' => $desc]; - } - } - } - } - - return $retVal; - } - - /** - * Get all record links related to the current record. Each link is returned as - * array. - * Format: - * array( - * array( - * 'title' => label_for_title - * 'value' => link_name - * 'link' => link_URI - * ), - * ... - * ) - * - * @return null|array - */ - public function getAllRecordLinks() - { - // Load configurations: - $fieldsNames = isset($this->mainConfig->Record->marc_links) - ? explode(',', $this->mainConfig->Record->marc_links) : []; - $useVisibilityIndicator - = isset($this->mainConfig->Record->marc_links_use_visibility_indicator) - ? $this->mainConfig->Record->marc_links_use_visibility_indicator : true; - - $retVal = []; - foreach ($fieldsNames as $value) { - $value = trim($value); - $fields = $this->getMarcRecord()->getFields($value); - if (!empty($fields)) { - foreach ($fields as $field) { - // Check to see if we should display at all - if ($useVisibilityIndicator) { - $visibilityIndicator = $field->getIndicator('1'); - if ($visibilityIndicator == '1') { - continue; - } - } - - // Get data for field - $tmp = $this->getFieldData($field); - if (is_array($tmp)) { - $retVal[] = $tmp; - } - } - } - } - return empty($retVal) ? null : $retVal; - } - - /** - * Support method for getFieldData() -- factor the relationship indicator - * into the field number where relevant to generate a note to associate - * with a record link. - * - * @param File_MARC_Data_Field $field Field to examine - * - * @return string - */ - protected function getRecordLinkNote($field) - { - // If set, use relationship information from subfield i - if ($subfieldI = $field->getSubfield('i')) { - $data = trim($subfieldI->getData()); - if (!empty($data)) { - return $data; - } - } - - // Normalize blank relationship indicator to 0: - $relationshipIndicator = $field->getIndicator('2'); - if ($relationshipIndicator == ' ') { - $relationshipIndicator = '0'; - } - - // Assign notes based on the relationship type - $value = $field->getTag(); - switch ($value) { - case '780': - if (in_array($relationshipIndicator, range('0', '7'))) { - $value .= '_' . $relationshipIndicator; - } - break; - case '785': - if (in_array($relationshipIndicator, range('0', '8'))) { - $value .= '_' . $relationshipIndicator; - } - break; - } - - return 'note_' . $value; - } - - /** - * Returns the array element for the 'getAllRecordLinks' method - * - * @param File_MARC_Data_Field $field Field to examine - * - * @return array|bool Array on success, boolean false if no - * valid link could be found in the data. - */ - protected function getFieldData($field) - { - // Make sure that there is a t field to be displayed: - if ($title = $field->getSubfield('t')) { - $title = $title->getData(); - } else { - return false; - } - - $linkTypeSetting = isset($this->mainConfig->Record->marc_links_link_types) - ? $this->mainConfig->Record->marc_links_link_types - : 'id,oclc,dlc,isbn,issn,title'; - $linkTypes = explode(',', $linkTypeSetting); - $linkFields = $field->getSubfields('w'); - - // Run through the link types specified in the config. - // For each type, check field for reference - // If reference found, exit loop and go straight to end - // If no reference found, check the next link type instead - foreach ($linkTypes as $linkType) { - switch (trim($linkType)){ - case 'oclc': - foreach ($linkFields as $current) { - if ($oclc = $this->getIdFromLinkingField($current, 'OCoLC')) { - $link = ['type' => 'oclc', 'value' => $oclc]; - } - } - break; - case 'dlc': - foreach ($linkFields as $current) { - if ($dlc = $this->getIdFromLinkingField($current, 'DLC', true)) { - $link = ['type' => 'dlc', 'value' => $dlc]; - } - } - break; - case 'id': - foreach ($linkFields as $current) { - if ($bibLink = $this->getIdFromLinkingField($current)) { - $link = ['type' => 'bib', 'value' => $bibLink]; - } - } - break; - case 'isbn': - if ($isbn = $field->getSubfield('z')) { - $link = [ - 'type' => 'isn', 'value' => trim($isbn->getData()), - 'exclude' => $this->getUniqueId() - ]; - } - break; - case 'issn': - if ($issn = $field->getSubfield('x')) { - $link = [ - 'type' => 'isn', 'value' => trim($issn->getData()), - 'exclude' => $this->getUniqueId() - ]; - } - break; - case 'title': - $link = ['type' => 'title', 'value' => $title]; - break; - } - // Exit loop if we have a link - if (isset($link)) { - break; - } - } - // Make sure we have something to display: - return !isset($link) ? false : [ - 'title' => $this->getRecordLinkNote($field), - 'value' => $title, - 'link' => $link - ]; - } - - /** - * Returns an id extracted from the identifier subfield passed in - * - * @param \File_MARC_Subfield $idField MARC field containing id information - * @param string $prefix Prefix to search for in id field - * @param bool $raw Return raw match, or normalize? - * - * @return string|bool ID on success, false on failure - */ - protected function getIdFromLinkingField($idField, $prefix = null, $raw = false) - { - $text = $idField->getData(); - if (preg_match('/\(([^)]+)\)(.+)/', $text, $matches)) { - // If prefix matches, return ID: - if ($matches[1] == $prefix) { - // Special case -- LCCN should not be stripped: - return $raw - ? $matches[2] - : trim(str_replace(range('a', 'z'), '', ($matches[2]))); - } - } else if ($prefix == null) { - // If no prefix was given or found, we presume it is a raw bib record - return $text; - } - return false; - } - - /** - * Get Status/Holdings Information from the internally stored MARC Record - * (support method used by the NoILS driver). - * - * @param array $field The MARC Field to retrieve - * @param array $data A keyed array of data to retrieve from subfields - * - * @return array - */ - public function getFormattedMarcDetails($field, $data) - { - // Initialize return array - $matches = []; - $i = 0; - - // Try to look up the specified field, return empty array if it doesn't - // exist. - $fields = $this->getMarcRecord()->getFields($field); - if (!is_array($fields)) { - return $matches; - } - - // Extract all the requested subfields, if applicable. - foreach ($fields as $currentField) { - foreach ($data as $key => $info) { - $split = explode("|", $info); - if ($split[0] == "msg") { - if ($split[1] == "true") { - $result = true; - } elseif ($split[1] == "false") { - $result = false; - } else { - $result = $split[1]; - } - $matches[$i][$key] = $result; - } else { - // Default to subfield a if nothing is specified. - if (count($split) < 2) { - $subfields = ['a']; - } else { - $subfields = str_split($split[1]); - } - $result = $this->getSubfieldArray( - $currentField, $subfields, true - ); - $matches[$i][$key] = count($result) > 0 - ? (string)$result[0] : ''; - } - } - $matches[$i]['id'] = $this->getUniqueID(); - $i++; - } - return $matches; - } - - /** - * Return an XML representation of the record using the specified format. - * Return false if the format is unsupported. - * - * @param string $format Name of format to use (corresponds with OAI-PMH - * metadataPrefix parameter). - * @param string $baseUrl Base URL of host containing VuFind (optional; - * may be used to inject record URLs into XML when appropriate). - * @param RecordLink $recordLink Record link helper (optional; may be used to - * inject record URLs into XML when appropriate). - * - * @return mixed XML, or false if format unsupported. - */ - public function getXML($format, $baseUrl = null, $recordLink = null) - { - // Special case for MARC: - if ($format == 'marc21') { - $xml = $this->getMarcRecord()->toXML(); - $xml = str_replace( - [chr(27), chr(28), chr(29), chr(30), chr(31)], ' ', $xml - ); - $xml = simplexml_load_string($xml); - if (!$xml || !isset($xml->record)) { - return false; - } - - // Set up proper namespacing and extract just the <record> tag: - $xml->record->addAttribute('xmlns', "http://www.loc.gov/MARC21/slim"); - $xml->record->addAttribute( - 'xsi:schemaLocation', - 'http://www.loc.gov/MARC21/slim ' . - 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd', - 'http://www.w3.org/2001/XMLSchema-instance' - ); - $xml->record->addAttribute('type', 'Bibliographic'); - return $xml->record->asXML(); - } - - // Try the parent method: - return parent::getXML($format, $baseUrl, $recordLink); - } - - /** - * Get access to the raw File_MARC object. - * - * @return \File_MARCBASE - */ - public function getMarcRecord() - { - if (null === $this->lazyMarcRecord) { - $marc = trim($this->fields['fullrecord']); - - // check if we are dealing with MARCXML - if (substr($marc, 0, 1) == '<') { - $marc = new \File_MARCXML($marc, \File_MARCXML::SOURCE_STRING); - } else { - // When indexing over HTTP, SolrMarc may use entities instead of - // certain control characters; we should normalize these: - $marc = str_replace( - ['#29;', '#30;', '#31;'], ["\x1D", "\x1E", "\x1F"], $marc - ); - $marc = new \File_MARC($marc, \File_MARC::SOURCE_STRING); - } - - $this->lazyMarcRecord = $marc->next(); - if (!$this->lazyMarcRecord) { - throw new \File_MARC_Exception('Cannot Process MARC Record'); - } - } - - return $this->lazyMarcRecord; - } - - /** - * Get an XML RDF representation of the data in this record. - * - * @return mixed XML RDF data (empty if unsupported or error). - */ - public function getRDFXML() - { - return XSLTProcessor::process( - 'record-rdf-mods.xsl', trim($this->getMarcRecord()->toXML()) - ); - } - - /** - * Return the list of "source records" for this consortial record. - * - * @return array - */ - public function getConsortialIDs() - { - return $this->getFieldArray('035', 'a', true); - } - - /** - * Magic method for legacy compatibility with marcRecord property. - * - * @param string $key Key to access. - * - * @return mixed - */ - public function __get($key) - { - if ($key === 'marcRecord') { - // property deprecated as of release 2.5. - trigger_error( - 'marcRecord property is deprecated; use getMarcRecord()', - E_USER_DEPRECATED - ); - return $this->getMarcRecord(); - } - return null; - } + use MarcReaderTrait; + use MarcAdvancedTrait; } diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrMarcRemote.php b/module/VuFind/src/VuFind/RecordDriver/SolrMarcRemote.php index 961bb3ab1b8406cdc08d3ec27f5fbb2b0774c61d..8970bb51adba78bead76d7d7b5323795c8ff1945 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrMarcRemote.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrMarcRemote.php @@ -3,7 +3,7 @@ * Model for MARC records without a fullrecord in Solr. The fullrecord is being * retrieved from an external source. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Leipzig University Library 2014. * @@ -29,8 +29,9 @@ * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ namespace VuFind\RecordDriver; -use VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface, - Zend\Log\LoggerAwareInterface as LoggerAwareInterface; + +use VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface; +use Zend\Log\LoggerAwareInterface as LoggerAwareInterface; /** * Model for MARC records without a fullrecord in Solr. The fullrecord is being diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrReserves.php b/module/VuFind/src/VuFind/RecordDriver/SolrReserves.php index 9aff7940a94583e9e4b94fdc831f5a6d0ded584b..795663417638714d508fb7f99bf5adce718564d2 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrReserves.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrReserves.php @@ -2,7 +2,7 @@ /** * Model for Solr reserves records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrWeb.php b/module/VuFind/src/VuFind/RecordDriver/SolrWeb.php index 57e6a75a98f7cb88e5d9af3e9d704cc628840533..6c1bcd707fa35afc69794426bfb7a0409a444b29 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrWeb.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrWeb.php @@ -2,7 +2,7 @@ /** * Model for Solr web records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..91cef38e71b86d113dba3849fce5eceeda3a7d35 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for SolrWeb record drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for SolrWeb record drivers. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SolrWebFactory extends AbstractBaseFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('website'); + $finalOptions = [$config, $config]; + return parent::__invoke($container, $requestedName, $finalOptions); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/Summon.php b/module/VuFind/src/VuFind/RecordDriver/Summon.php index 79b49428cca6cf7a8f2c0f29bf04044f6a222c84..36b2114463adeeeca39160426c8dcafbb6103d4a 100644 --- a/module/VuFind/src/VuFind/RecordDriver/Summon.php +++ b/module/VuFind/src/VuFind/RecordDriver/Summon.php @@ -2,7 +2,7 @@ /** * Model for Summon records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class Summon extends SolrDefault +class Summon extends DefaultRecord { /** * Fields that may contain subject headings, and their descriptions @@ -334,7 +334,7 @@ class Summon extends SolrDefault 'm-d-Y', "{$current['month']}-{$current['day']}-{$current['year']}" ); - } else if (isset($current['year'])) { + } elseif (isset($current['year'])) { $dates[] = $current['year']; } } @@ -444,7 +444,7 @@ class Summon extends SolrDefault ) { if ($size === 'small' && isset($this->fields['thumbnail_s'][0])) { return ['proxy' => $this->fields['thumbnail_s'][0]]; - } else if (isset($this->fields['thumbnail_m'][0])) { + } elseif (isset($this->fields['thumbnail_m'][0])) { return ['proxy' => $this->fields['thumbnail_m'][0]]; } } @@ -526,6 +526,7 @@ class Summon extends SolrDefault { return $this->fields['ID'][0]; } + /** * Get the title of the item that contains this record (i.e. MARC 773s of a * journal). @@ -581,7 +582,7 @@ class Summon extends SolrDefault { if (isset($this->fields['EndPage'])) { return $this->fields['EndPage'][0]; - } else if (isset($this->fields['PageCount']) + } elseif (isset($this->fields['PageCount']) && $this->fields['PageCount'] > 1 && intval($this->fields['StartPage'][0]) > 0 ) { diff --git a/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php b/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f39a757bca64a2ae17e9e24234f4379ae87e4884 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php @@ -0,0 +1,64 @@ +<?php +/** + * Factory for Summon record drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Summon record drivers. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SummonFactory extends NameBasedConfigFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $driver = parent::__invoke($container, $requestedName, $options); + $driver->setDateConverter($container->get('VuFind\Date\Converter')); + return $driver; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/WorldCat.php b/module/VuFind/src/VuFind/RecordDriver/WorldCat.php index a9d58936a8ccee7ac8c795201f44e0e71269a363..d1a535d167fb44202cf5c6acfa0fd1b3bf4ee8f6 100644 --- a/module/VuFind/src/VuFind/RecordDriver/WorldCat.php +++ b/module/VuFind/src/VuFind/RecordDriver/WorldCat.php @@ -2,7 +2,7 @@ /** * Model for MARC records in WorldCat. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,8 +36,13 @@ namespace VuFind\RecordDriver; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ -class WorldCat extends SolrMarc +class WorldCat extends DefaultRecord { + use MarcReaderTrait, MarcAdvancedTrait, MarcBasicTrait { + MarcBasicTrait::getNewerTitles insteadof MarcAdvancedTrait; + MarcBasicTrait::getPreviousTitles insteadof MarcAdvancedTrait; + } + /** * Set raw data to initialize the object. * @@ -65,70 +70,6 @@ class WorldCat extends SolrMarc parent::setRawData(['fullrecord' => $data]); } - /** - * Get an array of information about record holdings, obtained in real-time - * from the ILS. - * - * @return array - */ - public function getRealTimeHoldings() - { - // Not supported here: - return []; - } - - /** - * Get an array of information about record history, obtained in real-time - * from the ILS. - * - * @return array - */ - public function getRealTimeHistory() - { - // Not supported here: - return []; - } - - /** - * Returns true if the record supports real-time AJAX status lookups. - * - * @return bool - */ - public function supportsAjaxStatus() - { - return false; - } - - /** - * Get an array of all ISBNs associated with the record (may be empty). - * - * @return array - */ - public function getISBNs() - { - return $this->getFieldArray('020'); - } - - /** - * Get an array of all ISSNs associated with the record (may be empty). - * - * @return array - */ - public function getISSNs() - { - return $this->getFieldArray('022'); - } - - /** - * Get an array of all the formats associated with the record. - * - * @return array - */ - public function getFormats() - { - return $this->getFieldArray('245', ['h']); - } - /** * Get the OCLC number of the record. * @@ -138,173 +79,4 @@ class WorldCat extends SolrMarc { return [$this->getUniqueID()]; } - - /** - * Return the unique identifier of this record within the Solr index; - * useful for retrieving additional information (like tags and user - * comments) from the external MySQL database. - * - * @return string Unique identifier. - */ - public function getUniqueID() - { - return (string)$this->getMarcRecord()->getField('001')->getData(); - } - - /** - * Get the call numbers associated with the record (empty string if none). - * - * @return array - */ - public function getCallNumbers() - { - $retVal = []; - foreach (['090', '050'] as $field) { - $callNo = $this->getFirstFieldValue($field, ['a', 'b']); - if (!empty($callNo)) { - $retVal[] = $callNo; - } - } - $dewey = $this->getDeweyCallNumber(); - if (!empty($dewey)) { - $retVal[] = $dewey; - } - return $retVal; - } - - /** - * Get the Dewey call number associated with this record (empty string if none). - * - * @return string - */ - public function getDeweyCallNumber() - { - return $this->getFirstFieldValue('082', ['a']); - } - - /** - * Get the main authors of the record. - * - * @return array - */ - public function getPrimaryAuthors() - { - return [$this->getFirstFieldValue('100', ['a'])]; - } - - /** - * Get an array of all the languages associated with the record. - * - * @return array - */ - public function getLanguages() - { - $retVal = []; - $field = $this->getMarcRecord()->getField('008'); - if ($field) { - $content = $field->getData(); - if (strlen($content) >= 38) { - $retVal[] = substr($content, 35, 3); - } - } - return $retVal; - } - - /** - * Get the full title of the record. - * - * @return string - */ - public function getTitle() - { - return $this->getFirstFieldValue('245', ['a', 'b']); - } - - /** - * Get a sortable title for the record (i.e. no leading articles). - * - * @return string - */ - public function getSortTitle() - { - $field = $this->getMarcRecord()->getField('245'); - if ($field) { - $title = $field->getSubfield('a'); - if ($title) { - $skip = $field->getIndicator(2); - return substr($title->getData(), $skip); - } - } - return parent::getSortTitle(); - } - - /** - * Get the short (pre-subtitle) title of the record. - * - * @return string - */ - public function getShortTitle() - { - return $this->getFirstFieldValue('245', ['a']); - } - - /** - * Get the subtitle of the record. - * - * @return string - */ - public function getSubtitle() - { - return $this->getFirstFieldValue('245', ['b']); - } - - /** - * Get the publishers of the record. - * - * @return array - */ - public function getPublishers() - { - return $this->getPublicationInfo('b'); - } - - /** - * Get the publication dates of the record. See also getDateSpan(). - * - * @return array - */ - public function getPublicationDates() - { - return $this->getPublicationInfo('c'); - } - - /** - * Get an array of all secondary authors (complementing getPrimaryAuthors()). - * - * @return array - */ - public function getSecondaryAuthors() - { - return $this->getFieldArray('700', ['a', 'b', 'c', 'd']); - } - - /** - * Get an array of newer titles for the record. - * - * @return array - */ - public function getNewerTitles() - { - return $this->getFieldArray('785', ['a', 's', 't']); - } - - /** - * Get an array of previous titles for the record. - * - * @return array - */ - public function getPreviousTitles() - { - return $this->getFieldArray('780', ['a', 's', 't']); - } } diff --git a/module/VuFind/src/VuFind/RecordTab/AbstractBase.php b/module/VuFind/src/VuFind/RecordTab/AbstractBase.php index ec17fa281ddd9f11ac14303bd1658704e93f3696..cf7b9edb83625c61906d4e6596d57a2a92973021 100644 --- a/module/VuFind/src/VuFind/RecordTab/AbstractBase.php +++ b/module/VuFind/src/VuFind/RecordTab/AbstractBase.php @@ -2,7 +2,7 @@ /** * Record tab abstract base class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ namespace VuFind\RecordTab; -use ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; + +use ZfcRbac\Service\AuthorizationServiceAwareInterface; +use ZfcRbac\Service\AuthorizationServiceAwareTrait; /** * Record tab abstract base class diff --git a/module/VuFind/src/VuFind/RecordTab/AbstractContent.php b/module/VuFind/src/VuFind/RecordTab/AbstractContent.php index d9f86a45114305970a2aa4df63e81bf15eaad10c..5c5ea7fc16e72e10599148d180afe0518f0eb942 100644 --- a/module/VuFind/src/VuFind/RecordTab/AbstractContent.php +++ b/module/VuFind/src/VuFind/RecordTab/AbstractContent.php @@ -2,7 +2,7 @@ /** * Reviews tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ namespace VuFind\RecordTab; + use VuFind\Content\Loader; /** diff --git a/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTree.php b/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTree.php index 6fc8338a5bd32a2c1d6a46870cc66659d2f89028..1429a9699a43afd610bb41e12023793871d77ea9 100644 --- a/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTree.php +++ b/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTree.php @@ -2,7 +2,7 @@ /** * HierarchyTree tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/CollectionList.php b/module/VuFind/src/VuFind/RecordTab/CollectionList.php index 2fc8f4782ed47185c33ab09b59cdf0d6aee30f1a..f01983acdd0acf2b1698c026565e09080da6133d 100644 --- a/module/VuFind/src/VuFind/RecordTab/CollectionList.php +++ b/module/VuFind/src/VuFind/RecordTab/CollectionList.php @@ -2,7 +2,7 @@ /** * Collection list tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,10 @@ * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ namespace VuFind\RecordTab; -use VuFind\Recommend\PluginManager as RecommendManager, - VuFind\Search\RecommendListener, VuFind\Search\SearchRunner; + +use VuFind\Recommend\PluginManager as RecommendManager; +use VuFind\Search\RecommendListener; +use VuFind\Search\SearchRunner; /** * Collection list tab diff --git a/module/VuFind/src/VuFind/RecordTab/Description.php b/module/VuFind/src/VuFind/RecordTab/Description.php index af5b32e8f13c76a7c5ec4198e6ddd65e3b4718f9..ea7fdd53c840b9d016010b5b369da53be00ac2a5 100644 --- a/module/VuFind/src/VuFind/RecordTab/Description.php +++ b/module/VuFind/src/VuFind/RecordTab/Description.php @@ -2,7 +2,7 @@ /** * Description tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/Excerpt.php b/module/VuFind/src/VuFind/RecordTab/Excerpt.php index 2b8008dd4eac365df532659ba9e981baedaa971d..0f8ed8af95a4d2f58b3646c7bc144576da58236b 100644 --- a/module/VuFind/src/VuFind/RecordTab/Excerpt.php +++ b/module/VuFind/src/VuFind/RecordTab/Excerpt.php @@ -2,7 +2,7 @@ /** * Excerpt tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/Factory.php b/module/VuFind/src/VuFind/RecordTab/Factory.php index f68ba6a0fb19e0b2ea42e2ff159ecf6618780c41..2725d068153a057dcb3f4d8809fe1c7c83af2260 100644 --- a/module/VuFind/src/VuFind/RecordTab/Factory.php +++ b/module/VuFind/src/VuFind/RecordTab/Factory.php @@ -2,7 +2,7 @@ /** * Record Tab Factory Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ namespace VuFind\RecordTab; + use Zend\ServiceManager\ServiceManager; /** @@ -51,8 +52,8 @@ class Factory public static function getCollectionHierarchyTree(ServiceManager $sm) { return new CollectionHierarchyTree( - $sm->getServiceLocator()->get('VuFind\Config')->get('config'), - $sm->getServiceLocator()->get('VuFind\RecordLoader') + $sm->get('VuFind\Config\PluginManager')->get('config'), + $sm->get('VuFind\Record\Loader') ); } @@ -66,8 +67,8 @@ class Factory public static function getCollectionList(ServiceManager $sm) { return new CollectionList( - $sm->getServiceLocator()->get('VuFind\SearchRunner'), - $sm->getServiceLocator()->get('VuFind\RecommendPluginManager') + $sm->get('VuFind\Search\SearchRunner'), + $sm->get('VuFind\Recommend\PluginManager') ); } @@ -80,10 +81,10 @@ class Factory */ public static function getExcerpt(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); // Only instantiate the loader if the feature is enabled: if (isset($config->Content->excerpts)) { - $loader = $sm->getServiceLocator()->get('VuFind\ContentPluginManager') + $loader = $sm->get('VuFind\Content\PluginManager') ->get('excerpts'); } else { $loader = null; @@ -127,7 +128,7 @@ class Factory public static function getHierarchyTree(ServiceManager $sm) { return new HierarchyTree( - $sm->getServiceLocator()->get('VuFind\Config')->get('config') + $sm->get('VuFind\Config\PluginManager')->get('config') ); } @@ -143,15 +144,13 @@ class Factory // If VuFind is configured to suppress the holdings tab when the // ILS driver specifies no holdings, we need to pass in a connection // object: - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - if (isset($config->Site->hideHoldingsTabWhenEmpty) - && $config->Site->hideHoldingsTabWhenEmpty - ) { - $catalog = $sm->getServiceLocator()->get('VuFind\ILSConnection'); - } else { - $catalog = false; - } - return new HoldingsILS($catalog); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); + $catalog = ($config->Site->hideHoldingsTabWhenEmpty ?? false) + ? $sm->get('VuFind\ILS\Connection') : null; + return new HoldingsILS( + $catalog, + (string)($config->Site->holdingsTemplate ?? 'standard') + ); } /** @@ -163,7 +162,7 @@ class Factory */ public static function getHoldingsWorldCat(ServiceManager $sm) { - $bm = $sm->getServiceLocator()->get('VuFind\Search\BackendManager'); + $bm = $sm->get('VuFind\Search\BackendManager'); return new HoldingsWorldCat($bm->get('WorldCat')->getConnector()); } @@ -176,17 +175,16 @@ class Factory */ public static function getMap(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $mapType = isset($config->Content->recordMap) - ? $config->Content->recordMap : null; - $options = []; - $optionFields = ['displayCoords', 'mapLabels', 'graticule']; - foreach ($optionFields as $field) { - if (isset($config->Content->$field)) { - $options[$field] = $config->Content->$field; - } - } - return new Map($mapType, $options); + // get Map Tab config options + $mapTabConfig = $sm->get('VuFind\GeoFeatures\MapTabConfig'); + $mapTabOptions = $mapTabConfig->getMapTabOptions(); + $mapTabDisplay = $mapTabOptions['recordMap']; + + // add basemap options + $basemapConfig = $sm->get('VuFind\GeoFeatures\BasemapConfig'); + $basemapOptions = $basemapConfig->getBasemap('MapTab'); + + return new Map($mapTabDisplay, $basemapOptions, $mapTabOptions); } /** @@ -198,7 +196,7 @@ class Factory */ public static function getPreview(ServiceManager $sm) { - $cfg = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $cfg = $sm->get('VuFind\Config\PluginManager')->get('config'); // currently only active if config [content] [previews] contains google // and googleoptions[tab] is not empty. $active = false; @@ -225,9 +223,7 @@ class Factory */ public static function getSimilarItemsCarousel(ServiceManager $sm) { - return new SimilarItemsCarousel( - $sm->getServiceLocator()->get('VuFind\Search') - ); + return new SimilarItemsCarousel($sm->get('VuFindSearch\Service')); } /** @@ -239,10 +235,10 @@ class Factory */ public static function getReviews(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); // Only instantiate the loader if the feature is enabled: if (isset($config->Content->reviews)) { - $loader = $sm->getServiceLocator()->get('VuFind\ContentPluginManager') + $loader = $sm->get('VuFind\Content\PluginManager') ->get('reviews'); } else { $loader = null; @@ -250,6 +246,26 @@ class Factory return new Reviews($loader, static::getHideSetting($config, 'reviews')); } + /** + * Factory for TOC tab plugin. + * + * @param ServiceManager $sm Service manager. + * + * @return TablesOfContents + */ + public static function getTOC(ServiceManager $sm) + { + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); + // Only instantiate the loader if the feature is enabled: + if (isset($config->Content->toc)) { + $loader = $sm->get('VuFind\Content\PluginManager') + ->get('toc'); + } else { + $loader = null; + } + return new TOC($loader, static::getHideSetting($config, 'toc')); + } + /** * Factory for UserComments tab plugin. * @@ -259,8 +275,8 @@ class Factory */ public static function getUserComments(ServiceManager $sm) { - $capabilities = $sm->getServiceLocator()->get('VuFind\AccountCapabilities'); - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $capabilities = $sm->get('VuFind\Config\AccountCapabilities'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $useRecaptcha = isset($config->Captcha) && isset($config->Captcha->forms) && (trim($config->Captcha->forms) === '*' || strpos($config->Captcha->forms, 'userComments') !== false); diff --git a/module/VuFind/src/VuFind/RecordTab/HierarchyTree.php b/module/VuFind/src/VuFind/RecordTab/HierarchyTree.php index 4b6d3e025e1c4317cce411bb10171aba209d0943..453251b259d196432a9d5d22a173204879d1b52e 100644 --- a/module/VuFind/src/VuFind/RecordTab/HierarchyTree.php +++ b/module/VuFind/src/VuFind/RecordTab/HierarchyTree.php @@ -2,7 +2,7 @@ /** * HierarchyTree tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -191,7 +191,7 @@ class HierarchyTree extends AbstractBase public function searchActive() { $config = $this->getConfig(); - return (!isset($config->Hierarchy->search) || $config->Hierarchy->search); + return !isset($config->Hierarchy->search) || $config->Hierarchy->search; } /** diff --git a/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php b/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php index 8670d3bc121c228b613fb76ae64c3dbcd708cd63..fd760e0aaf60b704210a7ba1fa992a02c494d352 100644 --- a/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php +++ b/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php @@ -2,7 +2,7 @@ /** * Holdings (ILS) tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,8 @@ */ namespace VuFind\RecordTab; +use VuFind\ILS\Connection; + /** * Holdings (ILS) tab * @@ -39,22 +41,30 @@ namespace VuFind\RecordTab; class HoldingsILS extends AbstractBase { /** - * ILS connection (or false if not applicable) + * ILS connection (or null if not applicable) * - * @param \VuFind\ILS\Connection|bool + * @param Connection */ protected $catalog; + /** + * Name of template to use for rendering holdings. + * + * @param string + */ + protected $template; + /** * Constructor * - * @param \VuFind\ILS\Connection|bool $catalog ILS connection to use to check - * for holdings before displaying the tab; set to false if no check is needed + * @param \VuFind\ILS\Connection|bool $catalog ILS connection to use to check + * for holdings before displaying the tab; set to null if no check is needed + * @param string $template Holdings template to use */ - public function __construct($catalog) + public function __construct(Connection $catalog = null, $template = null) { - $this->catalog = ($catalog && $catalog instanceof \VuFind\ILS\Connection) - ? $catalog : false; + $this->catalog = $catalog; + $this->template = $template ?? 'standard'; } /** @@ -99,4 +109,14 @@ class HoldingsILS extends AbstractBase } return true; } + + /** + * Get name of template for rendering holdings. + * + * @return string + */ + public function getTemplate() + { + return $this->template; + } } diff --git a/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCat.php b/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCat.php index e346088115e53d86834a67f16a995f97d5ec07f8..1a2ea9a7b420e46ddc40584b8b345eb621c0e145 100644 --- a/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCat.php +++ b/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCat.php @@ -2,7 +2,7 @@ /** * Holdings (WorldCat) tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ namespace VuFind\RecordTab; + use VuFindSearch\Backend\WorldCat\Connector; /** diff --git a/module/VuFind/src/VuFind/RecordTab/Map.php b/module/VuFind/src/VuFind/RecordTab/Map.php index 2aa03248a19c9d97ff8d608257d51de483582e95..cdaf6c06abdd702fc8dfc95551ff8d4552e30f71 100644 --- a/module/VuFind/src/VuFind/RecordTab/Map.php +++ b/module/VuFind/src/VuFind/RecordTab/Map.php @@ -2,7 +2,7 @@ /** * Map tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -41,11 +41,11 @@ namespace VuFind\RecordTab; class Map extends AbstractBase { /** - * What type of map interface should be used? + * Should Map Tab be displayed? * - * @var string + * @var bool */ - protected $mapType = null; + protected $mapTabDisplay = false; /** * Should we display coordinates as part of labels? @@ -68,25 +68,33 @@ class Map extends AbstractBase */ protected $graticule = false; + /** + * Basemap settings + * + * @var array + */ + protected $basemapOptions = []; + /** * Constructor * - * @param string $mapType Map provider (valid options: 'openlayers'; - * null to disable this feature) - * @param array $options Additional settings + * @param bool $mapTabDisplay Display Map + * @param array $basemapOptions basemap settings + * @param array $mapTabOptions MapTab settings */ - public function __construct($mapType = null, $options = []) - { - switch (trim(strtolower($mapType))) { - case 'openlayers': - $this->mapType = trim(strtolower($mapType)); + public function __construct($mapTabDisplay = false, $basemapOptions = [], + $mapTabOptions = [] + ) { + if ($mapTabDisplay) { + $this->mapTabDisplay = $mapTabDisplay; $legalOptions = ['displayCoords', 'mapLabels', 'graticule']; foreach ($legalOptions as $option) { - if (isset($options[$option])) { - $this->$option = $options[$option]; + if (isset($mapTabOptions[$option])) { + $this->$option = $mapTabOptions[$option]; } } - break; + $this->basemapOptions[0] = $basemapOptions['basemap_url']; + $this->basemapOptions[1] = $basemapOptions['basemap_attribution']; } } @@ -112,23 +120,23 @@ class Map extends AbstractBase } /** - * Get the map type for determining template to use. + * Get the map graticule setting. * * @return string */ - public function getMapType() + public function getMapGraticule() { - return $this->mapType; + return $this->graticule; } /** - * Get the map graticule setting. + * Get the basemap configuration settings. * - * @return string + * @return array */ - public function getMapGraticule() + public function getBasemap() { - return $this->graticule; + return $this->basemapOptions; } /** @@ -138,7 +146,7 @@ class Map extends AbstractBase */ public function isActive() { - if ($this->mapType == 'openlayers') { + if ($this->mapTabDisplay) { $geocoords = $this->getRecordDriver()->tryMethod('getGeoLocation'); return !empty($geocoords); } @@ -165,14 +173,8 @@ class Map extends AbstractBase $lonE = (float)$match[2]; $latN = (float)$match[3]; $latS = (float)$match[4]; - // Display as point or polygon? - if (($lonE == $lonW) && ($latN == $latS)) { - $shape = 2; - } else { - $shape = 4; - } - // Coordinates ordered for ol3 display as WSEN - array_push($coordarray, [$lonW, $latS, $lonE, $latN, $shape]); + // Coordinates ordered for display as WSEN + array_push($coordarray, [$lonW, $latS, $lonE, $latN]); } } return $coordarray; @@ -241,8 +243,7 @@ class Map extends AbstractBase $coordmatch = implode('', explode(' ', $val)); /* See if coordinate string matches lookup table coordinates and if so return label */ - $labelname = isset($label_lookup[$coordmatch]) - ? $label_lookup[$coordmatch] : ''; + $labelname = $label_lookup[$coordmatch] ?? ''; array_push($labels, $labelname); } } @@ -265,7 +266,7 @@ class Map extends AbstractBase $mapDisplayCoords = []; $mapDisplayLabels = []; if ($this->displayCoords) { - $mapDisplayCoords = $this->getDisplayCoords(); + $mapDisplayCoords = $this->getDisplayCoords(); } if (isset($this->mapLabels)) { $mapDisplayLabels = $this->getMapLabels(); @@ -284,7 +285,7 @@ class Map extends AbstractBase $mapTabData, [ $geoCoords[$key][0], $geoCoords[$key][1], $geoCoords[$key][2], $geoCoords[$key][3], - $geoCoords[$key][4], $mapLabel, $mapCoords + $mapLabel, $mapCoords ] ); } diff --git a/module/VuFind/src/VuFind/RecordTab/PluginFactory.php b/module/VuFind/src/VuFind/RecordTab/PluginFactory.php index 9029d442cd2e89f2860d70bc4b9d86b23546d962..7d16b05918c49fabc9752d91de318046a304c215 100644 --- a/module/VuFind/src/VuFind/RecordTab/PluginFactory.php +++ b/module/VuFind/src/VuFind/RecordTab/PluginFactory.php @@ -2,7 +2,7 @@ /** * Record tab plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/PluginManager.php b/module/VuFind/src/VuFind/RecordTab/PluginManager.php index 28792dafdbfb55e994fe7a4ec3914ecbced62863..99e674a4e0c3b3f88418bd2f2d21a32a37e52baf 100644 --- a/module/VuFind/src/VuFind/RecordTab/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordTab/PluginManager.php @@ -2,7 +2,7 @@ /** * Record tab plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ namespace VuFind\RecordTab; + use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; /** @@ -39,6 +40,78 @@ use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'collectionhierarchytree' => 'VuFind\RecordTab\CollectionHierarchyTree', + 'collectionlist' => 'VuFind\RecordTab\CollectionList', + 'description' => 'VuFind\RecordTab\Description', + 'excerpt' => 'VuFind\RecordTab\Excerpt', + 'hierarchytree' => 'VuFind\RecordTab\HierarchyTree', + 'holdingsils' => 'VuFind\RecordTab\HoldingsILS', + 'holdingsworldcat' => 'VuFind\RecordTab\HoldingsWorldCat', + 'map' => 'VuFind\RecordTab\Map', + 'preview' => 'VuFind\RecordTab\Preview', + 'reviews' => 'VuFind\RecordTab\Reviews', + 'similaritemscarousel' => 'VuFind\RecordTab\SimilarItemsCarousel', + 'staffviewarray' => 'VuFind\RecordTab\StaffViewArray', + 'staffviewmarc' => 'VuFind\RecordTab\StaffViewMARC', + 'toc' => 'VuFind\RecordTab\TOC', + 'usercomments' => 'VuFind\RecordTab\UserComments', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\RecordTab\CollectionHierarchyTree' => + 'VuFind\RecordTab\Factory::getCollectionHierarchyTree', + 'VuFind\RecordTab\CollectionList' => + 'VuFind\RecordTab\Factory::getCollectionList', + 'VuFind\RecordTab\Description' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\RecordTab\Excerpt' => 'VuFind\RecordTab\Factory::getExcerpt', + 'VuFind\RecordTab\HierarchyTree' => + 'VuFind\RecordTab\Factory::getHierarchyTree', + 'VuFind\RecordTab\HoldingsILS' => 'VuFind\RecordTab\Factory::getHoldingsILS', + 'VuFind\RecordTab\HoldingsWorldCat' => + 'VuFind\RecordTab\Factory::getHoldingsWorldCat', + 'VuFind\RecordTab\Map' => 'VuFind\RecordTab\Factory::getMap', + 'VuFind\RecordTab\Preview' => 'VuFind\RecordTab\Factory::getPreview', + 'VuFind\RecordTab\Reviews' => 'VuFind\RecordTab\Factory::getReviews', + 'VuFind\RecordTab\SimilarItemsCarousel' => + 'VuFind\RecordTab\Factory::getSimilarItemsCarousel', + 'VuFind\RecordTab\StaffViewArray' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\RecordTab\StaffViewMARC' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\RecordTab\TOC' => 'VuFind\RecordTab\Factory::getTOC', + 'VuFind\RecordTab\UserComments' => + 'VuFind\RecordTab\Factory::getUserComments', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\RecordTab\PluginFactory'); + $this->addInitializer('ZfcRbac\Initializer\AuthorizationServiceInitializer'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Load the specified key from the configuration array using the best * available match to the class of the provided driver. Return the default diff --git a/module/VuFind/src/VuFind/RecordTab/Preview.php b/module/VuFind/src/VuFind/RecordTab/Preview.php index d9b6d9ce78ce7ef824df8e9834e4ed639574cceb..476bfb94458c6314645d62384c08b53a2d73daab 100644 --- a/module/VuFind/src/VuFind/RecordTab/Preview.php +++ b/module/VuFind/src/VuFind/RecordTab/Preview.php @@ -2,7 +2,7 @@ /** * Embedded Preview tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/Reviews.php b/module/VuFind/src/VuFind/RecordTab/Reviews.php index 66176a13a3b5464dd2c1e67edf57097c5c58fc9d..4bdf311e04209c09471fb538e26f352a584dcd93 100644 --- a/module/VuFind/src/VuFind/RecordTab/Reviews.php +++ b/module/VuFind/src/VuFind/RecordTab/Reviews.php @@ -2,7 +2,7 @@ /** * Reviews tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php b/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php index 4ddf208106685bdb9e5416f4c12df765d636f6d7..31c9151f666353a9d0554af8e3b8db679316dbbf 100644 --- a/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php +++ b/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php @@ -2,7 +2,7 @@ /** * Staff view (array dump) tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/StaffViewArray.php b/module/VuFind/src/VuFind/RecordTab/StaffViewArray.php index b87c5168459b70482e025092cd9e21a52fcd104d..a7bab2228a5cba9269832704e7a52483d33a8d46 100644 --- a/module/VuFind/src/VuFind/RecordTab/StaffViewArray.php +++ b/module/VuFind/src/VuFind/RecordTab/StaffViewArray.php @@ -2,7 +2,7 @@ /** * Staff view (array dump) tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/StaffViewMARC.php b/module/VuFind/src/VuFind/RecordTab/StaffViewMARC.php index 8b3b5c0d1376e1d3f6a3993da011d8116d6672bc..a624d3701b13c9a3d8e35455b09edc782fef6e0c 100644 --- a/module/VuFind/src/VuFind/RecordTab/StaffViewMARC.php +++ b/module/VuFind/src/VuFind/RecordTab/StaffViewMARC.php @@ -2,7 +2,7 @@ /** * Staff view (MARC dump) tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/TOC.php b/module/VuFind/src/VuFind/RecordTab/TOC.php index 76bc7621b3161382768746b8a27de8f03ee95ed6..b040e9565114c2cd0ee4a29c97a5b5a82791aef9 100644 --- a/module/VuFind/src/VuFind/RecordTab/TOC.php +++ b/module/VuFind/src/VuFind/RecordTab/TOC.php @@ -2,7 +2,7 @@ /** * Table of Contents tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFind\RecordTab; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ -class TOC extends AbstractBase +class TOC extends AbstractContent { /** * Get the on-screen description for this tab. @@ -56,6 +56,6 @@ class TOC extends AbstractBase public function isActive() { $toc = $this->getRecordDriver()->tryMethod('getTOC'); - return !empty($toc); + return !empty($toc) || parent::isActive(); } } diff --git a/module/VuFind/src/VuFind/RecordTab/TabInterface.php b/module/VuFind/src/VuFind/RecordTab/TabInterface.php index 08edaebcb43f8626c065c64d32aebebacea4886c..7f3522ba2fdc3cc45188416861764bab12b39d0c 100644 --- a/module/VuFind/src/VuFind/RecordTab/TabInterface.php +++ b/module/VuFind/src/VuFind/RecordTab/TabInterface.php @@ -2,7 +2,7 @@ /** * Record tab interface * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/RecordTab/UserComments.php b/module/VuFind/src/VuFind/RecordTab/UserComments.php index 8921c63e8d59b0e1b32ff5dda2efbf8693a22c05..209936953f24272d492474dadec0edf51ac611fd 100644 --- a/module/VuFind/src/VuFind/RecordTab/UserComments.php +++ b/module/VuFind/src/VuFind/RecordTab/UserComments.php @@ -2,7 +2,7 @@ /** * User comments tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Related/Channels.php b/module/VuFind/src/VuFind/Related/Channels.php index 16e4661c0746482eaef4c30893d6b1bd3d7adfbe..c3729e5fdae8b6a1925abe97d06a1a2a48918d9f 100644 --- a/module/VuFind/src/VuFind/Related/Channels.php +++ b/module/VuFind/src/VuFind/Related/Channels.php @@ -2,7 +2,7 @@ /** * Related Records: Link to Channels * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFind/src/VuFind/Related/Deprecated.php b/module/VuFind/src/VuFind/Related/Deprecated.php index 5a98193fde57fa2eb413c305ba98e9f57a0de9d6..899dc3a2681fee798c8387f60217d2ae233d8916 100644 --- a/module/VuFind/src/VuFind/Related/Deprecated.php +++ b/module/VuFind/src/VuFind/Related/Deprecated.php @@ -3,7 +3,7 @@ * Deprecated Related Records Module - used to replace legacy modules that no * longer function due to, for example, external APIs that have been shut down. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * diff --git a/module/VuFind/src/VuFind/Related/Factory.php b/module/VuFind/src/VuFind/Related/Factory.php deleted file mode 100644 index f09d4e5816fdc3fee7f019f47d23d79e10c495a9..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Related/Factory.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -/** - * Related Record Module Factory Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Related_Records - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\Related; -use Zend\ServiceManager\ServiceManager; - -/** - * Related Record Module Factory Class - * - * @category VuFind - * @package Related_Records - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for Editions module. - * - * @param ServiceManager $sm Service manager. - * - * @return Editions - */ - public static function getEditions(ServiceManager $sm) - { - return new Editions( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager'), - $sm->getServiceLocator()->get('VuFind\WorldCatUtils') - ); - } - - /** - * Factory for Similar module. - * - * @param ServiceManager $sm Service manager. - * - * @return Similar - */ - public static function getSimilar(ServiceManager $sm) - { - return new Similar($sm->getServiceLocator()->get('VuFind\Search')); - } - - /** - * Factory for WorldCatEditions module. - * - * @param ServiceManager $sm Service manager. - * - * @return WorldCatEditions - */ - public static function getWorldCatEditions(ServiceManager $sm) - { - return new WorldCatEditions( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager'), - $sm->getServiceLocator()->get('VuFind\WorldCatUtils') - ); - } - - /** - * Factory for WorldCatSimilar module. - * - * @param ServiceManager $sm Service manager. - * - * @return WorldCatSimilar - */ - public static function getWorldCatSimilar(ServiceManager $sm) - { - return new WorldCatSimilar($sm->getServiceLocator()->get('VuFind\Search')); - } -} diff --git a/module/VuFind/src/VuFind/Related/PluginFactory.php b/module/VuFind/src/VuFind/Related/PluginFactory.php index f31afdb99d67eaed3be3b4e46b42e0660cfaadbf..9bdd65b73a03d6cd2155e8d267ddcfe97471f036 100644 --- a/module/VuFind/src/VuFind/Related/PluginFactory.php +++ b/module/VuFind/src/VuFind/Related/PluginFactory.php @@ -2,7 +2,7 @@ /** * Related record plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Related/PluginManager.php b/module/VuFind/src/VuFind/Related/PluginManager.php index 03554270f98d0d02acc97ebc580c82a8c6ea3034..90e946f2871088e62d57e997d92f79b48c0e6847 100644 --- a/module/VuFind/src/VuFind/Related/PluginManager.php +++ b/module/VuFind/src/VuFind/Related/PluginManager.php @@ -2,7 +2,7 @@ /** * Related record plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,32 @@ namespace VuFind\Related; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'channels' => 'VuFind\Related\Channels', + 'editions' => 'VuFind\Related\Deprecated', + 'similar' => 'VuFind\Related\Similar', + 'worldcateditions' => 'VuFind\Related\Deprecated', + 'worldcatsimilar' => 'VuFind\Related\WorldCatSimilar', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Related\Channels' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Related\Deprecated' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Related\Similar' => 'VuFind\Related\SimilarFactory', + 'VuFind\Related\WorldCatSimilar' => 'VuFind\Related\SimilarFactory', + ]; + /** * Constructor * @@ -52,8 +78,8 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ) { // These objects are not meant to be shared -- every time we retrieve one, // we are building a brand new object. - $this->setShareByDefault(false); - + $this->sharedByDefault = false; + $this->addAbstractFactory('VuFind\Related\PluginFactory'); parent::__construct($configOrContainerInstance, $v3config); } diff --git a/module/VuFind/src/VuFind/Related/RelatedInterface.php b/module/VuFind/src/VuFind/Related/RelatedInterface.php index 29b98ad1d6a70100db1dacd76515e2bd170b8c1b..df6b02459eb8f232018bcb450ac85457b4decd7a 100644 --- a/module/VuFind/src/VuFind/Related/RelatedInterface.php +++ b/module/VuFind/src/VuFind/Related/RelatedInterface.php @@ -2,7 +2,7 @@ /** * Related Records Interface * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/module/VuFind/src/VuFind/Related/Similar.php b/module/VuFind/src/VuFind/Related/Similar.php index 3751134007513d6fc3bede0cf77e8374bf3b577a..7bc97800322c88ffc72ab39729cc0716a5ebf8d8 100644 --- a/module/VuFind/src/VuFind/Related/Similar.php +++ b/module/VuFind/src/VuFind/Related/Similar.php @@ -2,7 +2,7 @@ /** * Related Records: Solr-based similarity * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/module/VuFind/src/VuFind/Related/SimilarFactory.php b/module/VuFind/src/VuFind/Related/SimilarFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..514134011793e59c52c2119d3b2b145062b72903 --- /dev/null +++ b/module/VuFind/src/VuFind/Related/SimilarFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for Similar related record module (and subclasses). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Related_Records + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Related; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Similar related record module (and subclasses). + * + * @category VuFind + * @package Related_Records + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SimilarFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get('VuFindSearch\Service')); + } +} diff --git a/module/VuFind/src/VuFind/Related/WorldCatSimilar.php b/module/VuFind/src/VuFind/Related/WorldCatSimilar.php index 13d07a47f2b84dc3154fa39123b7e4fdd8f2691a..8223bc5d12d0600556906d389f77930ccb78c906 100644 --- a/module/VuFind/src/VuFind/Related/WorldCatSimilar.php +++ b/module/VuFind/src/VuFind/Related/WorldCatSimilar.php @@ -2,7 +2,7 @@ /** * Related Records: WorldCat-based similarity * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/module/VuFind/src/VuFind/Reserves/CsvReader.php b/module/VuFind/src/VuFind/Reserves/CsvReader.php index 00e6ebd8efdd0e2d15a3d5b133c259ef5479e3dc..5f68a343316d36541b0590a4adf7254a946cd242 100644 --- a/module/VuFind/src/VuFind/Reserves/CsvReader.php +++ b/module/VuFind/src/VuFind/Reserves/CsvReader.php @@ -2,7 +2,7 @@ /** * Support class to build reserves data from CSV file(s). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Resolver/Connection.php b/module/VuFind/src/VuFind/Resolver/Connection.php index 609f7c5b91228508cc9f497b74f8316c0068fac8..a7c8dfa7aa1766b116da6e8108369ba53a6638a9 100644 --- a/module/VuFind/src/VuFind/Resolver/Connection.php +++ b/module/VuFind/src/VuFind/Resolver/Connection.php @@ -2,7 +2,7 @@ /** * Link Resolver Driver Wrapper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Royal Holloway, University of London * diff --git a/module/VuFind/src/VuFind/Resolver/Driver/AbstractBase.php b/module/VuFind/src/VuFind/Resolver/Driver/AbstractBase.php index 2d9f1d5df71f55078a5fef80a2e8ad7e8a5c1b27..cdff08a703a7f8818b751ce39561c8b8383c2209 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/AbstractBase.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/AbstractBase.php @@ -2,7 +2,7 @@ /** * AbstractBase for Resolver Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Demo.php b/module/VuFind/src/VuFind/Resolver/Driver/Demo.php index d864b7cf74d6c04996bb841914603bbe21afaaa1..af33fe2555c1622cdad7516951bcfac68eba239f 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/Demo.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/Demo.php @@ -2,7 +2,7 @@ /** * Demo Link Resolver Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -28,7 +28,6 @@ * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki */ namespace VuFind\Resolver\Driver; -use DOMDocument, DOMXpath; /** * Demo Link Resolver Driver diff --git a/module/VuFind/src/VuFind/Resolver/Driver/DriverInterface.php b/module/VuFind/src/VuFind/Resolver/Driver/DriverInterface.php index f0ec93d8310579b36ebf8ad3689df73944ad9e8c..a6132e702eb56b6bad567ded4214542dbbbb218e 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/DriverInterface.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/DriverInterface.php @@ -2,7 +2,7 @@ /** * Interface for Link Resolver Drivers * - * PHP version 5 + * PHP version 7 * * Copyright (C) Royal Holloway, University of London * @@ -88,5 +88,4 @@ interface DriverInterface * @return bool */ public function supportsMoreOptionsLink(); - } diff --git a/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php b/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f9afb1ead8903020d64afc12bdfaca2c89009451 --- /dev/null +++ b/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Generic factory suitable for most resolver drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Resolver_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Resolver\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic factory suitable for most resolver drivers. + * + * @category VuFind + * @package Resolver_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DriverWithHttpClientFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName( + $config->OpenURL->url, + $container->get('VuFindHttp\HttpService')->createClient(), + ...($options ?: []) + ); + } +} diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Ezb.php b/module/VuFind/src/VuFind/Resolver/Driver/Ezb.php index c507608d975286dcd13e429f86b8eae365fcdef3..7a102637b149db17253127d5bac9fdbf261ac169 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/Ezb.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/Ezb.php @@ -8,7 +8,7 @@ * API documentation is available at * http://www.zeitschriftendatenbank.de/services/schnittstellen/journals-online-print * - * PHP version 5 + * PHP version 7 * * Copyright (C) Markus Fischer, info@flyingfischer.ch * @@ -35,7 +35,9 @@ * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki */ namespace VuFind\Resolver\Driver; -use DOMDocument, DOMXpath; + +use DOMDocument; +use DOMXpath; /** * EZB Link Resolver Driver @@ -197,17 +199,17 @@ class Ezb extends AbstractBase if (isset($value) && $value !== '') { if ($key == 'rfr_id') { $newKey = 'sid'; - } else if ($key == 'rft.date') { + } elseif ($key == 'rft.date') { $newKey = 'date'; - } else if ($key == 'rft.issn') { + } elseif ($key == 'rft.issn') { $newKey = 'issn'; - } else if ($key == 'rft.volume') { + } elseif ($key == 'rft.volume') { $newKey = 'volume'; - } else if ($key == 'rft.issue') { + } elseif ($key == 'rft.issue') { $newKey = 'issue'; - } else if ($key == 'rft.spage') { + } elseif ($key == 'rft.spage') { $newKey = 'spage'; - } else if ($key == 'rft.pages') { + } elseif ($key == 'rft.pages') { $newKey = 'pages'; } else { $newKey = false; @@ -220,7 +222,7 @@ class Ezb extends AbstractBase return implode('&', $downgraded); } - + /** * Extract electronic results from the EZB response and inject them into the * $records array. diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Factory.php b/module/VuFind/src/VuFind/Resolver/Driver/Factory.php deleted file mode 100644 index 1293fa49d3fd578930bca4b13484916dd2d06a7e..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Resolver/Driver/Factory.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -/** - * Resolver Driver Factory Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Resolver_Drivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\Resolver\Driver; -use Zend\ServiceManager\ServiceManager; - -/** - * Resolver Driver Factory Class - * - * @category VuFind - * @package Resolver_Drivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for Threesixtylink record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Threesixtylink - */ - public static function getThreesixtylink(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Threesixtylink( - $config->OpenURL->url, - $sm->getServiceLocator()->get('VuFind\Http')->createClient() - ); - } - - /** - * Factory for Ezb record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Ezb - */ - public static function getEzb(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Ezb( - $config->OpenURL->url, - $sm->getServiceLocator()->get('VuFind\Http')->createClient() - ); - } - - /** - * Factory for Sfx record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Sfx - */ - public static function getSfx(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Sfx( - $config->OpenURL->url, - $sm->getServiceLocator()->get('VuFind\Http')->createClient() - ); - } - - /** - * Factory for Redi record driver. - * - * @param ServiceManager $sm Service manager. - * - * @return Redi - */ - public static function getRedi(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new Redi( - $config->OpenURL->url, - $sm->getServiceLocator()->get('VuFind\Http')->createClient() - ); - } -} diff --git a/module/VuFind/src/VuFind/Resolver/Driver/PluginFactory.php b/module/VuFind/src/VuFind/Resolver/Driver/PluginFactory.php index 1fc12e15c37fc1f50cfa9a9f9ec8b9d13953e2ad..8e309eb8ea2717bf4137470c942e0ad746cf284f 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/PluginFactory.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/PluginFactory.php @@ -2,7 +2,7 @@ /** * Resolver driver plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php b/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php index a003c23f6d3b42392f785d4afd2a6094d04637fd..dc9f1167f5043c707608dec3b9ba9cc675d60179 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php @@ -2,7 +2,7 @@ /** * Resolver driver plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,54 @@ namespace VuFind\Resolver\Driver; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + '360link' => 'VuFind\Resolver\Driver\Threesixtylink', + 'demo' => 'VuFind\Resolver\Driver\Demo', + 'ezb' => 'VuFind\Resolver\Driver\Ezb', + 'sfx' => 'VuFind\Resolver\Driver\Sfx', + 'redi' => 'VuFind\Resolver\Driver\Redi', + 'threesixtylink' => 'VuFind\Resolver\Driver\Threesixtylink', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Resolver\Driver\Threesixtylink' => + 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', + 'VuFind\Resolver\Driver\Demo' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Resolver\Driver\Ezb' => + 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', + 'VuFind\Resolver\Driver\Sfx' => + 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', + 'VuFind\Resolver\Driver\Redi' => + 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Resolver\Driver\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Redi.php b/module/VuFind/src/VuFind/Resolver/Driver/Redi.php index 163fdeef2dfe3bc83e9f4fe8ec661a2624ac0b6b..6d4290d152500743375bef7ba781ae51ddae75a5 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/Redi.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/Redi.php @@ -2,7 +2,7 @@ /** * ReDi Link Resolver Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Leipzig University Library 2015 * @@ -28,7 +28,9 @@ * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki */ namespace VuFind\Resolver\Driver; -use DOMDocument, Zend\Dom\DOMXPath; + +use DOMDocument; +use Zend\Dom\DOMXPath; /** * ReDi Link Resolver Driver @@ -198,7 +200,6 @@ class Redi extends AbstractBase if ($ezbResultsNodesText->length == $ezbResultsNodesURL->length) { for ($i = 0; $i < $ezbResultsNodesText->length; $i++) { - $accessClass = 'unknown'; $accessClassExpressions = [ "denied" => "//div[@class='t_ezb_result'][" diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Sfx.php b/module/VuFind/src/VuFind/Resolver/Driver/Sfx.php index ccd6a7ec3c6b38204d293ac1e6792936851d0075..8d89d6d8f3f86fb8de131e2649a9f430a59b22dd 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/Sfx.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/Sfx.php @@ -2,7 +2,7 @@ /** * SFX Link Resolver Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Royal Holloway, University of London * diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Threesixtylink.php b/module/VuFind/src/VuFind/Resolver/Driver/Threesixtylink.php index 0c72b3444f070d909f92c9d09870f8c395f17d0b..5592448056b8d45d1e6779f1f5f00bea94bd4930 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/Threesixtylink.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/Threesixtylink.php @@ -2,7 +2,7 @@ /** * 360Link Link Resolver Driver * - * PHP version 5 + * PHP version 7 * * Copyright (C) Royal Holloway, University of London * @@ -28,7 +28,9 @@ * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki */ namespace VuFind\Resolver\Driver; -use DOMDocument, DOMXpath; + +use DOMDocument; +use DOMXpath; /** * 360Link Link Resolver Driver @@ -99,7 +101,7 @@ class Threesixtylink extends AbstractBase $xpath = new DOMXpath($xml); $linkGroups = $xpath->query("//ssopenurl:linkGroup[@type='holding']"); - if (!is_null($linkGroups)) { + if (null !== $linkGroups) { foreach ($linkGroups as $linkGroup) { $record = []; // select the deepest link returned diff --git a/module/VuFind/src/VuFind/Role/DynamicRoleProvider.php b/module/VuFind/src/VuFind/Role/DynamicRoleProvider.php index 510451e646101ed0a53ddda6514e898857729bce..d8da7cf7be5f81717b75eb399dac4d394e3f9853 100644 --- a/module/VuFind/src/VuFind/Role/DynamicRoleProvider.php +++ b/module/VuFind/src/VuFind/Role/DynamicRoleProvider.php @@ -2,7 +2,7 @@ /** * VuFind dynamic role provider. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role; -use ZfcRbac\Role\RoleProviderInterface; + use Rbac\Role\Role; +use ZfcRbac\Role\RoleProviderInterface; /** * VuFind dynamic role provider. @@ -183,7 +184,7 @@ class DynamicRoleProvider implements RoleProviderInterface $currentRoles = $providerObj->getPermissions($options); if ($roles === null) { $roles = $currentRoles; - } else if ($mode == 'ANY') { + } elseif ($mode == 'ANY') { $roles = array_merge($roles, $currentRoles); } else { $roles = array_intersect($roles, $currentRoles); diff --git a/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php b/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php index 9fdc312f2001716c009a1ad07f866a5b65861555..2068df49cf47546f893eb899395341a61c2775d3 100644 --- a/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php +++ b/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php @@ -2,7 +2,7 @@ /** * VuFind dynamic role provider factory. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,9 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role; + +use Interop\Container\ContainerInterface; use Zend\ServiceManager\Config; -use Zend\ServiceManager\FactoryInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * VuFind dynamic role provider factory. @@ -42,35 +43,39 @@ use Zend\ServiceManager\ServiceLocatorInterface; class DynamicRoleProviderFactory implements FactoryInterface { /** - * Create the service. + * Create service * - * @param ServiceLocatorInterface $serviceLocator Service locator + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) * * @return DynamicRoleProvider + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $config = $serviceLocator->getServiceLocator()->get('config'); + $config = $sm->get('config'); $rbacConfig = $config['zfc_rbac']; return new DynamicRoleProvider( - $this->getPermissionProviderPluginManager($serviceLocator, $rbacConfig), - $this->getPermissionConfiguration($serviceLocator, $rbacConfig) + $this->getPermissionProviderPluginManager($sm, $rbacConfig), + $this->getPermissionConfiguration($sm, $rbacConfig) ); } /** * Create the supporting plugin manager. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param array $rbacConfig ZfcRbac configuration + * @param ContainerInterface $serviceLocator Service locator + * @param array $rbacConfig ZfcRbac configuration * * @return PermissionProviderPluginManager */ protected function getPermissionProviderPluginManager( - ServiceLocatorInterface $serviceLocator, array $rbacConfig + ContainerInterface $serviceLocator, array $rbacConfig ) { $pm = new PermissionProvider\PluginManager( - $serviceLocator->getServiceLocator(), + $serviceLocator, $rbacConfig['vufind_permission_provider_manager'] ); return $pm; @@ -79,19 +84,19 @@ class DynamicRoleProviderFactory implements FactoryInterface /** * Get a configuration array. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param array $rbacConfig ZfcRbac configuration + * @param ContainerInterface $serviceLocator Service locator + * @param array $rbacConfig ZfcRbac configuration * * @return array */ protected function getPermissionConfiguration( - ServiceLocatorInterface $serviceLocator, array $rbacConfig + ContainerInterface $serviceLocator, array $rbacConfig ) { // Get role provider settings from the ZfcRbac configuration: $config = $rbacConfig['role_provider']['VuFind\Role\DynamicRoleProvider']; // Load the permissions: - $configLoader = $serviceLocator->getServiceLocator()->get('VuFind\Config'); + $configLoader = $serviceLocator->get('VuFind\Config\PluginManager'); $permissions = $configLoader->get('permissions')->toArray(); // If we're configured to map legacy settings, do so now: diff --git a/module/VuFind/src/VuFind/Role/PermissionDeniedManager.php b/module/VuFind/src/VuFind/Role/PermissionDeniedManager.php index a08e305bec15db20352a6030d4fd6a2f0b8baf34..adbbc633ad0709b43daef773a790658d45c78664 100644 --- a/module/VuFind/src/VuFind/Role/PermissionDeniedManager.php +++ b/module/VuFind/src/VuFind/Role/PermissionDeniedManager.php @@ -2,7 +2,7 @@ /** * Permission Manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php b/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e94d3b64d58be013ed34c07f87000a967181d5fe --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Permission denied manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Role; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Permission denied manager factory. + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PermissionDeniedManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('permissionBehavior') + ); + } +} diff --git a/module/VuFind/src/VuFind/Role/PermissionManager.php b/module/VuFind/src/VuFind/Role/PermissionManager.php index 4ec82c7ba8b192ae7d6fb7fb2f32d808ec0834b2..e9f6c377d79089ac6730995b22b7df31b52a789d 100644 --- a/module/VuFind/src/VuFind/Role/PermissionManager.php +++ b/module/VuFind/src/VuFind/Role/PermissionManager.php @@ -2,7 +2,7 @@ /** * Permission Manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link http://vufind.org/wiki/ Wiki */ namespace VuFind\Role; + use ZfcRbac\Service\AuthorizationServiceAwareTrait; /** @@ -92,7 +93,7 @@ class PermissionManager */ public function permissionRuleExists($context) { - foreach ($this->config as $key => $value) { + foreach ($this->config as $value) { if (!isset($value['permission'])) { continue; } diff --git a/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php b/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..794b33cdfab07a13fd06cd928deb3e0bbd043ff3 --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Permission manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Role; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Permission manager factory. + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PermissionManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $permissions = $container->get('VuFind\Config\PluginManager') + ->get('permissions')->toArray(); + $permManager = new $requestedName($permissions); + $permManager->setAuthorizationService( + $container->get('ZfcRbac\Service\AuthorizationService') + ); + return $permManager; + } +} diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php index 7a73d0db4e10fd511c9a52a22dbf8aa10ee9e347..efcfbadf7bd84f1c1b3abfd1e3b8d67a617f6dea 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php @@ -2,7 +2,7 @@ /** * Permission Provider Factory Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki */ namespace VuFind\Role\PermissionProvider; + use Zend\ServiceManager\ServiceManager; /** @@ -51,8 +52,8 @@ class Factory public static function getIpRange(ServiceManager $sm) { return new IpRange( - $sm->getServiceLocator()->get('Request'), - $sm->getServiceLocator()->get('VuFind\IpAddressUtils') + $sm->get('Request'), + $sm->get('VuFind\Net\IpAddressUtils') ); } @@ -65,7 +66,7 @@ class Factory */ public static function getIpRegEx(ServiceManager $sm) { - return new IpRegEx($sm->getServiceLocator()->get('Request')); + return new IpRegEx($sm->get('Request')); } /** @@ -77,7 +78,7 @@ class Factory */ public static function getServerParam(ServiceManager $sm) { - return new ServerParam($sm->getServiceLocator()->get('Request')); + return new ServerParam($sm->get('Request')); } /** @@ -90,8 +91,8 @@ class Factory public static function getShibboleth(ServiceManager $sm) { return new Shibboleth( - $sm->getServiceLocator()->get('Request'), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') + $sm->get('Request'), + $sm->get('VuFind\Config\PluginManager')->get('config') ); } @@ -105,10 +106,10 @@ class Factory public static function getUsername(ServiceManager $sm) { return new Username( - $sm->getServiceLocator()->get('ZfcRbac\Service\AuthorizationService') + $sm->get('ZfcRbac\Service\AuthorizationService') ); } - + /** * Factory for User * @@ -119,7 +120,7 @@ class Factory public static function getUser(ServiceManager $sm) { return new User( - $sm->getServiceLocator()->get('ZfcRbac\Service\AuthorizationService') + $sm->get('ZfcRbac\Service\AuthorizationService') ); } } diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php index 27c2bb9bc048b62f0b67ac8a9137747305e1ac3c..c656736b555648719083d360d17458516677c676 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php @@ -2,7 +2,7 @@ /** * IpRange permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * Copyright (C) The National Library of Finland 2015. @@ -29,8 +29,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role\PermissionProvider; -use Zend\Http\PhpEnvironment\Request; + use VuFind\Net\IpAddressUtils; +use Zend\Console\Console; +use Zend\Stdlib\RequestInterface; /** * IpRange permission provider for VuFind. @@ -62,10 +64,10 @@ class IpRange implements PermissionProviderInterface /** * Constructor * - * @param Request $request Request object - * @param IpAddressUtils $ipUtils IpAddressUtils object + * @param RequestInterface $request Request object + * @param IpAddressUtils $ipUtils IpAddressUtils object */ - public function __construct(Request $request, IpAddressUtils $ipUtils) + public function __construct(RequestInterface $request, IpAddressUtils $ipUtils) { $this->request = $request; $this->ipAddressUtils = $ipUtils; @@ -81,6 +83,9 @@ class IpRange implements PermissionProviderInterface */ public function getPermissions($options) { + if (Console::isConsole()) { + return []; + } // Check if any regex matches.... $ip = $this->request->getServer()->get('REMOTE_ADDR'); if ($this->ipAddressUtils->isInRange($ip, (array)$options)) { diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRegEx.php b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRegEx.php index a6550021cbaf54fa13e44ab38b4ed588eb7dc0df..62b688675e1e24f501b14d3563e522d531e7cb27 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRegEx.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRegEx.php @@ -2,7 +2,7 @@ /** * IpRegEx permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role\PermissionProvider; + use Zend\Http\PhpEnvironment\Request; /** diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/PermissionProviderInterface.php b/module/VuFind/src/VuFind/Role/PermissionProvider/PermissionProviderInterface.php index 72ae133737b7ac8edfecd3ec0ae31e97edb15761..0a45af7539551f1ebc4509c44d39ad6b83558e3a 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/PermissionProviderInterface.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/PermissionProviderInterface.php @@ -2,7 +2,7 @@ /** * Permission provider interface * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php b/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php index 499b0c414eac1589a1cc4a84196a3c5bd4c2d891..be82fd86c0705daad77e4738627836c08860fbaa 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php @@ -2,7 +2,7 @@ /** * Permission provider plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,43 @@ namespace VuFind\Role\PermissionProvider; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'ipRange' => 'VuFind\Role\PermissionProvider\IpRange', + 'ipRegEx' => 'VuFind\Role\PermissionProvider\IpRegEx', + 'role' => 'VuFind\Role\PermissionProvider\Role', + 'serverParam' => 'VuFind\Role\PermissionProvider\ServerParam', + 'shibboleth' => 'VuFind\Role\PermissionProvider\Shibboleth', + 'user' => 'VuFind\Role\PermissionProvider\User', + 'username' => 'VuFind\Role\PermissionProvider\Username', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Role\PermissionProvider\IpRange' => + 'VuFind\Role\PermissionProvider\Factory::getIpRange', + 'VuFind\Role\PermissionProvider\IpRegEx' => + 'VuFind\Role\PermissionProvider\Factory::getIpRegEx', + 'VuFind\Role\PermissionProvider\Role' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Role\PermissionProvider\ServerParam' => + 'VuFind\Role\PermissionProvider\Factory::getServerParam', + 'VuFind\Role\PermissionProvider\Shibboleth' => + 'VuFind\Role\PermissionProvider\Factory::getShibboleth', + 'VuFind\Role\PermissionProvider\User' => + 'VuFind\Role\PermissionProvider\Factory::getUser', + 'VuFind\Role\PermissionProvider\Username' => + 'VuFind\Role\PermissionProvider\Factory::getUsername', + ]; + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Role.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Role.php index 9c817e4ff71d87f90dccadc4fafb31da80b9a8cc..bbe7f7ecac44963dd170fd32711ce79e508b75b9 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/Role.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Role.php @@ -2,7 +2,7 @@ /** * Role permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php b/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php index b8c7cfc1b9dff0b75b0e622eb9e4bbd27260136c..144be0dd42b02d372b18e4f98647e5721a2f4cb7 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php @@ -2,7 +2,7 @@ /** * ServerParam permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -28,6 +28,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role\PermissionProvider; + use Zend\Http\PhpEnvironment\Request; /** diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Shibboleth.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Shibboleth.php index 9cb677aaf33b17b65a97028622693db6c528778f..3f34bbdd34ffd4d4f7d54c9beff1bbdffe922737 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/Shibboleth.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Shibboleth.php @@ -2,7 +2,7 @@ /** * Shibboleth permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -28,8 +28,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role\PermissionProvider; -use Zend\Http\PhpEnvironment\Request; + use VuFind\Auth\Shibboleth as ShibbolethAuth; +use Zend\Http\PhpEnvironment\Request; /** * Shibboleth permission provider for VuFind. diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/User.php b/module/VuFind/src/VuFind/Role/PermissionProvider/User.php index 12885985a65cd4d5750a1150795f581c122f603b..ecce32c37559bfcc5a5e17baf5466334fb5ec6de 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/User.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/User.php @@ -2,7 +2,7 @@ /** * User permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,6 +26,7 @@ * @link http://www.vufind.org Main Page */ namespace VuFind\Role\PermissionProvider; + use ZfcRbac\Service\AuthorizationService; /** diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Username.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Username.php index 6021c12083e124ad2fd2e82d14b68a92668fa803..ed69399261f1ced150a244b42f7cedb80c6fd3ac 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/Username.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Username.php @@ -2,7 +2,7 @@ /** * Username permission provider for VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Role\PermissionProvider; + use ZfcRbac\Service\AuthorizationService; /** diff --git a/module/VuFind/src/VuFind/Route/RouteGenerator.php b/module/VuFind/src/VuFind/Route/RouteGenerator.php index 8e1b229786b85842f8b45f9e395de7dd47492872..e735b28cc4acc075c4d573830ad8ba278de8f657 100644 --- a/module/VuFind/src/VuFind/Route/RouteGenerator.php +++ b/module/VuFind/src/VuFind/Route/RouteGenerator.php @@ -2,7 +2,7 @@ /** * Route Generator Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -81,7 +81,7 @@ class RouteGenerator { list($actionName) = explode('/', $action, 2); $config['router']['routes'][$routeName] = [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => "/$controller/$action", 'constraints' => [ @@ -128,7 +128,7 @@ class RouteGenerator { // catch-all "tab" route: $config['router']['routes'][$routeBase] = [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/' . $controller . '/[:id[/[:tab]]]', 'constraints' => [ @@ -144,7 +144,7 @@ class RouteGenerator // special non-tab actions that each need their own route: foreach ($this->nonTabRecordActions as $action) { $config['router']['routes'][$routeBase . '-' . strtolower($action)] = [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/' . $controller . '/[:id]/' . $action, 'constraints' => [ @@ -189,7 +189,7 @@ class RouteGenerator list($controller, $action) = explode('/', $route); $routeName = str_replace('/', '-', strtolower($route)); $config['router']['routes'][$routeName] = [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/' . $route, 'defaults' => [ diff --git a/module/VuFind/src/VuFind/SMS/AbstractBase.php b/module/VuFind/src/VuFind/SMS/AbstractBase.php index c8e72036650a0af8c6f2030d5da08656e65c42f7..46d4599e5699eb646158d272fa49e45516e492d8 100644 --- a/module/VuFind/src/VuFind/SMS/AbstractBase.php +++ b/module/VuFind/src/VuFind/SMS/AbstractBase.php @@ -2,7 +2,7 @@ /** * Base class to enable sharing of common methods between SMS subclasses * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/module/VuFind/src/VuFind/SMS/Clickatell.php b/module/VuFind/src/VuFind/SMS/Clickatell.php index a2f43afa7cfae1cd80b9568f7496d367451aaff3..f17d80d34e50aa095d5010ae5cba610ad5d9d007 100644 --- a/module/VuFind/src/VuFind/SMS/Clickatell.php +++ b/module/VuFind/src/VuFind/SMS/Clickatell.php @@ -2,7 +2,7 @@ /** * Class for text messaging via Clickatell's HTTP API * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\SMS; + use VuFind\Exception\Mail as MailException; /** @@ -56,8 +57,7 @@ class Clickatell extends AbstractBase public function __construct(\Zend\Config\Config $config, $options = []) { parent::__construct($config, $options); - $this->client = isset($options['client']) - ? $options['client'] : new \Zend\Http\Client(); + $this->client = $options['client'] ?? new \Zend\Http\Client(); } /** diff --git a/module/VuFind/src/VuFind/SMS/Factory.php b/module/VuFind/src/VuFind/SMS/Factory.php index 9d8975e51550f65463255ff294e22e1841f19823..4fb0cffb54828930887ee7bdcc7c82f3ff04c45e 100644 --- a/module/VuFind/src/VuFind/SMS/Factory.php +++ b/module/VuFind/src/VuFind/SMS/Factory.php @@ -2,7 +2,7 @@ /** * Factory for instantiating SMS objects * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\SMS; -use Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for instantiating SMS objects @@ -39,20 +41,25 @@ use Zend\ServiceManager\ServiceLocatorInterface; * * @codeCoverageIgnore */ -class Factory implements \Zend\ServiceManager\FactoryInterface +class Factory implements FactoryInterface { /** * Create service * - * @param ServiceLocatorInterface $sm Service manager + * @param ContainerInterface $container Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return SMSInterface * - * @return mixed + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $sm) - { + public function __invoke(ContainerInterface $container, $name, + array $options = null + ) { // Load configurations: - $mainConfig = $sm->get('VuFind\Config')->get('config'); - $smsConfig = $sm->get('VuFind\Config')->get('sms'); + $mainConfig = $container->get('VuFind\Config\PluginManager')->get('config'); + $smsConfig = $container->get('VuFind\Config\PluginManager')->get('sms'); // Determine SMS type: $type = isset($smsConfig->General->smsType) @@ -61,10 +68,10 @@ class Factory implements \Zend\ServiceManager\FactoryInterface // Initialize object based on requested type: switch (strtolower($type)) { case 'clickatell': - $client = $sm->get('VuFind\Http')->createClient(); + $client = $container->get('VuFindHttp\HttpService')->createClient(); return new Clickatell($smsConfig, ['client' => $client]); case 'mailer': - $options = ['mailer' => $sm->get('VuFind\Mailer')]; + $options = ['mailer' => $container->get('VuFind\Mailer\Mailer')]; if (isset($mainConfig->Site->email)) { $options['defaultFrom'] = $mainConfig->Site->email; } diff --git a/module/VuFind/src/VuFind/SMS/Mailer.php b/module/VuFind/src/VuFind/SMS/Mailer.php index 8c26f39d135c692d15233d8bfc139e7a1b2b3099..4bbe5fd62004689c9600b1ff406c512be917bb36 100644 --- a/module/VuFind/src/VuFind/SMS/Mailer.php +++ b/module/VuFind/src/VuFind/SMS/Mailer.php @@ -2,7 +2,7 @@ /** * VuFind Mailer Class for SMS messages * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\SMS; + use VuFind\Exception\Mail as MailException; /** @@ -93,7 +94,7 @@ class Mailer extends AbstractBase // Load default "from" address: $this->defaultFrom - = isset($options['defaultFrom']) ? $options['defaultFrom'] : ''; + = $options['defaultFrom'] ?? ''; // Make sure mailer dependency has been injected: if (!isset($options['mailer']) diff --git a/module/VuFind/src/VuFind/SMS/SMSInterface.php b/module/VuFind/src/VuFind/SMS/SMSInterface.php index 5aeaee3c1eb03121af4b04d543ee4622086b7ca0..558f252c9e1511b4ffa008060404f51e83990870 100644 --- a/module/VuFind/src/VuFind/SMS/SMSInterface.php +++ b/module/VuFind/src/VuFind/SMS/SMSInterface.php @@ -2,7 +2,7 @@ /** * Interface for SMS classes. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/module/VuFind/src/VuFind/Search/BackendManager.php b/module/VuFind/src/VuFind/Search/BackendManager.php index 177976165141f7f4f22d38176870cf309d12774a..89d557bce710767b4b9cb2f750057582e43ddf48 100644 --- a/module/VuFind/src/VuFind/Search/BackendManager.php +++ b/module/VuFind/src/VuFind/Search/BackendManager.php @@ -3,7 +3,7 @@ /** * Manager for search backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,15 +28,15 @@ */ namespace VuFind\Search; -use Zend\ServiceManager\ServiceLocatorInterface; - -use Zend\EventManager\SharedEventManagerInterface; -use Zend\EventManager\EventInterface; +use SplObjectStorage; +use UnexpectedValueException; use VuFindSearch\Backend\BackendInterface; -use SplObjectStorage; -use UnexpectedValueException; +use Zend\EventManager\EventInterface; + +use Zend\EventManager\SharedEventManagerInterface; +use Zend\ServiceManager\ServiceLocatorInterface; /** * Manager for search backends. @@ -99,7 +99,7 @@ class BackendManager */ public function get($name) { - $backend = $this->registry->get($name, false); + $backend = $this->registry->get($name); if (!is_object($backend)) { throw new UnexpectedValueException( sprintf( diff --git a/module/VuFind/src/VuFind/Search/BackendManagerFactory.php b/module/VuFind/src/VuFind/Search/BackendManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3d3f2d0822c99ef876bd5d96f590d45ea9ed2a79 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/BackendManagerFactory.php @@ -0,0 +1,81 @@ +<?php +/** + * Search Backend Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Search Backend Manager factory. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class BackendManagerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($this->getRegistry($container)); + } + + /** + * Create the backend registry. + * + * @param ContainerInterface $container Service manager + * + * @return BackendRegistry + */ + protected function getRegistry(ContainerInterface $container) + { + $config = $container->get('config'); + return new BackendRegistry( + $container, $config['vufind']['plugin_managers']['search_backend'] + ); + } +} diff --git a/module/VuFind/src/VuFind/Search/BackendRegistry.php b/module/VuFind/src/VuFind/Search/BackendRegistry.php new file mode 100644 index 0000000000000000000000000000000000000000..fad454710ad714020e5abb73678b4d5d221f26db --- /dev/null +++ b/module/VuFind/src/VuFind/Search/BackendRegistry.php @@ -0,0 +1,87 @@ +<?php + +/** + * Registry for search backends. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Search; + +/** + * Registry for search backends. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class BackendRegistry extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + // Allow Solr core names to be used as aliases for services: + 'authority' => 'SolrAuth', + 'biblio' => 'Solr', + 'reserves' => 'SolrReserves', + // Legacy: + 'VuFind' => 'Solr', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'BrowZine' => 'VuFind\Search\Factory\BrowZineBackendFactory', + 'EDS' => 'VuFind\Search\Factory\EdsBackendFactory', + 'EIT' => 'VuFind\Search\Factory\EITBackendFactory', + 'LibGuides' => 'VuFind\Search\Factory\LibGuidesBackendFactory', + 'Pazpar2' => 'VuFind\Search\Factory\Pazpar2BackendFactory', + 'Primo' => 'VuFind\Search\Factory\PrimoBackendFactory', + 'Search2' => 'VuFind\Search\Factory\Search2BackendFactory', + 'Solr' => 'VuFind\Search\Factory\SolrDefaultBackendFactory', + 'SolrAuth' => 'VuFind\Search\Factory\SolrAuthBackendFactory', + 'SolrReserves' => 'VuFind\Search\Factory\SolrReservesBackendFactory', + 'SolrWeb' => 'VuFind\Search\Factory\SolrWebBackendFactory', + 'Summon' => 'VuFind\Search\Factory\SummonBackendFactory', + 'WorldCat' => 'VuFind\Search\Factory\WorldCatBackendFactory', + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFindSearch\Backend\BackendInterface'; + } +} diff --git a/module/VuFind/src/VuFind/Search/Base/FacetCache.php b/module/VuFind/src/VuFind/Search/Base/FacetCache.php new file mode 100644 index 0000000000000000000000000000000000000000..740bb2f7734e8d819658e93bd0beb1e9a3cd896f --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Base/FacetCache.php @@ -0,0 +1,156 @@ +<?php +/** + * Abstract Base FacetCache. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Base + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Base; + +use VuFind\Cache\Manager as CacheManager; + +/** + * Solr FacetCache Factory. + * + * @category VuFind + * @package Search_Base + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class FacetCache +{ + /** + * Cache manager + * + * @var CacheManager + */ + protected $cacheManager; + + /** + * Currently selected language + * + * @var string + */ + protected $language; + + /** + * Search results object. + * + * @var Results + */ + protected $results; + + /** + * Constructor + * + * @param Results $r Search results object + * @param CacheManager $cm Cache manager + * @param string $language Active UI language + */ + public function __construct(Results $r, CacheManager $cm, $language = 'en') + { + $this->results = $r; + $this->cacheManager = $cm; + $this->language = $language; + } + + /** + * Get the namespace to use for caching facets. + * + * @return string + */ + abstract protected function getCacheNamespace(); + + /** + * Get the cache key for the provided method. + * + * @return string + */ + protected function getCacheKey() + { + $params = $this->results->getParams(); + $settings = [$params->getFacetConfig(), $params->getHiddenFilters()]; + return $this->language . md5(print_r($settings, true)); + } + + /** + * Perform the actual facet lookup. + * + * @param string $initMethod Name of params method to use to request facets + * + * @return array + */ + protected function getFacetResults($initMethod) + { + // Check if we have facet results cached, and build them if we don't. + $cache = $this->cacheManager->getCache('object', $this->getCacheNamespace()); + $params = $this->results->getParams(); + + // Note that we need to initialize the parameters BEFORE generating the + // cache key to ensure that the key is based on the proper settings. + $params->$initMethod(); + $cacheKey = $this->getCacheKey(); + if (!($list = $cache->getItem($cacheKey))) { + // Avoid a backend request if there are no facets configured by the given + // init method. + if (!empty($params->getFacetConfig())) { + // We only care about facet lists, so don't get any results (this + // improves performance): + $params->setLimit(0); + $list = $this->results->getFacetList(); + } else { + $list = []; + } + $cache->setItem($cacheKey, $list); + } + + return $list; + } + + /** + * Return facet information. This data may come from the cache. + * + * @param string $context Context of list to retrieve ('Advanced' or 'HomePage') + * + * @return array + */ + public function getList($context = 'Advanced') + { + if (!in_array($context, ['Advanced', 'HomePage'])) { + throw new \Exception('Invalid context: ' . $context); + } + // For now, all contexts are handled the same way. + return $this->getFacetResults('init' . $context . 'Facets'); + } + + /** + * Get results object used to retrieve facets. + * + * @return Results + */ + public function getResults() + { + return $this->results; + } +} diff --git a/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php b/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a8286d6ff8d6bb1547448948a268e7a48961f528 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php @@ -0,0 +1,85 @@ +<?php +/** + * Abstract FacetCache Factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Base + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Base; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Abstract FacetCache Factory. + * + * @category VuFind + * @package Search_Base + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FacetCacheFactory implements FactoryInterface +{ + /** + * Create a results object. + * + * @param ContainerInterface $container Service manager + * @param string $name Name of results object to load (based + * on name of FacetCache service name) + * + * @return Results + */ + protected function getResults(ContainerInterface $container, $name) + { + return $container->get('VuFind\Search\Results\PluginManager')->get($name); + } + + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $parts = explode('\\', $requestedName); + $requestedNamespace = $parts[count($parts) - 2]; + $results = $this->getResults($container, $requestedNamespace); + $cacheManager = $container->get('VuFind\Cache\Manager'); + $language = $container->get('Zend\Mvc\I18n\Translator')->getLocale(); + return new $requestedName($results, $cacheManager, $language); + } +} diff --git a/module/VuFind/src/VuFind/Search/Base/Options.php b/module/VuFind/src/VuFind/Search/Base/Options.php index 3c31b3454275225136be8738ad87d9b215fbf075..9e30bf8e117dcb271d21d8ead18bdfae22004d98 100644 --- a/module/VuFind/src/VuFind/Search/Base/Options.php +++ b/module/VuFind/src/VuFind/Search/Base/Options.php @@ -2,7 +2,7 @@ /** * Abstract options search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Base; + use VuFind\I18n\Translator\TranslatorAwareInterface; /** @@ -225,6 +226,13 @@ abstract class Options implements TranslatorAwareInterface */ protected $autocompleteEnabled = false; + /** + * Configuration file to read global settings from + * + * @var string + */ + protected $mainIni = 'config'; + /** * Configuration file to read search settings from * @@ -404,6 +412,17 @@ abstract class Options implements TranslatorAwareInterface return $this->facetsIni; } + /** + * Get the name of the ini file used for loading primary settings in this + * object. + * + * @return string + */ + public function getMainIni() + { + return $this->mainIni; + } + /** * Get the name of the ini file used for configuring search parameters in this * object. @@ -515,23 +534,23 @@ abstract class Options implements TranslatorAwareInterface } /** - * Returns the defaultFacetDelimiter value. - * - * @return string - */ + * Returns the defaultFacetDelimiter value. + * + * @return string + */ public function getDefaultFacetDelimiter() { return $this->defaultFacetDelimiter; } /** - * Set the defaultFacetDelimiter value. - * - * @param string $defaultFacetDelimiter A default delimiter to be used with - * delimited facets - * - * @return void - */ + * Set the defaultFacetDelimiter value. + * + * @param string $defaultFacetDelimiter A default delimiter to be used with + * delimited facets + * + * @return void + */ public function setDefaultFacetDelimiter($defaultFacetDelimiter) { $this->defaultFacetDelimiter = $defaultFacetDelimiter; @@ -567,12 +586,12 @@ abstract class Options implements TranslatorAwareInterface } /** - * Set the delimitedFacets value. - * - * @param array $delimitedFacets An array of delimited facet names - * - * @return void - */ + * Set the delimitedFacets value. + * + * @param array $delimitedFacets An array of delimited facet names + * + * @return void + */ public function setDelimitedFacets($delimitedFacets) { $this->delimitedFacets = $delimitedFacets; @@ -635,7 +654,7 @@ abstract class Options implements TranslatorAwareInterface */ public function spellcheckEnabled($bool = null) { - if (!is_null($bool)) { + if (null !== $bool) { $this->spellcheck = $bool; } return $this->spellcheck; @@ -663,7 +682,7 @@ abstract class Options implements TranslatorAwareInterface { if (isset($this->basicHandlers[$field])) { return $this->translate($this->basicHandlers[$field]); - } else if (isset($this->advancedHandlers[$field])) { + } elseif (isset($this->advancedHandlers[$field])) { return $this->translate($this->advancedHandlers[$field]); } else { return $field; diff --git a/module/VuFind/src/VuFind/Search/Base/Params.php b/module/VuFind/src/VuFind/Search/Base/Params.php index b2bf8fbd7a3b74c696cd54b3b11f3009e313351c..d035b0ff1169fbe5baf43ec5987a43239346f297 100644 --- a/module/VuFind/src/VuFind/Search/Base/Params.php +++ b/module/VuFind/src/VuFind/Search/Base/Params.php @@ -2,7 +2,7 @@ /** * Abstract parameters search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,12 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Base; -use VuFindSearch\Backend\Solr\LuceneSyntaxHelper, VuFindSearch\Query\Query, - VuFindSearch\Query\QueryGroup; -use VuFind\Search\QueryAdapter, VuFind\Solr\Utils as SolrUtils; + +use VuFind\Search\QueryAdapter; +use VuFind\Solr\Utils as SolrUtils; +use VuFindSearch\Backend\Solr\LuceneSyntaxHelper; +use VuFindSearch\Query\Query; +use VuFindSearch\Query\QueryGroup; /** * Abstract parameters search model. @@ -227,10 +230,10 @@ class Params public function __clone() { if (is_object($this->options)) { - $this->options = clone($this->options); + $this->options = clone $this->options; } if (is_object($this->query)) { - $this->query = clone($this->query); + $this->query = clone $this->query; } } @@ -381,7 +384,7 @@ class Params { // If no lookfor parameter was found, we have no search terms to // add to our array! - if (is_null($lookfor = $request->get('lookfor'))) { + if (null === ($lookfor = $request->get('lookfor'))) { return false; } @@ -524,10 +527,10 @@ class Params if ($view == 'rss') { // RSS is a special case that does not require config validation $this->setView('rss'); - } else if (!empty($view) && in_array($view, $validViews)) { + } elseif (!empty($view) && in_array($view, $validViews)) { // make sure the url parameter is a valid view $this->setView($view); - } else if (!empty($this->lastView) + } elseif (!empty($this->lastView) && in_array($this->lastView, $validViews) ) { // if there is nothing in the URL, see if we had a previous value @@ -665,7 +668,7 @@ class Params */ public function getView() { - return is_null($this->view) + return null === $this->view ? $this->getOptions()->getDefaultView() : $this->view; } @@ -719,7 +722,7 @@ class Params $value = count($temp) > 0 ? $temp[0] : ''; // Remove quotes from the value if there are any - if (substr($value, 0, 1) == '"') { + if (substr($value, 0, 1) == '"') { $value = substr($value, 1); } if (substr($value, -1, 1) == '"') { @@ -1069,7 +1072,7 @@ class Params if ($firstChar == '-') { $operator = 'NOT'; $field = substr($field, 1); - } else if ($firstChar == '~') { + } elseif ($firstChar == '~') { $operator = 'OR'; $field = substr($field, 1); } else { @@ -1153,7 +1156,7 @@ class Params // Pad to four digits: if (strlen($year) == 2) { $year = '19' . $year; - } else if (strlen($year) == 3) { + } elseif (strlen($year) == 3) { $year = '0' . $year; } @@ -1500,7 +1503,7 @@ class Params public function getDisplayQueryWithReplacedTerm($oldTerm, $newTerm) { // Stash our old data for a minute - $oldTerms = clone($this->query); + $oldTerms = clone $this->query; // Replace the search term $this->query->replaceTerm($oldTerm, $newTerm); // Get the new query string @@ -1711,13 +1714,15 @@ class Params * * @param string $facetList Config section containing fields to activate * @param string $facetSettings Config section containing related settings - * @param string $cfgFile Name of configuration to load + * @param string $cfgFile Name of configuration to load (null to load + * default facets configuration). * * @return bool True if facets set, false if no settings found */ - protected function initFacetList($facetList, $facetSettings, $cfgFile = 'facets') + protected function initFacetList($facetList, $facetSettings, $cfgFile = null) { - $config = $this->configLoader->get($cfgFile); + $config = $this->configLoader + ->get($cfgFile ?? $this->getOptions()->getFacetsIni()); if (!isset($config->$facetList)) { return false; } @@ -1750,14 +1755,16 @@ class Params * Initialize checkbox facet settings for the specified configuration sections. * * @param string $facetList Config section containing fields to activate - * @param string $cfgFile Name of configuration to load + * @param string $cfgFile Name of configuration to load (null to load + * default facets configuration). * * @return bool True if facets set, false if no settings found */ protected function initCheckboxFacets($facetList = 'CheckboxFacets', - $cfgFile = 'facets' + $cfgFile = null ) { - $config = $this->configLoader->get($cfgFile); + $config = $this->configLoader + ->get($cfgFile ?? $this->getOptions()->getFacetsIni()); if (empty($config->$facetList)) { return false; } diff --git a/module/VuFind/src/VuFind/Search/Base/Results.php b/module/VuFind/src/VuFind/Search/Base/Results.php index 1bd9c895a5684e8a7565cd16f4ce552d5a9315ea..2dd1b20d980b0b8ab97af47f4f591d60dfd107b7 100644 --- a/module/VuFind/src/VuFind/Search/Base/Results.php +++ b/module/VuFind/src/VuFind/Search/Base/Results.php @@ -2,7 +2,7 @@ /** * Abstract results search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Base; + use VuFind\Record\Loader; use VuFind\Search\Factory\UrlQueryHelperFactory; use VuFindSearch\Service as SearchService; @@ -167,7 +168,7 @@ abstract class Results public function __clone() { if (is_object($this->params)) { - $this->params = clone($this->params); + $this->params = clone $this->params; } $this->helpers = []; } @@ -300,7 +301,7 @@ abstract class Results */ public function getResultTotal() { - if (is_null($this->resultTotal)) { + if (null === $this->resultTotal) { $this->performAndProcessSearch(); } return $this->resultTotal; @@ -368,7 +369,7 @@ abstract class Results */ public function getResults() { - if (is_null($this->results)) { + if (null === $this->results) { $this->performAndProcessSearch(); } return $this->results; @@ -394,7 +395,7 @@ abstract class Results // This data is not available until \VuFind\Db\Table\Search::saveSearch() // is called... blow up if somebody tries to get data that is not yet // available. - if (is_null($this->savedSearch)) { + if (null === $this->savedSearch) { throw new \Exception( 'Cannot retrieve save status before updateSaveStatus is called.' ); @@ -449,7 +450,7 @@ abstract class Results */ public function getQuerySpeed() { - if (is_null($this->queryTime)) { + if (null === $this->queryTime) { $this->performAndProcessSearch(); } return $this->queryTime; @@ -462,7 +463,7 @@ abstract class Results */ public function getStartTime() { - if (is_null($this->queryStartTime)) { + if (null === $this->queryStartTime) { $this->performAndProcessSearch(); } return $this->queryStartTime; @@ -499,7 +500,7 @@ abstract class Results */ public function getRawSuggestions() { - if (is_null($this->suggestions)) { + if (null === $this->suggestions) { $this->performAndProcessSearch(); } return $this->suggestions; diff --git a/module/VuFind/src/VuFind/Search/BrowZine/Options.php b/module/VuFind/src/VuFind/Search/BrowZine/Options.php index 4bd0ead157d1bb55fa15c7e03147c34b36ca1379..4cb4a1e44f11390b6802384fd921587f9021ce76 100644 --- a/module/VuFind/src/VuFind/Search/BrowZine/Options.php +++ b/module/VuFind/src/VuFind/Search/BrowZine/Options.php @@ -2,7 +2,7 @@ /** * BrowZine aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -38,6 +38,8 @@ namespace VuFind\Search\BrowZine; */ class Options extends \VuFind\Search\Base\Options { + use \VuFind\Search\Options\ViewOptionsTrait; + /** * Constructor * @@ -47,6 +49,10 @@ class Options extends \VuFind\Search\Base\Options { $this->facetsIni = $this->searchIni = 'BrowZine'; parent::__construct($configLoader); + + // Set up views + $searchSettings = $configLoader->get($this->searchIni); + $this->initViewOptions($searchSettings); } /** diff --git a/module/VuFind/src/VuFind/Search/BrowZine/Params.php b/module/VuFind/src/VuFind/Search/BrowZine/Params.php index c34dbd73d3683f90574ef993203310cdee6a1f32..5f3000024e49c7a831387151e0cbeee05e1b9f1b 100644 --- a/module/VuFind/src/VuFind/Search/BrowZine/Params.php +++ b/module/VuFind/src/VuFind/Search/BrowZine/Params.php @@ -2,7 +2,7 @@ /** * BrowZine aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFind/src/VuFind/Search/BrowZine/Results.php b/module/VuFind/src/VuFind/Search/BrowZine/Results.php index 85f52a1c5a16aab97096863482b80cfa8862778c..4a77ad77bf555cb650d7a6bc1c755b42f05b5d9e 100644 --- a/module/VuFind/src/VuFind/Search/BrowZine/Results.php +++ b/module/VuFind/src/VuFind/Search/BrowZine/Results.php @@ -2,7 +2,7 @@ /** * BrowZine aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\BrowZine; + use VuFind\Record\Loader; use VuFindSearch\Service as SearchService; diff --git a/module/VuFind/src/VuFind/Search/Combined/Options.php b/module/VuFind/src/VuFind/Search/Combined/Options.php index 35748991640ec710f116225227b3ab2dc4b91cc2..d2dcf6098ea8ee92ce5f66fb0653b2f5dacae0f1 100644 --- a/module/VuFind/src/VuFind/Search/Combined/Options.php +++ b/module/VuFind/src/VuFind/Search/Combined/Options.php @@ -2,7 +2,7 @@ /** * Combined search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/Combined/Params.php b/module/VuFind/src/VuFind/Search/Combined/Params.php index 36e99db4ae9ce418008246f9db7400858a579921..e5b82a83ca6526cccf5b6af614a9150f810c930e 100644 --- a/module/VuFind/src/VuFind/Search/Combined/Params.php +++ b/module/VuFind/src/VuFind/Search/Combined/Params.php @@ -2,7 +2,7 @@ /** * Combined aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/Combined/Results.php b/module/VuFind/src/VuFind/Search/Combined/Results.php index 0fcd25636810f9f590d7ea622ea7d71230fc6fa3..8ea441310d8e4088a32768b1c5b29328c57407f4 100644 --- a/module/VuFind/src/VuFind/Search/Combined/Results.php +++ b/module/VuFind/src/VuFind/Search/Combined/Results.php @@ -2,7 +2,7 @@ /** * Combined results search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/EDS/Options.php b/module/VuFind/src/VuFind/Search/EDS/Options.php index 7fe292e463b181db1e9b2c9d27369dacf47d6e5d..b14e00526eb0542f71e8767caa12c4b90722739d 100644 --- a/module/VuFind/src/VuFind/Search/EDS/Options.php +++ b/module/VuFind/src/VuFind/Search/EDS/Options.php @@ -2,7 +2,7 @@ /** * EDS API Options * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -77,7 +77,7 @@ class Options extends \VuFind\Search\Base\Options * Available limiter options * * @var unknown - */ + */ protected $limiterOptions = []; /** @@ -137,6 +137,10 @@ class Options extends \VuFind\Search\Base\Options $facetConf->Advanced_Facet_Settings->translated_facets->toArray() ); } + // Load autocomplete preference: + if (isset($searchSettings->Autocomplete->enabled)) { + $this->autocompleteEnabled = $searchSettings->Autocomplete->enabled; + } } /** @@ -221,6 +225,7 @@ class Options extends \VuFind\Search\Base\Options { return $this->defaultExpanders; } + /** * Return the route name of the action used for performing advanced searches. * Returns false if the feature is not supported. @@ -459,7 +464,7 @@ class Options extends \VuFind\Search\Base\Options 'Label' => $mode['Label'], 'Value' => $mode['Mode'] ]; if (isset($mode['DefaultOn']) - && 'y' == $mode['DefaultOn'] + && 'y' == $mode['DefaultOn'] ) { $this->defaultMode = $mode['Mode']; } @@ -499,12 +504,9 @@ class Options extends \VuFind\Search\Base\Options $limiter['LimiterValues'] ) : [['Value' => $val]], - 'DefaultOn' => isset($limiter['DefaultOn']) - ? $limiter['DefaultOn'] : 'n', + 'DefaultOn' => $limiter['DefaultOn'] ?? 'n', ]; - } - } } } diff --git a/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php b/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3d32117ff0f987b553a69f800c270146f321639e --- /dev/null +++ b/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for EDS search options objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package EBSCO + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\EDS; + +use Interop\Container\ContainerInterface; + +/** + * Factory for EDS search options objects. + * + * @category VuFind + * @package EBSCO + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class OptionsFactory extends \VuFind\Search\Options\OptionsFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $session = new \Zend\Session\Container( + 'EBSCO', $container->get('Zend\Session\SessionManager') + ); + // No API info in session? Re-establish connection: + if (!isset($session->info)) { + $backend = $container->get('VuFind\Search\BackendManager')->get('EDS'); + $backend->getSessionToken(); + } + return parent::__invoke($container, $requestedName, [$session->info]); + } +} diff --git a/module/VuFind/src/VuFind/Search/EDS/Params.php b/module/VuFind/src/VuFind/Search/EDS/Params.php index 6af017e261382f633ba395f73067b417e1dc09d0..fe1b62460d31e7677239847b1d99e273ebffb5fc 100644 --- a/module/VuFind/src/VuFind/Search/EDS/Params.php +++ b/module/VuFind/src/VuFind/Search/EDS/Params.php @@ -2,7 +2,7 @@ /** * EDS API Params * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\EDS; -use VuFindSearch\ParamBag; + use VuFindSearch\Backend\EDS\SearchRequestModel as SearchRequestModel; +use VuFindSearch\ParamBag; /** * EDS API Params @@ -333,7 +334,6 @@ class Params extends \VuFind\Search\Base\Params $ssLimiter['selectedvalue'], $ssLimiter['description'] ); } - } } @@ -353,7 +353,6 @@ class Params extends \VuFind\Search\Base\Params $expander['selectedvalue'], $expander['description'] ); } - } } diff --git a/module/VuFind/src/VuFind/Search/EDS/QueryAdapter.php b/module/VuFind/src/VuFind/Search/EDS/QueryAdapter.php index 4c2548321efe761a1cf890338a13489b1e762907..c6db34647329521e35b077d4e2beb231679eaa25 100644 --- a/module/VuFind/src/VuFind/Search/EDS/QueryAdapter.php +++ b/module/VuFind/src/VuFind/Search/EDS/QueryAdapter.php @@ -3,7 +3,7 @@ /** * EDS API Query Adapter: search query parameters to AbstractQuery object * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -29,8 +29,8 @@ namespace VuFind\Search\EDS; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; +use VuFindSearch\Query\QueryGroup; use Zend\StdLib\Parameters; /** @@ -76,7 +76,7 @@ class QueryAdapter extends \VuFind\Search\QueryAdapter protected static function displayAdvanced(AbstractQuery $query, $translate, $showName ) { - $output = ''; + $output = ''; //There should only ever be 1 group with EDS queries. $all = []; foreach ($query->getQueries() as $search) { diff --git a/module/VuFind/src/VuFind/Search/EDS/Results.php b/module/VuFind/src/VuFind/Search/EDS/Results.php index eb5c8776dc1840963e76203aebfccaf9120e84b2..9d41785c130c01a51ab6a690d2d852abbc421722 100644 --- a/module/VuFind/src/VuFind/Search/EDS/Results.php +++ b/module/VuFind/src/VuFind/Search/EDS/Results.php @@ -2,7 +2,7 @@ /** * EDS API Results * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -27,8 +27,6 @@ */ namespace VuFind\Search\EDS; -use EBSCO\EdsApi\SearchCriteria; - /** * EDS API Results * @@ -40,23 +38,6 @@ use EBSCO\EdsApi\SearchCriteria; */ class Results extends \VuFind\Search\Base\Results { - /** - * Search criteria available for a given search - * - * @var array - */ - protected $searchCriteria; - - /** - * Obtain the search criteria available for this searching session (if present) - * - * @return SearchCriteria - */ - public function getSearchCriteria() - { - return $this->searchCriteria; - } - /** * Support method for performAndProcessSearch -- perform a search based on the * parameters passed to the object. @@ -99,6 +80,12 @@ class Results extends \VuFind\Search\Base\Results */ public function getFacetList($filter = null) { + // If there is no filter, we'll use all facets as the filter: + if (null === $filter) { + $filter = $this->getParams()->getFacetConfig(); + } + $filterFields = array_keys($filter); + // Loop through the facets returned by EDS $facetResult = []; if (is_array($this->responseFacets)) { @@ -111,6 +98,11 @@ class Results extends \VuFind\Search\Base\Results // different value for actual display! $field = $current['displayName']; + // If we are filtering out the field, skip it! + if (!in_array($field, $filterFields)) { + continue; + } + // Should we translate values for the current facet? if ($translate = in_array($field, $translatedFacets)) { $transTextDomain = $this->getOptions() @@ -124,8 +116,7 @@ class Results extends \VuFind\Search\Base\Results // present in the filter list? Second, is the current value // an active filter for the current field? $orField = '~' . $field; - $itemsToCheck = isset($filterList[$field]) - ? $filterList[$field] : []; + $itemsToCheck = $filterList[$field] ?? []; if (isset($filterList[$orField])) { $itemsToCheck += $filterList[$orField]; } @@ -149,8 +140,7 @@ class Results extends \VuFind\Search\Base\Results = $facetDetails['value']; } // The EDS API returns facets in the order they should be displayed - $current['label'] = isset($filter[$field]) - ? $filter[$field] : $field; + $current['label'] = $filter[$field] ?? $field; // Create a reference to counts called list for consistency with // Solr output format -- this allows the facet recommendations diff --git a/module/VuFind/src/VuFind/Search/EIT/Options.php b/module/VuFind/src/VuFind/Search/EIT/Options.php index ca4cf69ac141a0aff05c92fa2b65a5cc6d950f45..230d3b5f3c5d52c4a1654fddefea902000dc61e5 100644 --- a/module/VuFind/src/VuFind/Search/EIT/Options.php +++ b/module/VuFind/src/VuFind/Search/EIT/Options.php @@ -2,7 +2,7 @@ /** * EBSCO EIT API Search Options * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/EIT/Params.php b/module/VuFind/src/VuFind/Search/EIT/Params.php index 18aee0e88c646af977476b9c4bb66a3cde781c9a..dcb45fd51461bee395df3bd02cf3bf3fb0635fd6 100644 --- a/module/VuFind/src/VuFind/Search/EIT/Params.php +++ b/module/VuFind/src/VuFind/Search/EIT/Params.php @@ -2,7 +2,7 @@ /** * EBSCO Search Parameters * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,8 +27,9 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\EIT; -use VuFindSearch\ParamBag; + use VuFind\Search\Base\Params as BaseParams; +use VuFindSearch\ParamBag; /** * EBSCO Search Parameters diff --git a/module/VuFind/src/VuFind/Search/EIT/Results.php b/module/VuFind/src/VuFind/Search/EIT/Results.php index b9754f1edb75500cc073e2fb6f7666dbd6248666..de7ee5a08a560c1b81325770720bfdfacbbf1cf1 100644 --- a/module/VuFind/src/VuFind/Search/EIT/Results.php +++ b/module/VuFind/src/VuFind/Search/EIT/Results.php @@ -2,7 +2,7 @@ /** * EBSCO Search Results * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/EmptySet/Options.php b/module/VuFind/src/VuFind/Search/EmptySet/Options.php index 14944ba3f81cd6182f9856e37d0490fd18e837bd..cab5345219c61f4989ee712b4fea6bab2305862e 100644 --- a/module/VuFind/src/VuFind/Search/EmptySet/Options.php +++ b/module/VuFind/src/VuFind/Search/EmptySet/Options.php @@ -2,7 +2,7 @@ /** * EmptySet aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/EmptySet/Params.php b/module/VuFind/src/VuFind/Search/EmptySet/Params.php index 94f83d8abf36760095e2edfb986ad3de068347c3..e7c3c4d5e192ccd3e924babfa566991880790485 100644 --- a/module/VuFind/src/VuFind/Search/EmptySet/Params.php +++ b/module/VuFind/src/VuFind/Search/EmptySet/Params.php @@ -2,7 +2,7 @@ /** * EmptySet aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/EmptySet/Results.php b/module/VuFind/src/VuFind/Search/EmptySet/Results.php index 8719fafd1dafb8f320a1801320c88cd53d7f2dc5..52392ecd0dc5ab62c391f740340b149d12c69c62 100644 --- a/module/VuFind/src/VuFind/Search/EmptySet/Results.php +++ b/module/VuFind/src/VuFind/Search/EmptySet/Results.php @@ -2,7 +2,7 @@ /** * Empty Search Object * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\EmptySet; + use VuFind\Search\Base\Results as BaseResults; /** diff --git a/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php b/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..ca902cb6675be571aeeeaf52ebadfd1f3bb20d17 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php @@ -0,0 +1,73 @@ +<?php +/** + * Facet cache plugin manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Search\FacetCache; + +/** + * Facet cache plugin manager + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'search2' => 'VuFind\Search\Search2\FacetCache', + 'solr' => 'VuFind\Search\Solr\FacetCache', + 'summon' => 'VuFind\Search\Summon\FacetCache', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Search\Search2\FacetCache' => 'VuFind\Search\Solr\FacetCacheFactory', + 'VuFind\Search\Solr\FacetCache' => 'VuFind\Search\Solr\FacetCacheFactory', + 'VuFind\Search\Summon\FacetCache' => 'VuFind\Search\Base\FacetCacheFactory', + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\Search\Base\FacetCache'; + } +} diff --git a/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php index 6ec45e1e9b8c1f843934d3699250b2c3d78c23c3..d472ca2fd7f847673c2656f06cedefaa9da765e1 100644 --- a/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php @@ -3,7 +3,7 @@ /** * Abstract factory for SOLR backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,29 +28,30 @@ */ namespace VuFind\Search\Factory; +use Interop\Container\ContainerInterface; + +use VuFind\Search\Solr\DeduplicationListener; use VuFind\Search\Solr\FilterFieldConversionListener; use VuFind\Search\Solr\HideFacetValueListener; -use VuFind\Search\Solr\InjectHighlightingListener; +use VuFind\Search\Solr\HierarchicalFacetListener; use VuFind\Search\Solr\InjectConditionalFilterListener; +use VuFind\Search\Solr\InjectHighlightingListener; use VuFind\Search\Solr\InjectSpellingListener; use VuFind\Search\Solr\MultiIndexListener; use VuFind\Search\Solr\V3\ErrorListener as LegacyErrorListener; use VuFind\Search\Solr\V4\ErrorListener; -use VuFind\Search\Solr\DeduplicationListener; -use VuFind\Search\Solr\HierarchicalFacetListener; use VuFindSearch\Backend\BackendInterface; +use VuFindSearch\Backend\Solr\Backend; +use VuFindSearch\Backend\Solr\Connector; +use VuFindSearch\Backend\Solr\HandlerMap; use VuFindSearch\Backend\Solr\LuceneSyntaxHelper; use VuFindSearch\Backend\Solr\QueryBuilder; use VuFindSearch\Backend\Solr\SimilarBuilder; -use VuFindSearch\Backend\Solr\HandlerMap; -use VuFindSearch\Backend\Solr\Connector; -use VuFindSearch\Backend\Solr\Backend; use Zend\Config\Config; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Abstract factory for SOLR backends. @@ -73,10 +74,17 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; + /** + * Primary configuration file identifier. + * + * @var string + */ + protected $mainConfig = 'config'; + /** * Search configuration file identifier. * @@ -127,18 +135,22 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface } /** - * Create the backend. + * Create service + * + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $this->config = $this->serviceLocator->get('VuFind\Config'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + $this->serviceLocator = $sm; + $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -176,7 +188,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface $events = $this->serviceLocator->get('SharedEventManager'); // Load configurations: - $config = $this->config->get('config'); + $config = $this->config->get($this->mainConfig); $search = $this->config->get($this->searchConfig); $facet = $this->config->get($this->facetConfig); @@ -191,12 +203,9 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface } // Spellcheck - if (isset($config->Spelling->enabled) && $config->Spelling->enabled) { - if (isset($config->Spelling->simple) && $config->Spelling->simple) { - $dictionaries = ['basicSpell']; - } else { - $dictionaries = ['default', 'basicSpell']; - } + if ($config->Spelling->enabled ?? true) { + $dictionaries = ($config->Spelling->simple ?? false) + ? ['basicSpell'] : ['default', 'basicSpell']; $spellingListener = new InjectSpellingListener($backend, $dictionaries); $spellingListener->attach($events); } @@ -261,11 +270,13 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface /** * Get the Solr URL. * + * @param string $config name of configuration file (null for default) + * * @return string|array */ - protected function getSolrUrl() + protected function getSolrUrl($config = null) { - $url = $this->config->get('config')->Index->url; + $url = $this->config->get($config ?? $this->mainConfig)->Index->url; $core = $this->getSolrCore(); if (is_object($url)) { return array_map( @@ -312,7 +323,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface */ protected function createConnector() { - $config = $this->config->get('config'); + $config = $this->config->get($this->mainConfig); $handlers = [ 'select' => [ @@ -339,8 +350,9 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface if ($this->logger) { $connector->setLogger($this->logger); } - if ($this->serviceLocator->has('VuFind\Http')) { - $connector->setProxy($this->serviceLocator->get('VuFind\Http')); + if ($this->serviceLocator->has('VuFindHttp\HttpService')) { + $connector + ->setProxy($this->serviceLocator->get('VuFindHttp\HttpService')); } return $connector; } @@ -353,7 +365,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface protected function createQueryBuilder() { $specs = $this->loadSpecs(); - $config = $this->config->get('config'); + $config = $this->config->get($this->mainConfig); $defaultDismax = isset($config->Index->default_dismax_handler) ? $config->Index->default_dismax_handler : 'dismax'; $builder = new QueryBuilder($specs, $defaultDismax); @@ -393,7 +405,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface */ protected function loadSpecs() { - return $this->serviceLocator->get('VuFind\SearchSpecsReader') + return $this->serviceLocator->get('VuFind\Config\SearchSpecsReader') ->get($this->searchYaml); } @@ -417,13 +429,13 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface } /** - * Get a hide facet value listener for the backend - * - * @param BackendInterface $backend Search backend - * @param Config $facet Configuration of facets - * - * @return mixed null|HideFacetValueListener - */ + * Get a hide facet value listener for the backend + * + * @param BackendInterface $backend Search backend + * @param Config $facet Configuration of facets + * + * @return mixed null|HideFacetValueListener + */ protected function getHideFacetValueListener( BackendInterface $backend, Config $facet diff --git a/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php index 80d82d9b62518df0d3eef92c697ad1446a212263..13000425e3f2d07e337ea505074e0ee1f6f28aa3 100644 --- a/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for BrowZine backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,13 +28,14 @@ */ namespace VuFind\Search\Factory; +use Interop\Container\ContainerInterface; + use VuFindSearch\Backend\BrowZine\Backend; use VuFindSearch\Backend\BrowZine\Connector; use VuFindSearch\Backend\BrowZine\QueryBuilder; use VuFindSearch\Backend\BrowZine\Response\RecordCollectionFactory; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for BrowZine backend. @@ -57,7 +58,7 @@ class BrowZineBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -69,19 +70,23 @@ class BrowZineBackendFactory implements FactoryInterface protected $browzineConfig; /** - * Create the backend. + * Create service + * + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $configReader = $this->serviceLocator->get('VuFind\Config'); + $this->serviceLocator = $sm; + $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); $this->browzineConfig = $configReader->get('BrowZine'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); @@ -120,7 +125,8 @@ class BrowZineBackendFactory implements FactoryInterface throw new \Exception("Missing library ID in BrowZine.ini"); } // Build HTTP client: - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); + $client = $this->serviceLocator->get('VuFindHttp\HttpService') + ->createClient(); $timeout = isset($this->browzineConfig->General->timeout) ? $this->browzineConfig->General->timeout : 30; $client->setOptions(['timeout' => $timeout]); @@ -152,7 +158,7 @@ class BrowZineBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('BrowZine'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php index c9b2f0596fce21141cf3d8abd863ed43564f7111..6893ce0eaf8911e4825be7fb2ee0543100a40b55 100644 --- a/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php @@ -2,7 +2,7 @@ /** * Factory for EBSCO Integration Toolkit (EIT) backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Julia Bauder 2013. * @@ -28,14 +28,14 @@ */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Backend\EIT\Response\XML\RecordCollectionFactory; -use VuFindSearch\Backend\EIT\QueryBuilder; -use VuFindSearch\Backend\EIT\Connector; +use Interop\Container\ContainerInterface; + use VuFindSearch\Backend\EIT\Backend; +use VuFindSearch\Backend\EIT\Connector; +use VuFindSearch\Backend\EIT\QueryBuilder; +use VuFindSearch\Backend\EIT\Response\XML\RecordCollectionFactory; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for EIT backends. @@ -59,7 +59,7 @@ class EITBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -71,18 +71,23 @@ class EITBackendFactory implements FactoryInterface protected $config; /** - * Create the backend. + * Create service * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $this->config = $this->serviceLocator->get('VuFind\Config')->get('EIT'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + $this->serviceLocator = $sm; + $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('EIT'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -120,7 +125,7 @@ class EITBackendFactory implements FactoryInterface ? $this->config->General->dbs : null; $connector = new Connector( $base, - $this->serviceLocator->get('VuFind\Http')->createClient(), + $this->serviceLocator->get('VuFindHttp\HttpService')->createClient(), $prof, $pwd, $dbs ); $connector->setLogger($this->logger); @@ -144,7 +149,7 @@ class EITBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('EIT'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php index b67a1564a83b8f31617e819911dee6401652b5e4..f3b2cf906f8256a1a733bae581c867a957535898 100644 --- a/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for EDS backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,14 +28,14 @@ */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\EDS\Zend2 as Connector; -use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Backend\EDS\Response\RecordCollectionFactory; -use VuFindSearch\Backend\EDS\QueryBuilder; +use Interop\Container\ContainerInterface; + use VuFindSearch\Backend\EDS\Backend; +use VuFindSearch\Backend\EDS\QueryBuilder; +use VuFindSearch\Backend\EDS\Response\RecordCollectionFactory; +use VuFindSearch\Backend\EDS\Zend2 as Connector; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for EDS backends. @@ -58,7 +58,7 @@ class EdsBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -77,18 +77,23 @@ class EdsBackendFactory implements FactoryInterface protected $accountData; /** - * Create the backend. + * Create service * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $this->edsConfig = $this->serviceLocator->get('VuFind\Config')->get('EDS'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + $this->serviceLocator = $sm; + $this->edsConfig = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('EDS'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); return $this->createBackend($connector); @@ -106,14 +111,14 @@ class EdsBackendFactory implements FactoryInterface $auth = $this->serviceLocator->get('ZfcRbac\Service\AuthorizationService'); $isGuest = !$auth->isGranted('access.EDSExtendedResults'); $session = new \Zend\Session\Container( - 'EBSCO', $this->serviceLocator->get('VuFind\SessionManager') + 'EBSCO', $this->serviceLocator->get('Zend\Session\SessionManager') ); $backend = new Backend( $connector, $this->createRecordCollectionFactory(), - $this->serviceLocator->get('VuFind\CacheManager')->getCache('object'), + $this->serviceLocator->get('VuFind\Cache\Manager')->getCache('object'), $session, $this->edsConfig, $isGuest ); - $backend->setAuthManager($this->serviceLocator->get('VuFind\AuthManager')); + $backend->setAuthManager($this->serviceLocator->get('VuFind\Auth\Manager')); $backend->setLogger($this->logger); $backend->setQueryBuilder($this->createQueryBuilder()); return $backend; @@ -130,7 +135,8 @@ class EdsBackendFactory implements FactoryInterface $id = 'EDS'; $key = 'EDS'; // Build HTTP client: - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); + $client = $this->serviceLocator->get('VuFindHttp\HttpService') + ->createClient(); $timeout = isset($this->edsConfig->General->timeout) ? $this->edsConfig->General->timeout : 30; $client->setOptions(['timeout' => $timeout]); @@ -157,7 +163,7 @@ class EdsBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('EDS'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php index 8f571376ee55a4016ebe2f8767183fc72a66df21..40437ccd5e1c1559e67b7851edf8d2fe974df5e4 100644 --- a/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for LibGuides backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,14 +28,14 @@ */ namespace VuFind\Search\Factory; +use Interop\Container\ContainerInterface; + +use VuFindSearch\Backend\LibGuides\Backend; use VuFindSearch\Backend\LibGuides\Connector; -use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Backend\LibGuides\Response\RecordCollectionFactory; use VuFindSearch\Backend\LibGuides\QueryBuilder; -use VuFindSearch\Backend\LibGuides\Backend; +use VuFindSearch\Backend\LibGuides\Response\RecordCollectionFactory; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for LibGuides backends. @@ -58,7 +58,7 @@ class LibGuidesBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -70,19 +70,23 @@ class LibGuidesBackendFactory implements FactoryInterface protected $libGuidesConfig; /** - * Create the backend. + * Create service + * + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $configReader = $this->serviceLocator->get('VuFind\Config'); + $this->serviceLocator = $sm; + $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); $this->libGuidesConfig = $configReader->get('LibGuides'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -124,7 +128,8 @@ class LibGuidesBackendFactory implements FactoryInterface ? $this->libGuidesConfig->General->version : 1; // Build HTTP client: - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); + $client = $this->serviceLocator->get('VuFindHttp\HttpService') + ->createClient(); $timeout = isset($this->libGuidesConfig->General->timeout) ? $this->libGuidesConfig->General->timeout : 30; $client->setOptions(['timeout' => $timeout]); @@ -151,7 +156,7 @@ class LibGuidesBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('LibGuides'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php index 12286c35cd702f890de78ccbc32cc7db491486ec..973e7f6f1931f6ca2eafec4404578a89e89dc302 100644 --- a/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for Pazpar2 backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,14 +28,14 @@ */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Backend\Pazpar2\Response\RecordCollectionFactory; -use VuFindSearch\Backend\Pazpar2\QueryBuilder; -use VuFindSearch\Backend\Pazpar2\Connector; +use Interop\Container\ContainerInterface; + use VuFindSearch\Backend\Pazpar2\Backend; +use VuFindSearch\Backend\Pazpar2\Connector; +use VuFindSearch\Backend\Pazpar2\QueryBuilder; +use VuFindSearch\Backend\Pazpar2\Response\RecordCollectionFactory; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for Pazpar2 backends. @@ -58,7 +58,7 @@ class Pazpar2BackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -70,18 +70,23 @@ class Pazpar2BackendFactory implements FactoryInterface protected $config; /** - * Create the backend. + * Create service * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $this->config = $this->serviceLocator->get('VuFind\Config')->get('Pazpar2'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + $this->serviceLocator = $sm; + $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('Pazpar2'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -120,7 +125,7 @@ class Pazpar2BackendFactory implements FactoryInterface { $connector = new Connector( $this->config->General->base_url, - $this->serviceLocator->get('VuFind\Http')->createClient() + $this->serviceLocator->get('VuFindHttp\HttpService')->createClient() ); $connector->setLogger($this->logger); return $connector; @@ -143,7 +148,7 @@ class Pazpar2BackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('Pazpar2'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php index b423e134f5959380a8ea0df2b0dee4f22ae93984..941d93cfe915bca5d157841a170291952fa70e8b 100644 --- a/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for Primo Central backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,17 +28,17 @@ */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\Primo\Connector; -use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Backend\Primo\Response\RecordCollectionFactory; -use VuFindSearch\Backend\Primo\QueryBuilder; -use VuFindSearch\Backend\Primo\Backend; +use Interop\Container\ContainerInterface; use VuFind\Search\Primo\InjectOnCampusListener; use VuFind\Search\Primo\PrimoPermissionHandler; +use VuFindSearch\Backend\Primo\Backend; +use VuFindSearch\Backend\Primo\Connector; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use VuFindSearch\Backend\Primo\QueryBuilder; +use VuFindSearch\Backend\Primo\Response\RecordCollectionFactory; + +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for Primo Central backends. @@ -61,7 +61,7 @@ class PrimoBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -73,19 +73,23 @@ class PrimoBackendFactory implements FactoryInterface protected $primoConfig; /** - * Create the backend. + * Create service * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $configReader = $this->serviceLocator->get('VuFind\Config'); + $this->serviceLocator = $sm; + $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); $this->primoConfig = $configReader->get('Primo'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); @@ -142,7 +146,8 @@ class PrimoBackendFactory implements FactoryInterface : null; // Build HTTP client: - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); + $client = $this->serviceLocator->get('VuFindHttp\HttpService') + ->createClient(); $timeout = isset($this->primoConfig->General->timeout) ? $this->primoConfig->General->timeout : 30; $client->setOptions(['timeout' => $timeout]); @@ -172,7 +177,7 @@ class PrimoBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('Primo'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..17dfa2c34317c00e7af3daa754bfc51aaf87fc97 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php @@ -0,0 +1,51 @@ +<?php + +/** + * Factory for a second Solr backend + * + * PHP version 7 + * + * Copyright (C) Staats- und Universitätsbibliothek Hamburg 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Factory + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Search\Factory; + +/** + * Factory for a second Solr backend + * + * @category VuFind + * @package Search_Factory + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Search2BackendFactory extends SolrDefaultBackendFactory +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(); + $this->mainConfig = $this->searchConfig = $this->facetConfig = 'Search2'; + $this->searchYaml = 'searchspecs2.yaml'; + } +} diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php index 15c624ac676a34f582a99ea832cf76f9afbf0a36..df3086ddba7865f762eee59b10d02a586a90d2fd 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for the authority SOLR backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -27,8 +27,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; + use VuFindSearch\Backend\Solr\Connector; +use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; /** * Factory for the authority SOLR backend. @@ -47,12 +48,22 @@ class SolrAuthBackendFactory extends AbstractSolrBackendFactory public function __construct() { parent::__construct(); - $this->solrCore = 'authority'; $this->searchConfig = 'authority'; $this->searchYaml = 'authsearchspecs.yaml'; $this->facetConfig = 'authority'; } + /** + * Get the Solr core. + * + * @return string + */ + protected function getSolrCore() + { + $config = $this->config->get($this->mainConfig); + return $config->Index->default_authority_core ?? 'authority'; + } + /** * Create the SOLR backend. * @@ -63,13 +74,8 @@ class SolrAuthBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); - $callback = function ($data) use ($manager) { - $driver = $manager->get('SolrAuth'); - $driver->setRawData($data); - return $driver; - }; - $factory = new RecordCollectionFactory($callback); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $factory = new RecordCollectionFactory([$manager, 'getSolrAuthRecord']); $backend->setRecordCollectionFactory($factory); return $backend; } diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php index 3a4a0a34ec910c977b265fea8b60b878378b7544..2afeae41f39473c747ef212f1b72ba9c412e7afb 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for the default SOLR backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,9 +28,9 @@ */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; -use VuFindSearch\Backend\Solr\Connector; use VuFindSearch\Backend\Solr\Backend; +use VuFindSearch\Backend\Solr\Connector; +use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; /** * Factory for the default SOLR backend. @@ -61,7 +61,7 @@ class SolrDefaultBackendFactory extends AbstractSolrBackendFactory */ protected function getSolrCore() { - $config = $this->config->get('config'); + $config = $this->config->get($this->mainConfig); return isset($config->Index->default_core) ? $config->Index->default_core : 'biblio'; } @@ -76,7 +76,7 @@ class SolrDefaultBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $factory = new RecordCollectionFactory([$manager, 'getSolrRecord']); $backend->setRecordCollectionFactory($factory); return $backend; diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php index 4d3dc436094cc4e6c963a233a05437b86942a640..ac30b4323d8f2db60491e8f888698ca1d9469cca 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for the reserves SOLR backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -27,8 +27,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; + use VuFindSearch\Backend\Solr\Connector; +use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; /** * Factory for the reserves SOLR backend. @@ -63,7 +64,7 @@ class SolrReservesBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('SolrReserves'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php index e8123f1b540b45dbce9cd3644e210b05f7cfcd41..b2cb3f8377df0bbda35276bed04ea3b64ca18d1e 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for the website SOLR backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -27,8 +27,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; + use VuFindSearch\Backend\Solr\Connector; +use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; /** * Factory for the website SOLR backend. @@ -67,15 +68,17 @@ class SolrWebBackendFactory extends AbstractSolrBackendFactory /** * Get the Solr URL. * + * @param string $config name of configuration file (null for default) + * * @return string */ - protected function getSolrUrl() + protected function getSolrUrl($config = null) { - // Allow the searchConfig to override the default config if set. - $webconfig = $this->config->get($this->searchConfig); - return isset($webconfig->Index->url) - ? $webconfig->Index->url . '/' . $this->getSolrCore() - : parent::getSolrUrl(); + // Only override parent default if valid value present in config: + $configToCheck = $config ?? $this->searchConfig; + $webConfig = $this->config->get($configToCheck); + $finalConfig = isset($webConfig->Index->url) ? $configToCheck : null; + return parent::getSolrUrl($finalConfig); } /** @@ -88,7 +91,7 @@ class SolrWebBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('SolrWeb'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php index 4dc9b5a07a8dc4e71ba7861e55c0e5c8456a5b5a..45f4811ee56023a6c4e783e97316eb5f99df55e8 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for Summon backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,15 +28,15 @@ */ namespace VuFind\Search\Factory; +use Interop\Container\ContainerInterface; + use SerialsSolutions\Summon\Zend2 as Connector; -use VuFindSearch\Backend\BackendInterface; use VuFindSearch\Backend\Solr\LuceneSyntaxHelper; -use VuFindSearch\Backend\Summon\Response\RecordCollectionFactory; -use VuFindSearch\Backend\Summon\QueryBuilder; use VuFindSearch\Backend\Summon\Backend; +use VuFindSearch\Backend\Summon\QueryBuilder; +use VuFindSearch\Backend\Summon\Response\RecordCollectionFactory; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for Summon backends. @@ -59,7 +59,7 @@ class SummonBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -78,20 +78,24 @@ class SummonBackendFactory implements FactoryInterface protected $summonConfig; /** - * Create the backend. + * Create service + * + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $configReader = $this->serviceLocator->get('VuFind\Config'); + $this->serviceLocator = $sm; + $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); $this->config = $configReader->get('config'); $this->summonConfig = $configReader->get('Summon'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -127,7 +131,8 @@ class SummonBackendFactory implements FactoryInterface ? $this->config->Summon->apiKey : null; // Build HTTP client: - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); + $client = $this->serviceLocator->get('VuFindHttp\HttpService') + ->createClient(); $timeout = isset($this->summonConfig->General->timeout) ? $this->summonConfig->General->timeout : 30; $client->setOptions(['timeout' => $timeout]); @@ -172,7 +177,7 @@ class SummonBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $stripSnippets = !isset($this->summonConfig->General->snippets) || !$this->summonConfig->General->snippets; $callback = function ($data) use ($manager, $stripSnippets) { diff --git a/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php b/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php index a602fcd54081e54c631dce1d01b4a06b07b1144e..5888fae74ef4bfe8a133495684dc00865fc711d3 100644 --- a/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php @@ -2,7 +2,7 @@ /** * Factory to build UrlQueryHelper. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\Factory; -use VuFind\Search\UrlQueryHelper; + use VuFind\Search\Base\Params; +use VuFind\Search\UrlQueryHelper; /** * Factory to build UrlQueryHelper. diff --git a/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php index d39d468a09e3fa01d7dd2d27c35adb3e3ce0d060..92ac5ab10e2e5926f308afdc60dd7e79191a9856 100644 --- a/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php @@ -3,7 +3,7 @@ /** * Factory for WorldCat backends. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,14 +28,14 @@ */ namespace VuFind\Search\Factory; -use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Backend\WorldCat\Response\XML\RecordCollectionFactory; -use VuFindSearch\Backend\WorldCat\QueryBuilder; -use VuFindSearch\Backend\WorldCat\Connector; +use Interop\Container\ContainerInterface; + use VuFindSearch\Backend\WorldCat\Backend; +use VuFindSearch\Backend\WorldCat\Connector; +use VuFindSearch\Backend\WorldCat\QueryBuilder; +use VuFindSearch\Backend\WorldCat\Response\XML\RecordCollectionFactory; -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** * Factory for WorldCat backends. @@ -58,7 +58,7 @@ class WorldCatBackendFactory implements FactoryInterface /** * Superior service manager. * - * @var ServiceLocatorInterface + * @var ContainerInterface */ protected $serviceLocator; @@ -77,20 +77,25 @@ class WorldCatBackendFactory implements FactoryInterface protected $wcConfig; /** - * Create the backend. + * Create service * - * @param ServiceLocatorInterface $serviceLocator Superior service manager + * @param ContainerInterface $sm Service manager + * @param string $name Requested service name (unused) + * @param array $options Extra options (unused) + * + * @return Backend * - * @return BackendInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createService(ServiceLocatorInterface $serviceLocator) + public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $this->serviceLocator = $serviceLocator; - $this->config = $this->serviceLocator->get('VuFind\Config')->get('config'); + $this->serviceLocator = $sm; + $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get('config'); $this->wcConfig = $this->serviceLocator - ->get('VuFind\Config')->get('WorldCat'); - if ($this->serviceLocator->has('VuFind\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Logger'); + ->get('VuFind\Config\PluginManager')->get('WorldCat'); + if ($this->serviceLocator->has('VuFind\Log\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -124,7 +129,8 @@ class WorldCatBackendFactory implements FactoryInterface $connectorOptions = isset($this->wcConfig->Connector) ? $this->wcConfig->Connector->toArray() : []; $connector = new Connector( - $wsKey, $this->serviceLocator->get('VuFind\Http')->createClient(), + $wsKey, + $this->serviceLocator->get('VuFindHttp\HttpService')->createClient(), $connectorOptions ); $connector->setLogger($this->logger); @@ -150,7 +156,7 @@ class WorldCatBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); $callback = function ($data) use ($manager) { $driver = $manager->get('WorldCat'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Favorites/Options.php b/module/VuFind/src/VuFind/Search/Favorites/Options.php index f7ad8daf1d601d5bba0be34f140043aa9c4f0e3f..8c049e629278798898c84d33a9c01c47377fdfe1 100644 --- a/module/VuFind/src/VuFind/Search/Favorites/Options.php +++ b/module/VuFind/src/VuFind/Search/Favorites/Options.php @@ -2,7 +2,7 @@ /** * Favorites aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -52,7 +52,7 @@ class Options extends \VuFind\Search\Base\Options 'title' => 'sort_title', 'author' => 'sort_author', 'year DESC' => 'sort_year', 'year' => 'sort_year asc' ]; - $config = $configLoader->get('config'); + $config = $configLoader->get($this->mainIni); if (isset($config->Social->lists_default_limit)) { $this->defaultLimit = $config->Social->lists_default_limit; } diff --git a/module/VuFind/src/VuFind/Search/Favorites/Params.php b/module/VuFind/src/VuFind/Search/Favorites/Params.php index ccbc456e07d4d16258e2767031458cdf36e1ef12..339de9ae906dfb850acc369021f6f9ade594111d 100644 --- a/module/VuFind/src/VuFind/Search/Favorites/Params.php +++ b/module/VuFind/src/VuFind/Search/Favorites/Params.php @@ -2,7 +2,7 @@ /** * Favorites aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/Favorites/Results.php b/module/VuFind/src/VuFind/Search/Favorites/Results.php index b27520589bd5d190aee8253a1e88b9718984ed6f..e5159778756d5d36b53eb82f8596777efe7e512c 100644 --- a/module/VuFind/src/VuFind/Search/Favorites/Results.php +++ b/module/VuFind/src/VuFind/Search/Favorites/Results.php @@ -2,7 +2,7 @@ /** * Favorites aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,12 +26,13 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\Favorites; + use VuFind\Db\Table\Resource as ResourceTable; use VuFind\Db\Table\UserList as ListTable; use VuFind\Exception\ListPermission as ListPermissionException; -use VuFind\Search\Base\Results as BaseResults; use VuFind\Record\Cache; use VuFind\Record\Loader; +use VuFind\Search\Base\Results as BaseResults; use VuFindSearch\Service as SearchService; use ZfcRbac\Service\AuthorizationServiceAwareInterface; use ZfcRbac\Service\AuthorizationServiceAwareTrait; @@ -108,12 +109,12 @@ class Results extends BaseResults public function getFacetList($filter = null) { // Make sure we have processed the search before proceeding: - if (is_null($this->user)) { + if (null === $this->user) { $this->performAndProcessSearch(); } // If there is no filter, we'll use all facets as the filter: - if (is_null($filter)) { + if (null === $filter) { $filter = $this->getParams()->getFacetConfig(); } @@ -169,12 +170,12 @@ class Results extends BaseResults // Make sure the user and/or list objects make it possible to view // the current result set -- we need to check logged in status and // list permissions. - if (is_null($list) && !$this->user) { + if (null === $list && !$this->user) { throw new ListPermissionException( 'Cannot retrieve favorites without logged in user.' ); } - if (!is_null($list) && !$list->public + if (null !== $list && !$list->public && (!$this->user || $list->user_id != $this->user->id) ) { throw new ListPermissionException( @@ -183,8 +184,8 @@ class Results extends BaseResults } // How many results were there? - $userId = is_null($list) ? $this->user->id : $list->user_id; - $listId = is_null($list) ? null : $list->id; + $userId = null === $list ? $this->user->id : $list->user_id; + $listId = null === $list ? null : $list->id; $rawResults = $this->resourceTable->getFavorites( $userId, $listId, $this->getTagFilters(), $this->getParams()->getSort() ); @@ -222,7 +223,7 @@ class Results extends BaseResults protected function getTagFilters() { $filters = $this->getParams()->getFilters(); - return isset($filters['tags']) ? $filters['tags'] : []; + return $filters['tags'] ?? []; } /** @@ -238,7 +239,7 @@ class Results extends BaseResults // Check the filters for a list ID, and load the corresponding object // if one is found: $filters = $this->getParams()->getFilters(); - $listId = isset($filters['lists'][0]) ? $filters['lists'][0] : null; + $listId = $filters['lists'][0] ?? null; $this->list = (null === $listId) ? null : $this->listTable->getExisting($listId); } diff --git a/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..cdb78e616aa1079d4769f1ab74cbccde642d5c69 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for Favorites search results objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Favorites + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Favorites; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Favorites search results objects. + * + * @category VuFind + * @package Search_Favorites + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResultsFactory extends \VuFind\Search\Results\ResultsFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $tm = $container->get('VuFind\Db\Table\PluginManager'); + $obj = parent::__invoke( + $container, $requestedName, + [$tm->get('Resource'), $tm->get('UserList')] + ); + $init = new \ZfcRbac\Initializer\AuthorizationServiceInitializer(); + $init($container, $obj); + return $obj; + } +} diff --git a/module/VuFind/src/VuFind/Search/History.php b/module/VuFind/src/VuFind/Search/History.php new file mode 100644 index 0000000000000000000000000000000000000000..2c8be04066057d351adca316b63bdd8d207aa515 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/History.php @@ -0,0 +1,115 @@ +<?php +/** + * VuFind Search History Helper + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Böttger <boettger@hebis.uni-frankfurt.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Search; + +/** + * VuFind Search History Helper + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Böttger <boettger@hebis.uni-frankfurt.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class History +{ + /** + * Search table + * + * @var \VuFind\Db\Table\Search + */ + protected $searchTable; + + /** + * Current session ID + * + * @var string + */ + protected $sessionId; + + /** + * Results manager + * + * @var \VuFind\Search\Results\PluginManager + */ + protected $resultsManager; + + /** + * History constructor + * + * @param \VuFind\Db\Table\Search $searchTable Search table + * @param string $sessionId Session ID + * @param \VuFind\Search\Results\PluginManager $resultsManager Results manager + */ + public function __construct($searchTable, $sessionId, $resultsManager) + { + $this->searchTable = $searchTable; + $this->sessionId = $sessionId; + $this->resultsManager = $resultsManager; + } + + /** + * Purge the user's unsaved search history. + * + * @param int $userId User ID (null if logged out) + * + * @return void + */ + public function purgeSearchHistory($userId = null) + { + $this->searchTable->destroySession($this->sessionId, $userId); + } + + /** + * Get the user's saved and temporary search histories. + * + * @param int $userId User ID (null if logged out) + * + * @return array + */ + public function getSearchHistory($userId = null) + { + // Retrieve search history + $searchHistory = $this->searchTable->getSearches($this->sessionId, $userId); + + // Loop through and sort the history + $saved = $unsaved = []; + foreach ($searchHistory as $current) { + $search = $current->getSearchObject()->deminify($this->resultsManager); + if ($current->saved == 1) { + $saved[] = $search; + } else { + $unsaved[] = $search; + } + } + + return compact('saved', 'unsaved'); + } +} diff --git a/module/VuFind/src/VuFind/Search/HistoryFactory.php b/module/VuFind/src/VuFind/Search/HistoryFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ac418e347a83240839bdaadf078376955c80e87e --- /dev/null +++ b/module/VuFind/src/VuFind/Search/HistoryFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Search history factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Search history factory. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HistoryFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $searchTable = $container->get('VuFind\Db\Table\PluginManager') + ->get('Search'); + $resultsManager = $container->get('VuFind\Search\Results\PluginManager'); + $sessionId = $container->get('Zend\Session\SessionManager')->getId(); + return new $requestedName($searchTable, $sessionId, $resultsManager); + } +} diff --git a/module/VuFind/src/VuFind/Search/LibGuides/Options.php b/module/VuFind/src/VuFind/Search/LibGuides/Options.php index bba8ba0e9b385fb4c68bc58f3b4442d240069b48..14aeaca3ed65e3a4b0553111db4e6b7c4d247fb2 100644 --- a/module/VuFind/src/VuFind/Search/LibGuides/Options.php +++ b/module/VuFind/src/VuFind/Search/LibGuides/Options.php @@ -2,7 +2,7 @@ /** * LibGuides aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/LibGuides/Params.php b/module/VuFind/src/VuFind/Search/LibGuides/Params.php index 92909647a59deab79290277573c9787e7f431b4f..b2821d16be365966359924048cf7ab6338c31f12 100644 --- a/module/VuFind/src/VuFind/Search/LibGuides/Params.php +++ b/module/VuFind/src/VuFind/Search/LibGuides/Params.php @@ -2,7 +2,7 @@ /** * LibGuides aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/LibGuides/Results.php b/module/VuFind/src/VuFind/Search/LibGuides/Results.php index a59f2e3c7d291d5279e309b305a98b0b5352cf85..73f8346ab4fac5137d6f1f0b9b4c865027608a7b 100644 --- a/module/VuFind/src/VuFind/Search/LibGuides/Results.php +++ b/module/VuFind/src/VuFind/Search/LibGuides/Results.php @@ -2,7 +2,7 @@ /** * LibGuides aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/Memory.php b/module/VuFind/src/VuFind/Search/Memory.php index 077f9f860897070ee2493783220aed7b385f9306..bf7d3ee3614366fb1a1c98b6d849cc6ac3973ee0 100644 --- a/module/VuFind/src/VuFind/Search/Memory.php +++ b/module/VuFind/src/VuFind/Search/Memory.php @@ -2,7 +2,7 @@ /** * VuFind Search Memory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search; + use Zend\Session\Container; /** @@ -112,10 +113,16 @@ class Memory */ public function rememberParams(\VuFind\Search\Base\Params $params) { + // Since default sort may vary based on search handler, we don't want + // to force the sort value to stick unless the user chose a non-default + // option. Otherwise, if you switch between search types, unpredictable + // sort options may result. + $sort = $params->getSort(); + $defaultSort = $params->getDefaultSort(); $settings = [ 'hiddenFilters' => $params->getHiddenFilters(), 'limit' => $params->getLimit(), - 'sort' => $params->getSort(), + 'sort' => $sort === $defaultSort ? null : $sort, 'view' => $params->getView(), ]; // Special case: RSS view should not be persisted: diff --git a/module/VuFind/src/VuFind/Search/MemoryFactory.php b/module/VuFind/src/VuFind/Search/MemoryFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1e19eb540be7e702869ea6a45416668a08bf0d64 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/MemoryFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Search memory factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; +use Zend\Session\Container; + +/** + * Search memory factory. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class MemoryFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $session = new Container( + 'Search', $container->get('Zend\Session\SessionManager') + ); + return new $requestedName($session); + } +} diff --git a/module/VuFind/src/VuFind/Search/Minified.php b/module/VuFind/src/VuFind/Search/Minified.php index 2854a61b185ba4c0b0e1999eadd94894514f0b32..f4c5f77ff0d0786fc0749e9a8b04b38d88d80996 100644 --- a/module/VuFind/src/VuFind/Search/Minified.php +++ b/module/VuFind/src/VuFind/Search/Minified.php @@ -2,7 +2,7 @@ /** * VuFind Minified Search Object * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -76,7 +76,12 @@ class Minified /** * ID, start tIme, query Speed, Result total, search TYpe, search CLass id */ - public $id, $i, $s, $r, $ty, $cl; + public $id; + public $i; + public $s; + public $r; + public $ty; + public $cl; /** * Constructor. Building minified object from the @@ -138,7 +143,7 @@ class Minified // search class ID for the object we're about to construct: if (!isset($this->cl)) { $fixType = true; // by default, assume we need to fix type - switch($this->ty) { + switch ($this->ty) { case 'Summon': case 'SummonAdvanced': $this->cl = 'Summon'; diff --git a/module/VuFind/src/VuFind/Search/MixedList/Options.php b/module/VuFind/src/VuFind/Search/MixedList/Options.php index 855eee542fac06eb11f1f51a6ce4e7d3ef7a5fef..ff20e056f97882923e2cb4502c80b6df1fadb11e 100644 --- a/module/VuFind/src/VuFind/Search/MixedList/Options.php +++ b/module/VuFind/src/VuFind/Search/MixedList/Options.php @@ -2,7 +2,7 @@ /** * Mixed List aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/MixedList/Params.php b/module/VuFind/src/VuFind/Search/MixedList/Params.php index e3e7948268b843c70ac161ee3c4ac289ee8621ae..8ae44fdd865f1bbda7e00484dbb46af75451e2a0 100644 --- a/module/VuFind/src/VuFind/Search/MixedList/Params.php +++ b/module/VuFind/src/VuFind/Search/MixedList/Params.php @@ -2,7 +2,7 @@ /** * Mixed List aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/MixedList/Results.php b/module/VuFind/src/VuFind/Search/MixedList/Results.php index f77dddbcb95baf405833cb4c194822398a9500f0..e0d5dcb28eaa1d26ab0e8e43722d720a087cdda8 100644 --- a/module/VuFind/src/VuFind/Search/MixedList/Results.php +++ b/module/VuFind/src/VuFind/Search/MixedList/Results.php @@ -2,7 +2,7 @@ /** * Mixed List aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\MixedList; + use VuFind\Search\Base\Results as BaseResults; /** diff --git a/module/VuFind/src/VuFind/Search/Options/Factory.php b/module/VuFind/src/VuFind/Search/Options/OptionsFactory.php similarity index 50% rename from module/VuFind/src/VuFind/Search/Options/Factory.php rename to module/VuFind/src/VuFind/Search/Options/OptionsFactory.php index 266e3cdbfeddb46a1605ff956a4400a36b31f549..e2f1da2d1db9ba6eb35b8bead07f6414e81cbe74 100644 --- a/module/VuFind/src/VuFind/Search/Options/Factory.php +++ b/module/VuFind/src/VuFind/Search/Options/OptionsFactory.php @@ -1,10 +1,10 @@ <?php /** - * Search Options Object Factory Class + * Generic factory for search options objects. * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2014. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -23,44 +23,43 @@ * @package Search * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Search\Options; -use Zend\ServiceManager\ServiceManager; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** - * Search Options Object Factory Class + * Generic factory for search options objects. * * @category VuFind * @package Search * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore + * @link https://vufind.org/wiki/development Wiki */ -class Factory +class OptionsFactory implements FactoryInterface { /** - * Factory for Solr results object. + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * - * @param ServiceManager $sm Service manager. + * @return object * - * @return Solr + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs */ - public static function getEDS(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config'); - $container = new \Zend\Session\Container( - 'EBSCO', $sm->getServiceLocator()->get('VuFind\SessionManager') + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + return new $requestedName( + $container->get('VuFind\Config\PluginManager'), ...($options ?: []) ); - // No API info in session? Re-establish connection: - if (!isset($container->info)) { - $backend = $sm->getServiceLocator()->get('VuFind\Search\BackendManager') - ->get('EDS'); - $backend->getSessionToken(); - } - $eds = new \VuFind\Search\EDS\Options($config, $container->info); - return $eds; } } diff --git a/module/VuFind/src/VuFind/Search/Options/PluginFactory.php b/module/VuFind/src/VuFind/Search/Options/PluginFactory.php index e263c58186a51f20bae3fcef520b1bf5b36c1e64..586152dd6e3fc2d96cdef5c00c550c51b2e5e07d 100644 --- a/module/VuFind/src/VuFind/Search/Options/PluginFactory.php +++ b/module/VuFind/src/VuFind/Search/Options/PluginFactory.php @@ -2,7 +2,7 @@ /** * Search options plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ namespace VuFind\Search\Options; -use Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; /** * Search options plugin factory @@ -51,18 +52,18 @@ class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory /** * Create a service for the specified name. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service + * @param array $options Options (unused) * * @return object + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null ) { - $class = $this->getClassName($name, $requestedName); - return new $class( - $serviceLocator->getServiceLocator()->get('VuFind\Config') - ); + $class = $this->getClassName($requestedName); + return new $class($container->get('VuFind\Config\PluginManager')); } } diff --git a/module/VuFind/src/VuFind/Search/Options/PluginManager.php b/module/VuFind/src/VuFind/Search/Options/PluginManager.php index 75dcd6488b9d5584915f47ca1e14d7cb8f63699b..7522a644e56c847511e5a9367eed02ba4d8ccc92 100644 --- a/module/VuFind/src/VuFind/Search/Options/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/Options/PluginManager.php @@ -2,7 +2,7 @@ /** * Search options plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,83 @@ namespace VuFind\Search\Options; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'browzine' => 'VuFind\Search\BrowZine\Options', + 'combined' => 'VuFind\Search\Combined\Options', + 'eds' => 'VuFind\Search\EDS\Options', + 'eit' => 'VuFind\Search\EIT\Options', + 'emptyset' => 'VuFind\Search\EmptySet\Options', + 'favorites' => 'VuFind\Search\Favorites\Options', + 'libguides' => 'VuFind\Search\LibGuides\Options', + 'mixedlist' => 'VuFind\Search\MixedList\Options', + 'pazpar2' => 'VuFind\Search\Pazpar2\Options', + 'primo' => 'VuFind\Search\Primo\Options', + 'search2' => 'VuFind\Search\Search2\Options', + 'solr' => 'VuFind\Search\Solr\Options', + 'solrauth' => 'VuFind\Search\SolrAuth\Options', + 'solrauthor' => 'VuFind\Search\SolrAuthor\Options', + 'solrauthorfacets' => 'VuFind\Search\SolrAuthorFacets\Options', + 'solrcollection' => 'VuFind\Search\SolrCollection\Options', + 'solrreserves' => 'VuFind\Search\SolrReserves\Options', + 'solrweb' => 'VuFind\Search\SolrWeb\Options', + 'summon' => 'VuFind\Search\Summon\Options', + 'tags' => 'VuFind\Search\Tags\Options', + 'worldcat' => 'VuFind\Search\WorldCat\Options', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Search\BrowZine\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Combined\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\EDS\Options' => 'VuFind\Search\EDS\OptionsFactory', + 'VuFind\Search\EIT\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\EmptySet\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Favorites\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\LibGuides\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\MixedList\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Pazpar2\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Primo\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Search2\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Solr\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\SolrAuth\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\SolrAuthor\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\SolrAuthorFacets\Options' => + 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\SolrCollection\Options' => + 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\SolrReserves\Options' => + 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\SolrWeb\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Summon\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\Tags\Options' => 'VuFind\Search\Options\OptionsFactory', + 'VuFind\Search\WorldCat\Options' => 'VuFind\Search\Options\OptionsFactory', + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Search\Options\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. diff --git a/module/VuFind/src/VuFind/Search/Options/ViewOptionsTrait.php b/module/VuFind/src/VuFind/Search/Options/ViewOptionsTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..5d8b1e13d3118f6bfd724e0b98470747e8b4924c --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Options/ViewOptionsTrait.php @@ -0,0 +1,66 @@ +<?php +/** + * Trait for setting up view options. Designed to be included in a subclass of + * \VuFind\Search\Base\Options. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Search\Options; + +/** + * Trait for setting up view options. Designed to be included in a subclass of + * \VuFind\Search\Base\Options. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +trait ViewOptionsTrait +{ + /** + * Set up the view options. + * + * @param \Zend\Config\Config $searchSettings Search settings. + * + * @return void + */ + public function initViewOptions(\Zend\Config\Config $searchSettings) + { + if (isset($searchSettings->General->default_view)) { + $this->defaultView = $searchSettings->General->default_view; + } + // Load view preferences (or defaults if none in .ini file): + if (isset($searchSettings->Views)) { + foreach ($searchSettings->Views as $key => $value) { + $this->viewOptions[$key] = $value; + } + } elseif (isset($searchSettings->General->default_view)) { + $this->viewOptions = [$this->defaultView => $this->defaultView]; + } else { + $this->viewOptions = ['list' => 'List']; + } + } +} diff --git a/module/VuFind/src/VuFind/Search/Params/FacetLimitTrait.php b/module/VuFind/src/VuFind/Search/Params/FacetLimitTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..e432d14469daf1d53d4485227c37caab729ed954 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Params/FacetLimitTrait.php @@ -0,0 +1,109 @@ +<?php +/** + * Trait to add facet limiting settings to a Params object. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Search\Params; + +use Zend\Config\Config; + +/** + * Trait to add facet limiting settings to a Params object. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +trait FacetLimitTrait +{ + /** + * Default facet result limit + * + * @var int + */ + protected $facetLimit = 30; + + /** + * Per-field facet result limit + * + * @var array + */ + protected $facetLimitByField = []; + + /** + * Initialize facet limit from a Config object. + * + * @param Config $config Configuration + * + * @return void + */ + protected function initFacetLimitsFromConfig(Config $config = null) + { + if (is_numeric($config->facet_limit ?? null)) { + $this->setFacetLimit($config->facet_limit); + } + foreach ($config->facet_limit_by_field ?? [] as $k => $v) { + $this->facetLimitByField[$k] = $v; + } + } + + /** + * Set Facet Limit + * + * @param int $l the new limit value + * + * @return void + */ + public function setFacetLimit($l) + { + $this->facetLimit = $l; + } + + /** + * Set Facet Limit by Field + * + * @param array $new Associative array of $field name => $limit + * + * @return void + */ + public function setFacetLimitByField(array $new) + { + $this->facetLimitByField = $new; + } + + /** + * Get the facet limit for the specified field. + * + * @param string $field Field to look up + * + * @return int + */ + protected function getFacetLimitForField($field) + { + return $this->facetLimitByField[$field] ?? $this->facetLimit; + } +} diff --git a/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php b/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1cfdfb3bd1d7d6f830ba8ffb50a0690f5822afb6 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Generic factory for search params objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Params; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic factory for search params objects. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ParamsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + // Replace trailing "Params" with "Options" to get the options service: + $optionsService = preg_replace('/Params$/', 'Options', $requestedName); + $optionsObj = $container->get('VuFind\Search\Options\PluginManager') + ->get($optionsService); + $configLoader = $container->get('VuFind\Config\PluginManager'); + // Clone the options instance in case caller modifies it: + return new $requestedName( + clone $optionsObj, $configLoader, ...($options ?: []) + ); + } +} diff --git a/module/VuFind/src/VuFind/Search/Params/PluginFactory.php b/module/VuFind/src/VuFind/Search/Params/PluginFactory.php index 9e4c4c3a02e7181d78e1999398e956003cbb7be8..4c4b7aa45ce8d09dae3b84697fd0c060e1ac2b15 100644 --- a/module/VuFind/src/VuFind/Search/Params/PluginFactory.php +++ b/module/VuFind/src/VuFind/Search/Params/PluginFactory.php @@ -2,7 +2,7 @@ /** * Search params plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ namespace VuFind\Search\Params; -use Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; /** * Search params plugin factory @@ -51,22 +52,23 @@ class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory /** * Create a service for the specified name. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service - * @param array $extraParams Extra constructor parameters - * (to follow the Options object and config loader) + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service + * @param array $extras Extra options * * @return object + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName, array $extraParams = [] + public function __invoke(ContainerInterface $container, $requestedName, + array $extras = null ) { - $options = $serviceLocator->getServiceLocator() - ->get('VuFind\SearchOptionsPluginManager')->get($requestedName); - $class = $this->getClassName($name, $requestedName); - $configLoader = $serviceLocator->getServiceLocator()->get('VuFind\Config'); + $optionsService = preg_replace('/Params$/', 'Options', $requestedName); + $options = $container->get('VuFind\Search\Options\PluginManager') + ->get($optionsService); + $class = $this->getClassName($requestedName); + $configLoader = $container->get('VuFind\Config\PluginManager'); // Clone the options instance in case caller modifies it: - return new $class(clone($options), $configLoader, ...$extraParams); + return new $class(clone $options, $configLoader, ...($extras ?: [])); } } diff --git a/module/VuFind/src/VuFind/Search/Params/PluginManager.php b/module/VuFind/src/VuFind/Search/Params/PluginManager.php index 60664942f3f7c74b61c56d040f7d58786e66d228..d26e0db888d4850f152541f3f434f250925df82a 100644 --- a/module/VuFind/src/VuFind/Search/Params/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/Params/PluginManager.php @@ -2,7 +2,7 @@ /** * Search params plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,67 @@ namespace VuFind\Search\Params; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'browzine' => 'VuFind\Search\BrowZine\Params', + 'combined' => 'VuFind\Search\Combined\Params', + 'eds' => 'VuFind\Search\EDS\Params', + 'eit' => 'VuFind\Search\EIT\Params', + 'emptyset' => 'VuFind\Search\EmptySet\Params', + 'favorites' => 'VuFind\Search\Favorites\Params', + 'libguides' => 'VuFind\Search\LibGuides\Params', + 'mixedlist' => 'VuFind\Search\MixedList\Params', + 'pazpar2' => 'VuFind\Search\Pazpar2\Params', + 'primo' => 'VuFind\Search\Primo\Params', + 'search2' => 'VuFind\Search\Search2\Params', + 'solr' => 'VuFind\Search\Solr\Params', + 'solrauth' => 'VuFind\Search\SolrAuth\Params', + 'solrauthor' => 'VuFind\Search\SolrAuthor\Params', + 'solrauthorfacets' => 'VuFind\Search\SolrAuthorFacets\Params', + 'solrcollection' => 'VuFind\Search\SolrCollection\Params', + 'solrreserves' => 'VuFind\Search\SolrReserves\Params', + 'solrweb' => 'VuFind\Search\SolrWeb\Params', + 'summon' => 'VuFind\Search\Summon\Params', + 'tags' => 'VuFind\Search\Tags\Params', + 'worldcat' => 'VuFind\Search\WorldCat\Params', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Search\BrowZine\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Combined\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\EDS\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\EIT\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\EmptySet\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Favorites\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\LibGuides\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\MixedList\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Pazpar2\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Primo\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Search2\Params' => 'VuFind\Search\Solr\ParamsFactory', + 'VuFind\Search\Solr\Params' => 'VuFind\Search\Solr\ParamsFactory', + 'VuFind\Search\SolrAuth\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\SolrAuthor\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\SolrAuthorFacets\Params' => + 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\SolrCollection\Params' => + 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\SolrReserves\Params' => + 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\SolrWeb\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Summon\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\Tags\Params' => 'VuFind\Search\Params\ParamsFactory', + 'VuFind\Search\WorldCat\Params' => 'VuFind\Search\Params\ParamsFactory', + ]; + /** * Constructor * @@ -52,8 +113,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ) { // These objects are not meant to be shared -- every time we retrieve one, // we are building a brand new object. - $this->setShareByDefault(false); + $this->sharedByDefault = false; + $this->addAbstractFactory('VuFind\Search\Params\PluginFactory'); parent::__construct($configOrContainerInstance, $v3config); } diff --git a/module/VuFind/src/VuFind/Search/Pazpar2/Options.php b/module/VuFind/src/VuFind/Search/Pazpar2/Options.php index 1561404121cc8e6e416bd29004dae9a0f5c35727..69935a6a435b6ae0c0fe97fd89a33182fbde5093 100644 --- a/module/VuFind/src/VuFind/Search/Pazpar2/Options.php +++ b/module/VuFind/src/VuFind/Search/Pazpar2/Options.php @@ -2,7 +2,7 @@ /** * Pazpar2 Search Options * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -49,7 +49,7 @@ class Options extends \VuFind\Search\Base\Options $this->searchIni = $this->facetsIni = 'Pazpar2'; $this->limitOptions = [$this->defaultLimit]; - + // Load source settings $searchSettings = $configLoader->get($this->searchIni); if (isset($searchSettings->IndexSources) diff --git a/module/VuFind/src/VuFind/Search/Pazpar2/Params.php b/module/VuFind/src/VuFind/Search/Pazpar2/Params.php index 83ad96c7127de565098caff64aeefa1bf50f70d8..0c8f4b8fb6dc61589c75c725459b45a17e198f6d 100644 --- a/module/VuFind/src/VuFind/Search/Pazpar2/Params.php +++ b/module/VuFind/src/VuFind/Search/Pazpar2/Params.php @@ -2,7 +2,7 @@ /** * Pazpar2 Search Parameters * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Pazpar2; + use VuFindSearch\ParamBag; /** diff --git a/module/VuFind/src/VuFind/Search/Pazpar2/Results.php b/module/VuFind/src/VuFind/Search/Pazpar2/Results.php index 555c6258b1fa7eacad972649731d3b8ea22b68c9..eed844b0e3eb478197b86758af5dc6689fbd2544 100644 --- a/module/VuFind/src/VuFind/Search/Pazpar2/Results.php +++ b/module/VuFind/src/VuFind/Search/Pazpar2/Results.php @@ -2,7 +2,7 @@ /** * Pazpar2 Search Results * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/Primo/InjectOnCampusListener.php b/module/VuFind/src/VuFind/Search/Primo/InjectOnCampusListener.php index e75640b1ac293cc95a148f71cd73cb24b514c85a..7f8fa7f94dd520ea273e10ed5a3e9e034c86bb33 100644 --- a/module/VuFind/src/VuFind/Search/Primo/InjectOnCampusListener.php +++ b/module/VuFind/src/VuFind/Search/Primo/InjectOnCampusListener.php @@ -2,7 +2,7 @@ /** * OnCampus listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -27,11 +27,10 @@ */ namespace VuFind\Search\Primo; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; -use ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; +use ZfcRbac\Service\AuthorizationServiceAwareTrait; /** * OnCampus listener. @@ -126,5 +125,4 @@ class InjectOnCampusListener return $event; } - } diff --git a/module/VuFind/src/VuFind/Search/Primo/Options.php b/module/VuFind/src/VuFind/Search/Primo/Options.php index 0140dc41df9fc1569f499de30b1c8b280985a955..37e445dccc24701a3fa102f80596b604fefe5984 100644 --- a/module/VuFind/src/VuFind/Search/Primo/Options.php +++ b/module/VuFind/src/VuFind/Search/Primo/Options.php @@ -2,7 +2,7 @@ /** * Primo Central Search Options * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/Primo/Params.php b/module/VuFind/src/VuFind/Search/Primo/Params.php index 330a883f02229d8a58f65d1d9f3ea8c4691f030f..5338d197dcb08fb752cc32182dc6340f3aef9aa8 100644 --- a/module/VuFind/src/VuFind/Search/Primo/Params.php +++ b/module/VuFind/src/VuFind/Search/Primo/Params.php @@ -2,7 +2,7 @@ /** * Primo Central Search Parameters * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Primo; + use VuFindSearch\ParamBag; /** @@ -112,8 +113,8 @@ class Params extends \VuFind\Search\Base\Params */ public function activateAllFacets($preferredSection = false) { - $this->initFacetList('Facets', 'Results_Settings', 'Primo'); - $this->initFacetList('Advanced_Facets', 'Advanced_Facet_Settings', 'Primo'); - $this->initCheckboxFacets('CheckboxFacets', 'Primo'); + $this->initFacetList('Facets', 'Results_Settings'); + $this->initFacetList('Advanced_Facets', 'Advanced_Facet_Settings'); + $this->initCheckboxFacets(); } } diff --git a/module/VuFind/src/VuFind/Search/Primo/PrimoPermissionHandler.php b/module/VuFind/src/VuFind/Search/Primo/PrimoPermissionHandler.php index 8ef3535f2b4eaea1dd661724cdff018b2efbbd46..f35293a55e60380d94b84e99e0d24c9cc3668712 100644 --- a/module/VuFind/src/VuFind/Search/Primo/PrimoPermissionHandler.php +++ b/module/VuFind/src/VuFind/Search/Primo/PrimoPermissionHandler.php @@ -2,7 +2,7 @@ /** * Primo Permission Handler. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -27,8 +27,7 @@ */ namespace VuFind\Search\Primo; -use ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; +use ZfcRbac\Service\AuthorizationServiceAwareTrait; /** * Primo Permission Handler. @@ -98,7 +97,7 @@ class PrimoPermissionHandler */ public function instCodeExists($code) { - return (in_array($code, $this->getInstCodes()) === true); + return in_array($code, $this->getInstCodes()) === true; } /** @@ -123,7 +122,7 @@ class PrimoPermissionHandler public function hasPermission() { $code = $this->getInstCode(); - return (false !== $code && $this->checkPermission($code) === true); + return false !== $code && $this->checkPermission($code) === true; } /** @@ -242,7 +241,6 @@ class PrimoPermissionHandler if ($this->instCode === null) { $this->instCode = false; } - } /** @@ -309,6 +307,6 @@ class PrimoPermissionHandler $authService = $this->getAuthorizationService(); // if no authorization service is available, the user can't get permission - return ($authService && $authService->isGranted($onCampusRule)); + return $authService && $authService->isGranted($onCampusRule); } } diff --git a/module/VuFind/src/VuFind/Search/Primo/Results.php b/module/VuFind/src/VuFind/Search/Primo/Results.php index c6f2d2eec8c109e7a1bfb9fcafdb784312fbac82..6565d7100f06ddd3b4598f3d86c636df79b7aa1b 100644 --- a/module/VuFind/src/VuFind/Search/Primo/Results.php +++ b/module/VuFind/src/VuFind/Search/Primo/Results.php @@ -2,7 +2,7 @@ /** * Primo Central Search Results * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/QueryAdapter.php b/module/VuFind/src/VuFind/Search/QueryAdapter.php index 2fb6b089af082309fe652b32a51fb5b614186d6d..ca0f4fcbb356ea59194d12d2d59f9f22cd1bceba 100644 --- a/module/VuFind/src/VuFind/Search/QueryAdapter.php +++ b/module/VuFind/src/VuFind/Search/QueryAdapter.php @@ -3,7 +3,7 @@ /** * Legacy adapter: search query parameters to AbstractQuery object * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -29,8 +29,8 @@ namespace VuFind\Search; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; +use VuFindSearch\Query\QueryGroup; use Zend\StdLib\Parameters; /** @@ -58,9 +58,9 @@ abstract class QueryAdapter { // Use array_key_exists since null is also valid if (array_key_exists('l', $search)) { - $handler = isset($search['i']) ? $search['i'] : $search['f']; + $handler = $search['i'] ?? $search['f']; return new Query( - $search['l'], $handler, isset($search['o']) ? $search['o'] : null + $search['l'], $handler, $search['o'] ?? null ); } elseif (isset($search['g'])) { $operator = $search['g'][0]['b']; @@ -193,7 +193,7 @@ abstract class QueryAdapter // Add term to this group $boolArr = $request->get("bool$groupId"); - $lastBool = isset($boolArr[0]) ? $boolArr[0] : 'AND'; + $lastBool = $boolArr[0] ?? 'AND'; $group[] = new Query($value[$i], $handler, $operator); } } diff --git a/module/VuFind/src/VuFind/Search/RecommendListener.php b/module/VuFind/src/VuFind/Search/RecommendListener.php index c0043c342598855b50a454673229562cc8825135..85ac18478b469a6b1fb0cf7d5661171bbfc6d055 100644 --- a/module/VuFind/src/VuFind/Search/RecommendListener.php +++ b/module/VuFind/src/VuFind/Search/RecommendListener.php @@ -3,7 +3,7 @@ /** * Recommend listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,9 +28,9 @@ */ namespace VuFind\Search; -use VuFind\Recommend\PluginManager, VuFind\Search\SearchRunner; -use Zend\EventManager\SharedEventManagerInterface; +use VuFind\Recommend\PluginManager; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; /** * Recommend listener. diff --git a/module/VuFind/src/VuFind/Search/Results/Factory.php b/module/VuFind/src/VuFind/Search/Results/Factory.php deleted file mode 100644 index 03f4d1f300ca93787639e085ed91fd9eae352d45..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Search/Results/Factory.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -/** - * Search Results Object Factory Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Search - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\Search\Results; -use Zend\ServiceManager\ServiceManager; - -/** - * Search Results Object Factory Class - * - * @category VuFind - * @package Search - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for Favorites results object. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Favorites\Results - */ - public static function getFavorites(ServiceManager $sm) - { - $factory = new PluginFactory(); - $tm = $sm->getServiceLocator()->get('VuFind\DbTablePluginManager'); - $obj = $factory->createServiceWithName( - $sm, 'favorites', 'Favorites', - [$tm->get('Resource'), $tm->get('UserList')] - ); - $init = new \ZfcRbac\Initializer\AuthorizationServiceInitializer(); - $init->initialize($obj, $sm); - return $obj; - } - - /** - * Factory for Solr results object. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Solr\Results - */ - public static function getSolr(ServiceManager $sm) - { - $factory = new PluginFactory(); - $solr = $factory->createServiceWithName($sm, 'solr', 'Solr'); - $config = $sm->getServiceLocator() - ->get('VuFind\Config')->get('config'); - $spellConfig = isset($config->Spelling) - ? $config->Spelling : null; - $solr->setSpellingProcessor( - new \VuFind\Search\Solr\SpellingProcessor($spellConfig) - ); - return $solr; - } - - /** - * Factory for Tags results object. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Tags\Results - */ - public static function getTags(ServiceManager $sm) - { - $factory = new PluginFactory(); - $tm = $sm->getServiceLocator()->get('VuFind\DbTablePluginManager'); - return $factory->createServiceWithName( - $sm, 'tags', 'Tags', [$tm->get('Tags')] - ); - } -} diff --git a/module/VuFind/src/VuFind/Search/Results/PluginFactory.php b/module/VuFind/src/VuFind/Search/Results/PluginFactory.php index 1f0e2482c65d4c82e85eee65c37451a1f8a03dd6..022d9c17d8960f7d8bdbcaa4a31e8195072d0ca6 100644 --- a/module/VuFind/src/VuFind/Search/Results/PluginFactory.php +++ b/module/VuFind/src/VuFind/Search/Results/PluginFactory.php @@ -2,7 +2,7 @@ /** * Search results plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki */ namespace VuFind\Search\Results; -use Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; /** * Search results plugin factory @@ -51,24 +52,25 @@ class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory /** * Create a service for the specified name. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service - * @param array $extraParams Extra constructor parameters - * (to follow the Params, Search and RecordLoader objects) + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service + * @param array $extras Extra options * * @return object + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName, array $extraParams = [] + public function __invoke(ContainerInterface $container, $requestedName, + array $extras = null ) { - $params = $serviceLocator->getServiceLocator() - ->get('VuFind\SearchParamsPluginManager')->get($requestedName); - $searchService = $serviceLocator->getServiceLocator() - ->get('VuFind\Search'); - $recordLoader = $serviceLocator->getServiceLocator() - ->get('VuFind\RecordLoader'); - $class = $this->getClassName($name, $requestedName); - return new $class($params, $searchService, $recordLoader, ...$extraParams); + $paramsService = preg_replace('/Results$/', 'Params', $requestedName); + $params = $container->get('VuFind\Search\Params\PluginManager') + ->get($paramsService); + $searchService = $container->get('VuFindSearch\Service'); + $recordLoader = $container->get('VuFind\Record\Loader'); + $class = $this->getClassName($requestedName); + return new $class( + $params, $searchService, $recordLoader, ...($extras ?: []) + ); } } diff --git a/module/VuFind/src/VuFind/Search/Results/PluginManager.php b/module/VuFind/src/VuFind/Search/Results/PluginManager.php index 076f6ef5ec5527bfdcb634445c59cb2ce104db1a..4585f59a0801c6ca147f9c0e5ddb5d3ca0ffc548 100644 --- a/module/VuFind/src/VuFind/Search/Results/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/Results/PluginManager.php @@ -2,7 +2,7 @@ /** * Search results plugin manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -38,6 +38,68 @@ namespace VuFind\Search\Results; */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'browzine' => 'VuFind\Search\BrowZine\Results', + 'combined' => 'VuFind\Search\Combined\Results', + 'eds' => 'VuFind\Search\EDS\Results', + 'eit' => 'VuFind\Search\EIT\Results', + 'emptyset' => 'VuFind\Search\EmptySet\Results', + 'favorites' => 'VuFind\Search\Favorites\Results', + 'libguides' => 'VuFind\Search\LibGuides\Results', + 'mixedlist' => 'VuFind\Search\MixedList\Results', + 'pazpar2' => 'VuFind\Search\Pazpar2\Results', + 'primo' => 'VuFind\Search\Primo\Results', + 'search2' => 'VuFind\Search\Search2\Results', + 'solr' => 'VuFind\Search\Solr\Results', + 'solrauth' => 'VuFind\Search\SolrAuth\Results', + 'solrauthor' => 'VuFind\Search\SolrAuthor\Results', + 'solrauthorfacets' => 'VuFind\Search\SolrAuthorFacets\Results', + 'solrcollection' => 'VuFind\Search\SolrCollection\Results', + 'solrreserves' => 'VuFind\Search\SolrReserves\Results', + 'solrweb' => 'VuFind\Search\SolrWeb\Results', + 'summon' => 'VuFind\Search\Summon\Results', + 'tags' => 'VuFind\Search\Tags\Results', + 'worldcat' => 'VuFind\Search\WorldCat\Results', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Search\BrowZine\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Combined\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\EDS\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\EIT\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\EmptySet\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Favorites\Results' => + 'VuFind\Search\Favorites\ResultsFactory', + 'VuFind\Search\LibGuides\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\MixedList\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Pazpar2\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Primo\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Search2\Results' => 'VuFind\Search\Search2\ResultsFactory', + 'VuFind\Search\Solr\Results' => 'VuFind\Search\Solr\ResultsFactory', + 'VuFind\Search\SolrAuth\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\SolrAuthor\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\SolrAuthorFacets\Results' => + 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\SolrCollection\Results' => + 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\SolrReserves\Results' => + 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\SolrWeb\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Summon\Results' => 'VuFind\Search\Results\ResultsFactory', + 'VuFind\Search\Tags\Results' => 'VuFind\Search\Tags\ResultsFactory', + 'VuFind\Search\WorldCat\Results' => 'VuFind\Search\Results\ResultsFactory', + ]; + /** * Constructor * @@ -52,8 +114,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ) { // These objects are not meant to be shared -- every time we retrieve one, // we are building a brand new object. - $this->setShareByDefault(false); + $this->sharedByDefault = false; + $this->addAbstractFactory('VuFind\Search\Results\PluginFactory'); parent::__construct($configOrContainerInstance, $v3config); } diff --git a/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fe05add028f170b2a9ee03e06f06b8b42974e739 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Generic factory for search results objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Results; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic factory for search results objects. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResultsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + // Replace trailing "Results" with "Params" to get the params service: + $paramsService = preg_replace('/Results$/', 'Params', $requestedName); + $params = $container->get('VuFind\Search\Params\PluginManager') + ->get($paramsService); + $searchService = $container->get('VuFindSearch\Service'); + $recordLoader = $container->get('VuFind\Record\Loader'); + return new $requestedName( + $params, $searchService, $recordLoader, ...($options ?: []) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/RC4Test.php b/module/VuFind/src/VuFind/Search/Search2/FacetCache.php similarity index 62% rename from module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/RC4Test.php rename to module/VuFind/src/VuFind/Search/Search2/FacetCache.php index 8b9d06266c5803b4a49a872c1f33df5076ad9757..2ccc8cf9e465e084c0b5a48f89e9ba928eae706e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/RC4Test.php +++ b/module/VuFind/src/VuFind/Search/Search2/FacetCache.php @@ -1,10 +1,10 @@ <?php /** - * RC4 Test Class + * Search2 FacetCache. * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2010. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,34 +20,31 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Tests + * @package Search_Search2 * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + * @link https://vufind.org/wiki/development Wiki */ -namespace VuFindTest\Crypt; -use VuFind\Crypt\RC4; +namespace VuFind\Search\Search2; /** - * RC4 Test Class + * Search2 FacetCache. * * @category VuFind - * @package Tests + * @package Search_Search2 * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + * @link https://vufind.org/wiki/development Wiki */ -class RC4Test extends \VuFindTest\Unit\TestCase +class FacetCache extends \VuFind\Search\Base\FacetCache { /** - * Test encryption/decryption. + * Get the namespace to use for caching facets. * - * @return void + * @return string */ - public function testEncryptionAndDecryption() + protected function getCacheNamespace() { - $key = 'secret'; - $text = 'test'; - $this->assertEquals($text, RC4::decrypt($key, RC4::encrypt($key, $text))); + return 'search2-facets'; } } diff --git a/module/VuFind/src/VuFind/Search/Search2/Options.php b/module/VuFind/src/VuFind/Search/Search2/Options.php new file mode 100644 index 0000000000000000000000000000000000000000..7089af07a33f3667273befe117944dd37347658d --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2/Options.php @@ -0,0 +1,73 @@ +<?php + +/** + * Search Options for second Solr index + * + * PHP version 7 + * + * Copyright (C) Staats- und Universitätsbibliothek Hamburg 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Search2 + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Search\Search2; + +/** + * Search Options for second Solr index + * + * @category VuFind + * @package Search_Search2 + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Options extends \VuFind\Search\Solr\Options +{ + /** + * Constructor + * + * @param \VuFind\Config\PluginManager $configLoader Config loader + */ + public function __construct(\VuFind\Config\PluginManager $configLoader) + { + $this->mainIni = $this->searchIni = $this->facetsIni = 'Search2'; + parent::__construct($configLoader); + } + + /** + * Return the route name for the search results action. + * + * @return string + */ + public function getSearchAction() + { + return 'search2-results'; + } + + /** + * Return the route name of the action used for performing advanced searches. + * Returns false if the feature is not supported. + * + * @return string|bool + */ + public function getAdvancedSearchAction() + { + return 'search2-advanced'; + } +} diff --git a/module/VuFind/src/VuFind/Search/Search2/Params.php b/module/VuFind/src/VuFind/Search/Search2/Params.php new file mode 100644 index 0000000000000000000000000000000000000000..a139cb3f3404dbaf4c4cf38f00abc67fb3ea013a --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2/Params.php @@ -0,0 +1,64 @@ +<?php + +/** + * Search Params for second Solr index + * + * PHP version 7 + * + * Copyright (C) Staats- und Universitätsbibliothek Hamburg 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Search2 + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Search\Search2; + +/** + * Search Params for second Solr index + * + * @category VuFind + * @package Search_Search2 + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Params extends \VuFind\Search\Solr\Params +{ + /** + * Initialize facet settings for the advanced search screen. + * + * @return void + */ + public function initAdvancedFacets() + { + $this->initFacetList('Advanced_Facets', 'Advanced_Settings'); + } + + /** + * Initialize facet settings for the home page. + * + * @return void + */ + public function initHomePageFacets() + { + // Load Advanced settings if HomePage settings are missing (legacy support): + if (!$this->initFacetList('HomePage_Facets', 'HomePage_Facet_Settings')) { + $this->initAdvancedFacets(); + } + } +} diff --git a/module/VuFind/src/VuFind/Search/Search2/Results.php b/module/VuFind/src/VuFind/Search/Search2/Results.php new file mode 100644 index 0000000000000000000000000000000000000000..63205d0a948d36151d3991017c9cbaccfce91c47 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2/Results.php @@ -0,0 +1,48 @@ +<?php + +/** + * Search Results for second Solr index + * + * PHP version 7 + * + * Copyright (C) Staats- und Universitätsbibliothek Hamburg 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Search2 + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Search\Search2; + +/** + * Search Results for second Solr index + * + * @category VuFind + * @package Search_Search2 + * @author Hajo Seng <hajo.seng@sub.uni-hamburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Results extends \VuFind\Search\Solr\Results +{ + /** + * Backend ID + * + * @var string + */ + protected $backendId = 'Search2'; +} diff --git a/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..11d75b5bfb02dff20485d29f9a47ada40f342c3a --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Search2 results objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Search2 + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Search2; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Search2 results objects. + * + * @category VuFind + * @package Search_Search2 + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResultsFactory extends \VuFind\Search\Results\ResultsFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $solr = parent::__invoke($container, $requestedName, $options); + $config = $container->get('VuFind\Config\PluginManager')->get('Search2'); + $solr->setSpellingProcessor( + new \VuFind\Search\Solr\SpellingProcessor($config->Spelling ?? null) + ); + return $solr; + } +} diff --git a/module/VuFind/src/VuFind/Search/SearchRunner.php b/module/VuFind/src/VuFind/Search/SearchRunner.php index b5044ff498116c081f705e592947eab8fd935552..53a08fc0c19b06b83e67c72e7b3519826c7eb327 100644 --- a/module/VuFind/src/VuFind/Search/SearchRunner.php +++ b/module/VuFind/src/VuFind/Search/SearchRunner.php @@ -2,7 +2,7 @@ /** * VuFind Search Runner * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search; + use VuFind\Search\Results\PluginManager as ResultsManager; -use Zend\EventManager\EventManagerInterface; use Zend\EventManager\EventManager; +use Zend\EventManager\EventManagerInterface; use Zend\Stdlib\Parameters; /** diff --git a/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php b/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d7ed6bd3b979aa765b157beddf2b7a422edbab83 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Search runner factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search; + +use Interop\Container\ContainerInterface; +use Zend\EventManager\EventManager; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Search runner factory. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchRunnerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Search\Results\PluginManager'), + new EventManager($container->get('SharedEventManager')) + ); + } +} diff --git a/module/VuFind/src/VuFind/Search/SearchTabsHelper.php b/module/VuFind/src/VuFind/Search/SearchTabsHelper.php index d8b34dbd65bcfce7a900f77c446156f239dca732..999fa728163ed762ff9a3a79c4b44d69b90615a4 100644 --- a/module/VuFind/src/VuFind/Search/SearchTabsHelper.php +++ b/module/VuFind/src/VuFind/Search/SearchTabsHelper.php @@ -2,7 +2,7 @@ /** * "Search tabs" helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015-2016. @@ -28,7 +28,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Search; -use VuFind\Search\Results\PluginManager, Zend\View\Helper\Url, Zend\Http\Request; + +use VuFind\Search\Results\PluginManager; +use Zend\Http\Request; /** * "Search tabs" helper diff --git a/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php b/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..932742f3da709eae517fe5842c8e7d9f169fef4a --- /dev/null +++ b/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php @@ -0,0 +1,77 @@ +<?php +/** + * Search tabs helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Search tabs helper factory. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchTabsHelperFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $tabConfig = isset($config->SearchTabs) + ? $config->SearchTabs->toArray() : []; + $filterConfig = isset($config->SearchTabsFilters) + ? $config->SearchTabsFilters->toArray() : []; + $permissionConfig = isset($config->SearchTabsPermissions) + ? $config->SearchTabsPermissions->toArray() : []; + return new $requestedName( + $container->get('VuFind\Search\Results\PluginManager'), + $tabConfig, $filterConfig, + $container->get('Application')->getRequest(), $permissionConfig + ); + } +} diff --git a/module/VuFind/src/VuFind/Search/Solr/AbstractErrorListener.php b/module/VuFind/src/VuFind/Search/Solr/AbstractErrorListener.php index 446a3f45133bbc495b334d1ea7b2f96566f9714b..b54da8be1e8361a6b0f636dfd550b853836ea609 100644 --- a/module/VuFind/src/VuFind/Search/Solr/AbstractErrorListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/AbstractErrorListener.php @@ -3,7 +3,7 @@ /** * Abstract base class of SOLR error listeners. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,12 +28,12 @@ */ namespace VuFind\Search\Solr; -use VuFindSearch\Backend\BackendInterface; +use SplObjectStorage; -use Zend\EventManager\SharedEventManagerInterface; +use VuFindSearch\Backend\BackendInterface; use Zend\EventManager\EventInterface; -use SplObjectStorage; +use Zend\EventManager\SharedEventManagerInterface; /** * Abstract base class of SOLR error listeners. diff --git a/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php b/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php index e0512d76f38551989b68f2df6bc146bf27876725..37869df1ff74d68d2622b05ac9927dab2487c771 100644 --- a/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php @@ -3,7 +3,7 @@ /** * Solr deduplication (merged records) listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * Copyright (C) The National Library of Finland 2013. @@ -32,8 +32,8 @@ namespace VuFind\Search\Solr; use VuFindSearch\Backend\BackendInterface; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -183,7 +183,7 @@ class DeduplicationListener */ protected function fetchLocalRecords($event) { - $config = $this->serviceLocator->get('VuFind\Config'); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager'); $searchConfig = $config->get($this->searchConfig); $dataSourceConfig = $config->get($this->dataSourceConfig); $recordSources = !empty($searchConfig->Records->sources) @@ -238,7 +238,7 @@ class DeduplicationListener } $dedupData[$source] = [ 'id' => $localId, - 'priority' => isset($localPriority) ? $localPriority : 99999 + 'priority' => $localPriority ?? 99999 ]; } $fields['dedup_id'] = $dedupId; @@ -362,5 +362,4 @@ class DeduplicationListener array_unshift($result, ''); return array_flip($result); } - } diff --git a/module/VuFind/src/VuFind/Exception/Date.php b/module/VuFind/src/VuFind/Search/Solr/FacetCache.php similarity index 72% rename from module/VuFind/src/VuFind/Exception/Date.php rename to module/VuFind/src/VuFind/Search/Solr/FacetCache.php index a7315f109fc037f477689356cd4fbe0c7a733cf3..c3aab4c420e7c02afc849f3096daa3498e12fd6d 100644 --- a/module/VuFind/src/VuFind/Exception/Date.php +++ b/module/VuFind/src/VuFind/Search/Solr/FacetCache.php @@ -1,10 +1,10 @@ <?php /** - * Date Exception + * Solr FacetCache. * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2011. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,22 +20,31 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Exceptions + * @package Search_Solr * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -namespace VuFind\Exception; +namespace VuFind\Search\Solr; /** - * Date Exception + * Solr FacetCache. * * @category VuFind - * @package Exceptions + * @package Search_Solr * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Date extends \Exception +class FacetCache extends \VuFind\Search\Base\FacetCache { + /** + * Get the namespace to use for caching facets. + * + * @return string + */ + protected function getCacheNamespace() + { + return 'solr-facets'; + } } diff --git a/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php b/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..9edfd6ba3d4e7bb4757ddade5d2b403e108255f3 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Solr FacetCache Factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Solr; + +use Interop\Container\ContainerInterface; + +/** + * Solr FacetCache Factory. + * + * @category VuFind + * @package Search_Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FacetCacheFactory extends \VuFind\Search\Base\FacetCacheFactory +{ + /** + * Create a results object with hidden filters pre-populated. + * + * @param ContainerInterface $container Service manager + * @param string $name Name of results object to load (based + * on name of FacetCache service name) + * + * @return \VuFind\Search\Base\Results + */ + protected function getResults(ContainerInterface $container, $name) + { + $filters = $container->get('VuFind\Search\SearchTabsHelper') + ->getHiddenFilters($name); + $results = $container->get('VuFind\Search\Results\PluginManager') + ->get($name); + $params = $results->getParams(); + foreach ($filters as $key => $subFilters) { + foreach ($subFilters as $filter) { + $params->addHiddenFilter("$key:$filter"); + } + } + return $results; + } +} diff --git a/module/VuFind/src/VuFind/Search/Solr/FilterFieldConversionListener.php b/module/VuFind/src/VuFind/Search/Solr/FilterFieldConversionListener.php index 0e89b2cdda6ab341fc61920dfe26d149910c55f8..0e93fb8c8ed622eaa3679a3d32a809cdf9e9604c 100644 --- a/module/VuFind/src/VuFind/Search/Solr/FilterFieldConversionListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/FilterFieldConversionListener.php @@ -3,7 +3,7 @@ /** * Listener to convert one field to another in filters (for legacy purposes). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFind\Search\Solr; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; /** * Listener to convert one field to another in filters (for legacy purposes). diff --git a/module/VuFind/src/VuFind/Search/Solr/HideFacetValueListener.php b/module/VuFind/src/VuFind/Search/Solr/HideFacetValueListener.php index ef47bdbdbfe85738f7d47316de8a0974807f62bc..f3939d53f84334dc4af5053af6a2f91883ae4c6f 100644 --- a/module/VuFind/src/VuFind/Search/Solr/HideFacetValueListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/HideFacetValueListener.php @@ -2,7 +2,7 @@ /** * Hide values of facet for displaying * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2014. * @@ -28,8 +28,8 @@ namespace VuFind\Search\Solr; use VuFindSearch\Backend\BackendInterface; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; /** * Hide single facet values from displaying. diff --git a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php index 980e7cf9cead13905a6b88a6ab04c509e94b219a..41ded201203ecd17b50a536852a029f1d0b722f7 100644 --- a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php +++ b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php @@ -2,7 +2,7 @@ /** * Facet Helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2014. * diff --git a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php index cde2967b025b58c45c4041a456e690d0241691bd..ac99cd2371fb521f101f3a63d5d45af46de887f3 100644 --- a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php @@ -3,7 +3,7 @@ /** * Solr hierarchical facet listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * Copyright (C) The National Library of Finland 2014. @@ -32,8 +32,8 @@ namespace VuFind\Search\Solr; use VuFindSearch\Backend\BackendInterface; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -107,10 +107,10 @@ class HierarchicalFacetListener $this->backend = $backend; $this->serviceLocator = $serviceLocator; - $config = $this->serviceLocator->get('VuFind\Config'); + $config = $this->serviceLocator->get('VuFind\Config\PluginManager'); $this->facetConfig = $config->get($facetConfig); - $this->facetHelper - = $this->serviceLocator->get('VuFind\HierarchicalFacetHelper'); + $this->facetHelper = $this->serviceLocator + ->get('VuFind\Search\Solr\HierarchicalFacetHelper'); $specialFacets = $this->facetConfig->SpecialFacets; $this->displayStyles diff --git a/module/VuFind/src/VuFind/Search/Solr/InjectConditionalFilterListener.php b/module/VuFind/src/VuFind/Search/Solr/InjectConditionalFilterListener.php index 15ea7983688e67bf41e3f5c27f913cf0d219fc1c..fe2c42352c52dca5e99591d4870d5105317655fe 100644 --- a/module/VuFind/src/VuFind/Search/Solr/InjectConditionalFilterListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/InjectConditionalFilterListener.php @@ -3,7 +3,7 @@ /** * Conditional Filter listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,11 +28,10 @@ */ namespace VuFind\Search\Solr; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; -use ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; +use ZfcRbac\Service\AuthorizationServiceAwareTrait; /** * Conditional Filter listener. @@ -143,5 +142,4 @@ class InjectConditionalFilterListener return $event; } - } diff --git a/module/VuFind/src/VuFind/Search/Solr/InjectHighlightingListener.php b/module/VuFind/src/VuFind/Search/Solr/InjectHighlightingListener.php index 181e5e6bfeef2d35a35c48ecbfe65abd9afbd904..8f9c970eabd6afae89c2ad145edb2595bca42dbf 100644 --- a/module/VuFind/src/VuFind/Search/Solr/InjectHighlightingListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/InjectHighlightingListener.php @@ -3,7 +3,7 @@ /** * Solr highlighting listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -30,8 +30,8 @@ namespace VuFind\Search\Solr; use VuFindSearch\Backend\BackendInterface; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; /** * Solr highlighting listener. @@ -113,13 +113,12 @@ class InjectHighlightingListener if (!isset($hl[0]) || $hl[0] != 'false') { $this->active = true; $params->set('hl', 'true'); - $params->set('hl.fl', $this->fieldList); $params->set('hl.simple.pre', '{{{{START_HILITE}}}}'); $params->set('hl.simple.post', '{{{{END_HILITE}}}}'); // Turn on hl.q generation in query builder: $this->backend->getQueryBuilder() - ->setCreateHighlightingQuery(true); + ->setFieldsToHighlight($this->fieldList); } } } diff --git a/module/VuFind/src/VuFind/Search/Solr/InjectSpellingListener.php b/module/VuFind/src/VuFind/Search/Solr/InjectSpellingListener.php index ee6662a75dc6bd64eae486b3765739adf51f6bc5..9816dbeef4b9f2ba92e69383c31ee98039f6f795 100644 --- a/module/VuFind/src/VuFind/Search/Solr/InjectSpellingListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/InjectSpellingListener.php @@ -3,7 +3,7 @@ /** * Solr spelling listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,14 +28,14 @@ */ namespace VuFind\Search\Solr; -use VuFindSearch\Service; use VuFindSearch\Backend\BackendInterface; use VuFindSearch\Backend\Solr\Response\Json\Spellcheck; use VuFindSearch\ParamBag; use VuFindSearch\Query\Query; +use VuFindSearch\Service; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; /** * Solr spelling listener. diff --git a/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php b/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php index 64aefab9ca7b564d7b15c1d3fcf210152183f285..57c776a2e8b7d67c1eaaf89219885a178ffd1459 100644 --- a/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php @@ -3,7 +3,7 @@ /** * MultiIndex listener class file. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -30,8 +30,8 @@ namespace VuFind\Search\Solr; use VuFindSearch\Backend\BackendInterface; -use Zend\EventManager\SharedEventManagerInterface; use Zend\EventManager\EventInterface; +use Zend\EventManager\SharedEventManagerInterface; /** * MultiIndex listener class file. diff --git a/module/VuFind/src/VuFind/Search/Solr/Options.php b/module/VuFind/src/VuFind/Search/Solr/Options.php index 6855645189e91a24835f076624333cd46820ea11..760b81e44b2d62026cd2f4ae58b7591697df8870 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Options.php +++ b/module/VuFind/src/VuFind/Search/Solr/Options.php @@ -2,7 +2,7 @@ /** * Solr aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -38,6 +38,8 @@ namespace VuFind\Search\Solr; */ class Options extends \VuFind\Search\Base\Options { + use \VuFind\Search\Options\ViewOptionsTrait; + /** * Available sort options for facets * @@ -102,9 +104,6 @@ class Options extends \VuFind\Search\Base\Options if (isset($searchSettings->RSS->sort)) { $this->rssSort = $searchSettings->RSS->sort; } - if (isset($searchSettings->General->default_view)) { - $this->defaultView = $searchSettings->General->default_view; - } if (isset($searchSettings->General->default_handler)) { $this->defaultHandler = $searchSettings->General->default_handler; } @@ -142,16 +141,10 @@ class Options extends \VuFind\Search\Base\Options 'callnumber-sort' => 'sort_callnumber', 'author' => 'sort_author', 'title' => 'sort_title']; } - // Load view preferences (or defaults if none in .ini file): - if (isset($searchSettings->Views)) { - foreach ($searchSettings->Views as $key => $value) { - $this->viewOptions[$key] = $value; - } - } elseif (isset($searchSettings->General->default_view)) { - $this->viewOptions = [$this->defaultView => $this->defaultView]; - } else { - $this->viewOptions = ['list' => 'List']; - } + + // Set up views + $this->initViewOptions($searchSettings); + // Load list view for result (controls AJAX embedding vs. linking) if (isset($searchSettings->List->view)) { $this->listviewOption = $searchSettings->List->view; @@ -193,7 +186,7 @@ class Options extends \VuFind\Search\Base\Options } // Load Spelling preferences - $config = $configLoader->get('config'); + $config = $configLoader->get($this->mainIni); if (isset($config->Spelling->enabled)) { $this->spellcheck = $config->Spelling->enabled; } diff --git a/module/VuFind/src/VuFind/Search/Solr/Params.php b/module/VuFind/src/VuFind/Search/Solr/Params.php index 29fafbc07565bb2e63cd474e968bbd28c4243bdd..06ac6f635efc49dbaa480855a60058466b7a5bc9 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Params.php +++ b/module/VuFind/src/VuFind/Search/Solr/Params.php @@ -2,7 +2,7 @@ /** * Solr aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Solr; + use VuFindSearch\ParamBag; /** @@ -39,19 +40,7 @@ use VuFindSearch\ParamBag; */ class Params extends \VuFind\Search\Base\Params { - /** - * Default facet result limit - * - * @var int - */ - protected $facetLimit = 30; - - /** - * Per-field facet result limit - * - * @var array - */ - protected $facetLimitByField = []; + use \VuFind\Search\Params\FacetLimitTrait; /** * Offset for facet results @@ -110,22 +99,13 @@ class Params extends \VuFind\Search\Base\Params // Use basic facet limit by default, if set: $config = $configLoader->get($options->getFacetsIni()); - if (isset($config->Results_Settings->facet_limit) - && is_numeric($config->Results_Settings->facet_limit) - ) { - $this->setFacetLimit($config->Results_Settings->facet_limit); - } + $this->initFacetLimitsFromConfig($config->Results_Settings ?? null); if (isset($config->LegacyFields)) { $this->facetAliases = $config->LegacyFields->toArray(); } if (isset($config->ExtraFacetLabels)) { $this->extraFacetLabels = $config->ExtraFacetLabels->toArray(); } - if (isset($config->Results_Settings->facet_limit_by_field)) { - foreach ($config->Results_Settings->facet_limit_by_field as $k => $v) { - $this->facetLimitByField[$k] = $v; - } - } if (isset($config->Results_Settings->sorted_by_index) && count($config->Results_Settings->sorted_by_index) > 0 ) { @@ -157,7 +137,7 @@ class Params extends \VuFind\Search\Base\Params // Special case -- complex filter, that should be taken as-is: if ($field == '#') { $q = $value; - } else if (substr($value, -1) == '*' + } elseif (substr($value, -1) == '*' || preg_match('/\[[^\]]+\s+TO\s+[^\]]+\]/', $value) ) { // Special case -- allow trailing wildcards and ranges @@ -166,8 +146,7 @@ class Params extends \VuFind\Search\Base\Params $q = $field . ':"' . addcslashes($value, '"\\') . '"'; } if ($orFacet) { - $orFilters[$field] = isset($orFilters[$field]) - ? $orFilters[$field] : []; + $orFilters[$field] = $orFilters[$field] ?? []; $orFilters[$field][] = $q; } else { $filterQuery[] = $q; @@ -194,9 +173,9 @@ class Params extends \VuFind\Search\Base\Params if (!empty($this->facetConfig)) { $facetSet['limit'] = $this->facetLimit; foreach (array_keys($this->facetConfig) as $facetField) { - if (isset($this->facetLimitByField[$facetField])) { - $facetSet["f.{$facetField}.facet.limit"] - = $this->facetLimitByField[$facetField]; + $fieldLimit = $this->getFacetLimitForField($facetField); + if ($fieldLimit != $this->facetLimit) { + $facetSet["f.{$facetField}.facet.limit"] = $fieldLimit; } if ($this->getFacetOperator($facetField) == 'OR') { $facetField = '{!ex=' . $facetField . '_filter}' . $facetField; @@ -239,30 +218,6 @@ class Params extends \VuFind\Search\Base\Params } } - /** - * Set Facet Limit - * - * @param int $l the new limit value - * - * @return void - */ - public function setFacetLimit($l) - { - $this->facetLimit = $l; - } - - /** - * Set Facet Limit by Field - * - * @param array $new Associative array of $field name => $limit - * - * @return void - */ - public function setFacetLimitByField(array $new) - { - $this->facetLimitByField = $new; - } - /** * Set Facet Offset * @@ -316,18 +271,16 @@ class Params extends \VuFind\Search\Base\Params * * @param string $facetList Config section containing fields to activate * @param string $facetSettings Config section containing related settings - * @param string $cfgFile Name of configuration to load + * @param string $cfgFile Name of configuration to load (null to load + * default facets configuration). * * @return bool True if facets set, false if no settings found */ - protected function initFacetList($facetList, $facetSettings, $cfgFile = 'facets') + protected function initFacetList($facetList, $facetSettings, $cfgFile = null) { - $config = $this->configLoader->get('facets'); - if (isset($config->$facetSettings->facet_limit) - && is_numeric($config->$facetSettings->facet_limit) - ) { - $this->setFacetLimit($config->$facetSettings->facet_limit); - } + $config = $this->configLoader + ->get($cfgFile ?? $this->getOptions()->getFacetsIni()); + $this->initFacetLimitsFromConfig($config->$facetSettings ?? null); return parent::initFacetList($facetList, $facetSettings, $cfgFile); } @@ -449,7 +402,7 @@ class Params extends \VuFind\Search\Base\Params */ public function getQueryIDLimit() { - $config = $this->configLoader->get('config'); + $config = $this->configLoader->get($this->getOptions()->getMainIni()); return isset($config->Index->maxBooleanClauses) ? $config->Index->maxBooleanClauses : 1024; } @@ -547,9 +500,12 @@ class Params extends \VuFind\Search\Base\Params // Sort $sort = $this->getSort(); if ($sort) { - // If we have an empty search with relevance sort, see if there is - // an override configured: - if ($sort == 'relevance' && $this->getQuery()->getAllTerms() == '' + // If we have an empty search with relevance sort as the primary sort + // field, see if there is an override configured: + $sortFields = explode(',', $sort); + $allTerms = trim($this->getQuery()->getAllTerms()); + if ('relevance' === $sortFields[0] + && ('' === $allTerms || '*:*' === $allTerms) && ($relOv = $this->getOptions()->getEmptySearchRelevanceOverride()) ) { $sort = $relOv; @@ -617,7 +573,7 @@ class Params extends \VuFind\Search\Base\Params if (preg_match('/^\[(.*) TO (.*)\]$/', $value, $matches)) { // Simple case: [X TO Y] $filter['displayText'] = $matches[1] . '-' . $matches[2]; - } else if (preg_match($caseInsensitiveRegex, $value, $matches)) { + } elseif (preg_match($caseInsensitiveRegex, $value, $matches)) { // Case insensitive case: [x TO y] OR [X TO Y]; convert // only if values in both ranges match up! if (strtolower($matches[3]) == strtolower($matches[1]) @@ -625,11 +581,9 @@ class Params extends \VuFind\Search\Base\Params ) { $filter['displayText'] = $matches[1] . '-' . $matches[2]; } - } else if ($this->facetHelper && in_array($field, $hierarchicalFacets)) { + } elseif ($this->facetHelper && in_array($field, $hierarchicalFacets)) { // Display hierarchical facet levels nicely - $separator = isset($hierarchicalFacetSeparators[$field]) - ? $hierarchicalFacetSeparators[$field] - : '/'; + $separator = $hierarchicalFacetSeparators[$field] ?? '/'; $filter['displayText'] = $this->facetHelper->formatDisplayText( $filter['displayText'], true, $separator ); diff --git a/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php b/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5e6fa1aed652826cd33bb0f5c8640c933c9deeea --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for Solr search params objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Solr; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Solr search params objects. + * + * @category VuFind + * @package Search_Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ParamsFactory extends \VuFind\Search\Params\ParamsFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $helper = $container->get('VuFind\Search\Solr\HierarchicalFacetHelper'); + return parent::__invoke($container, $requestedName, [$helper]); + } +} diff --git a/module/VuFind/src/VuFind/Search/Solr/Results.php b/module/VuFind/src/VuFind/Search/Solr/Results.php index 7e79db1c9e0ef8d4b24682ae19730b7c47c8c5fc..83bdb021061652584ecd4297b3a3d3668d449792 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Results.php +++ b/module/VuFind/src/VuFind/Search/Solr/Results.php @@ -2,7 +2,7 @@ /** * Solr aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Solr; + use VuFindSearch\Backend\Solr\Response\Json\Spellcheck; use VuFindSearch\Query\AbstractQuery; use VuFindSearch\Query\QueryGroup; @@ -231,7 +232,7 @@ class Results extends \VuFind\Search\Base\Results } // If there is no filter, we'll use all facets as the filter: - if (is_null($filter)) { + if (null === $filter) { $filter = $this->getParams()->getFacetConfig(); } @@ -242,7 +243,7 @@ class Results extends \VuFind\Search\Base\Results $fieldFacets = $this->responseFacets->getFieldFacets(); $translatedFacets = $this->getOptions()->getTranslatedFacets(); foreach (array_keys($filter) as $field) { - $data = isset($fieldFacets[$field]) ? $fieldFacets[$field] : []; + $data = $fieldFacets[$field] ?? []; // Skip empty arrays: if (count($data) < 1) { continue; @@ -302,7 +303,7 @@ class Results extends \VuFind\Search\Base\Results public function getPartialFieldFacets($facetfields, $removeFilter = true, $limit = -1, $facetSort = null, $page = null, $ored = false ) { - $clone = clone($this); + $clone = clone $this; $params = $clone->getParams(); // Manipulate facet settings temporarily: diff --git a/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5eea28ca728e0ebb5e2db9c167b8950f7e9929da --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Solr search results objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Solr; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Solr search results objects. + * + * @category VuFind + * @package Search_Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResultsFactory extends \VuFind\Search\Results\ResultsFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $solr = parent::__invoke($container, $requestedName, $options); + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $solr->setSpellingProcessor( + new \VuFind\Search\Solr\SpellingProcessor($config->Spelling ?? null) + ); + return $solr; + } +} diff --git a/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php b/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php index 05532e44a2fe25e1fa3ec40d461cb81e0b63b3a6..03720d3fbc75c857d3efacee16fa1703ab5a9144 100644 --- a/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php +++ b/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php @@ -2,7 +2,7 @@ /** * Solr spelling processor. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Solr; + use VuFindSearch\Backend\Solr\Response\Json\Spellcheck; use VuFindSearch\Query\AbstractQuery; use Zend\Config\Config; @@ -46,21 +47,21 @@ class SpellingProcessor * * @var int */ - protected $spellingLimit = 3; + protected $spellingLimit; /** * Spell check words with numbers in them? * * @var bool */ - protected $spellSkipNumeric = true; + protected $spellSkipNumeric; /** * Offer expansions on terms as well as basic replacements? * * @var bool */ - protected $expand = true; + protected $expand; /** * Show the full modified search phrase on screen rather then just the suggested @@ -68,7 +69,7 @@ class SpellingProcessor * * @var bool */ - protected $phrase = false; + protected $phrase; /** * Constructor @@ -77,18 +78,10 @@ class SpellingProcessor */ public function __construct($config = null) { - if (isset($config->limit)) { - $this->spellingLimit = $config->limit; - } - if (isset($config->skip_numeric)) { - $this->spellSkipNumeric = $config->skip_numeric; - } - if (isset($config->expand)) { - $this->expand = $config->expand; - } - if (isset($config->phrase)) { - $this->phrase = $config->phrase; - } + $this->spellingLimit = $config->limit ?? 3; + $this->spellSkipNumeric = $config->skip_numeric ?? true; + $this->expand = $config->expand ?? true; + $this->phrase = $config->phrase ?? false; } /** diff --git a/module/VuFind/src/VuFind/Search/Solr/V3/ErrorListener.php b/module/VuFind/src/VuFind/Search/Solr/V3/ErrorListener.php index f27dd86a921b1c1a843f2f9788062315fb7ff4ce..f9c789ab2b8cca50c45671762f74bfa26af7ad12 100644 --- a/module/VuFind/src/VuFind/Search/Solr/V3/ErrorListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/V3/ErrorListener.php @@ -3,7 +3,7 @@ /** * SOLR 3.x error listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * diff --git a/module/VuFind/src/VuFind/Search/Solr/V4/ErrorListener.php b/module/VuFind/src/VuFind/Search/Solr/V4/ErrorListener.php index 02845f34d9dc4fadb72d6ef8576622d655599c5c..c34e0fcb184e90123389b982d926c9d6c6a3f1cd 100644 --- a/module/VuFind/src/VuFind/Search/Solr/V4/ErrorListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/V4/ErrorListener.php @@ -3,7 +3,7 @@ /** * SOLR 4.x error listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,11 +28,11 @@ */ namespace VuFind\Search\Solr\V4; -use VuFindSearch\Backend\Exception\HttpErrorException; use VuFind\Search\Solr\AbstractErrorListener; +use VuFindSearch\Backend\Exception\HttpErrorException; -use Zend\Http\Response; use Zend\EventManager\EventInterface; +use Zend\Http\Response; /** * SOLR 3.x error listener. @@ -129,5 +129,4 @@ class ErrorListener extends AbstractErrorListener } return self::TYPE_OTHER; } - } diff --git a/module/VuFind/src/VuFind/Search/SolrAuth/Options.php b/module/VuFind/src/VuFind/Search/SolrAuth/Options.php index 4db4a16228c4e5fc418e309169cdf43a684291ad..d28eb2e6d2d91c5a6b07913aca8889f2e12b8ccf 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuth/Options.php +++ b/module/VuFind/src/VuFind/Search/SolrAuth/Options.php @@ -2,7 +2,7 @@ /** * Solr Authority aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/SolrAuth/Params.php b/module/VuFind/src/VuFind/Search/SolrAuth/Params.php index 231df92091baeac48247251bad2db3b2b41fe5e8..b4470567affd4e0b0c4e692381262aed5369093a 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuth/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrAuth/Params.php @@ -2,7 +2,7 @@ /** * Solr Authority aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/SolrAuth/Results.php b/module/VuFind/src/VuFind/Search/SolrAuth/Results.php index aa8ffc769b6d6651147c42bd6bf5b5f15a544044..df86e8c891e77f6ab9ef48a6c32c874cf1b2d249 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuth/Results.php +++ b/module/VuFind/src/VuFind/Search/SolrAuth/Results.php @@ -2,7 +2,7 @@ /** * Solr Authority aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\SolrAuth; + use VuFind\Record\Loader; use VuFindSearch\Service as SearchService; diff --git a/module/VuFind/src/VuFind/Search/SolrAuthor/Options.php b/module/VuFind/src/VuFind/Search/SolrAuthor/Options.php index 240c8faa19ff0ee4a5b6e6d837189d86b4cd89aa..b2e8c2050505a064c5924a5f06c15c330ade922b 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuthor/Options.php +++ b/module/VuFind/src/VuFind/Search/SolrAuthor/Options.php @@ -2,7 +2,7 @@ /** * Author aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/SolrAuthor/Params.php b/module/VuFind/src/VuFind/Search/SolrAuthor/Params.php index 72923ad6006961eaeea4a8b409a85dfe8d6d7da6..68b766475938c1d354a8e20b8aea4abf3119e7ef 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuthor/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrAuthor/Params.php @@ -2,7 +2,7 @@ /** * Author aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -50,7 +50,7 @@ class Params extends \VuFind\Search\Solr\Params { // If no lookfor parameter was found, we have no search terms to // add to our array! - if (is_null($lookfor = $request->get('author'))) { + if (null === ($lookfor = $request->get('author'))) { return false; } diff --git a/module/VuFind/src/VuFind/Search/SolrAuthor/Results.php b/module/VuFind/src/VuFind/Search/SolrAuthor/Results.php index 4d896f9a8fc3089e216472ebeab56a3377e0d666..87d9e5c61f81292072948cb8514a138ec9b75eab 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuthor/Results.php +++ b/module/VuFind/src/VuFind/Search/SolrAuthor/Results.php @@ -2,7 +2,7 @@ /** * Author aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\SolrAuthor; -use VuFind\Search\Solr\Results as SolrResults; + use VuFind\Record\Loader; +use VuFind\Search\Solr\Results as SolrResults; use VuFindSearch\Service as SearchService; /** diff --git a/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Options.php b/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Options.php index 0e38efe8dd3c6fb2aff8d0d828cdca0babe2d981..052d07e629dcaaceacd2da632b922a8460674c5d 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Options.php +++ b/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Options.php @@ -2,7 +2,7 @@ /** * AuthorFacets aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Params.php b/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Params.php index 00b8ffd9152a1fbc592c8d3d0f8decfb32a5aff6..19f54d74edaa22e3ddb6938a775f9c85b5a04ed0 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Params.php @@ -2,7 +2,7 @@ /** * AuthorFacets aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -76,7 +76,7 @@ class Params extends \VuFind\Search\Solr\Params { // If no lookfor parameter was found, we have no search terms to // add to our array! - if (is_null($lookfor = $request->get('lookfor'))) { + if (null === ($lookfor = $request->get('lookfor'))) { return false; } diff --git a/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Results.php b/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Results.php index d842355edd0303bfd1d5e453ba2cd0a1a475efb9..df1313e83980abf4e344d4f3b0a7e184a2bc785f 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Results.php +++ b/module/VuFind/src/VuFind/Search/SolrAuthorFacets/Results.php @@ -2,7 +2,7 @@ /** * AuthorFacets aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/SolrCollection/Options.php b/module/VuFind/src/VuFind/Search/SolrCollection/Options.php index 37f671c31c2fb987fe7b8b39fa2b5782315079e8..3ee2b5066a713d372baa9c3f4cb1f217e0860289 100644 --- a/module/VuFind/src/VuFind/Search/SolrCollection/Options.php +++ b/module/VuFind/src/VuFind/Search/SolrCollection/Options.php @@ -2,7 +2,7 @@ /** * Solr Collection aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -45,6 +45,7 @@ class Options extends \VuFind\Search\Solr\Options */ public function __construct(\VuFind\Config\PluginManager $configLoader) { + $this->facetsIni = 'Collection'; parent::__construct($configLoader); // Load sort preferences (or defaults if none in .ini file): @@ -72,8 +73,7 @@ class Options extends \VuFind\Search\Solr\Options */ public function getFacetListAction() { - // TODO: implement support for this if needed. - return false; + return 'search-collectionfacetlist'; } /** @@ -94,4 +94,14 @@ class Options extends \VuFind\Search\Solr\Options ? $searchSettings->Recommend->toArray() : ['side' => ['CollectionSideFacets:Facets::Collection:true']]; } + + /** + * Return the route name for the search results action. + * + * @return string + */ + public function getSearchAction() + { + return 'collection'; + } } diff --git a/module/VuFind/src/VuFind/Search/SolrCollection/Params.php b/module/VuFind/src/VuFind/Search/SolrCollection/Params.php index 068ba1dc1b96a8ba196c4053de1fc538e6ff8c78..046557c3d573d51bbc220531d694d88838bf7368 100644 --- a/module/VuFind/src/VuFind/Search/SolrCollection/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrCollection/Params.php @@ -2,7 +2,7 @@ /** * Solr Collection aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -90,6 +90,22 @@ class Params extends \VuFind\Search\Solr\Params $safeId = addcslashes($this->collectionID, '"'); $this->addHiddenFilter($this->collectionField . ':"' . $safeId . '"'); $this->addHiddenFilter('!id:"' . $safeId . '"'); + + // Because the [HiddenFilters] and [RawHiddenFilters] settings for the + // Solr search backend come from searches.ini and are set up in the + // AbstractSolrBackendFactory, we need to account for additional ones + // from Collection.ini here. + $collectionConfig = $this->configLoader->get('Collection'); + if (isset($collectionConfig->HiddenFilters)) { + foreach ($collectionConfig->HiddenFilters as $field => $value) { + $this->addHiddenFilter(sprintf('%s:"%s"', $field, $value)); + } + } + if (isset($collectionConfig->RawHiddenFilters)) { + foreach ($collectionConfig->RawHiddenFilters as $current) { + $this->addHiddenFilter($current); + } + } } /** @@ -101,4 +117,14 @@ class Params extends \VuFind\Search\Solr\Params { return $this->collectionField; } + + /** + * Get collection id + * + * @return string + */ + public function getCollectionId() + { + return $this->collectionID; + } } diff --git a/module/VuFind/src/VuFind/Search/SolrCollection/Results.php b/module/VuFind/src/VuFind/Search/SolrCollection/Results.php index fb6a977d47843d2cf6232a735e897f5bf1896577..dc755276f9e73c2cab2843bb250759f831c3bdfc 100644 --- a/module/VuFind/src/VuFind/Search/SolrCollection/Results.php +++ b/module/VuFind/src/VuFind/Search/SolrCollection/Results.php @@ -2,7 +2,7 @@ /** * Solr Collection aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/SolrReserves/Options.php b/module/VuFind/src/VuFind/Search/SolrReserves/Options.php index 47231216d96ed097afb5ffc7449c0ff5637b3516..3a29c69829d1a99e1afacc08b0cec9b81eee59ef 100644 --- a/module/VuFind/src/VuFind/Search/SolrReserves/Options.php +++ b/module/VuFind/src/VuFind/Search/SolrReserves/Options.php @@ -2,7 +2,7 @@ /** * Solr Reserves aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/SolrReserves/Params.php b/module/VuFind/src/VuFind/Search/SolrReserves/Params.php index 62248b08eea0a55d7db3dc288b72d09ff12deae0..c115b436aa8e3158aee83dfc3b40127aa56e6308 100644 --- a/module/VuFind/src/VuFind/Search/SolrReserves/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrReserves/Params.php @@ -2,7 +2,7 @@ /** * Solr Reserves aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/SolrReserves/Results.php b/module/VuFind/src/VuFind/Search/SolrReserves/Results.php index eb1ce36626a0515e26e799a9bc621dfc149f4738..404b235784347d0e17a09f346103fcd09eee8cee 100644 --- a/module/VuFind/src/VuFind/Search/SolrReserves/Results.php +++ b/module/VuFind/src/VuFind/Search/SolrReserves/Results.php @@ -2,7 +2,7 @@ /** * Solr Reserves aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,6 +27,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\SolrReserves; + use VuFind\Record\Loader; use VuFindSearch\Service as SearchService; diff --git a/module/VuFind/src/VuFind/Search/SolrWeb/Options.php b/module/VuFind/src/VuFind/Search/SolrWeb/Options.php index 33ab274b4405db496a8aa05f3384546de136b7f1..467af38375a91aad813dfdf040faabd16437cf0f 100644 --- a/module/VuFind/src/VuFind/Search/SolrWeb/Options.php +++ b/module/VuFind/src/VuFind/Search/SolrWeb/Options.php @@ -2,7 +2,7 @@ /** * Solr Web aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/SolrWeb/Params.php b/module/VuFind/src/VuFind/Search/SolrWeb/Params.php index b028a5ba8d6a2d073cfe7a1b24130584d5c15e79..6f4bd1d8cfc615b871cfc1c98f78d6236a2922eb 100644 --- a/module/VuFind/src/VuFind/Search/SolrWeb/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrWeb/Params.php @@ -2,7 +2,7 @@ /** * Solr Web aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/SolrWeb/Results.php b/module/VuFind/src/VuFind/Search/SolrWeb/Results.php index c3644b63f69115f38fe3daf8769171fc40211ffb..df4f375295ce07c2ca84ead5eae8f997299db5e0 100644 --- a/module/VuFind/src/VuFind/Search/SolrWeb/Results.php +++ b/module/VuFind/src/VuFind/Search/SolrWeb/Results.php @@ -2,7 +2,7 @@ /** * Solr Web aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\SolrWeb; + use VuFind\Record\Loader; use VuFindSearch\Service as SearchService; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/ClaviusSQLTest.php b/module/VuFind/src/VuFind/Search/Summon/FacetCache.php similarity index 66% rename from module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/ClaviusSQLTest.php rename to module/VuFind/src/VuFind/Search/Summon/FacetCache.php index d7e58d4df4d75a1f62d37982a46ade1dd63ec0fe..11bffd4c2e591ee5e2141acc740e3b80f4433044 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/ClaviusSQLTest.php +++ b/module/VuFind/src/VuFind/Search/Summon/FacetCache.php @@ -1,10 +1,10 @@ <?php /** - * ILS driver test + * Summon FacetCache. * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2011. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,30 +20,31 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Tests + * @package Search_Summon * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Page + * @link https://vufind.org/wiki/development Wiki */ -namespace VuFindTest\ILS\Driver; -use VuFind\ILS\Driver\ClaviusSQL; +namespace VuFind\Search\Summon; /** - * ILS driver test + * Summon FacetCache. * * @category VuFind - * @package Tests + * @package Search_Summon * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Page + * @link https://vufind.org/wiki/development Wiki */ -class ClaviusSQLTest extends \VuFindTest\Unit\ILSDriverTestCase +class FacetCache extends \VuFind\Search\Base\FacetCache { /** - * Constructor + * Get the namespace to use for caching facets. + * + * @return string */ - public function __construct() + protected function getCacheNamespace() { - $this->driver = new ClaviusSQL(); + return 'summon-facets'; } } diff --git a/module/VuFind/src/VuFind/Search/Summon/Options.php b/module/VuFind/src/VuFind/Search/Summon/Options.php index 9b4382871491123c5ce6e991832beed3d8bb61ba..ac5fb409c1c9c2cd8452bc2bd373985aa6c567e9 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Options.php +++ b/module/VuFind/src/VuFind/Search/Summon/Options.php @@ -2,7 +2,7 @@ /** * Summon Search Options * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -38,6 +38,8 @@ namespace VuFind\Search\Summon; */ class Options extends \VuFind\Search\Base\Options { + use \VuFind\Search\Options\ViewOptionsTrait; + /** * Maximum number of topic recommendations to show (false for none) * @@ -98,9 +100,6 @@ class Options extends \VuFind\Search\Base\Options } // Load search preferences: - if (isset($searchSettings->General->default_view)) { - $this->defaultView = $searchSettings->General->default_view; - } if (isset($searchSettings->General->retain_filters_by_default)) { $this->retainFiltersByDefault = $searchSettings->General->retain_filters_by_default; @@ -148,16 +147,9 @@ class Options extends \VuFind\Search\Base\Options = $searchSettings->General->empty_search_relevance_override; } - // Load view preferences (or defaults if none in .ini file): - if (isset($searchSettings->Views)) { - foreach ($searchSettings->Views as $key => $value) { - $this->viewOptions[$key] = $value; - } - } elseif (isset($searchSettings->General->default_view)) { - $this->viewOptions = [$this->defaultView => $this->defaultView]; - } else { - $this->viewOptions = ['list' => 'List']; - } + // Set up views + $this->initViewOptions($searchSettings); + // Load list view for result (controls AJAX embedding vs. linking) if (isset($searchSettings->List->view)) { $this->listviewOption = $searchSettings->List->view; diff --git a/module/VuFind/src/VuFind/Search/Summon/Params.php b/module/VuFind/src/VuFind/Search/Summon/Params.php index dd4ec33a6f79df864c513b7bafe3c414f296267f..2b4acec0e3fea75588b00cdf26557085563eff80 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Params.php +++ b/module/VuFind/src/VuFind/Search/Summon/Params.php @@ -2,7 +2,7 @@ /** * Summon Search Parameters * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,9 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\Summon; -use SerialsSolutions_Summon_Query as SummonQuery, - VuFind\Solr\Utils as SolrUtils, - VuFindSearch\ParamBag; + +use SerialsSolutions_Summon_Query as SummonQuery; +use VuFind\Solr\Utils as SolrUtils; +use VuFindSearch\ParamBag; /** * Summon Search Parameters @@ -41,6 +42,8 @@ use SerialsSolutions_Summon_Query as SummonQuery, */ class Params extends \VuFind\Search\Base\Params { + use \VuFind\Search\Params\FacetLimitTrait; + /** * Settings for all the facets * @@ -55,6 +58,19 @@ class Params extends \VuFind\Search\Base\Params */ protected $dateFacetSettings = []; + /** + * Constructor + * + * @param \VuFind\Search\Base\Options $options Options to use + * @param \VuFind\Config\PluginManager $configLoader Config loader + */ + public function __construct($options, \VuFind\Config\PluginManager $configLoader) + { + parent::__construct($options, $configLoader); + $config = $configLoader->get($options->getFacetsIni()); + $this->initFacetLimitsFromConfig($config->Facet_Settings ?? null); + } + /** * Add a field to facet on. * @@ -212,24 +228,16 @@ class Params extends \VuFind\Search\Base\Params */ protected function getBackendFacetParameters() { - $config = $this->configLoader->get('Summon'); - $defaultFacetLimit = isset($config->Facet_Settings->facet_limit) - ? $config->Facet_Settings->facet_limit : 30; - $fieldSpecificLimits = isset($config->Facet_Settings->facet_limit_by_field) - ? $config->Facet_Settings->facet_limit_by_field : null; - $finalFacets = []; foreach ($this->getFullFacetSettings() as $facet) { // See if parameters are included as part of the facet name; // if not, override them with defaults. $parts = explode(',', $facet); $facetName = $parts[0]; - $bestDefaultFacetLimit = isset($fieldSpecificLimits->$facetName) - ? $fieldSpecificLimits->$facetName : $defaultFacetLimit; $defaultMode = ($this->getFacetOperator($facet) == 'OR') ? 'or' : 'and'; - $facetMode = isset($parts[1]) ? $parts[1] : $defaultMode; - $facetPage = isset($parts[2]) ? $parts[2] : 1; - $facetLimit = isset($parts[3]) ? $parts[3] : $bestDefaultFacetLimit; + $facetMode = $parts[1] ?? $defaultMode; + $facetPage = $parts[2] ?? 1; + $facetLimit = $parts[3] ?? $this->getFacetLimitForField($facetName); $facetParams = "{$facetMode},{$facetPage},{$facetLimit}"; $finalFacets[] = "{$facetName},{$facetParams}"; } @@ -260,34 +268,33 @@ class Params extends \VuFind\Search\Base\Params $params->set( 'holdings', strtolower(trim($safeValue)) == 'true' ); - } else if ($filt['field'] == 'queryExpansion') { + } elseif ($filt['field'] == 'queryExpansion') { // Special case -- "query expansion" is a separate parameter // from other facets. $params->set( 'expand', strtolower(trim($safeValue)) == 'true' ); - } else if ($filt['field'] == 'openAccessFilter') { + } elseif ($filt['field'] == 'openAccessFilter') { // Special case -- "open access filter" is a separate // parameter from other facets. $params->set( 'openAccessFilter', strtolower(trim($safeValue)) == 'true' ); - } else if ($filt['field'] == 'excludeNewspapers') { + } elseif ($filt['field'] == 'excludeNewspapers') { // Special case -- support a checkbox for excluding // newspapers: $params ->add('filters', "ContentType,Newspaper Article,true"); - } else if ($range = SolrUtils::parseRange($filt['value'])) { + } elseif ($range = SolrUtils::parseRange($filt['value'])) { // Special case -- range query (translate [x TO y] syntax): $from = SummonQuery::escapeParam($range['from']); $to = SummonQuery::escapeParam($range['to']); $params ->add('rangeFilters', "{$filt['field']},{$from}:{$to}"); - } else if ($filt['operator'] == 'OR') { + } elseif ($filt['operator'] == 'OR') { // Special case -- OR facets: - $orFacets[$filt['field']] = isset($orFacets[$filt['field']]) - ? $orFacets[$filt['field']] : []; + $orFacets[$filt['field']] = $orFacets[$filt['field']] ?? []; $orFacets[$filt['field']][] = $safeValue; } else { // Standard case: @@ -330,7 +337,7 @@ class Params extends \VuFind\Search\Base\Params if (preg_match('/^\[(.*) TO (.*)\]$/', $value, $matches)) { // Simple case: [X TO Y] $filter['displayText'] = $matches[1] . '-' . $matches[2]; - } else if (preg_match($caseInsensitiveRegex, $value, $matches)) { + } elseif (preg_match($caseInsensitiveRegex, $value, $matches)) { // Case insensitive case: [x TO y] OR [X TO Y]; convert // only if values in both ranges match up! if (strtolower($matches[3]) == strtolower($matches[1]) @@ -343,6 +350,67 @@ class Params extends \VuFind\Search\Base\Params return $filter; } + /** + * Initialize facet settings for the specified configuration sections. + * + * @param string $facetList Config section containing fields to activate + * @param string $facetSettings Config section containing related settings + * @param string $cfgFile Name of configuration to load (null to load + * default facets configuration). + * + * @return bool True if facets set, false if no settings found + */ + protected function initFacetList($facetList, $facetSettings, $cfgFile = null) + { + $config = $this->configLoader + ->get($cfgFile ?? $this->getOptions()->getFacetsIni()); + // Special case -- when most settings are in Results_Settings, the limits + // can be found in Facet_Settings. + $limitSection = ($facetSettings === 'Results_Settings') + ? 'Facet_Settings' : $facetSettings; + $this->initFacetLimitsFromConfig($config->$limitSection ?? null); + return parent::initFacetList($facetList, $facetSettings, $cfgFile); + } + + /** + * Initialize facet settings for the advanced search screen. + * + * @return void + */ + public function initAdvancedFacets() + { + // If no configuration was found, set up defaults instead: + if (!$this->initFacetList('Advanced_Facets', 'Advanced_Facet_Settings')) { + $defaults = ['Language' => 'Language', 'ContentType' => 'Format']; + foreach ($defaults as $key => $value) { + $this->addFacet($key, $value); + } + } + } + + /** + * Initialize facet settings for the home page. + * + * @return void + */ + public function initHomePageFacets() + { + // Load Advanced settings if HomePage settings are missing (legacy support): + if (!$this->initFacetList('HomePage_Facets', 'HomePage_Facet_Settings')) { + $this->initAdvancedFacets(); + } + } + + /** + * Initialize facet settings for the standard search screen. + * + * @return void + */ + public function initBasicFacets() + { + $this->initFacetList('Facets', 'Results_Settings'); + } + /** * Load all available facet settings. This is mainly useful for showing * appropriate labels when an existing search has multiple filters associated @@ -356,8 +424,17 @@ class Params extends \VuFind\Search\Base\Params */ public function activateAllFacets($preferredSection = false) { - $this->initFacetList('Facets', 'Results_Settings', 'Summon'); - $this->initFacetList('Advanced_Facets', 'Advanced_Facet_Settings', 'Summon'); - $this->initCheckboxFacets('CheckboxFacets', 'Summon'); + // Based on preference, change the order of initialization to make sure + // that preferred facet labels come in last. + if ($preferredSection == 'Advanced') { + $this->initHomePageFacets(); + $this->initBasicFacets(); + $this->initAdvancedFacets(); + } else { + $this->initHomePageFacets(); + $this->initAdvancedFacets(); + $this->initBasicFacets(); + } + $this->initCheckboxFacets(); } } diff --git a/module/VuFind/src/VuFind/Search/Summon/Results.php b/module/VuFind/src/VuFind/Search/Summon/Results.php index 2fdaecd7ca20c787ba125294162b2f9381b5b732..78f1fea14348ca11610d2aa6a6860405d03adf1d 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Results.php +++ b/module/VuFind/src/VuFind/Search/Summon/Results.php @@ -2,7 +2,7 @@ /** * Summon Search Results * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -131,7 +131,7 @@ class Results extends \VuFind\Search\Base\Results } // If there is no filter, we'll use all facets as the filter: - $filter = is_null($filter) + $filter = null === $filter ? $this->getParams()->getFacetConfig() : $this->stripFilterParameters($filter); @@ -229,8 +229,7 @@ class Results extends \VuFind\Search\Base\Results // present in the filter list? Second, is the current value // an active filter for the current field? $orField = '~' . $field; - $itemsToCheck = isset($filterList[$field]) - ? $filterList[$field] : []; + $itemsToCheck = $filterList[$field] ?? []; if (isset($filterList[$orField])) { $itemsToCheck += $filterList[$orField]; } diff --git a/module/VuFind/src/VuFind/Search/Tags/Options.php b/module/VuFind/src/VuFind/Search/Tags/Options.php index 665321e7a3130349de2e56626ba8c1827fc817fe..05dcca6376703a1ff32821660d9de8eb3be60360 100644 --- a/module/VuFind/src/VuFind/Search/Tags/Options.php +++ b/module/VuFind/src/VuFind/Search/Tags/Options.php @@ -2,7 +2,7 @@ /** * Tags aspect of the Search Multi-class (Options) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -54,7 +54,7 @@ class Options extends \VuFind\Search\Base\Options public function __construct(\VuFind\Config\PluginManager $configLoader) { parent::__construct($configLoader); - $config = $configLoader->get('config'); + $config = $configLoader->get($this->mainIni); if (isset($config->Social->show_solr_options_in_tag_search) && $config->Social->show_solr_options_in_tag_search ) { diff --git a/module/VuFind/src/VuFind/Search/Tags/Params.php b/module/VuFind/src/VuFind/Search/Tags/Params.php index cdbf1908095a5a8c31bb9f34702673f65b226d61..2932b867c6a053f4ee04ef998bc71dc9dfc41388 100644 --- a/module/VuFind/src/VuFind/Search/Tags/Params.php +++ b/module/VuFind/src/VuFind/Search/Tags/Params.php @@ -2,7 +2,7 @@ /** * Tags aspect of the Search Multi-class (Params) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Search/Tags/Results.php b/module/VuFind/src/VuFind/Search/Tags/Results.php index d511e718a18021e25977a8da1e7116e3a79538c5..b654ad803efb76b9438c253749f1e7744eded3d4 100644 --- a/module/VuFind/src/VuFind/Search/Tags/Results.php +++ b/module/VuFind/src/VuFind/Search/Tags/Results.php @@ -2,7 +2,7 @@ /** * Tags aspect of the Search Multi-class (Results) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search\Tags; + use VuFind\Db\Table\Tags as TagsTable; use VuFind\Record\Loader; use VuFind\Search\Base\Results as BaseResults; diff --git a/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..513966b94b2153953ae89d88e2efca41d96b76b9 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for Tags search results objects. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_Tags + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Search\Tags; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Tags search results objects. + * + * @category VuFind + * @package Search_Tags + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResultsFactory extends \VuFind\Search\Results\ResultsFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $tm = $container->get('VuFind\Db\Table\PluginManager'); + return parent::__invoke($container, $requestedName, [$tm->get('Tags')]); + } +} diff --git a/module/VuFind/src/VuFind/Search/UrlQueryHelper.php b/module/VuFind/src/VuFind/Search/UrlQueryHelper.php index 03b5cf7d40e4f0d2d532334264eec508f859f50c..61e2e0500169afe1cfb3ccd7a8d2602845e7be6f 100644 --- a/module/VuFind/src/VuFind/Search/UrlQueryHelper.php +++ b/module/VuFind/src/VuFind/Search/UrlQueryHelper.php @@ -2,7 +2,7 @@ /** * Class to help build URLs and forms in the view based on search settings. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\Search; + use VuFind\Search\Base\Options; use VuFindSearch\Query\AbstractQuery; use VuFindSearch\Query\Query; @@ -148,7 +149,7 @@ class UrlQueryHelper } } } - } else if ($this->queryObject instanceof Query) { + } elseif ($this->queryObject instanceof Query) { $search = $this->queryObject->getString(); if (!empty($search)) { $this->urlParams[$this->getBasicSearchParam()] = $search; @@ -243,7 +244,7 @@ class UrlQueryHelper */ public function replaceTerm($from, $to) { - $query = clone($this->queryObject); + $query = clone $this->queryObject; $query->replaceTerm($from, $to); return new static($this->urlParams, $query, $this->config); } @@ -370,7 +371,7 @@ class UrlQueryHelper // Account for operators: if ($operator == 'NOT') { $field = '-' . $field; - } else if ($operator == 'OR') { + } elseif ($operator == 'OR') { $field = '~' . $field; } @@ -452,7 +453,7 @@ class UrlQueryHelper */ public function setHandler($handler) { - $query = clone($this->queryObject); + $query = clone $this->queryObject; // We can only set the handler on basic queries: if ($query instanceof Query) { $query->setHandler($handler); @@ -551,7 +552,7 @@ class UrlQueryHelper */ protected function filtered($field, $value, $filter) { - return (isset($filter[$field]) && preg_match($filter[$field], $value)); + return isset($filter[$field]) && preg_match($filter[$field], $value); } /** diff --git a/module/VuFind/src/VuFind/Search/WorldCat/Options.php b/module/VuFind/src/VuFind/Search/WorldCat/Options.php index f16ccd0fb614c30bca4a9f6e95084f7b5b15b511..7e2773c819728cbdc0a41ae32ff65a91a53146aa 100644 --- a/module/VuFind/src/VuFind/Search/WorldCat/Options.php +++ b/module/VuFind/src/VuFind/Search/WorldCat/Options.php @@ -2,7 +2,7 @@ /** * WorldCat Search Options * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/WorldCat/Params.php b/module/VuFind/src/VuFind/Search/WorldCat/Params.php index 4725e954f61d1a90e3958f7a1fd5ff541dab615c..ae0482e8462568674ddcb24fb80a52f18771adf6 100644 --- a/module/VuFind/src/VuFind/Search/WorldCat/Params.php +++ b/module/VuFind/src/VuFind/Search/WorldCat/Params.php @@ -2,7 +2,7 @@ /** * WorldCat Search Parameters * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\Search\WorldCat; + use VuFindSearch\ParamBag; /** diff --git a/module/VuFind/src/VuFind/Search/WorldCat/Results.php b/module/VuFind/src/VuFind/Search/WorldCat/Results.php index c7a54f1b36f12000b8158a56af52225528ee5260..8b98ffa18027aa693f4228c769691ccb8bce5d60 100644 --- a/module/VuFind/src/VuFind/Search/WorldCat/Results.php +++ b/module/VuFind/src/VuFind/Search/WorldCat/Results.php @@ -2,7 +2,7 @@ /** * WorldCat Search Results * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/Search/minSO.php b/module/VuFind/src/VuFind/Search/minSO.php index aff83f8c8169f8910eb439c6e2784a4065af8b37..ee1dcdf6297261e5907ac3c1486a5b0532ef7799 100644 --- a/module/VuFind/src/VuFind/Search/minSO.php +++ b/module/VuFind/src/VuFind/Search/minSO.php @@ -2,7 +2,7 @@ /** * VuFind Minified Search Object * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Service/DateConverterFactory.php b/module/VuFind/src/VuFind/Service/DateConverterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2df544f7bad0ec3eda1f9c658e596766a7bc2d61 --- /dev/null +++ b/module/VuFind/src/VuFind/Service/DateConverterFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for \VuFind\Date\Converter + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Service; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for \VuFind\Date\Converter + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DateConverterFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + // Pass along key [Site] settings: displayDateFormat, displayTimeFormat, + // timezone + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $settings = isset($config->Site) ? $config->Site->toArray() : []; + return new $requestedName($settings); + } +} diff --git a/module/VuFind/src/VuFind/Service/Factory.php b/module/VuFind/src/VuFind/Service/Factory.php index ca233a419fd24faaa6aeff31b7ba538f0543dabb..ec700667adabe2a503740653cb09770d30629c56 100644 --- a/module/VuFind/src/VuFind/Service/Factory.php +++ b/module/VuFind/src/VuFind/Service/Factory.php @@ -2,7 +2,7 @@ /** * Factory for various top-level VuFind services. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Service; + use Zend\ServiceManager\ServiceManager; /** @@ -41,230 +42,6 @@ use Zend\ServiceManager\ServiceManager; */ class Factory { - /** - * Construct the Account Capabilities helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Config\AccountCapabilities - */ - public static function getAccountCapabilities(ServiceManager $sm) - { - return new \VuFind\Config\AccountCapabilities( - $sm->get('VuFind\Config')->get('config'), - $sm->get('VuFind\AuthManager') - ); - } - - /** - * Construct the Auth Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Auth\PluginManager - */ - public static function getAuthPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Auth'); - } - - /** - * Construct the Autocomplete Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Autocomplete\PluginManager - */ - public static function getAutocompletePluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Autocomplete'); - } - - /** - * Construct the cache manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Cache\Manager - */ - public static function getCacheManager(ServiceManager $sm) - { - return new \VuFind\Cache\Manager( - $sm->get('VuFind\Config')->get('config'), - $sm->get('VuFind\Config')->get('searches') - ); - } - - /** - * Construct the cart. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Cart - */ - public static function getCart(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config')->get('config'); - $active = isset($config->Site->showBookBag) - ? (bool)$config->Site->showBookBag : false; - $size = isset($config->Site->bookBagMaxSize) - ? $config->Site->bookBagMaxSize : 100; - $activeInSearch = isset($config->Site->bookbagTogglesInSearch) - ? $config->Site->bookbagTogglesInSearch : true; - return new \VuFind\Cart( - $sm->get('VuFind\RecordLoader'), $sm->get('VuFind\CookieManager'), - $size, $active, $activeInSearch - ); - } - - /** - * Construct the Channel Provider Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\ChannelProvider\PluginManager - */ - public static function getChannelProviderPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'ChannelProvider'); - } - - /** - * Construct the config manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Config\PluginManager - */ - public static function getConfig(ServiceManager $sm) - { - $config = $sm->get('Config'); - return new \VuFind\Config\PluginManager( - $sm, $config['vufind']['config_reader'] - ); - } - - /** - * Construct the Content Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Content\PluginManager - */ - public static function getContentPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Content'); - } - - /** - * Construct the Content\AuthorNotes Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Content\AuthorNotes\PluginManager - */ - public static function getContentAuthorNotesPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Content\AuthorNotes'); - } - - /** - * Construct the Content\Covers Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Content\Covers\PluginManager - */ - public static function getContentCoversPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Content\Covers'); - } - - /** - * Construct the Content\Excerpts Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Content\Excerpts\PluginManager - */ - public static function getContentExcerptsPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Content\Excerpts'); - } - - /** - * Construct the Content\Reviews Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Content\Reviews\PluginManager - */ - public static function getContentReviewsPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Content\Reviews'); - } - - /** - * Construct the cookie manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Cookie\CookieManager - */ - public static function getCookieManager(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config')->get('config'); - $path = '/'; - if (isset($config->Cookies->limit_by_path) - && $config->Cookies->limit_by_path - ) { - $path = $sm->get('Request')->getBasePath(); - if (empty($path)) { - $path = '/'; - } - } - $secure = isset($config->Cookies->only_secure) - ? $config->Cookies->only_secure - : false; - $domain = isset($config->Cookies->domain) - ? $config->Cookies->domain - : null; - $session_name = isset($config->Cookies->session_name) - ? $config->Cookies->session_name - : null; - return new \VuFind\Cookie\CookieManager( - $_COOKIE, $path, $domain, $secure, $session_name - ); - } - - /** - * Construct the cover router. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Cover\Router - */ - public static function getCoverRouter(ServiceManager $sm) - { - $base = $sm->get('ControllerPluginManager')->get('url') - ->fromRoute('cover-show'); - return new \VuFind\Cover\Router($base); - } - - /** - * Construct the date converter. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Date\Converter - */ - public static function getDateConverter(ServiceManager $sm) - { - return new \VuFind\Date\Converter( - $sm->get('VuFind\Config')->get('config') - ); - } - /** * Construct the date converter. * @@ -274,128 +51,7 @@ class Factory */ public static function getDbAdapter(ServiceManager $sm) { - return $sm->get('VuFind\DbAdapterFactory')->getAdapter(); - } - - /** - * Construct the date converter. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Db\AdapterFactory - */ - public static function getDbAdapterFactory(ServiceManager $sm) - { - return new \VuFind\Db\AdapterFactory( - $sm->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the Db\Row Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Db\Row\PluginManager - */ - public static function getDbRowPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Db\Row'); - } - - /** - * Construct the Db\Table Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Db\Table\PluginManager - */ - public static function getDbTablePluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Db\Table'); - } - - /** - * Construct the export helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Export - */ - public static function getExport(ServiceManager $sm) - { - return new \VuFind\Export( - $sm->get('VuFind\Config')->get('config'), - $sm->get('VuFind\Config')->get('export') - ); - } - - /** - * Generic plugin manager factory (support method). - * - * @param ServiceManager $sm Service manager. - * @param string $ns VuFind namespace containing plugin manager - * - * @return object - */ - public static function getGenericPluginManager(ServiceManager $sm, $ns) - { - $className = 'VuFind\\' . $ns . '\PluginManager'; - $configKey = strtolower(str_replace('\\', '_', $ns)); - $config = $sm->get('Config'); - return new $className( - $sm, $config['vufind']['plugin_managers'][$configKey] - ); - } - - /** - * Construct the Hierarchy\Driver Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Hierarchy\Driver\PluginManager - */ - public static function getHierarchyDriverPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Hierarchy\Driver'); - } - - /** - * Construct the Hierarchy\TreeDataFormatter Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Hierarchy\TreeDataFormatter\PluginManager - */ - public static function getHierarchyTreeDataFormatterPluginManager( - ServiceManager $sm - ) { - return static::getGenericPluginManager($sm, 'Hierarchy\TreeDataFormatter'); - } - - /** - * Construct the Hierarchy\TreeDataSource Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Hierarchy\TreeDataSource\PluginManager - */ - public static function getHierarchyTreeDataSourcePluginManager( - ServiceManager $sm - ) { - return static::getGenericPluginManager($sm, 'Hierarchy\TreeDataSource'); - } - - /** - * Construct the Hierarchy\TreeRenderer Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Hierarchy\TreeRenderer\PluginManager - */ - public static function getHierarchyTreeRendererPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Hierarchy\TreeRenderer'); + return $sm->get('VuFind\Db\AdapterFactory')->getAdapter(); } /** @@ -407,7 +63,7 @@ class Factory */ public static function getHttp(ServiceManager $sm) { - $config = $sm->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $options = []; if (isset($config->Proxy->host)) { $options['proxy_host'] = $config->Proxy->host; @@ -423,93 +79,6 @@ class Factory return new \VuFindHttp\HttpService($options, $defaults); } - /** - * Construct the HMAC service. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Crypt\HMAC - */ - public static function getHMAC(ServiceManager $sm) - { - return new \VuFind\Crypt\HMAC( - $sm->get('VuFind\Config')->get('config')->Security->HMACkey - ); - } - - /** - * Construct the ILS connection. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\ILS\Connection - */ - public static function getILSConnection(ServiceManager $sm) - { - $catalog = new \VuFind\ILS\Connection( - $sm->get('VuFind\Config')->get('config')->Catalog, - $sm->get('VuFind\ILSDriverPluginManager'), - $sm->get('VuFind\Config') - ); - return $catalog->setHoldConfig($sm->get('VuFind\ILSHoldSettings')); - } - - /** - * Construct the ILS\Driver Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\ILS\Driver\PluginManager - */ - public static function getILSDriverPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'ILS\Driver'); - } - - /** - * Construct the ILS hold logic. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\ILS\Logic\Holds - */ - public static function getILSHoldLogic(ServiceManager $sm) - { - return new \VuFind\ILS\Logic\Holds( - $sm->get('VuFind\ILSAuthenticator'), $sm->get('VuFind\ILSConnection'), - $sm->get('VuFind\HMAC'), $sm->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the ILS hold settings helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\ILS\HoldSettings - */ - public static function getILSHoldSettings(ServiceManager $sm) - { - return new \VuFind\ILS\HoldSettings( - $sm->get('VuFind\Config')->get('config')->Catalog - ); - } - - /** - * Construct the ILS title hold logic. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\ILS\Logic\TitleHolds - */ - public static function getILSTitleHoldLogic(ServiceManager $sm) - { - return new \VuFind\ILS\Logic\TitleHolds( - $sm->get('VuFind\ILSAuthenticator'), $sm->get('VuFind\ILSConnection'), - $sm->get('VuFind\HMAC'), $sm->get('VuFind\Config')->get('config') - ); - } - /** * Construct the ProxyManager configuration. * @@ -520,7 +89,7 @@ class Factory public static function getProxyConfig(ServiceManager $sm) { $config = new \ProxyManager\Configuration(); - $cacheManager = $sm->get('VuFind\CacheManager'); + $cacheManager = $sm->get('VuFind\Cache\Manager'); $dir = $cacheManager->getCacheDir() . 'objects'; $config->setProxiesTargetDir($dir); if (APPLICATION_ENV != 'development') { @@ -529,263 +98,6 @@ class Factory return $config; } - /** - * Construct the recaptcha helper - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Record\Loader - */ - public static function getRecaptcha(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config')->get('config'); - $siteKey = isset($config->Captcha->siteKey) - ? $config->Captcha->siteKey - : (isset($config->Captcha->publicKey) - ? $config->Captcha->publicKey - : ''); - $secretKey = isset($config->Captcha->secretKey) - ? $config->Captcha->secretKey - : (isset($config->Captcha->privateKey) - ? $config->Captcha->privateKey - : ''); - $httpClient = $sm->get('VuFind\Http')->createClient(); - $translator = $sm->get('VuFind\Translator'); - $options = ['lang' => $translator->getLocale()]; - if (isset($config->Captcha->theme)) { - $options['theme'] = $config->Captcha->theme; - } - $recaptcha = new \VuFind\Service\ReCaptcha( - $siteKey, $secretKey, ['ssl' => true], $options, null, $httpClient - ); - - return $recaptcha; - } - - /** - * Construct the Recommend Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Recommend\PluginManager - */ - public static function getRecommendPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Recommend'); - } - - /** - * Construct the record cache. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Record\Cache - */ - public static function getRecordCache(ServiceManager $sm) - { - return new \VuFind\Record\Cache( - $sm->get('VuFind\RecordDriverPluginManager'), - $sm->get('VuFind\Config')->get('RecordCache'), - $sm->get('VuFind\DbTablePluginManager')->get('Record') - ); - } - - /** - * Construct the PermissionDeniedManager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Role\PermissionDeniedManager - */ - public static function getPermissionDeniedManager(ServiceManager $sm) - { - return new \VuFind\Role\PermissionDeniedManager( - $sm->get('VuFind\Config')->get('permissionBehavior') - ); - } - - /** - * Construct the PermissionManager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Role\PermissionManager - */ - public static function getPermissionManager(ServiceManager $sm) - { - $permManager = new \VuFind\Role\PermissionManager( - $sm->get('VuFind\Config')->get('permissions')->toArray() - ); - $permManager->setAuthorizationService( - $sm->get('ZfcRbac\Service\AuthorizationService') - ); - return $permManager; - } - - /** - * Construct the RecordDriver Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\RecordDriver\PluginManager - */ - public static function getRecordDriverPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'RecordDriver'); - } - - /** - * Construct the record loader. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Record\Loader - */ - public static function getRecordLoader(ServiceManager $sm) - { - return new \VuFind\Record\Loader( - $sm->get('VuFind\Search'), - $sm->get('VuFind\RecordDriverPluginManager'), - $sm->get('VuFind\RecordCache') - ); - } - - /** - * Construct the record router. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Record\Router - */ - public static function getRecordRouter(ServiceManager $sm) - { - return new \VuFind\Record\Router( - $sm->get('VuFind\RecordLoader'), - $sm->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the RecordTab Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\RecordTab\PluginManager - */ - public static function getRecordTabPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'RecordTab'); - } - - /** - * Construct the Related Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Related\PluginManager - */ - public static function getRelatedPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Related'); - } - - /** - * Construct the Resolver\Driver Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Resolver\Driver\PluginManager - */ - public static function getResolverDriverPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Resolver\Driver'); - } - - /** - * Construct the search backend manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\BackendManager - */ - public static function getSearchBackendManager(ServiceManager $sm) - { - $config = $sm->get('config'); - $smConfig = new \Zend\ServiceManager\Config( - $config['vufind']['plugin_managers']['search_backend'] - ); - $registry = $sm->createScopedServiceManager(); - $smConfig->configureServiceManager($registry); - $manager = new \VuFind\Search\BackendManager($registry); - - return $manager; - } - - /** - * Construct the search memory helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Memory - */ - public static function getSearchMemory(ServiceManager $sm) - { - return new \VuFind\Search\Memory( - new \Zend\Session\Container('Search', $sm->get('VuFind\SessionManager')) - ); - } - - /** - * Construct the Search\Options Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Options\PluginManager - */ - public static function getSearchOptionsPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Search\Options'); - } - - /** - * Construct the Search\Params Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Params\PluginManager - */ - public static function getSearchParamsPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Search\Params'); - } - - /** - * Construct the Search\Results Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\Results\PluginManager - */ - public static function getSearchResultsPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Search\Results'); - } - - /** - * Construct the Search runner. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\SearchRunner - */ - public static function getSearchRunner(ServiceManager $sm) - { - return new \VuFind\Search\SearchRunner( - $sm->get('VuFind\SearchResultsPluginManager'), - new \Zend\EventManager\EventManager($sm->get('SharedEventManager')) - ); - } - /** * Construct the search service. * @@ -800,99 +112,20 @@ class Factory ); } - /** - * Construct the search specs reader. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Config\SearchSpecsReader - */ - public static function getSearchSpecsReader(ServiceManager $sm) - { - return new \VuFind\Config\SearchSpecsReader( - $sm->get('VuFind\CacheManager') - ); - } - - /** - * Construct the SearchTabs helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Search\SearchTabsHelper - */ - public static function getSearchTabsHelper(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config')->get('config'); - $tabConfig = isset($config->SearchTabs) - ? $config->SearchTabs->toArray() : []; - $filterConfig = isset($config->SearchTabsFilters) - ? $config->SearchTabsFilters->toArray() : []; - $permissionConfig = isset($config->SearchTabsPermissions) - ? $config->SearchTabsPermissions->toArray() : []; - return new \VuFind\Search\SearchTabsHelper( - $sm->get('VuFind\SearchResultsPluginManager'), - $tabConfig, $filterConfig, - $sm->get('Application')->getRequest(), $permissionConfig - ); - } - - /** - * Construct the Session Plugin Manager. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Session\PluginManager - */ - public static function getSessionPluginManager(ServiceManager $sm) - { - return static::getGenericPluginManager($sm, 'Session'); - } - - /** - * Construct the Solr writer. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Solr\Writer - */ - public static function getSolrWriter(ServiceManager $sm) - { - return new \VuFind\Solr\Writer( - $sm->get('VuFind\Search\BackendManager'), - $sm->get('VuFind\DbTablePluginManager')->get('changetracker') - ); - } - - /** - * Construct the tag helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Tags - */ - public static function getTags(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config')->get('config'); - $maxLength = isset($config->Social->max_tag_length) - ? $config->Social->max_tag_length : 64; - return new \VuFind\Tags($maxLength); - } - /** * Construct the translator. * * @param ServiceManager $sm Service manager. * - * @return \Zend\I18n\Translator\TranslatorInterface + * @return \Zend\Mvc\I18n\Translator */ public static function getTranslator(ServiceManager $sm) { - $factory = new \Zend\Mvc\Service\TranslatorServiceFactory(); + $factory = new \Zend\Mvc\I18n\TranslatorFactory(); $translator = $factory->createService($sm); // Set up the ExtendedIni plugin: - $config = $sm->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $pathStack = [ APPLICATION_PATH . '/languages', LOCAL_OVERRIDE_DIR . '/languages' @@ -902,14 +135,14 @@ class Factory : [$config->Site->language, 'en']; try { $pm = $translator->getPluginManager(); - } catch (\Zend\Mvc\Exception\BadMethodCallException $ex) { + } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $ex) { // If getPluginManager is missing, this means that the user has // disabled translation in module.config.php or PHP's intl extension // is missing. We can do no further configuration of the object. return $translator; } $pm->setService( - 'extendedini', + 'ExtendedIni', new \VuFind\I18n\Translator\Loader\ExtendedIni( $pathStack, $fallbackLocales ) @@ -918,12 +151,12 @@ class Factory // Set up language caching for better performance: try { $translator->setCache( - $sm->get('VuFind\CacheManager')->getCache('language') + $sm->get('VuFind\Cache\Manager')->getCache('language') ); } catch (\Exception $e) { // Don't let a cache failure kill the whole application, but make // note of it: - $logger = $sm->get('VuFind\Logger'); + $logger = $sm->get('VuFind\Log\Logger'); $logger->debug( 'Problem loading cache: ' . get_class($e) . ' exception: ' . $e->getMessage() @@ -932,36 +165,4 @@ class Factory return $translator; } - - /** - * Construct the WorldCat helper. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Connection\WorldCatUtils - */ - public static function getWorldCatUtils(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config')->get('config'); - $client = $sm->get('VuFind\Http')->createClient(); - $ip = $sm->get('Request')->getServer()->get('SERVER_ADDR'); - return new \VuFind\Connection\WorldCatUtils( - isset($config->WorldCat) ? $config->WorldCat : null, - $client, true, $ip - ); - } - - /** - * Construct the YAML reader. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFind\Config\YamlReader - */ - public static function getYamlReader(ServiceManager $sm) - { - return new \VuFind\Config\YamlReader( - $sm->get('VuFind\CacheManager') - ); - } } diff --git a/module/VuFind/src/VuFind/Service/ReCaptcha.php b/module/VuFind/src/VuFind/Service/ReCaptcha.php index fc2a3c2ae81c5028cd25702838e97ab0d84af4b2..b4126a6b7cfb856ea198ebc82701387a4becf5e9 100644 --- a/module/VuFind/src/VuFind/Service/ReCaptcha.php +++ b/module/VuFind/src/VuFind/Service/ReCaptcha.php @@ -2,7 +2,7 @@ /** * Recaptcha service * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php b/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3719d3e7c7492e072d436d36405be442b4f2f7ab --- /dev/null +++ b/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php @@ -0,0 +1,85 @@ +<?php +/** + * ReCaptcha factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Service; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ReCaptcha factory. + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ReCaptchaFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $siteKey = isset($config->Captcha->siteKey) + ? $config->Captcha->siteKey + : (isset($config->Captcha->publicKey) + ? $config->Captcha->publicKey + : ''); + $secretKey = isset($config->Captcha->secretKey) + ? $config->Captcha->secretKey + : (isset($config->Captcha->privateKey) + ? $config->Captcha->privateKey + : ''); + $httpClient = $container->get('VuFindHttp\HttpService')->createClient(); + $translator = $container->get('Zend\Mvc\I18n\Translator'); + $rcOptions = ['lang' => $translator->getLocale()]; + if (isset($config->Captcha->theme)) { + $rcOptions['theme'] = $config->Captcha->theme; + } + return new $requestedName( + $siteKey, $secretKey, ['ssl' => true], $rcOptions, null, $httpClient + ); + } +} diff --git a/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php b/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ecba0637e88df1663e3ba37586c59e8ba6e242f0 --- /dev/null +++ b/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php @@ -0,0 +1,64 @@ +<?php +/** + * Generic factory to constructor-inject the config.ini settings. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Service; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generic factory to constructor-inject the config.ini settings. + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ServiceWithConfigIniFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config, ...($options ?: [])); + } +} diff --git a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php index f4cc0a7b5363709ab86c5c4697d5bc1b3c738623..66004153b1803c8f89087a31c0cec2797a34d893 100644 --- a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php +++ b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php @@ -2,7 +2,7 @@ /** * VuFind Abstract Plugin Factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ServiceManager; -use Zend\ServiceManager\AbstractFactoryInterface, - Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\AbstractFactoryInterface; /** * VuFind Abstract Plugin Factory @@ -57,12 +58,11 @@ abstract class AbstractPluginFactory implements AbstractFactoryInterface /** * Get the name of a class for a given plugin name. * - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service + * @param string $requestedName Name of service * * @return string Fully qualified class name */ - protected function getClassName($name, $requestedName) + protected function getClassName($requestedName) { // If we have a FQCN that refers to an existing class, return it as-is: if (strpos($requestedName, '\\') !== false && class_exists($requestedName)) { @@ -72,8 +72,8 @@ abstract class AbstractPluginFactory implements AbstractFactoryInterface $finalName = $this->defaultNamespace . '\\' . $requestedName . $this->classSuffix; if (!class_exists($finalName)) { - $finalName = $this->defaultNamespace . '\\' . ucwords(strtolower($name)) - . $this->classSuffix; + $finalName = $this->defaultNamespace . '\\' + . ucwords(strtolower($requestedName)) . $this->classSuffix; } return $finalName; } @@ -81,36 +81,33 @@ abstract class AbstractPluginFactory implements AbstractFactoryInterface /** * Can we create a service for the specified name? * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service * * @return bool * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName - ) { - $className = $this->getClassName($name, $requestedName); - return class_exists($className); + public function canCreate(ContainerInterface $container, $requestedName) + { + return class_exists($this->getClassName($requestedName)); } /** * Create a service for the specified name. * - * @param ServiceLocatorInterface $serviceLocator Service locator - * @param string $name Name of service - * @param string $requestedName Unfiltered name of service + * @param ContainerInterface $container Service container + * @param string $requestedName Name of service + * @param array $options Options (unused) * * @return object * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function createServiceWithName(ServiceLocatorInterface $serviceLocator, - $name, $requestedName + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null ) { - $class = $this->getClassName($name, $requestedName); + $class = $this->getClassName($requestedName); return new $class(); } } diff --git a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php index 4c555e8237ffdedad478abf6096bb42a47c64324..fb6c94ef2eab7f9d5c7428462677d5dde1288a37 100644 --- a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php +++ b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php @@ -2,7 +2,7 @@ /** * VuFind Plugin Manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ServiceManager; -use Zend\ServiceManager\AbstractPluginManager as Base, - Zend\ServiceManager\ConfigInterface, - Zend\ServiceManager\DelegatorFactoryInterface, - Zend\ServiceManager\Exception\RuntimeException as ServiceManagerRuntimeException; + +use Zend\ServiceManager\AbstractPluginManager as Base; +use Zend\ServiceManager\Exception\InvalidServiceException; /** * VuFind Plugin Manager @@ -44,6 +43,8 @@ use Zend\ServiceManager\AbstractPluginManager as Base, */ abstract class AbstractPluginManager extends Base { + use LowerCaseServiceNameTrait; + /** * Constructor * @@ -58,7 +59,7 @@ abstract class AbstractPluginManager extends Base ) { parent::__construct($configOrContainerInstance, $v3config); $this->addInitializer( - ['VuFind\ServiceManager\Initializer', 'initPlugin'], false + 'VuFind\ServiceManager\ServiceInitializer', false ); } @@ -70,18 +71,14 @@ abstract class AbstractPluginManager extends Base * * @param mixed $plugin Plugin to validate * - * @throws ServiceManagerRuntimeException if invalid + * @throws InvalidServiceException if invalid * @return void */ - public function validatePlugin($plugin) + public function validate($plugin) { - if ($plugin instanceof DelegatorFactoryInterface) { - return; - } - $expectedInterface = $this->getExpectedInterface(); if (!$plugin instanceof $expectedInterface) { - throw new ServiceManagerRuntimeException( + throw new InvalidServiceException( 'Plugin ' . get_class($plugin) . ' does not belong to ' . $expectedInterface ); diff --git a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManagerFactory.php b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..de8271911d08037f4965e1bef0ab68b12101ddee --- /dev/null +++ b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManagerFactory.php @@ -0,0 +1,90 @@ +<?php +/** + * VuFind Plugin Manager factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ServiceManager + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ServiceManager; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * VuFind Plugin Manager factory. + * + * @category VuFind + * @package ServiceManager + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AbstractPluginManagerFactory implements FactoryInterface +{ + /** + * Determine the configuration key for the specified class name. + * + * @param string $requestedName Service being created + * + * @return string + */ + public function getConfigKey($requestedName) + { + // Extract namespace of plugin manager (chop off leading top-level + // namespace -- e.g. VuFind -- and trailing PluginManager class). + $regex = '/^[^\\\\]+\\\\(.*)\\\\PluginManager$/'; + preg_match($regex, $requestedName, $matches); + return strtolower(str_replace('\\', '_', $matches[1])); + } + + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $configKey = $this->getConfigKey($requestedName); + if (empty($configKey)) { + $error = 'Problem determining config key for ' . $requestedName; + throw new \Exception($error); + } + $config = $container->get('Config'); + return new $requestedName( + $container, $config['vufind']['plugin_managers'][$configKey] + ); + } +} diff --git a/module/VuFind/src/VuFind/ServiceManager/LowerCaseServiceNameTrait.php b/module/VuFind/src/VuFind/ServiceManager/LowerCaseServiceNameTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..6bd42475c7e73356305e5d584c100381562d96d1 --- /dev/null +++ b/module/VuFind/src/VuFind/ServiceManager/LowerCaseServiceNameTrait.php @@ -0,0 +1,88 @@ +<?php +/** + * Trait for plugin managers that allows service names to be normalized to lowercase + * (for backward compatibility with ServiceManager v2). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ServiceManager + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ServiceManager; + +/** + * Trait for plugin managers that allows service names to be normalized to lowercase + * (for backward compatibility with ServiceManager v2). + * + * @category VuFind + * @package ServiceManager + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +trait LowerCaseServiceNameTrait +{ + /** + * Retrieve a plugin + * + * @param string $name Name of plugin + * @param null|array $options Options to use when creating the instance. + * + * @return mixed + */ + public function get($name, array $options = null) + { + return parent::get($this->getNormalizedServiceName($name), $options); + } + + /** + * Returns true if the container can return an entry for the given identifier. + * Returns false otherwise. + * + * @param string $id Identifier of the entry to look for. + * + * @return bool + */ + public function has($id) + { + return parent::has($this->getNormalizedServiceName($id)); + } + + /** + * Hack for backward compatibility with services defined under + * ServiceManager v2, when service names were case-insensitive. + * TODO: set up aliases and/or normalize code to eliminate the need for this. + * + * @param string $name Service name + * + * @return string + */ + protected function getNormalizedServiceName($name) + { + if ($name != ($lower = strtolower($name)) + && (isset($this->services[$lower]) || isset($this->factories[$lower]) + || isset($this->aliases[$lower])) + ) { + return $lower; + } + return $name; + } +} diff --git a/module/VuFind/src/VuFind/ServiceManager/Initializer.php b/module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php similarity index 58% rename from module/VuFind/src/VuFind/ServiceManager/Initializer.php rename to module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php index 834b6983551c658ec25a950362e392ee3ef63011..f383c4515acf300f34ca34169ecde0d9b516160d 100644 --- a/module/VuFind/src/VuFind/ServiceManager/Initializer.php +++ b/module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php @@ -2,7 +2,7 @@ /** * VuFind Service Initializer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\ServiceManager; -use Zend\ServiceManager\ServiceManager; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Initializer\InitializerInterface; /** * VuFind Service Initializer @@ -37,22 +39,23 @@ use Zend\ServiceManager\ServiceManager; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Initializer +class ServiceInitializer implements InitializerInterface { /** * Check if the record cache is enabled within a service manager. * - * @param ServiceManager $sm Service manager + * @param ContainerInterface $sm Service manager * * @return bool */ - protected static function isCacheEnabled(ServiceManager $sm) + protected function isCacheEnabled(ContainerInterface $sm) { // Use static cache to save time on repeated lookups: static $enabled = null; if (null === $enabled) { // Return true if Record Cache is enabled for any data source - $cacheConfig = $sm->get('VuFind\Config')->get('RecordCache'); + $cacheConfig = $sm->get('VuFind\Config\PluginManager') + ->get('RecordCache'); $enabled = false; foreach ($cacheConfig as $section) { foreach ($section as $setting) { @@ -71,66 +74,30 @@ class Initializer /** * Given an instance and a Service Manager, initialize the instance. * - * @param object $instance Instance to initialize - * @param ServiceManager $sm Service manager + * @param ContainerInterface $sm Service manager + * @param object $instance Instance to initialize * * @return object */ - public static function initInstance($instance, ServiceManager $sm) + public function __invoke(ContainerInterface $sm, $instance) { if ($instance instanceof \VuFind\Db\Table\DbTableAwareInterface) { - $instance->setDbTableManager($sm->get('VuFind\DbTablePluginManager')); + $instance->setDbTableManager($sm->get('VuFind\Db\Table\PluginManager')); } if ($instance instanceof \Zend\Log\LoggerAwareInterface) { - $instance->setLogger($sm->get('VuFind\Logger')); + $instance->setLogger($sm->get('VuFind\Log\Logger')); } if ($instance instanceof \VuFind\I18n\Translator\TranslatorAwareInterface) { - $instance->setTranslator($sm->get('VuFind\Translator')); + $instance->setTranslator($sm->get('Zend\Mvc\I18n\Translator')); } if ($instance instanceof \VuFindHttp\HttpServiceAwareInterface) { - $instance->setHttpService($sm->get('VuFind\Http')); + $instance->setHttpService($sm->get('VuFindHttp\HttpService')); } // Only inject cache if configuration enabled (to save resources): if ($instance instanceof \VuFind\Record\Cache\RecordCacheAwareInterface - && static::isCacheEnabled($sm) + && $this->isCacheEnabled($sm) ) { - $instance->setRecordCache($sm->get('VuFind\RecordCache')); - } - return $instance; - } - - /** - * Given a Zend Framework Plugin Manager, initialize the instance. - * - * @param object $instance Instance to - * initialize - * @param \Zend\ServiceManager\AbstractPluginManager $manager Plugin manager - * - * @return object - */ - public static function initZendPlugin($instance, - \Zend\ServiceManager\AbstractPluginManager $manager - ) { - $sm = $manager->getServiceLocator(); - if (null !== $sm) { - static::initInstance($instance, $sm); - } - return $instance; - } - - /** - * Given an instance and a Plugin Manager, initialize the instance. - * - * @param object $instance Instance to initialize - * @param AbstractPluginManager $manager Plugin manager - * - * @return object - */ - public static function initPlugin($instance, AbstractPluginManager $manager) - { - static::initZendPlugin($instance, $manager); - if (method_exists($instance, 'setPluginManager')) { - $instance->setPluginManager($manager); + $instance->setRecordCache($sm->get('VuFind\Record\Cache')); } return $instance; } diff --git a/module/VuFind/src/VuFind/Session/AbstractBase.php b/module/VuFind/src/VuFind/Session/AbstractBase.php index 748a7491287911c701302297e953b6f075ccfd16..1e5c24847ecab5efdb1e007822e94385f6d00c45 100644 --- a/module/VuFind/src/VuFind/Session/AbstractBase.php +++ b/module/VuFind/src/VuFind/Session/AbstractBase.php @@ -2,9 +2,10 @@ /** * Base class for session handling * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2010. + * Copyright (C) Villanova University 2010, + * Leipzig University Library <info@ub.uni-leipzig.de> 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -22,11 +23,13 @@ * @category VuFind * @package Session_Handlers * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki */ namespace VuFind\Session; -use Zend\Session\SaveHandler\SaveHandlerInterface; + +use Zend\Config\Config; /** * Base class for session handling @@ -34,11 +37,11 @@ use Zend\Session\SaveHandler\SaveHandlerInterface; * @category VuFind * @package Session_Handlers * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki */ -abstract class AbstractBase implements SaveHandlerInterface, - \VuFind\Db\Table\DbTableAwareInterface +abstract class AbstractBase implements HandlerInterface { use \VuFind\Db\Table\DbTableAwareTrait { getDbTable as getTable; @@ -54,7 +57,7 @@ abstract class AbstractBase implements SaveHandlerInterface, /** * Session configuration settings * - * @var \Zend\Config\Config + * @var Config */ protected $config = null; @@ -89,12 +92,12 @@ abstract class AbstractBase implements SaveHandlerInterface, /** * Set configuration. * - * @param \Zend\Config\Config $config Session configuration ([Session] section of + * @param Config $config Session configuration ([Session] section of * config.ini) * * @return void */ - public function setConfig($config) + public function setConfig(Config $config) { if (isset($config->lifetime)) { $this->lifetime = $config->lifetime; diff --git a/module/VuFind/src/VuFind/Session/Database.php b/module/VuFind/src/VuFind/Session/Database.php index 752f7a251201833d6931d27617c9f1a115ad3a4b..da9b7f3ea1faaea0d633430385449a6fdc93fe0c 100644 --- a/module/VuFind/src/VuFind/Session/Database.php +++ b/module/VuFind/src/VuFind/Session/Database.php @@ -2,7 +2,7 @@ /** * Database session handler * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki */ namespace VuFind\Session; + use VuFind\Exception\SessionExpired as SessionExpiredException; /** diff --git a/module/VuFind/src/VuFind/Session/File.php b/module/VuFind/src/VuFind/Session/File.php index b93936576425cbad5bed561dc4b92a5d5e44fb07..a4773f693a0e6f6994e12e9144a5b971df599627 100644 --- a/module/VuFind/src/VuFind/Session/File.php +++ b/module/VuFind/src/VuFind/Session/File.php @@ -2,7 +2,7 @@ /** * File-based session handler * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -116,7 +116,7 @@ class File extends AbstractBase // Perform file-specific cleanup: $sess_file = $this->getPath() . '/sess_' . $sess_id; if (file_exists($sess_file)) { - return(unlink($sess_file)); + return unlink($sess_file); } return true; } diff --git a/module/VuFind/src/VuFind/Session/HandlerInterface.php b/module/VuFind/src/VuFind/Session/HandlerInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..bd69dc3452a42de90b0e51082341b190e8b52293 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/HandlerInterface.php @@ -0,0 +1,71 @@ +<?php +/** + * Session handler interface + * + * Copyright (C) Villanova University 2018, + * Leipzig University Library <info@ub.uni-leipzig.de> 2018. + * + * PHP version 7 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +namespace VuFind\Session; + +use VuFind\Db\Table\DbTableAwareInterface; +use Zend\Config\Config; +use Zend\Session\SaveHandler\SaveHandlerInterface; + +/** + * Session handler interface + * + * @category VuFind + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +interface HandlerInterface extends SaveHandlerInterface, DbTableAwareInterface +{ + /** + * Enable session writing (default) + * + * @return void + */ + public function enableWrites(); + + /** + * Disable session writing, i.e. make it read-only + * + * @return void + */ + public function disableWrites(); + + /** + * Set configuration. + * + * @param Config $config Session configuration ([Session] section of + * config.ini) + * + * @return void + */ + public function setConfig(Config $config); +} diff --git a/module/VuFind/src/VuFind/Session/ManagerFactory.php b/module/VuFind/src/VuFind/Session/ManagerFactory.php index db2c4de87114998e67075ec46b1ddef8ab4f5a43..302aec1bf00b37e052f1a0188b587db7a9dd2b27 100644 --- a/module/VuFind/src/VuFind/Session/ManagerFactory.php +++ b/module/VuFind/src/VuFind/Session/ManagerFactory.php @@ -2,7 +2,7 @@ /** * Factory for instantiating Session Manager * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Session; -use Zend\ServiceManager\ServiceLocatorInterface; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; use Zend\Session\SessionManager; /** @@ -40,18 +42,18 @@ use Zend\Session\SessionManager; * * @codeCoverageIgnore */ -class ManagerFactory implements \Zend\ServiceManager\FactoryInterface +class ManagerFactory implements FactoryInterface { /** * Build the options array. * - * @param ServiceLocatorInterface $sm Service manager + * @param ContainerInterface $container Service manager * * @return array */ - protected function getOptions(ServiceLocatorInterface $sm) + protected function getOptions(ContainerInterface $container) { - $cookieManager = $sm->get('VuFind\CookieManager'); + $cookieManager = $container->get('VuFind\Cookie\CookieManager'); $options = [ 'cookie_path' => $cookieManager->getPath(), 'cookie_secure' => $cookieManager->isSecure() @@ -74,19 +76,19 @@ class ManagerFactory implements \Zend\ServiceManager\FactoryInterface * Set up the session handler by retrieving all the pieces from the service * manager and injecting appropriate dependencies. * - * @param ServiceLocatorInterface $sm Service manager + * @param ContainerInterface $container Service manager * * @return array */ - protected function getHandler(ServiceLocatorInterface $sm) + protected function getHandler(ContainerInterface $container) { // Load and validate session configuration: - $config = $sm->get('VuFind\Config')->get('config'); + $config = $container->get('VuFind\Config\PluginManager')->get('config'); if (!isset($config->Session->type)) { throw new \Exception('Cannot initialize session; configuration missing'); } - $sessionPluginManager = $sm->get('VuFind\SessionPluginManager'); + $sessionPluginManager = $container->get('VuFind\Session\PluginManager'); $sessionHandler = $sessionPluginManager->get($config->Session->type); $sessionHandler->setConfig($config->Session); return $sessionHandler; @@ -116,21 +118,33 @@ class ManagerFactory implements \Zend\ServiceManager\FactoryInterface } /** - * Create service + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * - * @param ServiceLocatorInterface $sm Service manager + * @return object * - * @return mixed + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs */ - public function createService(ServiceLocatorInterface $sm) - { + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + // Build configuration: $sessionConfig = new \Zend\Session\Config\SessionConfig(); - $sessionConfig->setOptions($this->getOptions($sm)); + $sessionConfig->setOptions($this->getOptions($container)); // Build session manager and attach handler: - $sessionManager = new SessionManager($sessionConfig); - $sessionManager->setSaveHandler($this->getHandler($sm)); + $sessionManager = new $requestedName($sessionConfig); + $sessionManager->setSaveHandler($this->getHandler($container)); // Start up the session: $sessionManager->start(); @@ -155,7 +169,7 @@ class ManagerFactory implements \Zend\ServiceManager\FactoryInterface // Check if we need to immediately stop it based on the settings object // (which may have been informed by a controller that sessions should not // be written as part of the current process): - $settings = $sm->get('VuFind\Session\Settings'); + $settings = $container->get('VuFind\Session\Settings'); if ($settings->setSessionManager($sessionManager)->isWriteDisabled()) { $sessionManager->getSaveHandler()->disableWrites(); } else { diff --git a/module/VuFind/src/VuFind/Session/Memcache.php b/module/VuFind/src/VuFind/Session/Memcache.php index 10d3f12e14ac568678b853a0534e54f8829f36c2..97b81134c6ec61c636063bb760a8ba8ab338a00a 100644 --- a/module/VuFind/src/VuFind/Session/Memcache.php +++ b/module/VuFind/src/VuFind/Session/Memcache.php @@ -5,7 +5,7 @@ * Note: This relies on PHP's Memcache extension * (see http://us.php.net/manual/en/book.memcache.php) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Session/PluginFactory.php b/module/VuFind/src/VuFind/Session/PluginFactory.php index 55b19de2c1e3004bc774f4e3eef6458bf3403ecb..3e8b10ae5770dacf8086af720d406066edb23329 100644 --- a/module/VuFind/src/VuFind/Session/PluginFactory.php +++ b/module/VuFind/src/VuFind/Session/PluginFactory.php @@ -2,7 +2,7 @@ /** * Session handler plugin factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Session/PluginManager.php b/module/VuFind/src/VuFind/Session/PluginManager.php index 65c603da1ca6a3182f1ddca3cfa6c0e843bf3f11..61ad67cc59a4a03326ff02ea0fba4b34ec9daf1b 100644 --- a/module/VuFind/src/VuFind/Session/PluginManager.php +++ b/module/VuFind/src/VuFind/Session/PluginManager.php @@ -2,9 +2,10 @@ /** * Session handler plugin manager * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2010. + * Copyright (C) Villanova University 2010, + * Leipzig University Library <info@ub.uni-leipzig.de> 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -22,6 +23,7 @@ * @category VuFind * @package Session_Handlers * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki */ @@ -33,11 +35,65 @@ namespace VuFind\Session; * @category VuFind * @package Session_Handlers * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki */ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'database' => 'VuFind\Session\Database', + 'file' => 'VuFind\Session\File', + 'memcache' => 'VuFind\Session\Memcache', + // for legacy 1.x compatibility + 'filesession' => 'VuFind\Session\File', + 'memcachesession' => 'VuFind\Session\Memcache', + 'mysqlsession' => 'VuFind\Session\Database', + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + 'VuFind\Session\Database' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Session\File' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\Session\Memcache' => 'Zend\ServiceManager\Factory\InvokableFactory', + ]; + + /** + * Default delegator factories. + * + * @var string[][]|\Zend\ServiceManager\Factory\DelegatorFactoryInterface[][] + */ + protected $delegators = [ + 'VuFind\Session\Database' => ['VuFind\Session\SecureDelegatorFactory'], + 'VuFind\Session\File' => ['VuFind\Session\SecureDelegatorFactory'], + 'VuFind\Session\Memcache' => ['VuFind\Session\SecureDelegatorFactory'], + ]; + + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory('VuFind\Session\PluginFactory'); + parent::__construct($configOrContainerInstance, $v3config); + } + /** * Return the name of the base class or interface that plug-ins must conform * to. @@ -46,6 +102,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'Zend\Session\SaveHandler\SaveHandlerInterface'; + return 'VuFind\Session\HandlerInterface'; } } diff --git a/module/VuFind/src/VuFind/Session/SecureDelegator.php b/module/VuFind/src/VuFind/Session/SecureDelegator.php new file mode 100644 index 0000000000000000000000000000000000000000..8425c42fba18d1863bb3749005ac4755b173266b --- /dev/null +++ b/module/VuFind/src/VuFind/Session/SecureDelegator.php @@ -0,0 +1,146 @@ +<?php +/** + * Secure session delegator + * + * Copyright (C) Villanova University 2018, + * Leipzig University Library <info@ub.uni-leipzig.de> 2018. + * + * PHP version 7 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +namespace VuFind\Session; + +use VuFind\Cookie\CookieManager; +use Zend\Crypt\BlockCipher; +use Zend\Math\Rand; + +/** + * Secure session delegator + * + * @category VuFind + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +class SecureDelegator +{ + /** + * The block cipher for en/decrypting session data. + * + * @var BlockCipher + */ + protected $cipher; + + /** + * VuFind cookie manager service. + * + * @var CookieManager + */ + protected $cookieManager; + + /** + * The wrapped session handler. + * + * @var HandlerInterface + */ + protected $handler; + + /** + * SecureDelegator constructor. + * + * @param CookieManager $cookieManager {@see $cookieHandler} + * @param HandlerInterface $handler {@see $handler} + */ + public function __construct( + CookieManager $cookieManager, HandlerInterface $handler + ) { + $this->handler = $handler; + $this->cookieManager = $cookieManager; + $this->cipher = BlockCipher::factory('openssl'); + } + + /** + * Opens a session. + * + * @param string $save_path Session save path + * @param string $name Session name + * + * @return bool + */ + public function open($save_path, $name) + { + $cookieName = "{$name}_KEY"; + $cipherKey = ($cookieValue = $this->cookieManager->get($cookieName)) + ?? base64_encode(Rand::getBytes(64)); + + if (!$cookieValue) { + $lifetime = session_get_cookie_params()['lifetime']; + $expire = $lifetime ? $lifetime + time() : 0; + $this->cookieManager->set($cookieName, $cipherKey, $expire); + } + + $this->cipher->setKey(base64_decode($cipherKey)); + return $this->handler->open($save_path, $name); + } + + /** + * Read a sessions data. + * + * @param string $session_id Session id + * + * @return bool|string + */ + public function read($session_id) + { + $data = $this->handler->read($session_id); + return $data ? $this->cipher->decrypt($data) : $data; + } + + /** + * Writes session data. + * + * @param string $session_id Session id + * @param string $session_data Session data + * + * @return bool + */ + public function write($session_id, $session_data) + { + $data = $this->cipher->encrypt($session_data); + return $this->handler->write($session_id, $data); + } + + /** + * Pass calls to non-existing methods to the wrapped Handler + * + * @param string $name Name of the method being called + * @param array $arguments Passed Arguments + * + * @return mixed + */ + public function __call($name, $arguments) + { + return $this->handler->{$name}(...$arguments); + } +} diff --git a/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php b/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..edfb35daf94ec5927943619f45f65a24bd655b79 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php @@ -0,0 +1,106 @@ +<?php +/** + * Secure session delegator factory + * + * Copyright (C) Villanova University 2018, + * Leipzig University Library <info@ub.uni-leipzig.de> 2018. + * + * PHP version 7 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +namespace VuFind\Session; + +use Interop\Container\ContainerInterface; +use ProxyManager\Factory\LazyLoadingValueHolderFactory; +use Zend\ServiceManager\Factory\DelegatorFactoryInterface; + +/** + * Secure session delegator factory + * + * @category VuFind + * @package Session_Handlers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Sebastian Kehr <kehr@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:session_handlers Wiki + */ +class SecureDelegatorFactory implements DelegatorFactoryInterface +{ + /** + * Invokes this factory. + * + * @param ContainerInterface $container Service container + * @param string $name Service name + * @param callable $callback Service callback + * @param array|null $options Service options + * + * @return SecureDelegator + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke( + ContainerInterface $container, $name, callable $callback, + array $options = null + ): HandlerInterface { + /** + * The wrapped session handler. + * + * @var HandlerInterface $handler + */ + $handler = call_user_func($callback); + $config = $container->get('VuFind\Config\PluginManager'); + $secure = $config->get('config')->Session->secure ?? false; + return $secure ? $this->delegate($container, $handler) : $handler; + } + + /** + * Creates the delegating session handler + * + * @param ContainerInterface $container Service Container + * @param HandlerInterface $handler Wrapped session handler + * + * @return HandlerInterface + */ + protected function delegate( + ContainerInterface $container, HandlerInterface $handler + ): HandlerInterface { + $cookieManager = $container->get('VuFind\Cookie\CookieManager'); + $config = $container->get('ProxyManager\Configuration'); + $factory = new LazyLoadingValueHolderFactory($config); + $delegator = new SecureDelegator($cookieManager, $handler); + /** + * The handler proxy. + * + * @var HandlerInterface $handler + */ + $handler = $factory->createProxy( + HandlerInterface::class, function ( + &$target, $proxy, $method, array $params, &$init + ) use ($delegator) { + $init = null; + $target = $delegator; + return true; + } + ); + return $handler; + } +} diff --git a/module/VuFind/src/VuFind/Session/Settings.php b/module/VuFind/src/VuFind/Session/Settings.php index 42b67e682617f679164b8aee76c519832328e347..35591025ef50689d89b19299ac18e67226af5c7e 100644 --- a/module/VuFind/src/VuFind/Session/Settings.php +++ b/module/VuFind/src/VuFind/Session/Settings.php @@ -5,7 +5,7 @@ * instantiating the session itself. See \VuFind\Session\ManagerFactory for * details on the use of this object. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -29,6 +29,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Session; + use Zend\Session\SessionManager; /** diff --git a/module/VuFind/src/VuFind/SimpleXML.php b/module/VuFind/src/VuFind/SimpleXML.php index b750fd5e8c3aa27016f472674faf645d09e30dae..20b8912df9056f84e861face6efdb1b27d4429c5 100644 --- a/module/VuFind/src/VuFind/SimpleXML.php +++ b/module/VuFind/src/VuFind/SimpleXML.php @@ -2,7 +2,7 @@ /** * VuFind SimpleXML enhancement functionality * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind; + use SimpleXMLElement; /** diff --git a/module/VuFind/src/VuFind/Sitemap/AbstractFile.php b/module/VuFind/src/VuFind/Sitemap/AbstractFile.php index eac5e5537229f4a03f80f6d895a814e124b8d286..f395ac846d5e709720fc56964ebb6374712b0960 100644 --- a/module/VuFind/src/VuFind/Sitemap/AbstractFile.php +++ b/module/VuFind/src/VuFind/Sitemap/AbstractFile.php @@ -2,7 +2,7 @@ /** * Abstract class for representing XML sitemaps * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Sitemap/Generator.php b/module/VuFind/src/VuFind/Sitemap/Generator.php index 4cfb8b5d4e66435fc4b824551a1d829c92a75bb8..c891288689075b18b417133c41129d3f0472ee81 100644 --- a/module/VuFind/src/VuFind/Sitemap/Generator.php +++ b/module/VuFind/src/VuFind/Sitemap/Generator.php @@ -2,7 +2,7 @@ /** * VuFind Sitemap * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,11 @@ * @link https://vufind.org Main Page */ namespace VuFind\Sitemap; -use VuFindSearch\Backend\Solr\Backend, VuFind\Search\BackendManager, - VuFindSearch\ParamBag, Zend\Config\Config; + +use VuFind\Search\BackendManager; +use VuFindSearch\Backend\Solr\Backend; +use VuFindSearch\ParamBag; +use Zend\Config\Config; /** * Class for generating sitemaps diff --git a/module/VuFind/src/VuFind/Sitemap/Sitemap.php b/module/VuFind/src/VuFind/Sitemap/Sitemap.php index 6585efe13e1e0eb7adfe838593ed30a5e91e31bc..23ca9bccfde7db4cf74b5caf760f222b577f9425 100644 --- a/module/VuFind/src/VuFind/Sitemap/Sitemap.php +++ b/module/VuFind/src/VuFind/Sitemap/Sitemap.php @@ -2,7 +2,7 @@ /** * Class for representing sitemap files * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Sitemap/SitemapIndex.php b/module/VuFind/src/VuFind/Sitemap/SitemapIndex.php index 98a088fe6b2362bb8398f2172c0e08dcd252e370..dca6d8599c733657b98e3acc4795b3a78ffc0b11 100644 --- a/module/VuFind/src/VuFind/Sitemap/SitemapIndex.php +++ b/module/VuFind/src/VuFind/Sitemap/SitemapIndex.php @@ -2,7 +2,7 @@ /** * Class for representing sitemap index files * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/Solr/Utils.php b/module/VuFind/src/VuFind/Solr/Utils.php index 96c04a0ab96ea9196338e26b0c8522dbb928c985..394c4f364e2ff1a00dae9aa04a3f5f0eb98a1889 100644 --- a/module/VuFind/src/VuFind/Solr/Utils.php +++ b/module/VuFind/src/VuFind/Solr/Utils.php @@ -2,7 +2,7 @@ /** * Solr Utility Functions * - * PHP version 5 + * PHP version 7 * * Copyright (C) Andrew Nagy 2009. * @@ -102,7 +102,7 @@ class Utils if ($year) { $date = str_replace('1999', $year, $date); } - } else if ($year) { + } elseif ($year) { // If the best we can do is extract a 4-digit year, that's better // than nothing.... $date = $year; diff --git a/module/VuFind/src/VuFind/Solr/Writer.php b/module/VuFind/src/VuFind/Solr/Writer.php index d6afec54e4b61fb6b8ac6680c7e05607fa36af0a..bdf2fe69b7927488c0f454ae2e9a9d8f88020a5a 100644 --- a/module/VuFind/src/VuFind/Solr/Writer.php +++ b/module/VuFind/src/VuFind/Solr/Writer.php @@ -2,7 +2,7 @@ /** * Solr Writer service * - * PHP version 5 + * PHP version 7 * * Copyright (C) Demian Katz 2013. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\Solr; -use VuFind\Db\Table\ChangeTracker, VuFind\Search\BackendManager; + +use VuFind\Db\Table\ChangeTracker; +use VuFind\Search\BackendManager; use VuFindSearch\Backend\Solr\Connector; use VuFindSearch\Backend\Solr\Document\AbstractDocument; use VuFindSearch\Backend\Solr\Document\CommitDocument; diff --git a/module/VuFind/src/VuFind/Solr/WriterFactory.php b/module/VuFind/src/VuFind/Solr/WriterFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e37493415f531a98518db8b2b6209291687a8e2d --- /dev/null +++ b/module/VuFind/src/VuFind/Solr/WriterFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Solr writer factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Solr; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Solr writer factory. + * + * @category VuFind + * @package Search + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class WriterFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Search\BackendManager'), + $container->get('VuFind\Db\Table\PluginManager')->get('changetracker') + ); + } +} diff --git a/module/VuFind/src/VuFind/Tags.php b/module/VuFind/src/VuFind/Tags.php index d36dd97d4c1c7a5f51a7106cfdfbbb44b7f179c4..c2b6b022177fd8753475142308983033f1ac226e 100644 --- a/module/VuFind/src/VuFind/Tags.php +++ b/module/VuFind/src/VuFind/Tags.php @@ -2,7 +2,7 @@ /** * VuFind tag processing logic * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/TagsFactory.php b/module/VuFind/src/VuFind/TagsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..58e5c5222c46d25fefb79f51bdb2b587d2909002 --- /dev/null +++ b/module/VuFind/src/VuFind/TagsFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Tags factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tags + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Tags factory. + * + * @category VuFind + * @package Tags + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TagsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $maxLength = isset($config->Social->max_tag_length) + ? $config->Social->max_tag_length : 64; + return new $requestedName($maxLength); + } +} diff --git a/module/VuFind/src/VuFind/Validator/Csrf.php b/module/VuFind/src/VuFind/Validator/Csrf.php new file mode 100644 index 0000000000000000000000000000000000000000..21ddfd6c3d567370df374ef18e1355fffbe6ae27 --- /dev/null +++ b/module/VuFind/src/VuFind/Validator/Csrf.php @@ -0,0 +1,70 @@ +<?php +/** + * Extension of Zend\Validator\Csrf with token counting/clearing functions added. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Validator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Validator; + +/** + * Extension of Zend\Validator\Csrf with token counting/clearing functions added. + * + * @category VuFind + * @package Solr + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Csrf extends \Zend\Validator\Csrf +{ + /** + * How many tokens are currently stored in the session? + * + * @return int + */ + public function getTokenCount() + { + return count($this->getSession()->tokenList ?? []); + } + + /** + * Keep only the most recent N tokens. + * + * @param int $limit Number of tokens to keep. + * + * @return void + */ + public function trimTokenList($limit) + { + $session = $this->getSession(); + if ($limit < 1) { + // Reset the array if necessary: + $session->tokenList = []; + } elseif ($limit < $this->getTokenCount()) { + // Trim the array if necessary: + $session->tokenList + = array_slice($session->tokenList, -1 * $limit, null, true); + } + } +} diff --git a/module/VuFind/src/VuFind/Validator/CsrfFactory.php b/module/VuFind/src/VuFind/Validator/CsrfFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e50365789b6d25980adf5e7bfddbdec4aa4c04fa --- /dev/null +++ b/module/VuFind/src/VuFind/Validator/CsrfFactory.php @@ -0,0 +1,78 @@ +<?php +/** + * CSRF Validator factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2014. + * Copyright (C) The National Library of Finland 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Validator + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Validator; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * CSRF Validator factory. + * + * @category VuFind + * @package Validator + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + * + * @codeCoverageIgnore + */ +class CsrfFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $sessionManager = $container->get('Zend\Session\SessionManager'); + return new $requestedName( + [ + 'session' => new \Zend\Session\Container('csrf', $sessionManager), + 'salt' => $config->Security->HMACkey ?? 'VuFindCsrfSalt' + ] + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/AbstractLayoutClass.php b/module/VuFind/src/VuFind/View/Helper/AbstractLayoutClass.php index 33a1115d55eb6e4638b2c95f751c8ca5f8d67897..6c08eeaa94e5c338ebf8dc8b70cadda2630a5c48 100644 --- a/module/VuFind/src/VuFind/View/Helper/AbstractLayoutClass.php +++ b/module/VuFind/src/VuFind/View/Helper/AbstractLayoutClass.php @@ -2,7 +2,7 @@ /** * Helper class for managing high-level (body vs. sidebar) page layout. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php b/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php index 04a70abd3cb78ef8e68768c44a1c1a7c5f28453d..b8eca57de70a06051c0ebcb40c116c20a2548832 100644 --- a/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php +++ b/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php @@ -2,7 +2,7 @@ /** * Helper class for displaying search-related HTML chunks. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Factory.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Factory.php deleted file mode 100644 index 3743de4a0914ffdf200fc1c64c8436cc42c98caf..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Factory.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * Factory for Bootstrap view helpers. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\View\Helper\Bootstrap3; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for Bootstrap view helpers. - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the Flashmessages helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Flashmessages - */ - public static function getFlashmessages(ServiceManager $sm) - { - $messenger = $sm->getServiceLocator()->get('ControllerPluginManager') - ->get('FlashMessenger'); - return new Flashmessages($messenger); - } - - /** - * Construct the LayoutClass helper. - * - * @param ServiceManager $sm Service manager. - * - * @return LayoutClass - */ - public static function getLayoutClass(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $sidebarOnLeft = !isset($config->Site->sidebarOnLeft) - ? false : $config->Site->sidebarOnLeft; - $mirror = !isset($config->Site->mirrorSidebarInRTL) - ? true : $config->Site->mirrorSidebarInRTL; - $offcanvas = !isset($config->Site->offcanvas) - ? false : $config->Site->offcanvas; - // The right-to-left setting is injected into the layout by the Bootstrapper; - // pull it back out here to avoid duplicate effort, then use it to apply - // the mirror setting appropriately. - $layout = $sm->getServiceLocator()->get('viewmanager')->getViewModel(); - if ($layout->rtl && $mirror) { - $sidebarOnLeft = !$sidebarOnLeft; - } - return new LayoutClass($sidebarOnLeft, $offcanvas); - } - - /** - * Construct the Recaptcha helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Recaptcha - */ - public static function getRecaptcha(ServiceManager $sm) - { - return new Recaptcha( - $sm->getServiceLocator()->get('VuFind\Recaptcha'), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } -} diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Flashmessages.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Flashmessages.php index 1a4124993e6fb787b0a25ddfb370ba4dc1c0f07c..fa06b4be6e0aa2d9a4f2ca533b1942ab453e2287 100644 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Flashmessages.php +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Flashmessages.php @@ -2,7 +2,7 @@ /** * Flash message view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Highlight.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Highlight.php index f02155c4dafe45aebe4383b5a9024ad54d4010d7..933f67580c30dbd7fe88ce338d78eef68ad8243f 100644 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Highlight.php +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Highlight.php @@ -2,7 +2,7 @@ /** * Highlight view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClass.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClass.php index 9592853267b21a038a6ffe004e0fa1396bfe4027..7859fd1460a390c6b0bc556f57e3cb020735719f 100644 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClass.php +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClass.php @@ -3,7 +3,7 @@ * Helper class for managing bootstrap theme's high-level (body vs. sidebar) page * layout. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8703255b27111b61cee00d7f7e5ec82f52504eba --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php @@ -0,0 +1,77 @@ +<?php +/** + * LayoutClass helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Bootstrap3; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * LayoutClass helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class LayoutClassFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $sidebarOnLeft = $config->Site->sidebarOnLeft ?? false; + $mirror = $config->Site->mirrorSidebarInRTL ?? true; + $offcanvas = $config->Site->offcanvas ?? false; + // The right-to-left setting is injected into the layout by the Bootstrapper; + // pull it back out here to avoid duplicate effort, then use it to apply + // the mirror setting appropriately. + $layout = $container->get('ViewManager')->getViewModel(); + if ($layout->rtl && $mirror) { + $sidebarOnLeft = !$sidebarOnLeft; + } + return new $requestedName($sidebarOnLeft, $offcanvas); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Recaptcha.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Recaptcha.php index 0bc879aacb956d0a3cbb48c36da91106a0b0c99c..6282856358584e342f2fb6c5ade2a06f3b603202 100644 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Recaptcha.php +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Recaptcha.php @@ -2,7 +2,7 @@ /** * Recaptcha view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Search.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Search.php index 0cd60f9f0b4124ed42bc28ef0273ecf2c7eef0f9..10cb856cce386a9d4edd33a1b58f1fabb649f7e7 100644 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Search.php +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/Search.php @@ -2,7 +2,7 @@ /** * Helper class for displaying search-related HTML chunks. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php b/module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php new file mode 100644 index 0000000000000000000000000000000000000000..0d89815f9fe5571e3a3a8a83322b022247e91a8d --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AbstractClassBasedTemplateRenderer.php @@ -0,0 +1,119 @@ +<?php +/** + * Abstract base class for helpers that render a template based on a class name. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Zend\View\Exception\RuntimeException; +use Zend\View\Helper\AbstractHelper; + +/** + * Authentication view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractClassBasedTemplateRenderer extends AbstractHelper +{ + /** + * Recursively locate and render a template that matches the provided class + * name (or one of its parent classes); throw an exception if no match is + * found. + * + * @param string $template Template path (with %s as class name placeholder) + * @param string $className Name of class to apply to template. + * @param string $topClassName Top-level parent class of $className (or null + * if $className is already the top level; used for recursion only). + * + * @return string + * @throws RuntimeException + */ + protected function resolveClassTemplate($template, $className, + $topClassName = null + ) { + // If the template resolves, we can render it! + $templateWithClass = sprintf($template, $this->getBriefClass($className)); + if ($this->getView()->resolver()->resolve($templateWithClass)) { + return $this->getView()->render($templateWithClass); + } + + // If the template doesn't resolve, let's see if we can inherit a + // template from a parent class: + $parentClass = get_parent_class($className); + if (empty($parentClass)) { + // No more parent classes left to try? Throw an exception! + throw new RuntimeException( + 'Cannot find ' . $templateWithClass . ' template for class: ' + . ($topClassName ?? $className) + ); + } + + // Recurse until we find a template or run out of parents... + return $this->resolveClassTemplate( + $template, $parentClass, $topClassName ?? $className + ); + } + + /** + * Render a template associated with the provided class name, applying to + * specified context variables. + * + * @param string $template Template path (with %s as class name placeholder) + * @param string $className Name of class to apply to template. + * @param array $context Context for rendering template + * + * @return string + */ + protected function renderClassTemplate($template, $className, $context = []) + { + // Set up the needed context in the view: + $contextHelper = $this->getView()->plugin('context'); + $oldContext = $contextHelper($this->getView())->apply($context); + + // Render the template for the current class: + $html = $this->resolveClassTemplate($template, $className); + + // Restore the original context before returning the result: + $contextHelper($this->getView())->restore($oldContext); + return $html; + } + + /** + * Helper to grab the end of the class name + * + * @param string $className Class name to abbreviate + * + * @return string + */ + protected function getBriefClass($className) + { + $classParts = explode('\\', $className); + return array_pop($classParts); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilities.php b/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilities.php index b13a774656779548ce55b91d8c61b264bca66406..734472f91559ee870da5146b55915c24894d1ba8 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilities.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilities.php @@ -2,7 +2,7 @@ /** * AccountCapabilities view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Config\AccountCapabilities as Helper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f3f1bfc0052ef8591abc6574ea8ebe26daf1b882 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * AccountCapabilities helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * AccountCapabilities helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AccountCapabilitiesFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Config\AccountCapabilities') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AddEllipsis.php b/module/VuFind/src/VuFind/View/Helper/Root/AddEllipsis.php index 3a4f4dc432a153820e5e8ad1c06cc8020a034922..0e1772ffe83e76077e3e788a4c1dd262fb4e4fb9 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AddEllipsis.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AddEllipsis.php @@ -2,7 +2,7 @@ /** * "Add ellipsis" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** @@ -69,7 +70,7 @@ class AddEllipsis extends AbstractHelper // otherwise, unless we already have the full string, add ellipses. if (strlen($fullString) < 160) { $title .= substr($fullString, $pos + strlen($dehighlighted)); - } else if ($pos + strlen($dehighlighted) < strlen($fullString)) { + } elseif ($pos + strlen($dehighlighted) < strlen($fullString)) { $title = trim($title) . '...'; } return $title; diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AddThis.php b/module/VuFind/src/VuFind/View/Helper/Root/AddThis.php index fc7c85ad288a055941fba41787109be640ca75de..54d6a9ae723a2d2e06b2d0b2794fa2cc08ebe935 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AddThis.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AddThis.php @@ -2,7 +2,7 @@ /** * AddThis view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ec73c23d32b1aa7e50e49364b14f3be1823d86b6 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * AddThis helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * AddThis helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AddThisFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config->AddThis->key ?? false); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowse.php b/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowse.php index abe8d1ad492d31ef536b5320bf284fa9d4f89da2..e13fdbd6ffa2ac0a485618bc4bdd7f94379534b7 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowse.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowse.php @@ -2,7 +2,7 @@ /** * Authentication view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\Url; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowseFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowseFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b80ad7d70156107ef4fdd1c6d9d1326585f63266 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AlphaBrowseFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * AlphaBrowse helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * AlphaBrowse helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AlphaBrowseFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $helpers = $container->get('ViewHelperManager'); + return new $requestedName($helpers->get('url')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Auth.php b/module/VuFind/src/VuFind/View/Helper/Root/Auth.php index 3dba8a89222950a47db73a34aa92aa2c517b8da5..137385bd357ae7523b767948cc38dab3555ff365 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Auth.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Auth.php @@ -2,7 +2,7 @@ /** * Authentication view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use Zend\View\Exception\RuntimeException; + +use VuFind\Exception\ILS as ILSException; /** * Authentication view helper @@ -37,7 +38,7 @@ use Zend\View\Exception\RuntimeException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Auth extends \Zend\View\Helper\AbstractHelper +class Auth extends AbstractClassBasedTemplateRenderer { /** * Authentication manager @@ -46,14 +47,24 @@ class Auth extends \Zend\View\Helper\AbstractHelper */ protected $manager; + /** + * ILS Authenticator + * + * @var \VuFind\Auth\ILSAuthenticator + */ + protected $ilsAuthenticator; + /** * Constructor * - * @param \VuFind\Auth\Manager $manager Authentication manager + * @param \VuFind\Auth\Manager $manager Authentication manager + * @param \VuFind\Auth\ILSAuthenticator $ilsAuthenticator ILS Authenticator */ - public function __construct(\VuFind\Auth\Manager $manager) - { + public function __construct(\VuFind\Auth\Manager $manager, + \VuFind\Auth\ILSAuthenticator $ilsAuthenticator + ) { $this->manager = $manager; + $this->ilsAuthenticator = $ilsAuthenticator; } /** @@ -68,37 +79,9 @@ class Auth extends \Zend\View\Helper\AbstractHelper { // Get the current auth module's class name $className = $this->getManager()->getAuthClassForTemplateRendering(); - - // Set up the needed context in the view: - $contextHelper = $this->getView()->plugin('context'); + $template = 'Auth/%s/' . $name; $context['topClass'] = $this->getBriefClass($className); - $oldContext = $contextHelper($this->getView())->apply($context); - - // Start a loop in case we need to use a parent class' name to find the - // appropriate template. - $topClassName = $className; // for error message - $resolver = $this->getView()->resolver(); - while (true) { - // Guess the template name for the current class: - $template = 'Auth/' . $this->getBriefClass($className) . '/' . $name; - if ($resolver->resolve($template)) { - // Try to render the template.... - $html = $this->getView()->render($template); - $contextHelper($this->getView())->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find ' . $name . ' template for auth module: ' - . $topClassName - ); - } - } - } + return $this->renderClassTemplate($template, $className, $context); } /** @@ -134,6 +117,20 @@ class Auth extends \Zend\View\Helper\AbstractHelper return $this->renderTemplate('create.phtml', $context); } + /** + * Get ILS patron record for the currently logged-in user. + * + * @return array|bool Patron array if available, false otherwise. + */ + public function getILSPatron() + { + try { + return $this->ilsAuthenticator->storedCatalogLogin(); + } catch (ILSException $e) { + return false; + } + } + /** * Render the login form fields. * @@ -170,19 +167,6 @@ class Auth extends \Zend\View\Helper\AbstractHelper return $this->renderTemplate('logindesc.phtml', $context); } - /** - * Helper to grab the end of the class name - * - * @param string $className Class name to abbreviate - * - * @return string - */ - protected function getBriefClass($className) - { - $classParts = explode('\\', $className); - return array_pop($classParts); - } - /** * Render the new password form template. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e90a0280cd3a053e521abddf193a4de1027f52d2 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Auth helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Auth helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AuthFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Auth\Manager'), + $container->get('VuFind\Auth\ILSAuthenticator') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e90a0280cd3a053e521abddf193a4de1027f52d2 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Auth helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Auth helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AuthFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Auth\Manager'), + $container->get('VuFind\Auth\ILSAuthenticator') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Browse.php b/module/VuFind/src/VuFind/View/Helper/Root/Browse.php index a1b2accb8d2625170f9d3fe901fafdfbdc25511f..01d7d8308599e22f1dcab33f47562831a7f44921 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Browse.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Browse.php @@ -2,7 +2,7 @@ /** * Browse controller view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** @@ -49,9 +50,9 @@ class Browse extends AbstractHelper */ public function getSolrField($action, $backup = null) { - $action = strToLower($action); - $backup = strToLower($backup); - switch($action) { + $action = strtolower($action); + $backup = strtolower($backup); + switch ($action) { case 'dewey': return 'dewey-hundreds'; case 'lcc': diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Cart.php b/module/VuFind/src/VuFind/View/Helper/Root/Cart.php index 28c02aabc9cbb27b62892f419817e8e67e804594..a32b5a4f41a0a25fab05faa9921e3a35346a762c 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Cart.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Cart.php @@ -2,7 +2,7 @@ /** * Cart view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b9b69a13f99aa38860bb4f21115a32b83f41d5d6 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Cart helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Cart helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CartFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Cart')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Citation.php b/module/VuFind/src/VuFind/View/Helper/Root/Citation.php index 14550d90995bc84b3ea3167ac6afd72d0c13951a..dff3e52860b1077d7df7608ec9f4bd1a670a5c71 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Citation.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Citation.php @@ -2,7 +2,7 @@ /** * Citation view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use VuFind\Exception\Date as DateException; + +use VuFind\Date\DateException; /** * Citation view helper @@ -119,9 +120,9 @@ class Citation extends \Zend\View\Helper\AbstractHelper $this->details = [ 'authors' => $this->prepareAuthors($authors), 'title' => trim($title), 'subtitle' => trim($subtitle), - 'pubPlace' => isset($pubPlaces[0]) ? $pubPlaces[0] : null, - 'pubName' => isset($publishers[0]) ? $publishers[0] : null, - 'pubDate' => isset($pubDates[0]) ? $pubDates[0] : null, + 'pubPlace' => $pubPlaces[0] ?? null, + 'pubName' => $publishers[0] ?? null, + 'pubDate' => $pubDates[0] ?? null, 'edition' => empty($edition) ? [] : [$edition], 'journal' => $driver->tryMethod('getContainerTitle') ]; @@ -507,7 +508,7 @@ class Citation extends \Zend\View\Helper\AbstractHelper protected function isPunctuated($string) { $punctuation = ['.', '?', '!']; - return (in_array(substr($string, -1), $punctuation)); + return in_array(substr($string, -1), $punctuation); } /** @@ -637,7 +638,7 @@ class Citation extends \Zend\View\Helper\AbstractHelper $i++; } } - return (empty($authorStr) ? false : $authorStr); + return empty($authorStr) ? false : $authorStr; } /** @@ -719,7 +720,7 @@ class Citation extends \Zend\View\Helper\AbstractHelper } } } - return (empty($authorStr) ? false : $this->stripPunctuation($authorStr)); + return empty($authorStr) ? false : $this->stripPunctuation($authorStr); } /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..18a6a4b9a50ed79c31c5df5555ea7050c6224645 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Citation helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Citation helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CitationFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Date\Converter')); + } +} diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/MobileUrl.php b/module/VuFind/src/VuFind/View/Helper/Root/Config.php similarity index 50% rename from module/VuFindTheme/src/VuFindTheme/View/Helper/MobileUrl.php rename to module/VuFind/src/VuFind/View/Helper/Root/Config.php index 7cb72877a8ab611a134b47facff6a7e4f026ef64..868e7d7dd861282f409e8deba4f666d715461f6e 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/MobileUrl.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Config.php @@ -1,10 +1,10 @@ <?php /** - * Mobile URL view helper + * Config view helper * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2010. + * Copyright (C) Villanova University 2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -25,10 +25,12 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -namespace VuFindTheme\View\Helper; +namespace VuFind\View\Helper\Root; + +use VuFind\Config\PluginManager; /** - * Mobile URL view helper + * Config view helper * * @category VuFind * @package View_Helpers @@ -36,46 +38,44 @@ namespace VuFindTheme\View\Helper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class MobileUrl extends \Zend\View\Helper\AbstractHelper +class Config extends \Zend\View\Helper\AbstractHelper { /** - * Mobile service + * Configuration plugin manager * - * @var \VuFindTheme\Mobile + * @var PluginManager */ - protected $mobile; + protected $configLoader; /** - * Constructor + * Config constructor. * - * @param \VuFindTheme\Mobile $mobile Mobile service + * @param PluginManager $configLoader Configuration loader */ - public function __construct(\VuFindTheme\Mobile $mobile) + public function __construct(PluginManager $configLoader) { - $this->mobile = $mobile; + $this->configLoader = $configLoader; } /** - * Return the mobile version of the current URL if the user is on a mobile device - * and might want to switch over. Return false when not on a mobile device. + * Get the specified configuration. + * + * @param string $config Name of configuration * - * @return string + * @return \Zend\Config\Config */ - public function __invoke() + public function get($config) { - // Do nothing special if we're not on a mobile device or no mobile theme is - // enabled: - if (!$this->mobile->enabled() || !$this->mobile->detect()) { - return false; - } + return $this->configLoader->get($config); + } - $urlHelper = $this->getView()->plugin('serverurl'); - $currentUrl = $urlHelper(true); - $currentUrl = preg_replace( - ['/\&ui=[^&]*/', '/\?ui=[^&]*\&?/'], ['', '?'], $currentUrl - ); - $currentUrl = rtrim($currentUrl, '?'); - $currentUrl .= strstr($currentUrl, '?') ? '&' : '?'; - return $currentUrl .= 'ui=mobile'; + /** + * Is non-Javascript support enabled? + * + * @return bool + */ + public function nonJavascriptSupportEnabled() + { + return $this->get('config')->Site->nonJavascriptSupportEnabled ?? false; } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..cdaa45072584d81ba4df3b18a52e5890545ad088 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Config helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Config helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ConfigFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Config\PluginManager')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ContentBlock.php b/module/VuFind/src/VuFind/View/Helper/Root/ContentBlock.php new file mode 100644 index 0000000000000000000000000000000000000000..86a0661aee11930ca4056811ee5d71ec4fb7d96b --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ContentBlock.php @@ -0,0 +1,56 @@ +<?php +/** + * ContentBlock view helper + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +/** + * ContentBlock view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ContentBlock extends AbstractClassBasedTemplateRenderer +{ + /** + * Render the output of a ContentBlock plugin. + * + * @param \VuFind\ContentBlock\ContentBlockInterface $block The ContentBlock + * object to render + * + * @return string + */ + public function __invoke($block) + { + $template = 'ContentBlock/%s.phtml'; + $className = get_class($block); + $context = $block->getContext(); + return $this->renderClassTemplate($template, $className, $context); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ContentLoader.php b/module/VuFind/src/VuFind/View/Helper/Root/ContentLoader.php index b8a70240b0db3d601f5b554303a48e2acf846e7a..068ef66d5d5eb60cf90bc653c2c8d6959661918e 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ContentLoader.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ContentLoader.php @@ -2,7 +2,7 @@ /** * Content loader view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fc4a74f885452432918100ae5cbdff82a96c9935 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * ContentLoader helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ContentLoader helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ContentLoaderFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + // Use the requested service name to look up the appropriate content loader: + $parts = explode('\\', $requestedName); + $type = strtolower(array_pop($parts)); + $loader = $container->get('VuFind\Content\PluginManager')->get($type); + return new ContentLoader($loader); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Context.php b/module/VuFind/src/VuFind/View/Helper/Root/Context.php index 8381ef6c324da55c21a458b1ef29437f015fe404..9f9b1add0d364d2db7e889f4c03f74d92d935994 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Context.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Context.php @@ -4,7 +4,7 @@ * performance -- this allows us to set and roll back variables in the global * scope instead of relying on the overhead of building a whole new scope). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,7 +28,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use Zend\View\Helper\AbstractHelper, Zend\View\Renderer\RendererInterface; + +use Zend\View\Helper\AbstractHelper; +use Zend\View\Renderer\RendererInterface; /** * Context manager (useful for using render() instead of partial() for better @@ -57,7 +59,7 @@ class Context extends AbstractHelper $oldVars = []; foreach ($vars as $k => $v) { - $oldVars[$k] = isset($view->$k) ? $view->$k : null; + $oldVars[$k] = $view->$k ?? null; $view->$k = $v; } return $oldVars; @@ -75,7 +77,7 @@ class Context extends AbstractHelper $view = $this->getView(); foreach ($vars as $k => $v) { - if (is_null($v)) { + if (null === $v) { unset($view->$k); } else { $view->$k = $v; @@ -111,7 +113,7 @@ class Context extends AbstractHelper */ public function __invoke(RendererInterface $view = null) { - if (!is_null($view)) { + if (null !== $view) { $this->setView($view); } return $this; diff --git a/module/VuFind/src/VuFind/View/Helper/Root/CurrentPath.php b/module/VuFind/src/VuFind/View/Helper/Root/CurrentPath.php index 3a89af1460281815ef4d88d6bbee5965dace6b40..62a9d3d2303c3274417a462ca01165abc5dae2c0 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/CurrentPath.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/CurrentPath.php @@ -2,7 +2,7 @@ /** * Current path view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DateTime.php b/module/VuFind/src/VuFind/View/Helper/Root/DateTime.php index 7f4ad0557d575deae644661ed9b1ed909ad4e280..3dd5139b9b7b8e4457e41c00106b4d4ee758ee8b 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/DateTime.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/DateTime.php @@ -2,7 +2,7 @@ /** * View helper for formatting dates and times. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -67,7 +67,7 @@ class DateTime extends \Zend\View\Helper\AbstractHelper { try { return $this->converter->convertFromDisplayDate('Y', $date); - } catch (\VuFind\Exception\Date $e) { + } catch (\VuFind\Date\DateException $e) { // bad date? just ignore it! return false; } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f86154c1c95b17cdc94b27fbdbfecf2a3671f69d --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * DateTime helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * DateTime helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DateTimeFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Date\Converter')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOption.php b/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOption.php index 3db515ca754058a20b8a0288bb8634bfe1bdb35b..5a2cfb8074e659fa1e477e1ca8425b8ed05f7584 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOption.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOption.php @@ -2,7 +2,7 @@ /** * DisplayLanguageOption view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\I18n\Translator\TranslatorInterface; /** @@ -59,7 +60,7 @@ class DisplayLanguageOption extends \Zend\View\Helper\AbstractHelper 'ExtendedIni', null, 'default', 'native' ); $this->translator->setLocale('native'); - } catch (\Zend\Mvc\Exception\BadMethodCallException $e) { + } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $e) { if (!extension_loaded('intl')) { throw new \Exception( 'Translation broken due to missing PHP intl extension.' diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c5b0380e94b45fd53fa214c6cf7a9c0f0996fb5f --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * DisplayLanguageOption helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * DisplayLanguageOption helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DisplayLanguageOptionFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + // We want to construct a separate translator instance for this helper, + // since it configures different language/locale than the core shared + // instance! + return new $requestedName( + \VuFind\Service\Factory::getTranslator($container) + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Export.php b/module/VuFind/src/VuFind/View/Helper/Root/Export.php index a6f230d773a16539f1cb7d1f5634cee0504eefe8..95f7828af5152f910c60e4e7a7cc69c309bfb99a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Export.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Export.php @@ -2,7 +2,7 @@ /** * Export view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f61d09e92057af6ef97272f4e9765e67a98d2208 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Export helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Export helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ExportFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Export')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php deleted file mode 100644 index 021e1b7b1f75244358bd0a8f5c05f9f5bdd3c28a..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php +++ /dev/null @@ -1,599 +0,0 @@ -<?php -/** - * Factory for Root view helpers. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\View\Helper\Root; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for Root view helpers. - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the AddThis helper. - * - * @param ServiceManager $sm Service manager. - * - * @return AddThis - */ - public static function getAddThis(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new AddThis( - isset($config->AddThis->key) ? $config->AddThis->key : false - ); - } - - /** - * Construct the AccountCapabilities helper. - * - * @param ServiceManager $sm Service manager. - * - * @return AccountCapabilities - */ - public static function getAccountCapabilities(ServiceManager $sm) - { - return new AccountCapabilities( - $sm->getServiceLocator()->get('VuFind\AccountCapabilities') - ); - } - - /** - * Construct the AlphaBrowse helper. - * - * @param ServiceManager $sm Service manager. - * - * @return AlphaBrowse - */ - public static function getAlphaBrowse(ServiceManager $sm) - { - return new AlphaBrowse($sm->get('url')); - } - - /** - * Construct the Auth helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Auth - */ - public static function getAuth(ServiceManager $sm) - { - return new Auth($sm->getServiceLocator()->get('VuFind\AuthManager')); - } - - /** - * Construct the AuthorNotes helper. - * - * @param ServiceManager $sm Service manager. - * - * @return AuthorNotes - */ - public static function getAuthorNotes(ServiceManager $sm) - { - $loader = $sm->getServiceLocator()->get('VuFind\ContentPluginManager') - ->get('authornotes'); - return new ContentLoader($loader); - } - - /** - * Construct the Cart helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Cart - */ - public static function getCart(ServiceManager $sm) - { - return new Cart($sm->getServiceLocator()->get('VuFind\Cart')); - } - - /** - * Construct the Citation helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Citation - */ - public static function getCitation(ServiceManager $sm) - { - return new Citation($sm->getServiceLocator()->get('VuFind\DateConverter')); - } - - /** - * Construct the DateTime helper. - * - * @param ServiceManager $sm Service manager. - * - * @return DateTime - */ - public static function getDateTime(ServiceManager $sm) - { - return new DateTime($sm->getServiceLocator()->get('VuFind\DateConverter')); - } - - /** - * Construct the DisplayLanguageOption helper. - * - * @param ServiceManager $sm Service manager. - * - * @return DisplayLanguageOption - */ - public static function getDisplayLanguageOption(ServiceManager $sm) - { - // We want to construct a separate translator instance for this helper, - // since it configures different language/locale than the core shared - // instance! - return new DisplayLanguageOption( - \VuFind\Service\Factory::getTranslator($sm->getServiceLocator()) - ); - } - - /** - * Construct the Export helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Export - */ - public static function getExport(ServiceManager $sm) - { - return new Export($sm->getServiceLocator()->get('VuFind\Export')); - } - - /** - * Construct the Feedback helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Feedback - */ - public static function getFeedback(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $enabled = isset($config->Feedback->tab_enabled) - ? $config->Feedback->tab_enabled : false; - return new Feedback($enabled); - } - - /** - * Construct the Flashmessages helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Flashmessages - */ - public static function getFlashmessages(ServiceManager $sm) - { - $messenger = $sm->getServiceLocator()->get('ControllerPluginManager') - ->get('FlashMessenger'); - return new Flashmessages($messenger); - } - - /** - * Construct the GeoCoords helper. - * - * @param ServiceManager $sm Service manager. - * - * @return GeoCoords - */ - public static function getGeoCoords(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('searches'); - $coords = isset($config->MapSelection->default_coordinates) - ? $config->MapSelection->default_coordinates : false; - return new GeoCoords($coords); - } - - /** - * Construct the GoogleAnalytics helper. - * - * @param ServiceManager $sm Service manager. - * - * @return GoogleAnalytics - */ - public static function getGoogleAnalytics(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $key = isset($config->GoogleAnalytics->apiKey) - ? $config->GoogleAnalytics->apiKey : false; - $universal = isset($config->GoogleAnalytics->universal) - ? $config->GoogleAnalytics->universal : false; - return new GoogleAnalytics($key, $universal); - } - - /** - * Construct the Permission helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Permission - */ - public static function getPermission(ServiceManager $sm) - { - $ld = new Permission( - $sm->getServiceLocator()->get('VuFind\Role\PermissionManager'), - $sm->getServiceLocator()->get('VuFind\Role\PermissionDeniedManager') - ); - return $ld; - } - - /** - * Construct the Piwik helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Piwik - */ - public static function getPiwik(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $url = isset($config->Piwik->url) ? $config->Piwik->url : false; - $options = [ - 'siteId' => isset($config->Piwik->site_id) ? $config->Piwik->site_id : 1, - 'searchPrefix' => isset($config->Piwik->searchPrefix) - ? $config->Piwik->searchPrefix : null - ]; - $customVars = isset($config->Piwik->custom_variables) - ? $config->Piwik->custom_variables - : false; - $request = $sm->getServiceLocator()->get('Request'); - $router = $sm->getServiceLocator()->get('Router'); - return new Piwik($url, $options, $customVars, $router, $request); - } - - /** - * Construct the HelpText helper. - * - * @param ServiceManager $sm Service manager. - * - * @return HelpText - */ - public static function getHelpText(ServiceManager $sm) - { - $lang = $sm->getServiceLocator()->has('VuFind\Translator') - ? $sm->getServiceLocator()->get('VuFind\Translator')->getLocale() - : 'en'; - return new HelpText($sm->get('context'), $lang); - } - - /** - * Construct the HistoryLabel helper. - * - * @param ServiceManager $sm Service manager. - * - * @return HistoryLabel - */ - public static function getHistoryLabel(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $config = isset($config->SearchHistoryLabels) - ? $config->SearchHistoryLabels->toArray() : []; - return new HistoryLabel($config, $sm->get('transesc')); - } - - /** - * Construct the Ils helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Ils - */ - public static function getIls(ServiceManager $sm) - { - return new Ils($sm->getServiceLocator()->get('VuFind\ILSConnection')); - } - - /** - * Construct the JsTranslations helper. - * - * @param ServiceManager $sm Service manager. - * - * @return JsTranslations - */ - public static function getJsTranslations(ServiceManager $sm) - { - return new JsTranslations($sm->get('transesc')); - } - - /** - * Construct the KeepAlive helper. - * - * @param ServiceManager $sm Service manager. - * - * @return KeepAlive - */ - public static function getKeepAlive(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new KeepAlive( - isset($config->Session->keepAlive) ? $config->Session->keepAlive : 0 - ); - } - - /** - * Construct the OpenUrl helper. - * - * @param ServiceManager $sm Service manager. - * - * @return OpenUrl - */ - public static function getOpenUrl(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $openUrlRules = json_decode( - file_get_contents( - \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') - ), - true - ); - $resolverPluginManager = $sm->getServiceLocator() - ->get('VuFind\ResolverDriverPluginManager'); - return new OpenUrl( - $sm->get('context'), - $openUrlRules, - $resolverPluginManager, - isset($config->OpenURL) ? $config->OpenURL : null - ); - } - - /** - * Construct the ProxyUrl helper. - * - * @param ServiceManager $sm Service manager. - * - * @return ProxyUrl - */ - public static function getProxyUrl(ServiceManager $sm) - { - return new ProxyUrl( - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the Recaptcha helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Recaptcha - */ - public static function getRecaptcha(ServiceManager $sm) - { - return new Recaptcha( - $sm->getServiceLocator()->get('VuFind\Recaptcha'), - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - } - - /** - * Construct the Record helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Record - */ - public static function getRecord(ServiceManager $sm) - { - $helper = new Record( - $sm->getServiceLocator()->get('VuFind\Config')->get('config') - ); - $helper->setCoverRouter( - $sm->getServiceLocator()->get('VuFind\Cover\Router') - ); - return $helper; - } - - /** - * Construct the RecordLink helper. - * - * @param ServiceManager $sm Service manager. - * - * @return RecordLink - */ - public static function getRecordLink(ServiceManager $sm) - { - return new RecordLink($sm->getServiceLocator()->get('VuFind\RecordRouter')); - } - - /** - * Construct the Related helper. - * - * @param ServiceManager $sm Service manager. - * - * @return Related - */ - public static function getRelated(ServiceManager $sm) - { - return new Related( - $sm->getServiceLocator()->get('VuFind\RelatedPluginManager') - ); - } - - /** - * Construct the SafeMoneyFormat helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SafeMoneyFormat - */ - public static function getSafeMoneyFormat(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - $defaultCurrency = isset($config->Site->defaultCurrency) - ? $config->Site->defaultCurrency : null; - return new SafeMoneyFormat($defaultCurrency); - } - - /** - * Construct the SearchBox helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SearchBox - */ - public static function getSearchBox(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config'); - $mainConfig = $config->get('config'); - $searchboxConfig = $config->get('searchbox')->toArray(); - $includeAlphaOptions - = isset($searchboxConfig['General']['includeAlphaBrowse']) - && $searchboxConfig['General']['includeAlphaBrowse']; - return new SearchBox( - $sm->getServiceLocator()->get('VuFind\SearchOptionsPluginManager'), - $searchboxConfig, - isset($mainConfig->SearchPlaceholder) - ? $mainConfig->SearchPlaceholder->toArray() : [], - $includeAlphaOptions && isset($mainConfig->AlphaBrowse_Types) - ? $mainConfig->AlphaBrowse_Types->toArray() : [] - ); - } - - /** - * Construct the SearchMemory helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SearchMemory - */ - public static function getSearchMemory(ServiceManager $sm) - { - return new SearchMemory( - $sm->getServiceLocator()->get('VuFind\Search\Memory') - ); - } - - /** - * Construct the SearchOptions helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SearchOptions - */ - public static function getSearchOptions(ServiceManager $sm) - { - return new SearchOptions( - $sm->getServiceLocator()->get('VuFind\SearchOptionsPluginManager') - ); - } - - /** - * Construct the SearchParams helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SearchParams - */ - public static function getSearchParams(ServiceManager $sm) - { - return new SearchParams( - $sm->getServiceLocator()->get('VuFind\SearchParamsPluginManager') - ); - } - - /** - * Construct the SearchTabs helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SearchTabs - */ - public static function getSearchTabs(ServiceManager $sm) - { - return new SearchTabs( - $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager'), - $sm->get('url'), $sm->getServiceLocator()->get('VuFind\SearchTabsHelper') - ); - } - - /** - * Construct the SyndeticsPlus helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SyndeticsPlus - */ - public static function getSyndeticsPlus(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new SyndeticsPlus( - isset($config->Syndetics) ? $config->Syndetics : null - ); - } - - /** - * Construct the SystemEmail helper. - * - * @param ServiceManager $sm Service manager. - * - * @return SystemEmail - */ - public static function getSystemEmail(ServiceManager $sm) - { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); - return new SystemEmail( - isset($config->Site->email) ? $config->Site->email : '' - ); - } - - /** - * Construct the UserList helper. - * - * @param ServiceManager $sm Service manager. - * - * @return UserList - */ - public static function getUserList(ServiceManager $sm) - { - $sessionManager = $sm->getServiceLocator()->get('VuFind\SessionManager'); - $session = new \Zend\Session\Container('List', $sessionManager); - $capabilities = $sm->getServiceLocator()->get('VuFind\AccountCapabilities'); - return new UserList($session, $capabilities->getListSetting()); - } - - /** - * Construct the UserTags helper. - * - * @param ServiceManager $sm Service manager. - * - * @return UserTags - */ - public static function getUserTags(ServiceManager $sm) - { - $capabilities = $sm->getServiceLocator()->get('VuFind\AccountCapabilities'); - return new UserTags($capabilities->getTagSetting()); - } -} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Feedback.php b/module/VuFind/src/VuFind/View/Helper/Root/Feedback.php index cbdf45338c668213091e9e6d241f21ba92acd335..0793e41c0d0b9d14b87793ca1e6a5ed149fa87b7 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Feedback.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Feedback.php @@ -3,7 +3,7 @@ * This is a helper that lets the layout know whether or not to include the feedback * tab * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..120bd276a05b86842dad2eb9a6f81d683357421d --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Feedback helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Feedback helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FeedbackFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config->Feedback->tab_enabled ?? false); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Flashmessages.php b/module/VuFind/src/VuFind/View/Helper/Root/Flashmessages.php index 26703238acc071d6f25f4b4cf12ddf74279bfbe4..d955a1255efaa708ee3e772397f2b32d48f554e0 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Flashmessages.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Flashmessages.php @@ -2,7 +2,7 @@ /** * Flash message view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use Zend\View\Helper\AbstractHelper, Zend\Mvc\Controller\Plugin\FlashMessenger; + +use Zend\Mvc\Plugin\FlashMessenger\FlashMessenger; +use Zend\View\Helper\AbstractHelper; /** * Flash message view helper @@ -103,8 +105,8 @@ class Flashmessages extends AbstractHelper } $helper = $helper ? $this->getView()->plugin($helper) : false; - $tokens = isset($msg['tokens']) ? $msg['tokens'] : []; - $default = isset($msg['default']) ? $msg['default'] : null; + $tokens = $msg['tokens'] ?? []; + $default = $msg['default'] ?? null; $html .= $helper ? $helper($msg['msg'], $tokens, $default) : $msg['msg']; } else { diff --git a/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..bcd67fff84f289858b03c64efefc24b73ec4fe5d --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Flashmessages helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Flashmessages helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FlashmessagesFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $messenger = $container->get('ControllerPluginManager') + ->get('Zend\Mvc\Plugin\FlashMessenger\FlashMessenger'); + return new $requestedName($messenger); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php index b5ae5296247fcb2fa69eea67f5d3bece4ff33e0b..0cb6e50c5225770551cb066a77a8446ff3ccc4fe 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php @@ -2,7 +2,7 @@ /** * GeoCoords view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFind\View\Helper\Root; + use VuFind\Search\Base\Options; /** @@ -47,7 +48,7 @@ class GeoCoords extends \Zend\View\Helper\AbstractHelper protected $enabled; /** - * Default coordinates + * Default coordinates * * @var string */ diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5627bdf90dfd092b30b19bc69cc216b4e42caff1 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * GeoCoords helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * GeoCoords helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GeoCoordsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\GeoFeatures\MapSelectionConfig') + ->getMapSelectionOptions(); + return new $requestedName($config['default_coordinates']); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalytics.php b/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalytics.php index 78ae8ccdbce137ccdda2aa95f159aed970c86895..e5e2daed54b841e19107a317633c1a3173d0c34a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalytics.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalytics.php @@ -2,7 +2,7 @@ /** * GoogleAnalytics view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0d63b823b6ad138ec75b0b5094d3af4d8bdc6dc7 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * GoogleAnalytics helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * GoogleAnalytics helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GoogleAnalyticsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $key = $config->GoogleAnalytics->apiKey ?? false; + $universal = $config->GoogleAnalytics->universal ?? false; + return new $requestedName($key, $universal); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/HelpText.php b/module/VuFind/src/VuFind/View/Helper/Root/HelpText.php index e7bdbbbfd716d4154773b34557d6632521249dbb..1642474aeff6b249f19e5a1286658093fe0c8042 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/HelpText.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/HelpText.php @@ -2,7 +2,7 @@ /** * "Load help text" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..218894eec934ee85575121d057214df625a74918 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * HelpText helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * HelpText helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HelpTextFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $lang = $container->has('Zend\Mvc\I18n\Translator') + ? $container->get('Zend\Mvc\I18n\Translator')->getLocale() + : 'en'; + $helpers = $container->get('ViewHelperManager'); + return new $requestedName($helpers->get('context'), $lang); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Highlight.php b/module/VuFind/src/VuFind/View/Helper/Root/Highlight.php index 33a0b088ddd16621a59024391efe529cf44841ef..63d898b75ce4ed7a49d5ef5e5f489a38e054bab0 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Highlight.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Highlight.php @@ -2,7 +2,7 @@ /** * Highlight view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabel.php b/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabel.php index 3d1af23ba24db8c53e23ade9d495077273a79190..b8046973834e8fe786c12ee76300c51d4c98462a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabel.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabel.php @@ -2,7 +2,7 @@ /** * "Search history label" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b41a08cf1d0953301fb0d07a1a38623ee59079ff --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * HistoryLabel helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * HistoryLabel helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HistoryLabelFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $labels = isset($config->SearchHistoryLabels) + ? $config->SearchHistoryLabels->toArray() : []; + $helpers = $container->get('ViewHelperManager'); + return new $requestedName($labels, $helpers->get('transEsc')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Ils.php b/module/VuFind/src/VuFind/View/Helper/Root/Ils.php index eb3d913c26b9041678e2f11039587e1a69105120..540cb40775cea257f5d139cf87b6d8833283209c 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Ils.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Ils.php @@ -2,7 +2,7 @@ /** * ILS (integrated library system) view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b83f818ee9c53de5660fc5ec4ae9907c84655dbd --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Ils helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Ils helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class IlsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\ILS\Connection')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/JqueryValidation.php b/module/VuFind/src/VuFind/View/Helper/Root/JqueryValidation.php deleted file mode 100644 index c80c6c94f5bb8db004216024b5546ac379cf9374..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/View/Helper/Root/JqueryValidation.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php -/** - * View helper for jQuery validation - * - * PHP version 5 - * - * Copyright (C) Villanova University 2010. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package View_Helpers - * @author Tuan Nguyen <tuan@yorku.ca> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Site - * @link http://www.jquery.com jQuery Project Page - */ -namespace VuFind\View\Helper\Root; -use Zend\View\Helper\AbstractHelper; - -/** - * Print a formatted string so jquery metadata and validation plugins can understand. - * - * @category VuFind - * @package View_Helpers - * @author Tuan Nguyen <tuan@yorku.ca> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Site - * @link http://www.jquery.com jQuery Project Page - */ -class JqueryValidation extends AbstractHelper -{ - /** - * Print a formatted string so jquery metadata - * and validation plugins can understand. - * - * @param array $params rules to test jquery against - * - * @return string - */ - public function __invoke($params) - { - // jquery validation rules that this plugin currently supports - $supported_rules = ['required', 'email', 'digits', 'equalTo', - 'phoneUS', 'mobileUK']; - $messages = []; - $rules = []; - foreach ($supported_rules as $rule) { - if (isset($params[$rule])) { - switch($rule) { - case 'equalTo': - $rules[] = "equalTo:'" . $params['equalToField'] . "'"; - $messages[$rule] = $params[$rule]; - break; - default: - $rules[] = "$rule:true"; - $messages[$rule] = $params[$rule]; - break; - } - } - } - - // format the output - $output = '{' . implode(',', $rules) . ',messages:{'; - $first = true; - foreach ($messages as $rule => $message) { - if (!$first) { - $output .= ','; - } - $translator = $this->getView()->plugin('translate'); - $message = addslashes($translator($message)); - $output .= "$rule:'$message'"; - $first = false; - } - $output .= '}}'; - $escaper = $this->getView()->plugin('escapeHtml'); - return $escaper($output); - } -} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/JsTranslations.php b/module/VuFind/src/VuFind/View/Helper/Root/JsTranslations.php index 517ccffbabd1722bccf92873f542b559472d9188..51956abd022eceec5e02fa38f334574103219792 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/JsTranslations.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/JsTranslations.php @@ -2,7 +2,7 @@ /** * JsTranslations helper for passing translation text to Javascript * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** @@ -98,6 +99,10 @@ class JsTranslations extends AbstractHelper $translation = is_array($v) ? call_user_func_array([$this->transEsc, '__invoke'], $v) : $this->transEsc->__invoke($v); + // Special case: do not escape _html translations: + if (substr($k, -5) === '_html') { + $translation = html_entity_decode($translation); + } $parts[] = '"' . addslashes($k) . '": "' . addslashes($translation) . '"'; } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/JsTranslationsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/JsTranslationsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..21a21e565d9c44eaf4580c392aef4cfc120c9f17 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/JsTranslationsFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * JsTranslations helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * JsTranslations helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class JsTranslationsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $helpers = $container->get('ViewHelperManager'); + return new $requestedName($helpers->get('transEsc')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/KeepAlive.php b/module/VuFind/src/VuFind/View/Helper/Root/KeepAlive.php index 2ab79a4be25c9f9b83be3743b908a7341f49288d..59e950e2f5536359681e41517d166d5e40d316b5 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/KeepAlive.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/KeepAlive.php @@ -2,7 +2,7 @@ /** * KeepAlive view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2014. diff --git a/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..603b351ce93b50d04be2e6440ed40295bc33373f --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * KeepAlive helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * KeepAlive helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class KeepAliveFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config->Session->keepAlive ?? 0); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/LocalizedNumber.php b/module/VuFind/src/VuFind/View/Helper/Root/LocalizedNumber.php index 64ab9e31152d8fe0d7c707603af9eaa4e3c4d221..dfaaf03817beec5f28c24a64a9a0c93d2c0c4578 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/LocalizedNumber.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/LocalizedNumber.php @@ -2,7 +2,7 @@ /** * Localization based number formatting * - * PHP version 5 + * PHP version 7 * * Copyright (C) snowflake productions gmbh 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php index 55eaedeed62a03745ede6364f6287b2b51cdfc06..1b3ff016cb21e29bfb3d4c626b1367091724fdee 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php @@ -2,7 +2,7 @@ /** * OpenUrl view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Resolver\Driver\PluginManager; /** @@ -305,7 +306,7 @@ class OpenUrl extends \Zend\View\Helper\AbstractHelper // If we got this far, use the defaults -- true for results, false for // everywhere else. - return ($this->area == 'results'); + return $this->area == 'results'; } /** @@ -425,7 +426,7 @@ class OpenUrl extends \Zend\View\Helper\AbstractHelper } // Did all the rules match? - return ($ruleMatchCounter == count($rules)); + return $ruleMatchCounter == count($rules); } /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fc3ed6bd8754a25b27c9ce905020e827edd3a7d8 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php @@ -0,0 +1,81 @@ +<?php +/** + * OpenUrl helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * OpenUrl helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class OpenUrlFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $openUrlRules = json_decode( + file_get_contents( + \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') + ), + true + ); + $resolverPluginManager + = $container->get('VuFind\Resolver\Driver\PluginManager'); + $helpers = $container->get('ViewHelperManager'); + return new $requestedName( + $helpers->get('context'), + $openUrlRules, + $resolverPluginManager, + $config->OpenURL ?? null + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Permission.php b/module/VuFind/src/VuFind/View/Helper/Root/Permission.php index 816a128f01b313ff5a2a5646ef710d5a685785a6..208e40b15f8372bc1701e79a6c10b4a70e7d6b4b 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Permission.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Permission.php @@ -2,7 +2,7 @@ /** * Permission helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -27,6 +27,7 @@ * @link http://vufind.org/wiki/ Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Role\PermissionDeniedManager; use VuFind\Role\PermissionManager; use Zend\View\Helper\AbstractHelper; @@ -114,7 +115,7 @@ class Permission extends AbstractHelper $displayLogic = $this->permissionDeniedManager ->getDeniedTemplateBehavior($context); - switch (isset($displayLogic['action']) ? $displayLogic['action'] : '') { + switch ($displayLogic['action'] ?? '') { case 'showMessage': return $this->view->transEsc($displayLogic['value']); case 'showTemplate': diff --git a/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8b17ded223004b449e89f88dcab7daa43cb4160a --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Permission helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Permission helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PermissionFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Role\PermissionManager'), + $container->get('VuFind\Role\PermissionDeniedManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php b/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php index ae1f1a899e3eeb4e6224a822e6bd30576c87c61a..7ce3270573a6ec081ede17a9c601a20d0d449afc 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php @@ -2,9 +2,9 @@ /** * Piwik view helper * - * PHP version 5 + * PHP version 7 * - * Copyright (C) The National Library of Finland 2014-2016. + * Copyright (C) The National Library of Finland 2014-2018. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -76,7 +76,7 @@ class Piwik extends \Zend\View\Helper\AbstractHelper /** * Router object * - * @var Zend\Mvc\Router\Http\RouteMatch + * @var Zend\Router\Http\RouteMatch */ protected $router; @@ -111,7 +111,7 @@ class Piwik extends \Zend\View\Helper\AbstractHelper * if a single value, the Piwik site ID -- for backward compatibility) * @param bool $customVars Whether to track * additional information in custom variables - * @param Zend\Mvc\Router\Http\RouteMatch $router Request + * @param Zend\Router\Http\RouteMatch $router Request * @param Zend\Http\PhpEnvironment\Request $request Request */ public function __construct($url, $options, $customVars, $router, $request) @@ -122,8 +122,7 @@ class Piwik extends \Zend\View\Helper\AbstractHelper } if (is_array($options)) { $this->siteId = $options['siteId']; - $this->searchPrefix = isset($options['searchPrefix']) - ? $options['searchPrefix'] : ''; + $this->searchPrefix = $options['searchPrefix'] ?? ''; } else { $this->siteId = $options; } @@ -258,10 +257,11 @@ class Piwik extends \Zend\View\Helper\AbstractHelper protected function getSearchResults() { $viewModel = $this->getView()->plugin('view_model'); - if ('layout/lightbox' === $viewModel->getCurrent()->getTemplate()) { + $current = $viewModel->getCurrent(); + if (null === $current || 'layout/lightbox' === $current->getTemplate()) { return null; } - $children = $viewModel->getCurrent()->getChildren(); + $children = $current->getChildren(); if (isset($children[0])) { $template = $children[0]->getTemplate(); if (!strstr($template, '/home') && !strstr($template, 'facet-list')) { @@ -305,8 +305,16 @@ class Piwik extends \Zend\View\Helper\AbstractHelper */ protected function getRecordDriver() { - $viewModel = $this->getView()->plugin('view_model'); - $children = $viewModel->getCurrent()->getChildren(); + $view = $this->getView(); + $viewModel = $view->plugin('view_model'); + $current = $viewModel->getCurrent(); + if (null === $current) { + $driver = $view->vars('driver'); + if (is_a($driver, 'VuFind\RecordDriver\AbstractBase')) { + return $driver; + } + } + $children = $current->getChildren(); if (isset($children[0])) { $driver = $children[0]->getVariable('driver'); if (is_a($driver, 'VuFind\RecordDriver\AbstractBase')) { diff --git a/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..27fcd3dfd854cb1d2ba717077a8360a71a89c420 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * Piwik helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Piwik helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PiwikFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $url = $config->Piwik->url ?? false; + $settings = [ + 'siteId' => $config->Piwik->site_id ?? 1, + 'searchPrefix' => $config->Piwik->searchPrefix ?? null + ]; + $customVars = $config->Piwik->custom_variables ?? false; + $request = $container->get('Request'); + $router = $container->get('Router'); + return new $requestedName($url, $settings, $customVars, $router, $request); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Printms.php b/module/VuFind/src/VuFind/View/Helper/Root/Printms.php index 9a77195f0369582370e339cfe594ad591f9edb60..d8708293bc4a19b829794f011828932e8e91e53e 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Printms.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Printms.php @@ -2,7 +2,7 @@ /** * Prints a human readable format from a number of milliseconds * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrl.php b/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrl.php index 21bdbeb3c01758226b46ccd788bec9f687c4e83d..009dc386663ba8ee02ca4fdc9309f1e762cc4146 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrl.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrl.php @@ -2,7 +2,7 @@ /** * Proxy URL view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ea007570fe15226a6d945e2f1fc3c55ebd761bba --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * ProxyUrl helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ProxyUrl helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ProxyUrlFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php b/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php index 7a5d583b8bbe62618bc5fc15392f1b3c8be9725f..a6adae1e9a93da68a28f162ab920164ae0811def 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php @@ -2,7 +2,7 @@ /** * Recaptcha view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..411d01e06dbe41dce44d1301978d70fe01df4cc7 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Recaptcha helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Recaptcha helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RecaptchaFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Service\ReCaptcha'), + $container->get('VuFind\Config\PluginManager')->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php b/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php index 8d2d44c85540a4195790e73acf0d07c1553d88d8..e1f7dd3bf33d23a1f52f136b2bb4e791642b8666 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Recommend.php @@ -2,7 +2,7 @@ /** * Recommendation module view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,6 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; /** * Recommendation module view helper @@ -37,7 +36,7 @@ use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Recommend extends AbstractHelper +class Recommend extends AbstractClassBasedTemplateRenderer { /** * Render the output of a recommendation module. @@ -49,38 +48,9 @@ class Recommend extends AbstractHelper */ public function __invoke($recommend) { - // Set up the rendering context: - $contextHelper = $this->getView()->plugin('context'); - $oldContext = $contextHelper($this->getView())->apply( - ['recommend' => $recommend] - ); - - // Get the current recommendation module's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // template. + $template = 'Recommend/%s.phtml'; $className = get_class($recommend); - $resolver = $this->getView()->resolver(); - while (true) { - // Guess the template name for the current class: - $classParts = explode('\\', $className); - $template = 'Recommend/' . array_pop($classParts) . '.phtml'; - if ($resolver->resolve($template)) { - // Try to render the template.... - $html = $this->getView()->render($template); - $contextHelper($this->getView())->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent recommendation class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find template for recommendation class: ' . - get_class($recommend) - ); - } - } - } + $context = ['recommend' => $recommend]; + return $this->renderClassTemplate($template, $className, $context); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Record.php b/module/VuFind/src/VuFind/View/Helper/Root/Record.php index c9bf3514ca17720961ac0fcb1f8b75ad1e2d22b8..d67e241fad9101044ce4767619bce58349855263 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Record.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Record.php @@ -2,7 +2,7 @@ /** * Record driver view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,8 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Cover\Router as CoverRouter; -use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; /** * Record driver view helper @@ -38,7 +38,7 @@ use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Record extends AbstractHelper +class Record extends AbstractClassBasedTemplateRenderer { /** * Context view helper @@ -102,41 +102,11 @@ class Record extends AbstractHelper */ public function renderTemplate($name, $context = null) { - // Set default context if none provided: - if (is_null($context)) { - $context = ['driver' => $this->driver]; - } - - // Set up the needed context in the view: - $oldContext = $this->contextHelper->apply($context); - - // Get the current record driver's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // template. + $template = 'RecordDriver/%s/' . $name; $className = get_class($this->driver); - $resolver = $this->view->resolver(); - while (true) { - // Guess the template name for the current class: - $classParts = explode('\\', $className); - $template = 'RecordDriver/' . array_pop($classParts) . '/' . $name; - if ($resolver->resolve($template)) { - // Try to render the template.... - $html = $this->view->render($template); - $this->contextHelper->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find ' . $name . ' template for record driver: ' . - get_class($this->driver) - ); - } - } - } + return $this->renderClassTemplate( + $template, $className, $context ?? ['driver' => $this->driver] + ); } /** @@ -359,7 +329,8 @@ class Record extends AbstractHelper public function getLink($type, $lookfor) { $link = $this->renderTemplate( - 'link-' . $type . '.phtml', ['lookfor' => $lookfor] + 'link-' . $type . '.phtml', + ['driver' => $this->driver, 'lookfor' => $lookfor] ); $link .= $this->getView()->plugin('searchTabs') ->getCurrentHiddenFilterParams($this->driver->getSourceIdentifier()); @@ -411,16 +382,16 @@ class Record extends AbstractHelper * * @param string $idPrefix Prefix for checkbox HTML ids * @param string $formAttr ID of form for [form] attribute + * @param int $number Result number (for label of checkbox) * * @return string */ - public function getCheckbox($idPrefix = '', $formAttr = false) + public function getCheckbox($idPrefix = '', $formAttr = false, $number = null) { - static $checkboxCount = 0; $id = $this->driver->getSourceIdentifier() . '|' . $this->driver->getUniqueId(); $context - = ['id' => $id, 'count' => $checkboxCount++, 'prefix' => $idPrefix]; + = ['id' => $id, 'number' => $number, 'prefix' => $idPrefix]; if ($formAttr) { $context['formAttr'] = $formAttr; } @@ -560,9 +531,9 @@ class Record extends AbstractHelper return false; } - switch($context) { - case "core" : - case "results" : + switch ($context) { + case "core": + case "results": $key = 'showIn' . ucwords(strtolower($context)); break; default: @@ -646,8 +617,7 @@ class Record extends AbstractHelper // Build URL from route/query details if missing: if (!isset($link['url'])) { - $routeParams = isset($link['routeParams']) - ? $link['routeParams'] : []; + $routeParams = $link['routeParams'] ?? []; $link['url'] = $serverUrlHelper( $urlHelper($link['route'], $routeParams) diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php index 759096c7a62af436f0fcdc65f8d587d82ead8d4c..10bb8e6a7c086c5d25a8bbdc0691b6935edfed68 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php @@ -2,7 +2,7 @@ /** * Record driver data formatting view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -27,6 +27,7 @@ * Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\RecordDriver\AbstractBase as RecordDriver; use Zend\View\Helper\AbstractHelper; @@ -57,14 +58,75 @@ class RecordDataFormatter extends AbstractHelper * * @return int */ - public function specSortCallback($a, $b) + protected function sortCallback($a, $b) { - $posA = isset($a['pos']) ? $a['pos'] : 0; - $posB = isset($b['pos']) ? $b['pos'] : 0; - if ($posA === $posB) { - return 0; + // Sort on 'pos' with 'label' as tie-breaker. + return ($a['pos'] == $b['pos']) + ? $a['label'] <=> $b['label'] + : $a['pos'] <=> $b['pos']; + } + + /** + * Should we allow a value? (Always accepts non-empty values; for empty + * values, allows zero when configured to do so). + * + * @param mixed $value Data to check for zero value. + * @param array $options Rendering options. + * + * @return bool + */ + protected function allowValue($value, $options) + { + if (!empty($value)) { + return true; } - return $posA < $posB ? -1 : 1; + $allowZero = $options['allowZero'] ?? true; + return $allowZero && ($value === 0 || $value === '0'); + } + + /** + * Return rendered text (or null if nothing to render). + * + * @param RecordDriver $driver Record driver object + * @param string $field Field being rendered (i.e. default label) + * @param mixed $data Data to render + * @param array $options Rendering options + * + * @return array + */ + protected function render($driver, $field, $data, $options) + { + // Check whether the data is worth rendering. + if (!$this->allowValue($data, $options)) { + return null; + } + + // Determine the rendering method to use, and bail out if it's illegal: + $method = empty($options['renderType']) + ? 'renderSimple' : 'render' . $options['renderType']; + if (!is_callable([$this, $method])) { + return null; + } + + // If the value evaluates false, we should double-check our zero handling: + $value = $this->$method($driver, $data, $options); + if (!$this->allowValue($value, $options)) { + return null; + } + + // Special case: if we received an array rather than a string, we should + // return it as-is (it probably came from renderMulti()). + if (is_array($value)) { + return $value; + } + + // Allow dynamic label override: + $label = is_callable($options['labelFunction'] ?? null) + ? call_user_func($options['labelFunction'], $data, $driver) + : $field; + $context = $options['context'] ?? []; + $pos = $options['pos'] ?? 0; + return [compact('label', 'value', 'context', 'pos')]; } /** @@ -77,42 +139,18 @@ class RecordDataFormatter extends AbstractHelper */ public function getData(RecordDriver $driver, array $spec) { - $result = []; - - // Sort the spec into order by position: - uasort($spec, [$this, 'specSortCallback']); - // Apply the spec: + $result = []; foreach ($spec as $field => $current) { - // Extract the relevant data from the driver. + // Extract the relevant data from the driver and try to render it. $data = $this->extractData($driver, $current); - $allowZero = isset($current['allowZero']) ? $current['allowZero'] : true; - if (!empty($data) || ($allowZero && ($data === 0 || $data === '0'))) { - // Determine the rendering method to use with the second element - // of the current spec. - $renderMethod = empty($current['renderType']) - ? 'renderSimple' : 'render' . $current['renderType']; - - // Add the rendered data to the return value if it is non-empty: - if (is_callable([$this, $renderMethod])) { - $text = $this->$renderMethod($driver, $data, $current); - if (!$text && (!$allowZero || ($text !== 0 && $text !== '0'))) { - continue; - } - // Allow dynamic label override: - if (isset($current['labelFunction']) - && is_callable($current['labelFunction']) - ) { - $field = call_user_func($current['labelFunction'], $data); - } - $context = isset($current['context']) ? $current['context'] : []; - $result[$field] = [ - 'value' => $text, - 'context' => $context - ]; - } + $value = $this->render($driver, $field, $data, $current); + if ($value !== null) { + $result = array_merge($result, $value); } } + // Sort the result: + usort($result, [$this, 'sortCallback']); return $result; } @@ -173,14 +211,12 @@ class RecordDataFormatter extends AbstractHelper // If $method is a bool, return it as-is; this allows us to force the // rendering (or non-rendering) of particular data independent of the // record driver. - $method = isset($options['dataMethod']) ? $options['dataMethod'] : false; + $method = $options['dataMethod'] ?? false; if ($method === true || $method === false) { return $method; } - $useCache = isset($options['useCache']) && $options['useCache']; - - if ($useCache) { + if ($useCache = ($options['useCache'] ?? false)) { $cacheKey = $driver->getUniqueID() . '|' . $driver->getSourceIdentifier() . '|' . $method; if (isset($cache[$cacheKey])) { @@ -198,6 +234,44 @@ class RecordDataFormatter extends AbstractHelper return $data; } + /** + * Render multiple lines for a single set of data. + * + * @param RecordDriver $driver Reoord driver object. + * @param mixed $data Data to render + * @param array $options Rendering options. + * + * @return array + */ + protected function renderMulti(RecordDriver $driver, $data, + array $options + ) { + // Make sure we have a callback for sorting the $data into groups... + $callback = $options['multiFunction'] ?? null; + if (!is_callable($callback)) { + throw new \Exception('Invalid multiFunction callback.'); + } + + // Adjust the options array so we can use it to call the standard + // render function on the grouped data.... + $defaultOptions = ['renderType' => $options['multiRenderType'] ?? 'Simple'] + + $options; + + // Collect the results: + $results = []; + $input = $callback($data, $options, $driver); + foreach (is_array($input) ? $input : [] as $current) { + $label = $current['label'] ?? ''; + $values = $current['values'] ?? null; + $currentOptions = ($current['options'] ?? []) + $defaultOptions; + $next = $this->render($driver, $label, $values, $currentOptions); + if ($next !== null) { + $results = array_merge($results, $next); + } + } + return $results; + } + /** * Render using the record view helper. * @@ -210,7 +284,7 @@ class RecordDataFormatter extends AbstractHelper protected function renderRecordHelper(RecordDriver $driver, $data, array $options ) { - $method = isset($options['helperMethod']) ? $options['helperMethod'] : null; + $method = $options['helperMethod'] ?? null; $plugin = $this->getView()->plugin('record'); if (empty($method) || !is_callable([$plugin, $method])) { throw new \Exception('Cannot call "' . $method . '" on helper.'); @@ -234,7 +308,7 @@ class RecordDataFormatter extends AbstractHelper throw new \Exception('Template option missing.'); } $helper = $this->getView()->plugin('record'); - $context = isset($options['context']) ? $options['context'] : []; + $context = $options['context'] ?? []; $context['driver'] = $driver; $context['data'] = $data; return trim( @@ -253,7 +327,7 @@ class RecordDataFormatter extends AbstractHelper */ protected function getLink($value, $options) { - if (isset($options['recordLink']) && $options['recordLink']) { + if ($options['recordLink'] ?? false) { $helper = $this->getView()->plugin('record'); return $helper->getLink($options['recordLink'], $value); } @@ -274,12 +348,10 @@ class RecordDataFormatter extends AbstractHelper protected function renderSimple(RecordDriver $driver, $data, array $options) { $view = $this->getView(); - $escaper = (isset($options['translate']) && $options['translate']) + $escaper = ($options['translate'] ?? false) ? $view->plugin('transEsc') : $view->plugin('escapeHtml'); - $transDomain = isset($options['translationTextDomain']) - ? $options['translationTextDomain'] : ''; - $separator = isset($options['separator']) - ? $options['separator'] : '<br />'; + $transDomain = $options['translationTextDomain'] ?? ''; + $separator = $options['separator'] ?? '<br />'; $retVal = ''; $array = (array)$data; $remaining = count($array); @@ -292,8 +364,8 @@ class RecordDataFormatter extends AbstractHelper $retVal .= $separator; } } - return (isset($options['prefix']) ? $options['prefix'] : '') + return ($options['prefix'] ?? '') . $retVal - . (isset($options['suffix']) ? $options['suffix'] : ''); + . ($options['suffix'] ?? ''); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php index dd8a4f91cb292d1d3ff10ecda696a768ea40c2c7..0f41fe528408bd6f90b2909822052c8bdbe561a8 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php @@ -2,7 +2,7 @@ /** * Specification builder for record driver data formatting view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -66,6 +66,7 @@ class SpecBuilder } } } + /** * Set a generic spec line. * @@ -74,7 +75,7 @@ class SpecBuilder * @param string $renderType Type of rendering to use to generate output * @param array $options Additional options * - * @return array + * @return void */ public function setLine($key, $dataMethod, $renderType = null, $options = []) { @@ -87,6 +88,22 @@ class SpecBuilder $this->spec[$key] = $options; } + /** + * Construct a multi-function template spec line. + * + * @param string $key Label to associate with this spec line + * @param string $dataMethod Method of data retrieval for rendering element + * @param Callable $callback Callback function for multi-processing + * @param array $options Additional options + * + * @return void + */ + public function setMultiLine($key, $dataMethod, $callback, $options = []) + { + $options['multiFunction'] = $callback; + return $this->setLine($key, $dataMethod, 'Multi', $options); + } + /** * Construct a record driver template spec line. * @@ -95,7 +112,7 @@ class SpecBuilder * @param string $template Record driver template to render with data * @param array $options Additional options * - * @return array + * @return void */ public function setTemplateLine($key, $dataMethod, $template, $options = []) { diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php index 75e6dba5e6ddec9a19350336387d2f28463f0efa..5f1655b8018dc917d44f993b91dc4cff445fa4c8 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php @@ -2,7 +2,7 @@ /** * Factory for record driver data formatting view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -28,6 +28,9 @@ */ namespace VuFind\View\Helper\Root; +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + /** * Factory for record driver data formatting view helper * @@ -38,16 +41,31 @@ namespace VuFind\View\Helper\Root; * @link https://vufind.org/wiki/development:architecture:record_data_formatter * Wiki */ -class RecordDataFormatterFactory +class RecordDataFormatterFactory implements FactoryInterface { /** - * Create the helper. + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs * - * @return RecordDataFormatter + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function __invoke() - { - $helper = new RecordDataFormatter(); + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $helper = new $requestedName(); $helper->setDefaults( 'collection-info', [$this, 'getDefaultCollectionInfoSpecs'] ); @@ -59,6 +77,54 @@ class RecordDataFormatterFactory return $helper; } + /** + * Get the callback function for processing authors. + * + * @return Callable + */ + protected function getAuthorFunction() + { + return function ($data, $options) { + // Lookup array of singular/plural labels (note that Other is always + // plural right now due to lack of translation strings). + $labels = [ + 'primary' => ['Main Author', 'Main Authors'], + 'corporate' => ['Corporate Author', 'Corporate Authors'], + 'secondary' => ['Other Authors', 'Other Authors'], + ]; + // Lookup array of schema labels. + $schemaLabels = [ + 'primary' => 'author', + 'corporate' => 'creator', + 'secondary' => 'contributor', + ]; + // Lookup array of sort orders. + $order = ['primary' => 1, 'corporate' => 2, 'secondary' => 3]; + + // Sort the data: + $final = []; + foreach ($data as $type => $values) { + $final[] = [ + 'label' => $labels[$type][count($values) == 1 ? 0 : 1], + 'values' => [$type => $values], + 'options' => [ + 'pos' => $options['pos'] + $order[$type], + 'renderType' => 'RecordDriverTemplate', + 'template' => 'data-authors.phtml', + 'context' => [ + 'type' => $type, + 'schemaLabel' => $schemaLabels[$type], + 'requiredDataFields' => [ + ['name' => 'role', 'prefix' => 'CreatorRoles::'] + ], + ], + ], + ]; + } + return $final; + }; + } + /** * Get default specifications for displaying data in collection-info metadata. * @@ -67,36 +133,8 @@ class RecordDataFormatterFactory public function getDefaultCollectionInfoSpecs() { $spec = new RecordDataFormatter\SpecBuilder(); - $spec->setTemplateLine( - 'Main Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'labelFunction' => function ($data) { - return count($data['primary']) > 1 - ? 'Main Authors' : 'Main Author'; - }, - 'context' => ['type' => 'primary', 'schemaLabel' => 'author'], - ] - ); - $spec->setTemplateLine( - 'Corporate Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'labelFunction' => function ($data) { - return count($data['corporate']) > 1 - ? 'Corporate Authors' : 'Corporate Author'; - }, - 'context' => ['type' => 'corporate', 'schemaLabel' => 'creator'], - ] - ); - $spec->setTemplateLine( - 'Other Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'context' => [ - 'type' => 'secondary', 'schemaLabel' => 'contributor' - ], - ] + $spec->setMultiLine( + 'Authors', 'getDeduplicatedAuthors', $this->getAuthorFunction() ); $spec->setLine('Summary', 'getSummary'); $spec->setLine( @@ -135,36 +173,8 @@ class RecordDataFormatterFactory { $spec = new RecordDataFormatter\SpecBuilder(); $spec->setLine('Summary', 'getSummary'); - $spec->setTemplateLine( - 'Main Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'labelFunction' => function ($data) { - return count($data['primary']) > 1 - ? 'Main Authors' : 'Main Author'; - }, - 'context' => ['type' => 'primary', 'schemaLabel' => 'author'], - ] - ); - $spec->setTemplateLine( - 'Corporate Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'labelFunction' => function ($data) { - return count($data['corporate']) > 1 - ? 'Corporate Authors' : 'Corporate Author'; - }, - 'context' => ['type' => 'corporate', 'schemaLabel' => 'creator'], - ] - ); - $spec->setTemplateLine( - 'Other Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'context' => [ - 'type' => 'secondary', 'schemaLabel' => 'contributor' - ], - ] + $spec->setMultiLine( + 'Authors', 'getDeduplicatedAuthors', $this->getAuthorFunction() ); $spec->setLine('Language', 'getLanguages'); $spec->setLine( @@ -193,52 +203,8 @@ class RecordDataFormatterFactory $spec->setLine( 'Previous Title', 'getPreviousTitles', null, ['recordLink' => 'title'] ); - $spec->setTemplateLine( - 'Main Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'labelFunction' => function ($data) { - return count($data['primary']) > 1 - ? 'Main Authors' : 'Main Author'; - }, - 'context' => [ - 'type' => 'primary', - 'schemaLabel' => 'author', - 'requiredDataFields' => [ - ['name' => 'role', 'prefix' => 'CreatorRoles::'] - ] - ] - ] - ); - $spec->setTemplateLine( - 'Corporate Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'labelFunction' => function ($data) { - return count($data['corporate']) > 1 - ? 'Corporate Authors' : 'Corporate Author'; - }, - 'context' => [ - 'type' => 'corporate', - 'schemaLabel' => 'creator', - 'requiredDataFields' => [ - ['name' => 'role', 'prefix' => 'CreatorRoles::'] - ] - ] - ] - ); - $spec->setTemplateLine( - 'Other Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', - [ - 'useCache' => true, - 'context' => [ - 'type' => 'secondary', - 'schemaLabel' => 'contributor', - 'requiredDataFields' => [ - ['name' => 'role', 'prefix' => 'CreatorRoles::'] - ] - ], - ] + $spec->setMultiLine( + 'Authors', 'getDeduplicatedAuthors', $this->getAuthorFunction() ); $spec->setLine( 'Format', 'getFormats', 'RecordHelper', @@ -276,7 +242,7 @@ class RecordDataFormatterFactory public function getDefaultDescriptionSpecs() { $spec = new RecordDataFormatter\SpecBuilder(); - $spec->setLine('Summary', 'getSummary'); + $spec->setTemplateLine('Summary', true, 'data-summary.phtml'); $spec->setLine('Published', 'getDateSpan'); $spec->setLine('Item Description', 'getGeneralNotes'); $spec->setLine('Physical Description', 'getPhysicalDescriptions'); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5ae8ecde63f25dc907750acad951a03e06a81123 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Record helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Record helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RecordFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $helper = new $requestedName($config); + $helper->setCoverRouter($container->get('VuFind\Cover\Router')); + return $helper; + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php index f052b2efc781b18918b3cb4cde18b65f3f89d244..59c2814aa91726afb539ff4f7491020f16c06119 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php @@ -2,7 +2,7 @@ /** * Record link view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -59,27 +59,29 @@ class RecordLink extends \Zend\View\Helper\AbstractHelper * Given an array representing a related record (which may be a bib ID or OCLC * number), this helper renders a URL linking to that record. * - * @param array $link Link information from record model - * @param bool $escape Should we escape the rendered URL? + * @param array $link Link information from record model + * @param bool $escape Should we escape the rendered URL? + * @param string $source Source ID for backend being used to retrieve records * * @return string URL derived from link information */ - public function related($link, $escape = true) + public function related($link, $escape = true, $source = DEFAULT_SEARCH_BACKEND) { $urlHelper = $this->getView()->plugin('url'); + $baseUrl = $urlHelper($this->getSearchActionForSource($source)); switch ($link['type']) { case 'bib': - $url = $urlHelper('search-results') + $url = $baseUrl . '?lookfor=' . urlencode($link['value']) . '&type=id&jumpto=1'; break; case 'dlc': - $url = $urlHelper('search-results') + $url = $baseUrl . '?lookfor=' . urlencode('"' . $link['value'] . '"') . '&type=lccn&jumpto=1'; break; case 'isn': - $url = $urlHelper('search-results') + $url = $baseUrl . '?join=AND&bool0[]=AND&lookfor0[]=%22' . urlencode($link['value']) . '%22&type0[]=isn&bool1[]=NOT&lookfor1[]=%22' @@ -87,12 +89,12 @@ class RecordLink extends \Zend\View\Helper\AbstractHelper . '%22&type1[]=id&sort=title&view=list'; break; case 'oclc': - $url = $urlHelper('search-results') + $url = $baseUrl . '?lookfor=' . urlencode($link['value']) . '&type=oclc_num&jumpto=1'; break; case 'title': - $url = $urlHelper('search-results') + $url = $baseUrl . '?lookfor=' . urlencode($link['value']) . '&type=title'; break; @@ -148,8 +150,7 @@ class RecordLink extends \Zend\View\Helper\AbstractHelper { if (is_array($url)) { // Assemble URL string from array parts: - $source = isset($url['source']) - ? $url['source'] : DEFAULT_SEARCH_BACKEND; + $source = $url['source'] ?? DEFAULT_SEARCH_BACKEND; $finalUrl = $this->getActionUrl("{$source}|" . $url['record'], $url['action']); if (isset($url['query'])) { @@ -230,7 +231,8 @@ class RecordLink extends \Zend\View\Helper\AbstractHelper public function getChildRecordSearchUrl($driver) { $urlHelper = $this->getView()->plugin('url'); - $url = $urlHelper('search-results') + $route = $this->getSearchActionForSource($driver->getSourceIdentifier()); + $url = $urlHelper($route) . '?lookfor=' . urlencode(addcslashes($driver->getUniqueID(), '"')) . '&type=ParentID'; @@ -238,4 +240,17 @@ class RecordLink extends \Zend\View\Helper\AbstractHelper $escaper = $this->getView()->plugin('escapehtml'); return $escaper($url); } + + /** + * Given a record source ID, return the route name for searching its backend. + * + * @param string $source Record source identifier. + * + * @return string + */ + protected function getSearchActionForSource($source) + { + $optionsHelper = $this->getView()->plugin('searchOptions'); + return $optionsHelper->__invoke($source)->getSearchAction(); + } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c326093213766ac2b4f8e5d8daa85956881c5b66 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * RecordLink helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * RecordLink helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RecordLinkFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Record\Router')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Relais.php b/module/VuFind/src/VuFind/View/Helper/Root/Relais.php new file mode 100644 index 0000000000000000000000000000000000000000..f7c062aa925faf01cd8bd71eaa73da9658986f65 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/Relais.php @@ -0,0 +1,118 @@ +<?php +/** + * Relais view helper + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Zend\Config\Config; + +/** + * Relais view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Relais extends \Zend\View\Helper\AbstractHelper +{ + /** + * Relais configuration (or null if none found) + * + * @var Config + */ + protected $config; + + /** + * Login URL + * + * @var string + */ + protected $loginUrl; + + /** + * Constructor. + * + * @param Config $config Relais configuration (or null if none found) + * @param string $loginUrl Login base URL + */ + public function __construct($config, $loginUrl) + { + $this->config = $config; + $this->loginUrl = $loginUrl; + } + + /** + * Create a Relais search link from a record driver. + * + * @param object $driver Record driver + * + * @return string + */ + public function getSearchLink($driver) + { + // Get data elements: + $isbn = $driver->tryMethod('getCleanISBN'); + $title = $driver->tryMethod('getShortTitle'); + if (empty($title)) { + $title = $driver->tryMethod('getTitle'); + } + $mainAuthor = $driver->tryMethod('getPrimaryAuthor'); + + // Assemble and return URL: + $separator = strstr($this->loginUrl, '?') === false ? '?' : '&'; + $url = $this->loginUrl . $separator . 'query=' + . ($isbn ? 'isbn%3D' . urlencode($isbn) : 'ti%3D' . urlencode($title)); + if ($mainAuthor) { + $url .= '%20and%20au%3D' . urlencode($mainAuthor); + } + return $url; + } + + /** + * Render a button if Relais is active. + * + * @param object $driver Record driver + * + * @return string + */ + public function renderButtonIfActive($driver = null) + { + // Case 1: API enabled: + if ($this->config->apikey ?? false) { + return $this->getView()->render('relais/button.phtml'); + } + // Case 2: Search links enabled: + if ($this->config->loginUrl ?? false) { + return '<a href="' . htmlspecialchars($this->getSearchLink($driver)) + . '" target="new">' . $this->getView()->transEsc('relais_search') + . '</a>'; + } + // Case 3: Nothing enabled: + return ''; + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0000f6291959f159ca1103b73b8b854e9fa208be --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Relais helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Relais helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RelaisFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $urlHelper = $container->get('ViewHelperManager')->get('url'); + $loginUrl = $urlHelper->__invoke('relais-login'); + return new $requestedName($config->Relais ?? null, $loginUrl); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Related.php b/module/VuFind/src/VuFind/View/Helper/Root/Related.php index a66d73f7ef86f9755bd3e4119f2fe62ad4138211..ddf1c1d81097a05a8addc82db13e0f7be2ae40c2 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Related.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Related.php @@ -2,7 +2,7 @@ /** * Related records view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,10 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\Related\PluginManager as RelatedManager; +use VuFind\Search\Options\PluginManager as OptionsManager; /** * Related records view helper @@ -37,24 +40,61 @@ use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class Related extends AbstractHelper +class Related extends AbstractClassBasedTemplateRenderer { + /** + * Config manager + * + * @var ConfigManager + */ + protected $configManager; + + /** + * Plugin manager for search options. + * + * @var OptionsManager + */ + protected $optionsManager; + /** * Plugin manager for related record modules. * - * @var \VuFind\Related\PluginManager + * @var RelatedManager */ protected $pluginManager; /** * Constructor * - * @param \VuFind\Related\PluginManager $pluginManager Plugin manager for related - * record modules. + * @param RelatedManager $pluginManager Plugin manager for related record modules + * @param ConfigManager $cm Configuration manager + * @param OptionsManager $om Search options manager */ - public function __construct(\VuFind\Related\PluginManager $pluginManager) - { + public function __construct(RelatedManager $pluginManager, + ConfigManager $cm, OptionsManager $om + ) { $this->pluginManager = $pluginManager; + $this->configManager = $cm; + $this->optionsManager = $om; + } + + /** + * Given a record source ID, return the appropriate related record configuration. + * + * @param string $source Source identifier + * + * @return array + */ + protected function getConfigForSource($source) + { + $options = $this->optionsManager->get($source); + $configName = $options->getSearchIni(); + // Special case -- default Solr stores [Record] section in config.ini + if ($configName === 'searches') { + $configName = 'config'; + } + $config = $this->configManager->get($configName); + return $config->Record->related ?? []; } /** @@ -66,7 +106,21 @@ class Related extends AbstractHelper */ public function getList(\VuFind\RecordDriver\AbstractBase $driver) { - return $driver->getRelated($this->pluginManager); + $retVal = []; + $config = $this->getConfigForSource($driver->getSourceIdentifier()); + foreach ($config as $current) { + $parts = explode(':', $current); + $type = $parts[0]; + $params = $parts[1] ?? null; + if ($this->pluginManager->has($type)) { + $plugin = $this->pluginManager->get($type); + $plugin->init($params, $driver); + $retVal[] = $plugin; + } else { + throw new \Exception("Related module {$type} does not exist."); + } + } + return $retVal; } /** @@ -79,38 +133,9 @@ class Related extends AbstractHelper */ public function render($related) { - // Set up the rendering context: - $contextHelper = $this->getView()->plugin('context'); - $oldContext = $contextHelper($this->getView())->apply( - ['related' => $related] - ); - - // Get the current related item module's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // template. + $template = 'Related/%s.phtml'; $className = get_class($related); - $resolver = $this->getView()->resolver(); - while (true) { - // Guess the template name for the current class: - $classParts = explode('\\', $className); - $template = 'Related/' . array_pop($classParts) . '.phtml'; - // Try to resolve the template.... - if ($resolver->resolve($template)) { - $html = $this->getView()->render($template); - $contextHelper($this->getView())->restore($oldContext); - return $html; - } else { - // If the template doesn't exist, let's see if we can inherit a - // template from a parent class: - $className = get_parent_class($className); - if (empty($className)) { - // No more parent classes left to try? Throw an exception! - throw new RuntimeException( - 'Cannot find template for related items class: ' . - get_class($related) - ); - } - } - } + $context = ['related' => $related]; + return $this->renderClassTemplate($template, $className, $context); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..bb65c7ee17b2669881df3589b98f19f067fa4b87 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Related helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Related helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RelatedFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Related\PluginManager'), + $container->get('VuFind\Config\PluginManager'), + $container->get('VuFind\Search\Options\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php b/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php index 89bbda6b678c657e720e3a77d12d468f21ac6f59..31021ec000cf8d9c34d9a262ebab5334fb0152e2 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php @@ -2,7 +2,7 @@ /** * View helper to render a portion of an array. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ResultFeed.php b/module/VuFind/src/VuFind/View/Helper/Root/ResultFeed.php index 30a21166fbefe49aff1609a08e909444c958cdf7..4a8da2b281b7a293a30ef7a9568d7c3700fc2e0a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ResultFeed.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ResultFeed.php @@ -2,7 +2,7 @@ /** * "Results as feed" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,12 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use DateTime; use VuFind\I18n\Translator\TranslatorAwareInterface; -use Zend\Feed\Writer\Writer as FeedWriter; use Zend\Feed\Writer\Feed; +use Zend\Feed\Writer\Writer as FeedWriter; +use Zend\ServiceManager\ServiceManager; use Zend\View\Helper\AbstractHelper; /** @@ -65,26 +67,28 @@ class ResultFeed extends AbstractHelper implements TranslatorAwareInterface } /** - * Set up custom extensions. + * Set up custom extensions (should be called by factory). + * + * @param ServiceManager $sm Service manager. * * @return void */ - protected function registerExtensions() + public function registerExtensions(ServiceManager $sm) { - $manager = new \Zend\Feed\Writer\ExtensionPluginManager(); + $manager = new \Zend\Feed\Writer\ExtensionPluginManager($sm); $manager->setInvokableClass( - 'dublincorerendererentry', + 'DublinCore\Renderer\Entry', 'VuFind\Feed\Writer\Extension\DublinCore\Renderer\Entry' ); $manager->setInvokableClass( - 'dublincoreentry', 'VuFind\Feed\Writer\Extension\DublinCore\Entry' + 'DublinCore\Entry', 'VuFind\Feed\Writer\Extension\DublinCore\Entry' ); $manager->setInvokableClass( - 'opensearchrendererfeed', + 'OpenSearch\Renderer\Feed', 'VuFind\Feed\Writer\Extension\OpenSearch\Renderer\Feed' ); $manager->setInvokableClass( - 'opensearchfeed', 'VuFind\Feed\Writer\Extension\OpenSearch\Feed' + 'OpenSearch\Feed', 'VuFind\Feed\Writer\Extension\OpenSearch\Feed' ); FeedWriter::setExtensionManager($manager); FeedWriter::registerExtension('OpenSearch'); @@ -102,11 +106,9 @@ class ResultFeed extends AbstractHelper implements TranslatorAwareInterface */ public function __invoke($results, $currentPath = null) { - $this->registerExtensions(); - // Determine base URL if not already provided: - if (is_null($currentPath)) { - $currentPath = $this->getView()->plugin('currentpath')->__invoke(); + if (null === $currentPath) { + $currentPath = $this->getView()->plugin('currentPath')->__invoke(); } $serverUrl = $this->getView()->plugin('serverurl'); $baseUrl = $serverUrl($currentPath); @@ -236,10 +238,10 @@ class ResultFeed extends AbstractHelper implements TranslatorAwareInterface empty($title) ? $this->translate('Title not available') : $title ); $serverUrl = $this->getView()->plugin('serverurl'); - $recordLink = $this->getView()->plugin('recordlink'); + $recordLink = $this->getView()->plugin('recordLink'); try { $url = $serverUrl($recordLink->getUrl($record)); - } catch (\Zend\Mvc\Router\Exception\RuntimeException $e) { + } catch (\Zend\Router\Exception\RuntimeException $e) { // No route defined? See if we can get a URL out of the driver. // Useful for web results, among other things. $url = $record->tryMethod('getUrl'); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ResultFeedFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ResultFeedFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fe044576542608e6aafd4876cd5a2c6ef4a27ada --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ResultFeedFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * ResultFeed helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ResultFeed helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ResultFeedFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $helper = new $requestedName(); + $helper->registerExtensions($container); + return $helper; + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormat.php b/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormat.php index b27fc9dfd0956746f6a7d8d035d07ff8ef371820..9c2946e0ee91794e502ecea04276509f9a62bd04 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormat.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormat.php @@ -2,7 +2,7 @@ /** * Safe money format view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use NumberFormatter, Zend\View\Helper\AbstractHelper; + +use NumberFormatter; +use Zend\View\Helper\AbstractHelper; /** * Safe money format view helper diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d84fef199fe0ae4c3ace64b4ed2aea3ec88e285e --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * SafeMoneyFormat helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SafeMoneyFormat helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SafeMoneyFormatFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $defaultCurrency = $config->Site->defaultCurrency ?? null; + return new $requestedName($defaultCurrency); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php index 49b4b20a592875835aa71dc02cbba3e691cac3e6..87f8bcb5d8b90565fd2ddf1408681d44deb5ea92 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php @@ -2,7 +2,7 @@ /** * Search box view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Search\Options\PluginManager as OptionsManager; /** @@ -283,6 +284,33 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper return $this->cachedConfigs[$activeSearchClass]; } + /** + * Support method for getCombinedHandlers(): get alphabrowse options. + * + * @param string $activeHandler Current active search handler + * @param bool $indent Should we indent these options? + * + * @return array + */ + protected function getAlphabrowseHandlers($activeHandler, $indent = true) + { + $alphaBrowseBase = $this->getView()->plugin('url') + ->__invoke('alphabrowse-home'); + $labelPrefix = $this->getView()->translate('Browse Alphabetically') . ': '; + $handlers = []; + foreach ($this->alphabrowseConfig as $source => $label) { + $alphaBrowseUrl = $alphaBrowseBase . '?source=' . urlencode($source) + . '&from='; + $handlers[] = [ + 'value' => 'External:' . $alphaBrowseUrl, + 'label' => $labelPrefix . $this->getView()->translate($label), + 'indent' => $indent, + 'selected' => $activeHandler == 'AlphaBrowse:' . $source + ]; + } + return $handlers; + } + /** * Support method for getHandlers() -- load combined settings. * @@ -297,6 +325,7 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper $handlers = []; $selectedFound = false; $backupSelectedIndex = false; + $addedBrowseHandlers = false; $settings = $this->getCombinedHandlerConfig($activeSearchClass); $typeCount = count($settings['type']); for ($i = 0; $i < $typeCount; $i++) { @@ -317,7 +346,7 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper && $activeHandler == $searchVal; if ($selected) { $selectedFound = true; - } else if ($backupSelectedIndex === false + } elseif ($backupSelectedIndex === false && $target == $activeSearchClass ) { $backupSelectedIndex = count($handlers); @@ -329,7 +358,15 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper 'selected' => $selected ]; } - } else if ($type == 'External') { + + // Should we add alphabrowse links? + if ($target === 'Solr' && $this->alphaBrowseOptionsEnabled()) { + $addedBrowseHandlers = true; + $handlers = array_merge( + $handlers, $this->getAlphaBrowseHandlers($activeHandler) + ); + } + } elseif ($type == 'External') { $handlers[] = [ 'value' => $type . ':' . $target, 'label' => $label, 'indent' => false, 'selected' => false @@ -337,22 +374,12 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper } } - // Should we add alphabrowse links? - if ($this->alphaBrowseOptionsEnabled()) { - $alphaBrowseBase = $this->getView()->plugin('url') - ->__invoke('alphabrowse-home'); - $labelPrefix = $this->getView()->translate('Browse Alphabetically') - . ': '; - foreach ($this->alphabrowseConfig as $source => $label) { - $alphaBrowseUrl = $alphaBrowseBase . '?source=' . urlencode($source) - . '&from='; - $handlers[] = [ - 'value' => 'External:' . $alphaBrowseUrl, - 'label' => $labelPrefix . $this->getView()->translate($label), - 'indent' => false, - 'selected' => $activeHandler == 'AlphaBrowse:' . $source - ]; - } + // If we didn't add alphabrowse links above as part of the Solr section + // but we are configured to include them, we should add them now: + if (!$addedBrowseHandlers && $this->alphaBrowseOptionsEnabled()) { + $handlers = array_merge( + $handlers, $this->getAlphaBrowseHandlers($activeHandler, false) + ); } // If we didn't find an exact match for a selected index, use a fuzzy diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..76edeb356aaab53c2d8498ec902f92209704882d --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php @@ -0,0 +1,78 @@ +<?php +/** + * SearchBox helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SearchBox helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchBoxFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager'); + $mainConfig = $config->get('config'); + $searchboxConfig = $config->get('searchbox')->toArray(); + $includeAlphaOptions + = $searchboxConfig['General']['includeAlphaBrowse'] ?? false; + return new $requestedName( + $container->get('VuFind\Search\Options\PluginManager'), + $searchboxConfig, + isset($mainConfig->SearchPlaceholder) + ? $mainConfig->SearchPlaceholder->toArray() : [], + $includeAlphaOptions && isset($mainConfig->AlphaBrowse_Types) + ? $mainConfig->AlphaBrowse_Types->toArray() : [] + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php index ab05d2a53ebf6fa6edad14134f5c41ed803b94bb..a28d01f0dd9a3ec82d48061a81a5312cf8e575b6 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php @@ -2,7 +2,7 @@ /** * View helper for remembering recent user searches/parameters. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use VuFind\Search\Memory, Zend\View\Helper\AbstractHelper; + +use VuFind\Search\Memory; +use Zend\View\Helper\AbstractHelper; /** * View helper for remembering recent user searches/parameters. diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a4ca1a90c4bbe995682197fd60de1a94f619a903 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * SearchMemory helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SearchMemory helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchMemoryFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('VuFind\Search\Memory')); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchOptions.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchOptions.php index 74d1989dd0ee8268633929319651866b3311c967..bdcc5715ada77f6c22b5534d2a9eb7f18a470167 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchOptions.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchOptions.php @@ -2,7 +2,7 @@ /** * "Retrieve search options" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Search\Options\PluginManager; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..cf3deccbc2e44729fd9997d887d0850cd3a1983e --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * SearchOptions helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SearchOptions helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchOptionsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Search\Options\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchParams.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchParams.php index 33fa2cf003cc1c8e30873d50a89b309b2a3ec099..90710f88f4f8e2fdac07d34bc23cb20706e64bd9 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchParams.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchParams.php @@ -2,7 +2,7 @@ /** * "Retrieve search params" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use VuFind\Search\Params\PluginManager; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..73b6584b26e4d7a74fae00866cb97453ea6f2f27 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * SearchParams helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SearchParams helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchParamsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Search\Params\PluginManager') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchTabs.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchTabs.php index 99eba9159ac3a90556f932dd43cb3e659c4ce595..72e4ecb88d85bd42096d65707b9a0137c5d17948 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchTabs.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchTabs.php @@ -2,7 +2,7 @@ /** * "Search tabs" view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015-2016. @@ -28,11 +28,12 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use VuFind\Search\Base\Results, - VuFind\Search\Results\PluginManager, - VuFind\Search\SearchTabsHelper, - Zend\View\Helper\Url, - Zend\Http\Request; + +use VuFind\Search\Base\Results; +use VuFind\Search\Results\PluginManager; +use VuFind\Search\SearchTabsHelper; +use Zend\Http\Request; +use Zend\View\Helper\Url; /** * "Search tabs" view helper @@ -126,7 +127,7 @@ class SearchTabs extends \Zend\View\Helper\AbstractHelper $retVal['selected'] = $this ->createSelectedTab($key, $class, $label, $permissionName); $retVal['tabs'][] = $retVal['selected']; - } else if ($type == 'basic') { + } elseif ($type == 'basic') { if (!isset($activeOptions)) { $activeOptions = $this->results->get($activeSearchClass)->getOptions(); @@ -137,7 +138,7 @@ class SearchTabs extends \Zend\View\Helper\AbstractHelper $retVal['tabs'][] = $this->createBasicTab( $key, $class, $label, $newUrl, $permissionName ); - } else if ($type == 'advanced') { + } elseif ($type == 'advanced') { $retVal['tabs'][] = $this->createAdvancedTab( $key, $class, $label, $filters, $permissionName ); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f8acf7e416d2a561cc68d7a3fda034b2617a9923 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * SearchTabs helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SearchTabs helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SearchTabsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get('VuFind\Search\Results\PluginManager'), + $container->get('ViewHelperManager')->get('url'), + $container->get('VuFind\Search\SearchTabsHelper') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SortFacetList.php b/module/VuFind/src/VuFind/View/Helper/Root/SortFacetList.php index 3bfcf4a1783dfdc94142602d9f0aa7ed7e89d157..cb318e0d1ca8a126974f3f7abeb26b6f0e20d379 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SortFacetList.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SortFacetList.php @@ -3,7 +3,7 @@ /** * Sort facet list view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Summon.php b/module/VuFind/src/VuFind/View/Helper/Root/Summon.php index 8ae68f035c7cb3cf32e937666a27b8e2d3a7f3f1..2421a54eb8f69e4ab63ed537469a82cb6794268a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Summon.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Summon.php @@ -2,7 +2,7 @@ /** * Summon support functions. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2012. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlus.php b/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlus.php index 7de84f752eb08f78c8b52568841d5562423e2ac8..b710c08ae7ee1ece2c962d0f48dfe262945e6884 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlus.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlus.php @@ -2,7 +2,7 @@ /** * SyndeticsPlus view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1ac1eb26d590e8451bcc71798d75874c20e49e5e --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * SyndeticsPlus helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SyndeticsPlus helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SyndeticsPlusFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config->Syndetics ?? null); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SystemEmail.php b/module/VuFind/src/VuFind/View/Helper/Root/SystemEmail.php index 021fe52e0c38e68f7db535ac97d5f0ef562362af..cad50dbe39ccc96b5de20232af33445f838bc639 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SystemEmail.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SystemEmail.php @@ -2,7 +2,7 @@ /** * System contact email helper. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..832f79140c610b24f3329286181671d26b381b11 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * SystemEmail helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * SystemEmail helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SystemEmailFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + return new $requestedName($config->Site->email ?? ''); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/TransEsc.php b/module/VuFind/src/VuFind/View/Helper/Root/TransEsc.php index ce96b795007412d70fd112eb3f470eb892703d0f..5ae26a8735fdee7dddb98534dcc792c434b07f67 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/TransEsc.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/TransEsc.php @@ -2,7 +2,7 @@ /** * Translate + escape view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Translate.php b/module/VuFind/src/VuFind/View/Helper/Root/Translate.php index 1ed735e5e29483bec3737f5fd6c6cd5895755bba..d34bfa66a14aa17d59bfb3dade1b0e5524306086 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Translate.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Translate.php @@ -2,7 +2,7 @@ /** * Translate view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Truncate.php b/module/VuFind/src/VuFind/View/Helper/Root/Truncate.php index 058228e77e59df75023338878c3a31a97a2e95c0..f483d1ab9aa6683e4c01dcbdf1e112cb1656c43e 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Truncate.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Truncate.php @@ -2,7 +2,7 @@ /** * Truncate view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** @@ -53,7 +54,7 @@ class Truncate extends AbstractHelper { if ($len == 0) { return ''; - } else if (strlen($str) > $len) { + } elseif (strlen($str) > $len) { if (function_exists('mb_substr')) { return trim(mb_substr($str, 0, $len, 'UTF-8')) . $append; } else { diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UserList.php b/module/VuFind/src/VuFind/View/Helper/Root/UserList.php index 702bc0c52504a611c6068f9dae303843f9a0db9f..36337ab47df5dd5f2794f459bc1c6dadca87e9ec 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/UserList.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/UserList.php @@ -2,7 +2,7 @@ /** * List view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,8 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; -use VuFind\Db\Row\UserList as UserListRow, Zend\View\Helper\AbstractHelper; + +use Zend\View\Helper\AbstractHelper; /** * List view helper diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..584c20ad92882172132d8a17ba039c4725037a5c --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * UserList helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * UserList helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserListFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $sessionManager = $container->get('Zend\Session\SessionManager'); + $session = new \Zend\Session\Container('List', $sessionManager); + $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + return new $requestedName($session, $capabilities->getListSetting()); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UserTags.php b/module/VuFind/src/VuFind/View/Helper/Root/UserTags.php index b0fbf7ac81caf3d4c687876fc07f82d2e3f47b7a..3f3753f1a58a32dcebd0e22dd64f88269f8ccd87 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/UserTags.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/UserTags.php @@ -2,7 +2,7 @@ /** * Tag view helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; + use Zend\View\Helper\AbstractHelper; /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c7ba6c028b173df8fb71163d0c1f4bf212b7a524 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * UserTags helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * UserTags helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserTagsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + return new $requestedName($capabilities->getTagSetting()); + } +} diff --git a/module/VuFind/src/VuFind/XSLT/Import/VuFind.php b/module/VuFind/src/VuFind/XSLT/Import/VuFind.php index 421f2a3ec742b25cfffb45520b3993072edc87e8..746b395cfb5c2a20f77e25a2381513d869bc5341 100644 --- a/module/VuFind/src/VuFind/XSLT/Import/VuFind.php +++ b/module/VuFind/src/VuFind/XSLT/Import/VuFind.php @@ -2,7 +2,7 @@ /** * XSLT importer support methods. * - * PHP version 5 + * PHP version 7 * * Copyright (c) Demian Katz 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/indexing Wiki */ namespace VuFind\XSLT\Import; -use DOMDocument, VuFind\Config\Locator as ConfigLocator; + +use DOMDocument; +use VuFind\Config\Locator as ConfigLocator; /** * XSLT support class -- all methods of this class must be public and static; @@ -67,7 +69,7 @@ class VuFind */ public static function getChangeTracker() { - return static::$serviceLocator->get('VuFind\DbTablePluginManager') + return static::$serviceLocator->get('VuFind\Db\Table\PluginManager') ->get('ChangeTracker'); } @@ -80,7 +82,8 @@ class VuFind */ public static function getConfig($config = 'config') { - return static::$serviceLocator->get('VuFind\Config')->get($config); + return static::$serviceLocator->get('VuFind\Config\PluginManager') + ->get($config); } /** @@ -352,7 +355,7 @@ class VuFind $map[$key] = trim($parts[1]); } } - return isset($map[$in]) ? $map[$in] : $in; + return $map[$in] ?? $in; } /** diff --git a/module/VuFind/src/VuFind/XSLT/Import/VuFindSitemap.php b/module/VuFind/src/VuFind/XSLT/Import/VuFindSitemap.php index 723ab95c14e855e8494fa891ddd81ac8f1fa71b6..bfdd9efba0baa1c106ce28f2a798837fcfbff01e 100644 --- a/module/VuFind/src/VuFind/XSLT/Import/VuFindSitemap.php +++ b/module/VuFind/src/VuFind/XSLT/Import/VuFindSitemap.php @@ -2,7 +2,7 @@ /** * XSLT importer support methods for sitemaps. * - * PHP version 5 + * PHP version 7 * * Copyright (c) Demian Katz 2010. * @@ -174,7 +174,7 @@ class VuFindSitemap extends VuFind preg_match_all( '/<meta name="useCount" content="([^"]*)"/ms', $html, $matches ); - $useCount = isset($matches[1][0]) ? $matches[1][0] : 1; + $useCount = $matches[1][0] ?? 1; return [ 'category' => $categories, diff --git a/module/VuFind/src/VuFind/XSLT/Importer.php b/module/VuFind/src/VuFind/XSLT/Importer.php index 5707cf52ef0d7621b0358079eaf89969e33c283b..0b2111a9573e6fef08b24b65d10a36cdf44585ff 100644 --- a/module/VuFind/src/VuFind/XSLT/Importer.php +++ b/module/VuFind/src/VuFind/XSLT/Importer.php @@ -2,7 +2,7 @@ /** * VuFind XSLT importer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/ Wiki */ namespace VuFind\XSLT; + use DOMDocument; use VuFind\Config\Locator as ConfigLocator; use VuFindSearch\Backend\Solr\Document\RawXMLDocument; @@ -168,8 +169,7 @@ class Importer $classes = is_array($options['General']['custom_class']) ? $options['General']['custom_class'] : [$options['General']['custom_class']]; - $truncate = isset($options['General']['truncate_custom_class']) - ? $options['General']['truncate_custom_class'] : true; + $truncate = $options['General']['truncate_custom_class'] ?? true; foreach ($classes as $class) { // Add a default namespace if none was provided: if (false === strpos($class, '\\')) { diff --git a/module/VuFind/src/VuFind/XSLT/Processor.php b/module/VuFind/src/VuFind/XSLT/Processor.php index 8c532add0fa6bf88e4e8c3bca92b9b2d8da490d3..df99053ad84eb9d722cd06370bd1e62cf76a20e6 100644 --- a/module/VuFind/src/VuFind/XSLT/Processor.php +++ b/module/VuFind/src/VuFind/XSLT/Processor.php @@ -2,7 +2,7 @@ /** * VuFind XSLT wrapper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/ Wiki */ namespace VuFind\XSLT; -use DOMDocument, XSLTProcessor; + +use DOMDocument; +use XSLTProcessor; /** * VuFind XSLT wrapper diff --git a/module/VuFind/src/VuFindTest/RecordDriver/TestHarness.php b/module/VuFind/src/VuFindTest/RecordDriver/TestHarness.php index 3710d7aa60bf71c7fc4ca35d4dbfab27c25f5cb0..68549a7d8f819c296418ad1ee044161ccd212c6f 100644 --- a/module/VuFind/src/VuFindTest/RecordDriver/TestHarness.php +++ b/module/VuFind/src/VuFindTest/RecordDriver/TestHarness.php @@ -2,7 +2,7 @@ /** * Test harness for simulating record drivers (ignore outside of test suite!) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -51,7 +51,7 @@ class TestHarness extends \VuFind\RecordDriver\AbstractBase if (substr($method, 0, 3) == 'get') { $index = substr($method, 3); return isset($this->fields[$index]) ? $this->fields[$index] : null; - } else if (substr($method, 0, 3) == 'set') { + } elseif (substr($method, 0, 3) == 'set') { $index = substr($method, 3); $this->fields[$index] = $params[0]; } diff --git a/module/VuFind/src/VuFindTest/Search/TestHarness/Options.php b/module/VuFind/src/VuFindTest/Search/TestHarness/Options.php index 43e54aac847d098a417694168c6f7103133cc05d..e4e8f25225126104690bf3661ca40d1a32a528ed 100644 --- a/module/VuFind/src/VuFindTest/Search/TestHarness/Options.php +++ b/module/VuFind/src/VuFindTest/Search/TestHarness/Options.php @@ -2,7 +2,7 @@ /** * Test options search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFindTest/Search/TestHarness/Params.php b/module/VuFind/src/VuFindTest/Search/TestHarness/Params.php index 8cabb13eba3642f233029c131be0d65131c22366..426a7b6f02e4080ad8db94b66b04d0f49cc0b563 100644 --- a/module/VuFind/src/VuFindTest/Search/TestHarness/Params.php +++ b/module/VuFind/src/VuFindTest/Search/TestHarness/Params.php @@ -2,7 +2,7 @@ /** * Test params search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFindTest/Search/TestHarness/Results.php b/module/VuFind/src/VuFindTest/Search/TestHarness/Results.php index 99f4224e5b8bf9cc697f5febee247517f32db19c..8ffec44533d4e73a03218c44b28d7f8b32969c04 100644 --- a/module/VuFind/src/VuFindTest/Search/TestHarness/Results.php +++ b/module/VuFind/src/VuFindTest/Search/TestHarness/Results.php @@ -2,7 +2,7 @@ /** * Test results search model. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Search\TestHarness; + use VuFind\Record\Loader; use VuFindSearch\Service as SearchService; use VuFindTest\RecordDriver\TestHarness as RecordDriver; diff --git a/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php b/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..997fb9fbe6e359a303dbdd631d602f902422baea --- /dev/null +++ b/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Base class for AjaxHandler tests. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Unit; + +use Zend\Http\Request; +use Zend\Mvc\Controller\Plugin\Params; +use Zend\ServiceManager\ServiceManager; +use Zend\Stdlib\Parameters; + +/** + * Base class for AjaxHandler tests. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +abstract class AjaxHandlerTest extends \PHPUnit\Framework\TestCase +{ + /** + * Create a mock service. + * + * @param string $name Name of class implementing service + * @param array $methods Methods to mock + * + * @return object + */ + protected function getMockService($name, $methods = []) + { + return $this->getMockBuilder($name) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Create mock user object. + * + * @return \VuFind\Db\Row\User + */ + protected function getMockUser() + { + return $this->getMockService('VuFind\Db\Row\User'); + } + + /** + * Add a service to a container. + * + * @param ServiceManager $container Container to populate + * @param string $name Name of service to create + * @param mixed $value Value of service (or null to create mock) + * + * @return void + */ + protected function addServiceToContainer($container, $name, $value = null) + { + $container->setService($name, $value ?? $this->getMockService($name)); + } + + /** + * Get an auth manager with a value set for isLoggedIn. + * + * @param \VuFind\Db\Row\User $user Return value for isLoggedIn() + * + * @return \VuFind\Auth\Manager + */ + protected function getMockAuthManager($user) + { + $authManager = $this->getMockService('VuFind\Auth\Manager', ['isLoggedIn']); + $authManager->expects($this->any())->method('isLoggedIn') + ->will($this->returnValue($user)); + return $authManager; + } + + /** + * Build a Params helper for testing. + * + * @param array $get GET parameters + * @param array $post POST parameters + * + * @return Params + */ + protected function getParamsHelper($get = [], $post = []) + { + $params = new Params(); + $request = new Request(); + $request->setQuery(new Parameters($get)); + $request->setPost(new Parameters($post)); + $controller = $this->getMockService( + 'Zend\Mvc\Controller\AbstractActionController', ['getRequest'] + ); + $controller->expects($this->any())->method('getRequest') + ->will($this->returnValue($request)); + $params->setController($controller); + return $params; + } +} diff --git a/module/VuFind/src/VuFindTest/Unit/DbTestCase.php b/module/VuFind/src/VuFindTest/Unit/DbTestCase.php index 9a17aabd8959e3c795a11e5527c93f491f8d1b18..9c7ea6eb35bebc4f6cca4f7b3e41e0bbecedd11a 100644 --- a/module/VuFind/src/VuFindTest/Unit/DbTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/DbTestCase.php @@ -3,7 +3,7 @@ /** * Abstract base class for PHPUnit database test cases. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Unit; + use Zend\ServiceManager\ServiceManager; /** @@ -50,36 +51,34 @@ abstract class DbTestCase extends TestCase protected function addTableManager(ServiceManager $sm) { $factory = new \VuFind\Db\Table\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => - ['VuFind\Db\Table\PluginFactory'], - 'factories' => [ - 'changetracker' => - 'VuFind\Db\Table\Factory::getChangeTracker', - 'comments' => 'VuFind\Db\Table\Factory::getComments', - 'externalsession' => - 'VuFind\Db\Table\Factory::getExternalSession', - 'oairesumption' => - 'VuFind\Db\Table\Factory::getOaiResumption', - 'record' => 'VuFind\Db\Table\Factory::getRecord', - 'resource' => 'VuFind\Db\Table\Factory::getResource', - 'resourcetags' => - 'VuFind\Db\Table\Factory::getResourceTags', - 'search' => 'VuFind\Db\Table\Factory::getSearch', - 'session' => 'VuFind\Db\Table\Factory::getSession', - 'tags' => 'VuFind\Db\Table\Factory::getTags', - 'user' => 'VuFind\Db\Table\Factory::getUser', - 'usercard' => 'VuFind\Db\Table\Factory::getUserCard', - 'userlist' => 'VuFind\Db\Table\Factory::getUserList', - 'userresource' => - 'VuFind\Db\Table\Factory::getUserResource', - ], - ] - ) + $sm, + [ + 'abstract_factories' => + ['VuFind\Db\Table\PluginFactory'], + 'factories' => [ + 'changetracker' => + 'VuFind\Db\Table\Factory::getChangeTracker', + 'comments' => 'VuFind\Db\Table\Factory::getComments', + 'externalsession' => + 'VuFind\Db\Table\Factory::getExternalSession', + 'oairesumption' => + 'VuFind\Db\Table\Factory::getOaiResumption', + 'record' => 'VuFind\Db\Table\Factory::getRecord', + 'resource' => 'VuFind\Db\Table\Factory::getResource', + 'resourcetags' => + 'VuFind\Db\Table\Factory::getResourceTags', + 'search' => 'VuFind\Db\Table\Factory::getSearch', + 'session' => 'VuFind\Db\Table\Factory::getSession', + 'tags' => 'VuFind\Db\Table\Factory::getTags', + 'user' => 'VuFind\Db\Table\Factory::getUser', + 'usercard' => 'VuFind\Db\Table\Factory::getUserCard', + 'userlist' => 'VuFind\Db\Table\Factory::getUserList', + 'userresource' => + 'VuFind\Db\Table\Factory::getUserResource', + ], + ] ); - $factory->setServiceLocator($sm); - $sm->setService('VuFind\DbTablePluginManager', $factory); + $sm->setService('VuFind\Db\Table\PluginManager', $factory); } /** @@ -91,31 +90,8 @@ abstract class DbTestCase extends TestCase */ protected function addRowManager(ServiceManager $sm) { - $factory = new \VuFind\Db\Row\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'factories' => [ - 'changetracker' => 'VuFind\Db\Row\Factory::getChangeTracker', - 'comments' => 'VuFind\Db\Row\Factory::getComments', - 'externalsession' => - 'VuFind\Db\Row\Factory::getExternalSession', - 'oairesumption' => 'VuFind\Db\Row\Factory::getOaiResumption', - 'record' => 'VuFind\Db\Row\Factory::getRecord', - 'resource' => 'VuFind\Db\Row\Factory::getResource', - 'resourcetags' => 'VuFind\Db\Row\Factory::getResourceTags', - 'search' => 'VuFind\Db\Row\Factory::getSearch', - 'session' => 'VuFind\Db\Row\Factory::getSession', - 'tags' => 'VuFind\Db\Row\Factory::getTags', - 'user' => 'VuFind\Db\Row\Factory::getUser', - 'usercard' => 'VuFind\Db\Row\Factory::getUserCard', - 'userlist' => 'VuFind\Db\Row\Factory::getUserList', - 'userresource' => 'VuFind\Db\Row\Factory::getUserResource', - ], - ] - ) - ); - $factory->setServiceLocator($sm); - $sm->setService('VuFind\DbRowPluginManager', $factory); + $factory = new \VuFind\Db\Row\PluginManager($sm); + $sm->setService('VuFind\Db\Row\PluginManager', $factory); } /** @@ -129,15 +105,15 @@ abstract class DbTestCase extends TestCase $sm = parent::getServiceManager(); // Add database service: - if (!$sm->has('VuFind\DbTablePluginManager')) { + if (!$sm->has('VuFind\Db\Table\PluginManager')) { $dbFactory = new \VuFind\Db\AdapterFactory( - $sm->get('VuFind\Config')->get('config') + $sm->get('VuFind\Config\PluginManager')->get('config') ); - $sm->setService('VuFind\DbAdapter', $dbFactory->getAdapter()); + $sm->setService('Zend\Db\Adapter\Adapter', $dbFactory->getAdapter()); $this->addTableManager($sm); $this->addRowManager($sm); $sm->setService( - 'VuFind\SessionManager', + 'Zend\Session\SessionManager', $this->createMock('Zend\Session\SessionManager') ); @@ -178,6 +154,6 @@ abstract class DbTestCase extends TestCase public function getTable($table) { $sm = $this->getServiceManager(); - return $sm->get('VuFind\DbTablePluginManager')->get($table); + return $sm->get('VuFind\Db\Table\PluginManager')->get($table); } } diff --git a/module/VuFind/src/VuFindTest/Unit/ILSDriverTestCase.php b/module/VuFind/src/VuFindTest/Unit/ILSDriverTestCase.php index 8cee1886fd5afa0d5fbab8a709985f1448529eed..ae243edeae1e4a1ba434eb58b509726f073c2031 100644 --- a/module/VuFind/src/VuFindTest/Unit/ILSDriverTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/ILSDriverTestCase.php @@ -3,7 +3,7 @@ /** * Abstract base class for ILS driver test cases. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -50,10 +50,11 @@ abstract class ILSDriverTestCase extends TestCase * Test that driver complains about missing configuration. * * @return void + * + * @expectedException VuFind\Exception\ILS */ public function testMissingConfiguration() { - $this->setExpectedException('VuFind\Exception\ILS'); $this->driver->init(); } } diff --git a/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php b/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php index 6c6a3907d514e8095072b11a9adeb431b945c59a..1b8feabd351e8df17f7285b1f8beefcc956c1507 100644 --- a/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php @@ -3,7 +3,7 @@ /** * Abstract base class for PHPUnit test cases using Mink. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,10 +27,12 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Unit; -use Behat\Mink\Driver\Selenium2Driver, Behat\Mink\Session, - Behat\Mink\Element\Element, - VuFind\Config\Locator as ConfigLocator, - VuFind\Config\Writer as ConfigWriter; + +use Behat\Mink\Driver\Selenium2Driver; +use Behat\Mink\Element\Element; +use Behat\Mink\Session; +use VuFind\Config\Locator as ConfigLocator; +use VuFind\Config\Writer as ConfigWriter; /** * Abstract base class for PHPUnit test cases using Mink. @@ -241,6 +243,36 @@ abstract class MinkTestCase extends DbTestCase return $result; } + /** + * Set a value within an element selected via CSS; retry if set fails + * due to browser bugs. + * + * @param Element $page Page element + * @param string $selector CSS selector + * @param string $value Value to set + * @param int $timeout Wait timeout for CSS selection (in ms) + * @param int $retries Retry count for set loop + * + * @return mixed + */ + protected function findCssAndSetValue(Element $page, $selector, $value, + $timeout = 1000, $retries = 6 + ) { + $field = $this->findCss($page, $selector, $timeout); + + // Workaround for Chromedriver bug; sometimes setting a value + // doesn't work on the first try. + for ($i = 0; $i < $retries; $i++) { + $field->setValue($value); + // Did it work? If so, we're done and can leave.... + if ($field->getValue() === $value) { + return; + } + } + + throw new \Exception('Failed to set value after ' . $retries . ' attempts.'); + } + /** * Retrieve a link and assert that it exists before returning it. * @@ -275,6 +307,28 @@ abstract class MinkTestCase extends DbTestCase return false; } + /** + * Search for the specified query. + * + * @param string $query Search term(s) + * @param string $handler Search type (optional) + * + * @return \Behat\Mink\Element\Element + */ + protected function performSearch($query, $handler = null) + { + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl() . '/Search/Home'); + $page = $session->getPage(); + $this->findCss($page, '#searchForm_lookfor')->setValue($query); + if ($handler) { + $this->findCss($page, '#searchForm_type')->setValue($handler); + } + $this->findCss($page, '.btn.btn-primary')->click(); + $this->snooze(); + return $page; + } + /** * Standard setup method. * diff --git a/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php b/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php index 771bc7a0a481b6064a4e844861362ff16e26220e..d4c3dda218bbd6729dca9e7fce84ffe07569a005 100644 --- a/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php @@ -3,7 +3,7 @@ /** * Abstract base class for PHPUnit deferred recommendation module test cases. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/src/VuFindTest/Unit/TestCase.php b/module/VuFind/src/VuFindTest/Unit/TestCase.php index 5309f1d70cb7fc429362aecbfbb0a3ead8e9be74..eeb78a73326e9b396616e21981323c1c0e877ebf 100644 --- a/module/VuFind/src/VuFindTest/Unit/TestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/TestCase.php @@ -3,7 +3,7 @@ /** * Abstract base class for PHPUnit test cases. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -37,7 +37,7 @@ namespace VuFindTest\Unit; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -abstract class TestCase extends \PHPUnit_Framework_TestCase +abstract class TestCase extends \PHPUnit\Framework\TestCase { /** * The service manager instance @@ -112,22 +112,22 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase */ protected function setupSearchService() { - $smConfig = new \Zend\ServiceManager\Config( - [ - 'factories' => [ - 'Solr' => 'VuFind\Search\Factory\SolrDefaultBackendFactory', - 'SolrAuth' => 'VuFind\Search\Factory\SolrAuthBackendFactory', - ] + $config = [ + 'factories' => [ + 'Solr' => 'VuFind\Search\Factory\SolrDefaultBackendFactory', + 'SolrAuth' => 'VuFind\Search\Factory\SolrAuthBackendFactory', ] + ]; + $registry = new \VuFind\Search\BackendRegistry( + $this->serviceManager, $config ); - $registry = $this->serviceManager->createScopedServiceManager(); - $smConfig->configureServiceManager($registry); $bm = new \VuFind\Search\BackendManager($registry); $this->serviceManager->setService('VuFind\Search\BackendManager', $bm); $ss = new \VuFindSearch\Service(); - $this->serviceManager->setService('VuFind\Search', $ss); + $this->serviceManager->setService('VuFindSearch\Service', $ss); $fh = new \VuFind\Search\Solr\HierarchicalFacetHelper(); - $this->serviceManager->setService('VuFind\HierarchicalFacetHelper', $fh); + $this->serviceManager + ->setService('VuFind\Search\Solr\HierarchicalFacetHelper', $fh); $events = $ss->getEventManager(); $events->attach('resolve', [$bm, 'onResolve']); } @@ -142,81 +142,75 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase if (!$this->serviceManager) { $this->serviceManager = new \Zend\ServiceManager\ServiceManager(); $optionsFactory = new \VuFind\Search\Options\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => - ['VuFind\Search\Options\PluginFactory'], - ] - ) + $this->serviceManager, + [ + 'abstract_factories' => + ['VuFind\Search\Options\PluginFactory'], + ] ); - $optionsFactory->setServiceLocator($this->serviceManager); $this->serviceManager->setService( - 'VuFind\SearchOptionsPluginManager', $optionsFactory + 'VuFind\Search\Options\PluginManager', $optionsFactory ); $paramsFactory = new \VuFind\Search\Params\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => - ['VuFind\Search\Params\PluginFactory'], - ] - ) + $this->serviceManager, + [ + 'abstract_factories' => + ['VuFind\Search\Params\PluginFactory'], + ] ); - $paramsFactory->setServiceLocator($this->serviceManager); $this->serviceManager->setService( - 'VuFind\SearchParamsPluginManager', $paramsFactory + 'VuFind\Search\Params\PluginManager', $paramsFactory ); $resultsFactory = new \VuFind\Search\Results\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => - ['VuFind\Search\Results\PluginFactory'], - ] - ) + $this->serviceManager, + [ + 'abstract_factories' => + ['VuFind\Search\Results\PluginFactory'], + ] ); - $resultsFactory->setServiceLocator($this->serviceManager); $this->serviceManager->setService( - 'VuFind\SearchResultsPluginManager', $resultsFactory + 'VuFind\Search\Results\PluginManager', $resultsFactory ); $recordDriverFactory = new \VuFind\RecordDriver\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => - ['VuFind\RecordDriver\PluginFactory'] - ] - ) + $this->serviceManager, + [ + 'abstract_factories' => + ['VuFind\RecordDriver\PluginFactory'] + ] ); $this->serviceManager->setService( - 'VuFind\RecordDriverPluginManager', $recordDriverFactory + 'VuFind\RecordDriver\PluginManager', $recordDriverFactory ); $this->serviceManager->setService( - 'VuFind\SearchSpecsReader', new \VuFind\Config\SearchSpecsReader() + 'VuFind\Config\SearchSpecsReader', + new \VuFind\Config\SearchSpecsReader() ); $this->serviceManager->setService( - 'VuFind\Logger', $this->createMock('VuFind\Log\Logger') + 'VuFind\Log\Logger', $this->createMock('VuFind\Log\Logger') ); $this->serviceManager->setService( - 'VuFind\Http', new \VuFindHttp\HttpService() + 'VuFindHttp\HttpService', new \VuFindHttp\HttpService() ); $this->setupSearchService(); - $cfg = new \Zend\ServiceManager\Config( - ['abstract_factories' => ['VuFind\Config\PluginFactory']] - ); + $cfg = ['abstract_factories' => ['VuFind\Config\PluginFactory']]; $this->serviceManager->setService( - 'VuFind\Config', new \VuFind\Config\PluginManager($cfg) + 'VuFind\Config\PluginManager', + new \VuFind\Config\PluginManager($this->serviceManager, $cfg) ); $this->serviceManager->setService( 'SharedEventManager', new \Zend\EventManager\SharedEventManager() ); $this->serviceManager->setService( - 'VuFind\RecordLoader', new \VuFind\Record\Loader( - $this->serviceManager->get('VuFind\Search'), - $this->serviceManager->get('VuFind\RecordDriverPluginManager') + 'VuFind\Record\Loader', new \VuFind\Record\Loader( + $this->serviceManager->get('VuFindSearch\Service'), + $this->serviceManager->get('VuFind\RecordDriver\PluginManager') ) ); $this->serviceManager->setService('Config', []); - $factory = new \Zend\Mvc\Service\TranslatorServiceFactory(); + $factory = new \Zend\Mvc\I18n\TranslatorFactory(); $this->serviceManager->setService( - 'VuFind\Translator', $factory->createService($this->serviceManager) + 'Zend\Mvc\I18n\Translator', + $factory->createService($this->serviceManager) ); } return $this->serviceManager; @@ -230,18 +224,11 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase public function getAuthManager() { $sm = $this->getServiceManager(); - if (!$sm->has('VuFind\AuthPluginManager')) { - $authManager = new \VuFind\Auth\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => ['VuFind\Auth\PluginFactory'], - ] - ) - ); - $authManager->setServiceLocator($sm); - $sm->setService('VuFind\AuthPluginManager', $authManager); + if (!$sm->has('VuFind\Auth\PluginManager')) { + $authManager = new \VuFind\Auth\PluginManager($sm); + $sm->setService('VuFind\Auth\PluginManager', $authManager); } - return $sm->get('VuFind\AuthPluginManager'); + return $sm->get('VuFind\Auth\PluginManager'); } /** diff --git a/module/VuFind/src/VuFindTest/Unit/UserCreationTrait.php b/module/VuFind/src/VuFindTest/Unit/UserCreationTrait.php index acc54375e18644444e067ba31e724dee201aba48..7e7477ccb72189b22e4c33ebe7b200fe94552e8e 100644 --- a/module/VuFind/src/VuFindTest/Unit/UserCreationTrait.php +++ b/module/VuFind/src/VuFindTest/Unit/UserCreationTrait.php @@ -4,7 +4,7 @@ * Trait with utility methods for user creation/management. Assumes that it * will be applied to a subclass of DbTestCase. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,6 +28,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Unit; + use Behat\Mink\Element\Element; /** @@ -102,9 +103,9 @@ trait UserCreationTrait ]; foreach ($defaults as $field => $default) { - $element = $this->findCss($page, '#account_' . $field); - $element->setValue( - isset($overrides[$field]) ? $overrides[$field] : $default + $this->findCssAndSetValue( + $page, '#account_' . $field, + $overrides[$field] ?? $default ); } } @@ -125,16 +126,14 @@ trait UserCreationTrait ) { $prefix = ($inModal ? '.modal-body ' : '') . $prefix; if (null !== $username) { - $usernameField = $this->findCss($page, $prefix . '[name="username"]'); - // Workaround for Chromedriver bug; sometimes setting the username - // doesn't work on the first try. - while ($usernameField->getValue() !== $username) { - $usernameField->setValue($username); - } + $this->findCssAndSetValue( + $page, $prefix . '[name="username"]', $username + ); } if (null !== $password) { - $passwordField = $this->findCss($page, $prefix . '[name="password"]'); - $passwordField->setValue($password); + $this->findCssAndSetValue( + $page, $prefix . '[name="password"]', $password + ); } } @@ -153,12 +152,9 @@ trait UserCreationTrait $inModal = false, $prefix = '#newpassword ' ) { $prefix = ($inModal ? '.modal-body ' : '') . $prefix; - $usernameField = $this->findCss($page, $prefix . '[name="oldpwd"]'); - $usernameField->setValue($old); - $passwordField = $this->findCss($page, $prefix . '[name="password"]'); - $passwordField->setValue($new); - $password2Field = $this->findCss($page, $prefix . '[name="password2"]'); - $password2Field->setValue($new); + $this->findCssAndSetValue($page, $prefix . '[name="oldpwd"]', $old); + $this->findCssAndSetValue($page, $prefix . '[name="password"]', $new); + $this->findCssAndSetValue($page, $prefix . '[name="password2"]', $new); } /** diff --git a/module/VuFind/src/VuFindTest/Unit/ViewHelperTestCase.php b/module/VuFind/src/VuFindTest/Unit/ViewHelperTestCase.php index bbbc68549c1cb67c2dc6c27643661be943708d34..4a34b13d13b1ddb4ca9f41c9239c11900f2dcd88 100644 --- a/module/VuFind/src/VuFindTest/Unit/ViewHelperTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/ViewHelperTestCase.php @@ -3,7 +3,7 @@ /** * Abstract base class for PHPUnit database test cases. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -37,7 +37,7 @@ namespace VuFindTest\Unit; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -abstract class ViewHelperTestCase extends TestCase +abstract class ViewHelperTestCase extends DbTestCase { /** * Get a working renderer. diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/DatabaseTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/DatabaseTest.php index 8a6523ce38a5a085c23126b84e6988504a78b93d..ce187cdeaab304f3fb7035a6525a2866052eef3e 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/DatabaseTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/DatabaseTest.php @@ -2,7 +2,7 @@ /** * Database authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; + use VuFind\Auth\Database; /** @@ -135,10 +136,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testCreationWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getAccountCreationRequest(['username' => '']); $this->auth->create($request); } @@ -147,10 +149,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test blank password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testCreationWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getAccountCreationRequest(['password' => '']); $this->auth->create($request); } @@ -159,10 +162,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test password mismatch. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testCreationWithPasswordMismatch() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getAccountCreationRequest(['password2' => '']); $this->auth->create($request); } @@ -171,33 +175,41 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test invalid email. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testCreationWithInvalidEmail() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getAccountCreationRequest(['email' => 'garbage']); $this->auth->create($request); } /** - * Test password mismatch. + * Test successful account creation. * * @return void */ public function testSuccessfulCreation() { $request = $this->getAccountCreationRequest(); - $this->auth->create($request); + $newUser = $this->auth->create($request)->toArray(); + foreach ($request->getPost() as $key => $value) { + // Skip the password confirmation value! + if ($key !== 'password2') { + $this->assertEquals($value, $newUser[$key]); + } + } } /** * Test duplicate username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testCreationWithDuplicateUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getAccountCreationRequest(['email' => 'user2@test.com']); $this->auth->create($request); } @@ -206,10 +218,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test duplicate email. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testCreationWithDuplicateEmail() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getAccountCreationRequest(['username' => 'testuser2']); $this->auth->create($request); } @@ -218,10 +231,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => '']); $this->auth->authenticate($request); } @@ -230,10 +244,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test login with blank password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => '']); $this->auth->authenticate($request); } @@ -242,10 +257,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test login with unknown username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithUnrecognizedUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => 'unknown']); $this->auth->authenticate($request); } @@ -254,10 +270,11 @@ class DatabaseTest extends \VuFindTest\Unit\DbTestCase * Test login with bad password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBadPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => "' OR 1=1 LIMIT 1"]); $this->auth->authenticate($request); } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php index f00c990391c67588340e6af37ad8c1c45868d57b..5ad50b8fb2b68474bca8462da13332165e9cf184 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php @@ -2,7 +2,7 @@ /** * ILS authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -98,7 +98,9 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase $driver = $this->getMockDriver(); } $authenticator = $this->getMockILSAuthenticator($patron); - $driverManager = new \VuFind\ILS\Driver\PluginManager(); + $driverManager = new \VuFind\ILS\Driver\PluginManager( + $this->getServiceManager() + ); $driverManager->setService('Sample', $driver); $mockConfigReader = $this->createMock('VuFind\Config\PluginManager'); $mockConfigReader->expects($this->any())->method('get') @@ -111,7 +113,7 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase $authenticator ); $auth->setDbTableManager( - $this->getServiceManager()->get('VuFind\DbTablePluginManager') + $this->getServiceManager()->get('VuFind\Db\Table\PluginManager') ); $auth->getCatalog()->setDriver($driver); return $auth; @@ -149,10 +151,11 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => '']); $this->getAuth()->authenticate($request); } @@ -161,10 +164,11 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase * Test login with blank password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => '']); $this->getAuth()->authenticate($request); } @@ -173,6 +177,8 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase * Test login with technical error. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testBadLoginResponse() { @@ -183,7 +189,6 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase $driver->expects($this->once())->method('patronLogin') ->with($this->equalTo('testuser'), $this->equalTo('testpass')) ->will($this->returnValue($response)); - $this->setExpectedException('VuFind\Exception\Auth'); $this->getAuth($driver)->authenticate($this->getLoginRequest()); } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php index 2562efd8b585dbaee6e3381b19ba256bc81df2ab..6e9e72d2529fd09198e80913c2a929cb6f28f89e 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php @@ -2,7 +2,7 @@ /** * Shibboleth authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,7 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\Shibboleth, VuFind\Db\Table\User, Zend\Config\Config; + +use VuFind\Auth\Shibboleth; +use VuFind\Db\Table\User; +use Zend\Config\Config; /** * Shibboleth authentication test class. @@ -77,9 +80,8 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase $config = $this->getAuthConfig(); } $obj = new Shibboleth($this->createMock('Zend\Session\ManagerInterface')); - \VuFind\ServiceManager\Initializer::initInstance( - $obj, $this->getServiceManager() - ); + $initializer = new \VuFind\ServiceManager\ServiceInitializer(); + $initializer($this->getServiceManager(), $obj); $obj->setConfig($config); return $obj; } @@ -136,10 +138,11 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => '']); $this->getAuthObject()->authenticate($request); } @@ -148,10 +151,11 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => '']); $this->getAuthObject()->authenticate($request); } @@ -160,10 +164,11 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase * Test a configuration with a missing attribute value. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithMissingAttributeValue() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->Shibboleth->userattribute_value_1); $this->getAuthObject($config)->authenticate($this->getLoginRequest()); @@ -173,10 +178,11 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase * Test a configuration with missing username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithoutUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->Shibboleth->username); $this->getAuthObject($config)->authenticate($this->getLoginRequest()); @@ -186,10 +192,11 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase * Test a configuration with missing login setting. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithoutLoginSetting() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->Shibboleth->login); $this->getAuthObject($config)->getSessionInitiator('http://target'); @@ -227,6 +234,6 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase */ public static function tearDownAfterClass() { - static::removeUsers('testuser'); - } + static::removeUsers('testuser'); + } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php index 5aa35e166b711899921aba5c0ef2a668e04dc4f1..9db7e3e24e1638f5b966cf302ba92fdd87831253 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php @@ -2,7 +2,7 @@ /** * Solr Auth Connection Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Integration\Connection; + use VuFindSearch\Query\Query; /** @@ -37,7 +38,7 @@ use VuFindSearch\Query\Query; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class SolrAuthTest extends \VuFindTest\Unit\TestCase +class SolrAuthTest extends \VuFindTest\Unit\DbTestCase { /** * Standard setup method. diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php index bd26cea59ccae40f6652571d573549d45c831c14..da9e16a34615c76d1482ea9dd7fc9b2452c1a6b4 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php @@ -2,7 +2,7 @@ /** * Solr Connection Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Db/Table/ChangeTrackerTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Db/Table/ChangeTrackerTest.php index f3064c1563044fed0de8a0c26ee4c06aea358203..55462d130e8ff88237f045a4e58761d850dd4f67 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Db/Table/ChangeTrackerTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Db/Table/ChangeTrackerTest.php @@ -2,7 +2,7 @@ /** * ChangeTracker Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Db\Table; + use VuFind\Db\Table\ChangeTracker; /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php index ec4e43e9ab5a80ce822a991918b492d4b64ec603..b28792bf174d51b7e9cfecb4f06eac9e833da032 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php @@ -2,7 +2,7 @@ /** * Mink account actions test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -87,13 +87,16 @@ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '.logoutOptions a.logout')->click(); $this->snooze(); + // Go to profile page: + $session->visit($this->getVuFindUrl('/MyResearch/Profile')); + // Log back in $this->findCss($page, '#loginOptions a')->click(); $this->fillInLoginForm($page, 'username1', 'test'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); - // We should now be on account screen; go to change password page + // Now click change password button: $this->findAndAssertLink($page, 'Change Password')->click(); $this->snooze(); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php index 0c372fd778201c3c7c7609087c3f122257ed5e69..80553f01fb3b6481438386468a5f9184f13552c8 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php @@ -2,7 +2,7 @@ /** * Mink test class to test advanced search. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** @@ -138,12 +139,12 @@ class AdvancedSearchTest extends \VuFindTest\Unit\MinkTestCase // Test edit search $this->editAdvancedSearch($page); $this->assertEquals('bride', $this->findCss($page, '#search_lookfor0_0')->getValue()); - $this->assertEquals('tomb', $this->findCss($page, '#search_lookfor0_1')->getValue()); + $this->assertEquals('tomb', $this->findCss($page, '#search_lookfor0_1')->getValue()); $this->assertEquals('Title', $this->findCss($page, '#search_type0_1')->getValue()); - $this->assertEquals('garbage', $this->findCss($page, '#search_lookfor0_2')->getValue()); - $this->assertEquals('1883', $this->findCss($page, '#search_lookfor0_3')->getValue()); - $this->assertEquals('year', $this->findCss($page, '#search_type0_3')->getValue()); - $this->assertEquals('miller', $this->findCss($page, '#search_lookfor1_0')->getValue()); + $this->assertEquals('garbage', $this->findCss($page, '#search_lookfor0_2')->getValue()); + $this->assertEquals('1883', $this->findCss($page, '#search_lookfor0_3')->getValue()); + $this->assertEquals('year', $this->findCss($page, '#search_type0_3')->getValue()); + $this->assertEquals('miller', $this->findCss($page, '#search_lookfor1_0')->getValue()); // Term removal $session->executeScript("deleteSearch(0, 2)"); // search0_2 x click @@ -166,6 +167,6 @@ class AdvancedSearchTest extends \VuFindTest\Unit\MinkTestCase // Test edit search (modified search is restored properly) $this->editAdvancedSearch($page); - $this->assertEquals('miller', $this->findCss($page, '#search_lookfor0_0')->getValue()); + $this->assertEquals('miller', $this->findCss($page, '#search_lookfor0_0')->getValue()); } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php index 51e126d7e81eac6b6b7fc3ab380f24e60d62da8b..483756c9261d910e75e1fa65435e9e04f88449aa 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php @@ -2,7 +2,7 @@ /** * Very simple Mink test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php index 681c628859017b9c5893dba09bca283f047e1b9f..114409a8d84ebdcfe2ec00937372875017378d1f 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php @@ -2,7 +2,7 @@ /** * Mink bulk action test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** @@ -135,6 +136,7 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase // First try clicking without selecting anything: $button->click(); + $this->snooze(); $this->checkForNonSelectedMessage($page); $page->find('css', '.modal-body .btn')->click(); $this->snooze(); @@ -150,10 +152,11 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase $this->fillInAccountForm($page); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); - $this->findCss($page, '.modal #email_from')->setValue('asdf@asdf.com'); - $this->findCss($page, '.modal #email_message')->setValue('message'); - $this->findCss($page, '.modal #email_to') - ->setValue('demian.katz@villanova.edu'); + $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@asdf.com'); + $this->findCssAndSetValue($page, '.modal #email_message', 'message'); + $this->findCssAndSetValue( + $page, '.modal #email_to', 'demian.katz@villanova.edu' + ); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); /* TODO: add back this check when everything is working (as of this @@ -179,6 +182,7 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase // First try clicking without selecting anything: $button->click(); + $this->snooze(); $this->checkForNonSelectedMessage($page); $page->find('css', '.modal-body .btn')->click(); $this->snooze(); @@ -225,6 +229,7 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase // First try clicking without selecting anything: $button->click(); + $this->snooze(); $this->checkForNonSelectedMessage($page); $page->find('css', '.modal-body .btn')->click(); $this->snooze(); @@ -259,6 +264,7 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase // First try clicking without selecting anything: $button->click(); + $this->snooze(); $this->checkForNonSelectedMessage($page); $page->find('css', '.modal-body .btn')->click(); $this->snooze(); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php index 2b5b034b30dd66fd757e1f73a3b2ce244a739383..8cf96006b98e1b192aabee225647c5d55cd358ec 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php @@ -2,7 +2,7 @@ /** * Mink search actions test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -53,24 +53,6 @@ class CallnumberBrowseTest extends \VuFindTest\Unit\MinkTestCase } } - /** - * Search for the specified query. - * - * @param string $query Search term(s) - * - * @return \Behat\Mink\Element\Element - */ - protected function performSearch($query, $page = false) - { - $session = $this->getMinkSession(); - $session->visit($this->getVuFindUrl() . '/Search/Home'); - $page = $session->getPage(); - $this->findCss($page, '#searchForm_lookfor')->setValue($query); - $this->findCss($page, '.btn.btn-primary')->click(); - $this->snooze(); - return $page; - } - /** * Set config for callnumber tests * Sets callnumber_handler to false @@ -163,13 +145,8 @@ class CallnumberBrowseTest extends \VuFindTest\Unit\MinkTestCase $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); if ($expectLinks) { $this->checkLink($link, $type); - // TODO - // if 'all' - // - refresh until multiple - // - test multiple - // else } else { - $this->assertTrue(is_null($link)); + $this->assertTrue(null === $link); } } @@ -188,7 +165,7 @@ class CallnumberBrowseTest extends \VuFindTest\Unit\MinkTestCase $page = $this->performSearch('id:' . $this->id); // No link $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); - $this->assertTrue(is_null($link)); + $this->assertTrue(null === $link); // With dewey links $this->activateAndTestLinks('dewey', $page, $expectLinks); // With lcc links diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php index 399cb6c5536f77c0577ed35656b08dc7d00c325e..b0b8767375e3ac1cbc935cf955fa73dbaeceff46 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php @@ -2,7 +2,7 @@ /** * Mink cart test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** @@ -147,8 +148,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase */ protected function addCurrentPageToCart(Element $page, Element $updateCart, $selectAllId = '#addFormCheckboxSelectAll' - ) - { + ) { $selectAll = $page->find('css', $selectAllId); $selectAll->check(); $updateCart->click(); @@ -193,12 +193,14 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase * * @return Element */ - protected function setUpGenericCartTest($extraConfigs = []) { + protected function setUpGenericCartTest($extraConfigs = []) + { // Activate the cart: $extraConfigs['config']['Site'] = ['showBookBag' => true]; $this->changeConfigs($extraConfigs); $page = $this->getSearchResultsPage(); + $this->snooze(); $this->addCurrentPageToCartUsingButtons($page); $this->assertEquals('2', $this->findCss($page, '#cartItems strong')->getText()); @@ -305,7 +307,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase */ public function testAddingDuplicates() { - // Activate the cart: + // Activate the cart: $this->changeConfigs( [ 'config' => [ @@ -325,7 +327,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $this->assertEquals('2', $this->findCss($page, '#cartItems strong')->getText()); $this->tryAddingDuplicatesToCart($page, $updateCart); $this->assertEquals('2', $this->findCss($page, '#cartItems strong')->getText()); - } + } /** * Test that the cart limit is enforced from search results. @@ -334,7 +336,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase */ public function testOverfillingCart() { - // Activate the cart: + // Activate the cart: $this->changeConfigs( [ 'config' => [ @@ -353,7 +355,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $updateCart = $this->findCss($page, '#updateCart'); $this->addCurrentPageToCart($page, $updateCart); $this->assertEquals('1', $this->findCss($page, '#cartItems strong')->getText()); - } + } /** * Test that the cart limit is enforced from record pages. @@ -362,7 +364,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase */ public function testOverfillingCartFromRecordPage() { - // Activate the cart: + // Activate the cart: $this->changeConfigs( ['config' => ['Site' => ['showBookBag' => true, 'bookBagMaxSize' => 1]]] ); @@ -387,7 +389,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $add = $this->findCss($page, '.cart-add'); $add->click(); $this->assertEquals('1 items (Full)', $cartItems->getText()); - } + } /** * Test that the record "add to cart" button functions. @@ -396,7 +398,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase */ public function testAddingMultipleRecordsFromRecordPage() { - // Activate the cart: + // Activate the cart: $this->changeConfigs( ['config' => ['Site' => ['showBookBag' => true]]] ); @@ -409,7 +411,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $x . ' items', $this->findCss($page, '#cartItems')->getText() ); } - } + } /** * Test that we can put items in the cart and then remove them with the @@ -478,7 +480,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase */ public function testFillCartUsingBottomControls() { - // Activate the cart: + // Activate the cart: $this->changeConfigs( [ 'config' => [ @@ -555,10 +557,11 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); - $this->findCss($page, '.modal #email_from')->setValue('asdf@asdf.com'); - $this->findCss($page, '.modal #email_message')->setValue('message'); - $this->findCss($page, '.modal #email_to') - ->setValue('demian.katz@villanova.edu'); + $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@asdf.com'); + $this->findCssAndSetValue($page, '.modal #email_message', 'message'); + $this->findCssAndSetValue( + $page, '.modal #email_to', 'demian.katz@villanova.edu' + ); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); // Check for confirmation message @@ -746,7 +749,8 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase return $elements; } - public function testToolbarVisibilityConfigCombinations() { + public function testToolbarVisibilityConfigCombinations() + { $page = $this->getSearchResultsPage(); $elements = $this->runConfigCombo($page, [ 'showBookBag' => true, diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php index 7e2c480d23c41251145f0f722b9285e4ede951aa..ab64cfa2f9b7cd99e3eb6ebde08d8edf7c20c48a 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php @@ -2,7 +2,7 @@ /** * Mink cart test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** @@ -59,9 +60,9 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase { $page = $this->getChannelsPage(); // Channels are here - $this->findCss($page, 'div.channel'); + $this->findCss($page, 'div.channel-wrapper'); // Check number of channels - $channels = $page->findAll('css', 'div.channel'); + $channels = $page->findAll('css', 'div.channel-wrapper'); $this->assertEquals(6, count($channels)); // Make sure search input matches url $this->assertEquals( @@ -76,15 +77,15 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase public function testAddChannels() { $page = $this->getChannelsPage(); - $channel = $this->findCss($page, 'div.channel'); + $channel = $this->findCss($page, 'div.channel-wrapper'); // Initial counts - $this->assertEquals(6, count($page->findAll('css', 'div.channel'))); + $this->assertEquals(6, count($page->findAll('css', 'div.channel-wrapper'))); $this->assertEquals(8, count($channel->findAll('css', '.channel-add-menu .dropdown-menu li'))); // Click first add button $this->findCss($channel, '.add-btn')->click(); $this->snooze(); // Post count - $this->assertEquals(8, count($page->findAll('css', 'div.channel'))); + $this->assertEquals(8, count($page->findAll('css', 'div.channel-wrapper'))); $this->assertEquals(6, count($channel->findAll('css', '.channel-add-menu .dropdown-menu li'))); } @@ -94,9 +95,13 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase public function testSwitchToSearch() { $page = $this->getChannelsPage(); - $channel = $this->findCss($page, 'div.channel'); + $channel = $this->findCss($page, 'div.channel-wrapper'); + // Click dropdown to display links + $this->findCss($channel, '.dropdown')->click(); + $this->snooze(); // Click link to go to search results $this->findCss($channel, '.channel_search')->click(); + $this->snooze(); // Make sure the search translated $this->assertEquals( 'building:"weird_ids.mrc"', @@ -104,7 +109,7 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase ); // Check facet $this->assertEquals( - 'Suggested Topics: Adult children of aging parents', + 'Clear Filter Suggested Topics: Adult children of aging parents', $this->findCss($page, '.active-filters .facet')->getText() ); } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php index 1947cc81bf609a17fa468302b1c1dc5b42396690..b01ecb0c686d7a4a2a758626f350068bf7e909ac 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php @@ -2,7 +2,7 @@ /** * Mink ChoiceAuth test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php index dc9bd75ba5ef9fcc6540d8eb6a6af0ec57e8855d..ec7e23329856b1b489dd3fde2751c89bc4ccbe73 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php @@ -2,7 +2,7 @@ /** * Mink test class for basic collection functionality. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -73,6 +73,11 @@ class CollectionsTest extends \VuFindTest\Unit\MinkTestCase public function testBasic() { $this->changeConfigs([ + 'config' => [ + 'Collections' => [ + 'collections' => true + ], + ], 'HierarchyDefault' => [ 'Collections' => [ 'link_type' => 'Top' @@ -92,6 +97,11 @@ class CollectionsTest extends \VuFindTest\Unit\MinkTestCase public function testKeywordFilter() { $this->changeConfigs([ + 'config' => [ + 'Collections' => [ + 'collections' => true + ], + ], 'HierarchyDefault' => [ 'Collections' => [ 'link_type' => 'Top' @@ -120,7 +130,10 @@ class CollectionsTest extends \VuFindTest\Unit\MinkTestCase 'config' => [ 'Hierarchy' => [ 'showTree' => true - ] + ], + 'Collections' => [ + 'collections' => true + ], ], 'HierarchyDefault' => [ 'Collections' => [ diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php index 88ae8326ad87e03d48b1ed330f2a44dfddd74a6c..2d340dc66800d59049bbcf179c85eaeb563e5bb4 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php @@ -2,7 +2,7 @@ /** * Mink test class for combined search. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php index 817b9c454349174d7cdd706cc3679bce6ae401e4..94248564d8140e105195e96bb2933e1838658a32 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php @@ -2,7 +2,7 @@ /** * Mink favorites test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** @@ -74,7 +75,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $session = $this->getMinkSession(); $session->visit($this->getVuFindUrl() . '/Search/Home'); $page = $session->getPage(); - $this->findCss($page, '#searchForm_lookfor')->setValue('Dewey'); + $this->findCssAndSetValue($page, '#searchForm_lookfor', 'Dewey'); $this->findCss($page, '.btn.btn-primary')->click(); return $page; } @@ -127,7 +128,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '.modal-body .btn.btn-primary')->click(); // Correct - $this->findCss($page, '#account_email')->setValue('username1@ignore.com'); + $this->findCssAndSetValue($page, '#account_email', 'username1@ignore.com'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); $this->findCss($page, '#save_list'); @@ -137,11 +138,12 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase // Empty $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); - $this->findCss($page, '#list_title')->setValue('Test List'); - $this->findCss($page, '#list_desc')->setValue('Just. THE BEST.'); + $this->findCssAndSetValue($page, '#list_title', 'Test List'); + $this->findCssAndSetValue($page, '#list_desc', 'Just. THE BEST.'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->snooze(); $this->assertEquals($this->findCss($page, '#save_list option[selected]')->getHtml(), 'Test List'); - $this->findCss($page, '#add_mytags')->setValue('test1 test2 "test 3"'); + $this->findCssAndSetValue($page, '#add_mytags', 'test1 test2 "test 3"'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); $this->findCss($page, '.modal .alert.alert-success'); @@ -184,7 +186,8 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase // Make Two Lists // - One for the next test $this->findCss($page, '#make-list')->click(); - $this->findCss($page, '#list_title')->setValue('Future List'); + $this->snooze(); + $this->findCssAndSetValue($page, '#list_title', 'Future List'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); $this->assertEquals( @@ -193,8 +196,10 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase ); // - One for now $this->findCss($page, '#make-list')->click(); - $this->findCss($page, '#list_title')->setValue('Login Test List'); + $this->snooze(); + $this->findCssAndSetValue($page, '#list_title', 'Login Test List'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->snooze(); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Login Test List' @@ -247,9 +252,9 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $page, ['username' => 'username2', 'email' => 'blargasaurus'] ); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); - $this->findCss($page, '#account_email')->setValue('username2@ignore.com'); + $this->findCssAndSetValue($page, '#account_email', 'username2@ignore.com'); // Test taken username - $this->findCss($page, '#account_username')->setValue('username1'); + $this->findCssAndSetValue($page, '#account_username', 'username1'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->findCss($page, '#account_firstname'); // Correct @@ -265,14 +270,14 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase // Empty $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); - $this->findCss($page, '#list_title')->setValue('Test List'); - $this->findCss($page, '#list_desc')->setValue('Just. THE BEST.'); + $this->findCssAndSetValue($page, '#list_title', 'Test List'); + $this->findCssAndSetValue($page, '#list_desc', 'Just. THE BEST.'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Test List' ); - $this->findCss($page, '#add_mytags')->setValue('test1 test2 "test 3"'); + $this->findCssAndSetValue($page, '#add_mytags', 'test1 test2 "test 3"'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); $this->findCss($page, '.alert.alert-success'); @@ -317,7 +322,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase // - One for the next test $this->findCss($page, '#make-list')->click(); $this->snooze(); - $this->findCss($page, '#list_title')->setValue('Future List'); + $this->findCssAndSetValue($page, '#list_title', 'Future List'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); $this->assertEquals( @@ -327,7 +332,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase // - One for now $this->findCss($page, '#make-list')->click(); $this->snooze(); - $this->findCss($page, '#list_title')->setValue('Login Test List'); + $this->findCssAndSetValue($page, '#list_title', 'Login Test List'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); $this->assertEquals( @@ -449,9 +454,10 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase // Now do it for real. $this->selectAllItemsInList($page); $button->click(); - $this->findCss($page, '.modal #email_to')->setValue('tester@vufind.org'); - $this->findCss($page, '.modal #email_from')->setValue('asdf@vufind.org'); - $this->findCss($page, '.modal #email_message')->setValue('message'); + $this->snooze(); + $this->findCssAndSetValue($page, '.modal #email_to', 'tester@vufind.org'); + $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@vufind.org'); + $this->findCssAndSetValue($page, '.modal #email_message', 'message'); $this->findCss($page, '.modal-body .btn.btn-primary')->click(); $this->snooze(); // Check for confirmation message @@ -522,7 +528,54 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase } /** - * Test that the print control works. + * Test that it is possible to email a public list. + * + * @return void + */ + public function testEmailPublicList() + { + $page = $this->setupBulkTest(); + + // Click on the first list and make it public: + $link = $this->findAndAssertLink($page, 'Test List'); + $link->click(); + $this->snooze(); + $button = $this->findAndAssertLink($page, 'Edit List'); + $button->click(); + $this->snooze(); + $this->findCss($page, '#list_public_1')->click(); // radio button + $this->findCss($page, 'input[name="submit"]')->click(); // submit button + $this->snooze(); + + // Now log out: + $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->snooze(); + + // Now try to email the list: + $this->selectAllItemsInList($page); + $this->findCss($page, '[name=bulkActionForm] .btn-group [name=email]') + ->click(); + $this->snooze(); + + // Log in as different user: + $this->fillInLoginForm($page, 'username2', 'test'); + $this->submitLoginForm($page); + + // Send the email: + $this->findCssAndSetValue($page, '.modal #email_to', 'tester@vufind.org'); + $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@vufind.org'); + $this->findCssAndSetValue($page, '.modal #email_message', 'message'); + $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->snooze(); + // Check for confirmation message + $this->assertEquals( + 'Your item(s) were emailed', + $this->findCss($page, '.modal .alert-success')->getText() + ); + } + + /** + * Test that the bulk delete control works. * * @return void */ diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php index 7842bae9b99b0458bdf68c4be9037d584f285358..bda8668755aab33ced0597c696676ee74f56397d 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php @@ -2,7 +2,7 @@ /** * Mink Feedback module test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php index e130ac88c5ebef437364775ae66492178e6ef0f4..7e72aa87ab6689bc3bafe8840947ee9c40ee83af 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php @@ -2,7 +2,7 @@ /** * Mink ILS actions test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7f54a2fc1bf9729752345612e08b0dc142f92719 --- /dev/null +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php @@ -0,0 +1,78 @@ +<?php +/** + * "Jump to record" test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Mink; + +/** + * "Jump to record" test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class JumpToRecordTest extends \VuFindTest\Unit\MinkTestCase +{ + /** + * Test that we can jump to the first record in a single-record result set. + * + * @return void + */ + public function testJumpToFirst() + { + $this->changeConfigs( + ["config" => ["Record" => ["jump_to_single_search_result" => true]]] + ); + + $page = $this->performSearch('id:testbug2'); + + $this->assertEquals( + 'La congiura dei Principi Napoletani 1701 : (prima e seconda stesura) /', + trim($this->findCss($page, 'h3')->getText()) + ); + } + + /** + * Same as the previous test, but without switching on the jump setting; this + * should result in a result list. + * + * @return void + */ + public function testDoNotJumpToFirst() + { + $page = $this->performSearch('id:testbug2'); + + $expected = 'Showing 1 - 1 results of 1 for search \'id:testbug2\''; + $this->assertEquals( + $expected, substr( + $this->findCss($page, '.search-stats')->getText(), 0, + strlen($expected) + ) + ); + } +} diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php index 148013219e9e97692a5273e67ac573ff19d435d1..69cd495cb92e40bc523a352aecd8fd899c04f2e6 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php @@ -2,7 +2,7 @@ /** * Mink link resolver test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** @@ -157,6 +158,7 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#searchForm_lookfor') ->setValue('id:testsample1'); $this->findCss($page, '.btn.btn-primary')->click(); + $this->snooze(); // Verify the OpenURL $this->assertOpenUrl($page); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php index d69f9e21aed75515484b48f02eb6d7a826c0d8f1..c58101e08009ec9dbb6038255af03d4a5c2edc82 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php @@ -2,7 +2,7 @@ /** * List views (i.e. tabs/accordion) test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link http://www.vufind.org Main Page */ namespace VuFindTest\Mink; + use Behat\Mink\Element\Element; /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php index a630e2c33e7e8ee5e0c8b3bc3181422f287ef884..d8004889a0d98619b642889c642387b39fd92a5b 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php @@ -2,7 +2,7 @@ /** * Next/previous navigation test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2018. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php index 35a0ba129601c166c9fe6c68d9ea6abaf99cdba0..2883b66b768b39a577ef5fc0fc84f0d50fe4a445 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php @@ -2,7 +2,7 @@ /** * Mink record actions test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -70,11 +70,7 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase */ protected function gotoRecord() { - $session = $this->getMinkSession(); - $session->visit($this->getVuFindUrl() . '/Search/Home'); - $page = $session->getPage(); - $this->findCss($page, '#searchForm_lookfor')->setValue('Dewey'); - $this->findCss($page, '.btn.btn-primary')->click(); + $page = $this->performSearch('Dewey'); $this->findCss($page, '.result a.title')->click(); return $page; } @@ -232,6 +228,25 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '.logoutOptions a.logout')->click(); } + /** + * Test searching for one of the tags created above. + */ + public function testTagSearch() + { + // First try an undefined tag: + $page = $this->performSearch('tag-not-in-system', 'tag'); + $this->assertEquals('No Results!', $this->findCss($page, 'h2')->getText()); + // Now try a tag defined earlier: + $page = $this->performSearch('five', 'tag'); + $expected = 'Showing 1 - 1 results of 1 for search \'five\''; + $this->assertEquals( + $expected, substr( + $this->findCss($page, '.search-stats')->getText(), 0, + strlen($expected) + ) + ); + } + /** * Test adding case sensitive tags on records. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php index 557b2d3604877fc6559f6e93ac61ad7580a6183b..7d60daae4f510103b03a410986c38f0e440e85ff 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php @@ -2,7 +2,7 @@ /** * Mink test class for basic record functionality. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -146,6 +146,5 @@ class RecordTest extends \VuFindTest\Unit\MinkTestCase ); $this->tryRecordTabsOnId('testsample1'); $this->tryLoadingTabHashAndReturningToDefault('testsample2'); - } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a4056d8d5ef66cde67904d77e3b6ba7fab3eb9d9 --- /dev/null +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php @@ -0,0 +1,194 @@ +<?php +/** + * Mink saved searches test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2011. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Mink; + +/** + * Mink saved searches test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase +{ + use \VuFindTest\Unit\UserCreationTrait; + + /** + * Standard setup method. + * + * @return void + */ + public static function setUpBeforeClass() + { + return static::failIfUsersExist(); + } + + /** + * Standard setup method. + * + * @return void + */ + public function setUp() + { + // Give up if we're not running in CI: + if (!$this->continuousIntegrationRunning()) { + return $this->markTestSkipped('Continuous integration not running.'); + } + } + + /** + * Test saving and clearing a search. + * + * @return void + */ + public function testSaveSearch() + { + $page = $this->performSearch('test'); + $this->findCss($page, '.fa.fa-save')->click(); + $this->snooze(); + $this->findCss($page, '.createAccountLink')->click(); + $this->snooze(); + $this->fillInAccountForm($page); + $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->snooze(); + $this->assertEquals( + 'Search saved successfully.', + $this->findCss($page, '.alert.alert-success')->getText() + ); + } + + /** + * Test search history. + * + * @return void + */ + public function testSearchHistory() + { + // Use "foo \ bar" as our search because the backslash has been known + // to cause problems in some situations (e.g. PostgreSQL database with + // incorrect escaping); this allows us to catch regressions for a few + // different problems in a single test. + $page = $this->performSearch('foo \ bar'); + $this->findAndAssertLink($page, 'Search History')->click(); + $this->snooze(); + // We should see our "foo \ bar" search in the history, but no saved + // searches because we are logged out: + $this->assertEquals( + 'foo \ bar', $this->findAndAssertLink($page, 'foo \ bar')->getText() + ); + $this->assertFalse( + $this->hasElementsMatchingText($page, 'h2', 'Saved Searches') + ); + $this->assertNull($page->findLink('test')); + + // Now log in and see if our saved search shows up (without making the + // unsaved search go away): + $this->findCss($page, '#loginOptions a')->click(); + $this->snooze(); + $this->fillInLoginForm($page, 'username1', 'test'); + $this->submitLoginForm($page); + $this->assertEquals( + 'foo \ bar', $this->findAndAssertLink($page, 'foo \ bar')->getText() + ); + $this->assertTrue( + $this->hasElementsMatchingText($page, 'h2', 'Saved Searches') + ); + $this->assertEquals( + 'test', $this->findAndAssertLink($page, 'test')->getText() + ); + + // Now purge unsaved searches, confirm that unsaved search is gone + // but saved search is still present: + $this->findAndAssertLink($page, 'Purge unsaved searches')->click(); + $this->snooze(); + $this->assertNull($page->findLink('foo \ bar')); + $this->assertEquals( + 'test', $this->findAndAssertLink($page, 'test')->getText() + ); + } + + /** + * Test that user A cannot delete user B's favorites. + * + * @return void + */ + public function testSavedSearchSecurity() + { + // Log in as user A and get the ID of their saved search: + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl() . '/Search/History'); + $page = $session->getPage(); + $this->findCss($page, '#loginOptions a')->click(); + $this->snooze(); + $this->fillInLoginForm($page, 'username1', 'test'); + $this->submitLoginForm($page); + $delete = $this->findAndAssertLink($page, 'Delete')->getAttribute('href'); + $this->findAndAssertLink($page, 'Log Out')->click(); + $this->snooze(); + + // Use user A's delete link, but try to execute it as user B: + list($base, $params) = explode('?', $delete); + $session->visit($this->getVuFindUrl() . '/MyResearch/SaveSearch?' . $params); + $page = $session->getPage(); + $this->findCss($page, '.createAccountLink')->click(); + $this->snooze(); + $this->fillInAccountForm( + $page, ['username' => 'username2', 'email' => 'username2@example.com'] + ); + $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->snooze(); + $this->findAndAssertLink($page, 'Log Out')->click(); + $this->snooze(); + + // Go back in as user A -- see if the saved search still exists. + $this->findAndAssertLink($page, 'Search History')->click(); + $this->snooze(); + $this->findCss($page, '#loginOptions a')->click(); + $this->snooze(); + $this->fillInLoginForm($page, 'username1', 'test'); + $this->submitLoginForm($page); + $this->assertTrue( + $this->hasElementsMatchingText($page, 'h2', 'Saved Searches') + ); + $this->assertEquals( + 'test', $this->findAndAssertLink($page, 'test')->getText() + ); + } + + /** + * Standard teardown method. + * + * @return void + */ + public static function tearDownAfterClass() + { + static::removeUsers(['username1', 'username2']); + } +} diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php similarity index 65% rename from module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchActionsTest.php rename to module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php index 8d64f37e7b758afdabce5d2b142995ffe0812f91..c6d5283f969e8e762797201316732c1631484e0f 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php @@ -1,8 +1,8 @@ <?php /** - * Mink search actions test class. + * Mink search facet/filter functionality test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -28,7 +28,7 @@ namespace VuFindTest\Mink; /** - * Mink search actions test class. + * Mink search facet/filter functionality test class. * * @category VuFind * @package Tests @@ -36,20 +36,8 @@ namespace VuFindTest\Mink; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ -class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase +class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase { - use \VuFindTest\Unit\UserCreationTrait; - - /** - * Standard setup method. - * - * @return void - */ - public static function setUpBeforeClass() - { - return static::failIfUsersExist(); - } - /** * Standard setup method. * @@ -63,24 +51,6 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase } } - /** - * Search for the specified query. - * - * @param string $query Search term(s) - * - * @return \Behat\Mink\Element\Element - */ - protected function performSearch($query) - { - $session = $this->getMinkSession(); - $session->visit($this->getVuFindUrl() . '/Search/Home'); - $page = $session->getPage(); - $this->findCss($page, '#searchForm_lookfor')->setValue($query); - $this->findCss($page, '.btn.btn-primary')->click(); - $this->snooze(); - return $page; - } - /** * Get filtered search * @@ -93,125 +63,6 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase return $session->getPage(); } - /** - * Test saving and clearing a search. - * - * @return void - */ - public function testSaveSearch() - { - $page = $this->performSearch('test'); - $this->findCss($page, '.fa.fa-save')->click(); - $this->snooze(); - $this->findCss($page, '.createAccountLink')->click(); - $this->snooze(); - $this->fillInAccountForm($page); - $this->findCss($page, 'input.btn.btn-primary')->click(); - $this->snooze(); - $this->assertEquals( - 'Search saved successfully.', - $this->findCss($page, '.alert.alert-success')->getText() - ); - } - - /** - * Test search history. - * - * @return void - */ - public function testSearchHistory() - { - // Use "foo \ bar" as our search because the backslash has been known - // to cause problems in some situations (e.g. PostgreSQL database with - // incorrect escaping); this allows us to catch regressions for a few - // different problems in a single test. - $page = $this->performSearch('foo \ bar'); - $this->findAndAssertLink($page, 'Search History')->click(); - $this->snooze(); - // We should see our "foo \ bar" search in the history, but no saved - // searches because we are logged out: - $this->assertEquals( - 'foo \ bar', $this->findAndAssertLink($page, 'foo \ bar')->getText() - ); - $this->assertFalse( - $this->hasElementsMatchingText($page, 'h2', 'Saved Searches') - ); - $this->assertNull($page->findLink('test')); - - // Now log in and see if our saved search shows up (without making the - // unsaved search go away): - $this->findCss($page, '#loginOptions a')->click(); - $this->snooze(); - $this->fillInLoginForm($page, 'username1', 'test'); - $this->submitLoginForm($page); - $this->assertEquals( - 'foo \ bar', $this->findAndAssertLink($page, 'foo \ bar')->getText() - ); - $this->assertTrue( - $this->hasElementsMatchingText($page, 'h2', 'Saved Searches') - ); - $this->assertEquals( - 'test', $this->findAndAssertLink($page, 'test')->getText() - ); - - // Now purge unsaved searches, confirm that unsaved search is gone - // but saved search is still present: - $this->findAndAssertLink($page, 'Purge unsaved searches')->click(); - $this->snooze(); - $this->assertNull($page->findLink('foo \ bar')); - $this->assertEquals( - 'test', $this->findAndAssertLink($page, 'test')->getText() - ); - } - - /** - * Test that user A cannot delete user B's favorites. - * - * @return void - */ - public function testSavedSearchSecurity() - { - // Log in as user A and get the ID of their saved search: - $session = $this->getMinkSession(); - $session->visit($this->getVuFindUrl() . '/Search/History'); - $page = $session->getPage(); - $this->findCss($page, '#loginOptions a')->click(); - $this->snooze(); - $this->fillInLoginForm($page, 'username1', 'test'); - $this->submitLoginForm($page); - $delete = $this->findAndAssertLink($page, 'Delete')->getAttribute('href'); - $this->findAndAssertLink($page, 'Log Out')->click(); - $this->snooze(); - - // Use user A's delete link, but try to execute it as user B: - list($base, $params) = explode('?', $delete); - $session->visit($this->getVuFindUrl() . '/MyResearch/SaveSearch?' . $params); - $page = $session->getPage(); - $this->findCss($page, '.createAccountLink')->click(); - $this->snooze(); - $this->fillInAccountForm( - $page, ['username' => 'username2', 'email' => 'username2@example.com'] - ); - $this->findCss($page, 'input.btn.btn-primary')->click(); - $this->snooze(); - $this->findAndAssertLink($page, 'Log Out')->click(); - $this->snooze(); - - // Go back in as user A -- see if the saved search still exists. - $this->findAndAssertLink($page, 'Search History')->click(); - $this->snooze(); - $this->findCss($page, '#loginOptions a')->click(); - $this->snooze(); - $this->fillInLoginForm($page, 'username1', 'test'); - $this->submitLoginForm($page); - $this->assertTrue( - $this->hasElementsMatchingText($page, 'h2', 'Saved Searches') - ); - $this->assertEquals( - 'test', $this->findAndAssertLink($page, 'test')->getText() - ); - } - /** * Helper function for facets lists * @@ -227,7 +78,7 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit, count($items)); $excludes = $page - ->findAll('css', '#modal #facet-list-count .badge .fa-times'); + ->findAll('css', '#modal #facet-list-count .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // more $this->findCss($page, '#modal .js-facet-next-page')->click(); @@ -247,7 +98,7 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#modal #facet-list-count')->getText() ); $excludes = $page - ->findAll('css', '#modal #facet-list-count .badge .fa-times'); + ->findAll('css', '#modal #facet-list-count .fa-times'); $this->assertEquals($exclusionActive ? $limit * 2 : 0, count($excludes)); // sort by title @@ -264,7 +115,7 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#modal #facet-list-index')->getText() ); $excludes = $page - ->findAll('css', '#modal #facet-list-index .badge .fa-times'); + ->findAll('css', '#modal #facet-list-index .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // sort by index again $this->findCss($page, '[data-sort="count"]')->click(); @@ -309,7 +160,7 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#modal .js-facet-item.active')->click(); // remove facet $this->snooze(); - $this->assertNull($page->find('css', '.list-group.filters')); + $this->assertNull($page->find('css', '.active-filters')); } /** @@ -341,7 +192,7 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#modal .js-facet-item.active')->click(); // remove facet $this->snooze(); - $this->assertNull($page->find('css', '.list-group.filters')); + $this->assertNull($page->find('css', '.active-filters')); } /** @@ -387,7 +238,7 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#j1_2 a')->click(); $this->snooze(); $filter = $this->findCss($page, '.active-filters .facet'); - $this->assertEquals('hierarchy: 1/level1a/level2a/', $filter->getText()); + $this->assertEquals('Clear Filter hierarchy: 1/level1a/level2a/', $filter->getText()); $this->findCss($page, '#j1_2 .fa-check'); } @@ -477,14 +328,4 @@ class SearchActionsTest extends \VuFindTest\Unit\MinkTestCase $items = $page->findAll('css', '.active-filters'); $this->assertEquals(0, count($items)); } - - /** - * Standard teardown method. - * - * @return void - */ - public static function tearDownAfterClass() - { - static::removeUsers(['username1', 'username2']); - } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php index 2a725a1fa2d4f4a892129904d8b2b0f02fcb8a26..5e0024f5bca9cd21efa9bc163aa135585e56545b 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php @@ -2,7 +2,7 @@ /** * Mink test class for visualization view. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php index 5d581bdeca4c214e4741073c8aca4fdc670c0f46..3a22733b1151da229cfc8b6c825741226c5914c4 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php @@ -2,7 +2,7 @@ /** * ResultFeed Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Integration\View\Helper\Root; + use VuFind\View\Helper\Root\ResultFeed; /** @@ -67,7 +68,7 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase ->setConstructorArgs( [ new \VuFind\Record\Router( - $this->getServiceManager()->get('VuFind\RecordLoader'), + $this->getServiceManager()->get('VuFind\Record\Loader'), new \Zend\Config\Config([]) ) ] @@ -80,8 +81,8 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase ->will($this->returnValue('http://server/url')); return [ - 'currentpath' => $currentPath, - 'recordlink' => $recordLink, + 'currentPath' => $currentPath, + 'recordLink' => $recordLink, 'serverurl' => $serverUrl ]; } @@ -118,10 +119,11 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase $request->set('view', 'rss'); $results = $this->getServiceManager() - ->get('VuFind\SearchResultsPluginManager')->get('Solr'); + ->get('VuFind\Search\Results\PluginManager')->get('Solr'); $results->getParams()->initFromRequest($request); $helper = new ResultFeed(); + $helper->registerExtensions($this->getServiceManager()); $helper->setTranslator($this->getMockTranslator()); $helper->setView($this->getPhpRenderer($this->getPlugins())); $feed = $helper->__invoke($results, '/test/path'); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/CheckRequestIsValidTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/CheckRequestIsValidTest.php new file mode 100644 index 0000000000000000000000000000000000000000..17079e4996861705a9310830ca68779294fb03e8 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/CheckRequestIsValidTest.php @@ -0,0 +1,171 @@ +<?php +/** + * CheckRequestIsValid test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\AjaxHandler; + +use VuFind\AjaxHandler\AbstractIlsAndUserActionFactory; +use VuFind\AjaxHandler\CheckRequestIsValid; +use VuFind\Auth\ILSAuthenticator; +use VuFind\ILS\Connection; +use VuFind\Session\Settings; +use Zend\ServiceManager\ServiceManager; + +/** + * CheckRequestIsValid test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class CheckRequestIsValidTest extends \VuFindTest\Unit\AjaxHandlerTest +{ + /** + * Set up a CheckRequestIsValid handler for testing. + * + * @param Settings $ss Session settings (or null for default) + * @param Connection $ils ILS connection (or null for default) + * @param ILSAuthenticator $ilsAuth ILS authenticator (or null for default) + * @param User|bool $user Return value for isLoggedIn() in auth manager + * + * @return CheckRequestIsValid + */ + protected function getHandler($ss = null, $ils = null, $ilsAuth = null, + $user = false + ) { + // Create container + $container = new ServiceManager(); + + // Install or mock up services: + $this->addServiceToContainer($container, 'VuFind\Session\Settings', $ss); + $this->addServiceToContainer($container, 'VuFind\ILS\Connection', $ils); + $this->addServiceToContainer( + $container, 'VuFind\Auth\ILSAuthenticator', $ilsAuth + ); + + // Set up auth manager with user: + $authManager = $this->getMockAuthManager($user); + $container->setService('VuFind\Auth\Manager', $authManager); + + // Build the handler: + $factory = new AbstractIlsAndUserActionFactory(); + return $factory($container, CheckRequestIsValid::class); + } + + /** + * Test the AJAX handler's response when no one is logged in. + * + * @return void + */ + public function testLoggedOutUser() + { + $handler = $this->getHandler(); + $this->assertEquals( + ['You must be logged in first', 401], + $handler->handleRequest($this->getParamsHelper(['id' => 1, 'data' => 1])) + ); + } + + /** + * Test the AJAX handler's response when the query is empty. + * + * @return void + */ + public function testEmptyQuery() + { + $handler = $this->getHandler(null, null, null, $this->getMockUser()); + $this->assertEquals( + ['bulk_error_missing', 400], + $handler->handleRequest($this->getParamsHelper()) + ); + } + + /** + * Generic support function for successful request tests. + * + * @return void + */ + protected function runSuccessfulTest($ilsMethod, $requestType = null) + { + $ilsAuth = $this->getMockService( + 'VuFind\Auth\ILSAuthenticator', ['storedCatalogLogin'] + ); + $ilsAuth->expects($this->once())->method('storedCatalogLogin') + ->will($this->returnValue([3])); + $ils = $this + ->getMockService('VuFind\ILS\Connection', [$ilsMethod]); + $ils->expects($this->once())->method($ilsMethod) + ->with($this->equalTo(1), $this->equalTo(2), $this->equalTo([3])) + ->will($this->returnValue(true)); + $handler = $this->getHandler(null, $ils, $ilsAuth, $this->getMockUser()); + $params = ['id' => 1, 'data' => 2, 'requestType' => $requestType]; + return $handler->handleRequest($this->getParamsHelper($params)); + } + + /** + * Test a successful hold response. + * + * @return void + */ + public function testHoldResponse() + { + $this->assertEquals( + [['status' => true, 'msg' => 'request_place_text']], + $this->runSuccessfulTest('checkRequestIsValid') + ); + } + + /** + * Test a successful ILL response. + * + * @return void + */ + public function testILLResponse() + { + $this->assertEquals( + [['status' => true, 'msg' => 'ill_request_place_text']], + $this->runSuccessfulTest('checkILLRequestIsValid', 'ILLRequest') + ); + } + + /** + * Test a successful storage retrieval response. + * + * @return void + */ + public function testStorageResponse() + { + $this->assertEquals( + [ + ['status' => true, 'msg' => 'storage_retrieval_request_place_text'] + ], $this->runSuccessfulTest( + 'checkStorageRetrievalRequestIsValid', 'StorageRetrievalRequest' + ) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/CommentRecordTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/CommentRecordTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b2676de64988bab07e3cc74da0fe1c63735a8a66 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/CommentRecordTest.php @@ -0,0 +1,177 @@ +<?php +/** + * CommentRecord test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\AjaxHandler; + +use VuFind\AjaxHandler\CommentRecord; +use VuFind\AjaxHandler\CommentRecordFactory; +use VuFind\Controller\Plugin\Recaptcha; +use VuFind\Db\Row\Resource; +use VuFind\Db\Row\User; +use VuFind\Db\Table\Resource as ResourceTable; +use Zend\ServiceManager\ServiceManager; + +/** + * CommentRecord test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class CommentRecordTest extends \VuFindTest\Unit\AjaxHandlerTest +{ + /** + * Set up a CommentRecord handler for testing. + * + * @param ResourceTable $table Resource table mock (or null for default) + * @param Recaptcha $recaptcha Recaptcha plugin mock (or null for default) + * @param bool $enabled Are comments enabled? + * @param User|bool $user Return value for isLoggedIn() in auth manager + * + * @return CommentRecord + */ + protected function getHandler($table = null, $recaptcha = null, $enabled = true, + $user = false + ) { + // Create container + $container = new ServiceManager(); + + // For simplicity, let the top-level container stand in for the plugin + // managers: + $container->setService('VuFind\Db\Table\PluginManager', $container); + $container->setService('ControllerPluginManager', $container); + + // Install or mock up remaining services: + $this->addServiceToContainer( + $container, 'VuFind\Db\Table\Resource', $table + ); + $this->addServiceToContainer( + $container, 'VuFind\Controller\Plugin\Recaptcha', $recaptcha + ); + + // Set up auth manager with user: + $authManager = $this->getMockAuthManager($user); + $container->setService('VuFind\Auth\Manager', $authManager); + + // Set up capability configuration: + $cfg = new \Zend\Config\Config( + ['Social' => ['comments' => $enabled ? 'enabled' : 'disabled']] + ); + $capabilities = new \VuFind\Config\AccountCapabilities($cfg, $authManager); + $container->setService('VuFind\Config\AccountCapabilities', $capabilities); + + // Build the handler: + $factory = new CommentRecordFactory(); + return $factory($container, CommentRecord::class); + } + + /** + * Return a mock resource row that expects a specific user and comment. + * + * @param string $comment Comment to expect + * @param User $user User to expect + * + * @return Resource + */ + protected function getMockResource($comment, $user) + { + $row = $this->getMockService('VuFind\Db\Row\Resource', ['addComment']); + $row->expects($this->once())->method('addComment') + ->with($this->equalTo($comment), $this->equalTo($user)) + ->will($this->returnValue(true)); + return $row; + } + + /** + * Test the AJAX handler's response when comments are disabled. + * + * @return void + */ + public function testDisabledResponse() + { + $handler = $this->getHandler(null, null, false); + $this->assertEquals( + ['Comments disabled', 400], + $handler->handleRequest($this->getParamsHelper()) + ); + } + + /** + * Test the AJAX handler's response when no one is logged in. + * + * @return void + */ + public function testLoggedOutUser() + { + $handler = $this->getHandler(null, null, true); + $this->assertEquals( + ['You must be logged in first', 401], + $handler->handleRequest($this->getParamsHelper()) + ); + } + + /** + * Test the AJAX handler's response when the query is empty. + * + * @return void + */ + public function testEmptyQuery() + { + $handler = $this->getHandler(null, null, true, $this->getMockUser()); + $this->assertEquals( + ['bulk_error_missing', 400], + $handler->handleRequest($this->getParamsHelper()) + ); + } + + /** + * Test a successful scenario. + * + * @return void + */ + public function testSuccessfulTransaction() + { + $user = $this->getMockUser(); + $table = $this->getMockService('VuFind\Db\Table\Resource', ['findResource']); + $table->expects($this->once())->method('findResource') + ->with($this->equalTo('foo'), $this->equalTo('Solr')) + ->will($this->returnValue($this->getMockResource('bar', $user))); + $handler = $this->getHandler($table, null, true, $user); + $post = [ + 'id' => 'foo', + 'comment' => 'bar', + ]; + $this->assertEquals( + [ + ['commentId' => true] + ], + $handler->handleRequest($this->getParamsHelper([], $post)) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/KeepAliveTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/KeepAliveTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0fa748c52cfd2fb95060314c4a775af9ebe1d2aa --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/KeepAliveTest.php @@ -0,0 +1,60 @@ +<?php +/** + * KeepAlive test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\AjaxHandler; + +use VuFind\AjaxHandler\KeepAlive; +use VuFind\AjaxHandler\KeepAliveFactory; + +/** + * KeepAlive test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class KeepAliveTest extends \VuFindTest\Unit\AjaxHandlerTest +{ + /** + * Test the AJAX handler's basic response. + * + * @return void + */ + public function testResponse() + { + $sm = $this->getMockService('Zend\Session\SessionManager', ['getId']); + $sm->expects($this->once())->method('getId'); + $container = new \Zend\ServiceManager\ServiceManager(); + $container->setService('Zend\Session\SessionManager', $sm); + $factory = new KeepAliveFactory(); + $handler = $factory($container, KeepAlive::class); + $params = new \Zend\Mvc\Controller\Plugin\Params(); + $this->assertEquals([true], $handler->handleRequest($params)); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/RecommendTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/RecommendTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c6330d1f44af7f0928581896305d4153e16597a4 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/AjaxHandler/RecommendTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Recommend test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\AjaxHandler; + +use VuFind\AjaxHandler\Recommend; + +/** + * Recommend test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class RecommendTest extends \VuFindTest\Unit\AjaxHandlerTest +{ + /** + * Test the AJAX handler's basic response. + * + * @return void + */ + public function testResponse() + { + $ss = $this->getMockService('VuFind\Session\Settings', ['disableWrite']); + $ss->expects($this->once())->method('disableWrite'); + $mockPlugin = $this->getMockService('VuFind\Recommend\RecommendInterface'); + $rm = $this->getMockService('VuFind\Recommend\PluginManager', ['get']); + $rm->expects($this->once())->method('get')->with($this->equalTo('foo')) + ->will($this->returnValue($mockPlugin)); + $r = $this->getMockService('VuFind\Search\Solr\Results'); + $viewHelper = $this->getMockService('VuFind\View\Helper\Root\Recommend'); + $view = $this + ->getMockService('Zend\View\Renderer\PhpRenderer', ['plugin']); + $view->expects($this->once())->method('plugin') + ->with($this->equalTo('recommend')) + ->will($this->returnValue($viewHelper)); + $handler = new Recommend($ss, $rm, $r, $view); + $params = $this->getParamsHelper(['mod' => 'foo']); + $this->assertEquals([null], $handler->handleRequest($params)); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php index 99f78d8e24ef7cfa900f27d215960f2f226a78ad..38bc7407e14f616ff7fd10f510a45f1f5cc0df62 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php @@ -2,7 +2,7 @@ /** * ChoiceAuth test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,9 +26,12 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\ChoiceAuth, VuFind\Auth\PluginManager, - VuFind\Db\Row\User as UserRow, Zend\Config\Config, - Zend\Http\PhpEnvironment\Request; + +use VuFind\Auth\ChoiceAuth; +use VuFind\Auth\PluginManager; +use VuFind\Db\Row\User as UserRow; +use Zend\Config\Config; +use Zend\Http\PhpEnvironment\Request; /** * ChoiceAuth test class. @@ -244,15 +247,15 @@ class ChoiceAuthTest extends \VuFindTest\Unit\TestCase */ protected function getMockPluginManager() { - $pm = new PluginManager(); + $pm = new PluginManager($this->getServiceManager()); $mockDb = $this->getMockBuilder('VuFind\Auth\Database') ->disableOriginalConstructor() ->getMock(); $mockShib = $this->getMockBuilder('VuFind\Auth\Shibboleth') ->disableOriginalConstructor() ->getMock(); - $pm->setService('Database', $mockDb); - $pm->setService('Shibboleth', $mockShib); + $pm->setService('VuFind\Auth\Database', $mockDb); + $pm->setService('VuFind\Auth\Shibboleth', $mockShib); return $pm; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php index faaad67f294b3dfdc30648df9bff77df0ad1c545..3f068b4aaaeab22029c9ae053313ea93c2c307a8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php @@ -2,7 +2,7 @@ /** * Database authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,7 +26,10 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\Database, Zend\Db\ResultSet\ResultSet, Zend\Stdlib\Parameters; + +use VuFind\Auth\Database; +use Zend\Db\ResultSet\ResultSet; +use Zend\Stdlib\Parameters; /** * Database authentication test class. @@ -249,7 +252,7 @@ class DatabaseUnitTest extends \VuFindTest\Unit\DbTestCase protected function getDatabase($table) { $tableManager = $this->getMockBuilder('VuFind\Db\Table\PluginManager') - ->setMethods(['get'])->getMock(); + ->disableOriginalConstructor()->setMethods(['get'])->getMock(); $tableManager->expects($this->once())->method('get') ->with($this->equalTo('User')) ->will($this->returnValue($table)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php index c5202ab848bc644938fba372fc42d171972601ab..ff702404ca47a9c6e7091003a1cb0357974a7f95 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php @@ -2,7 +2,7 @@ /** * ILS Authenticator Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,11 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Auth; -use VuFind\Auth\ILSAuthenticator, VuFind\Auth\Manager, - VuFind\Db\Row\User, VuFind\ILS\Connection as ILSConnection; + +use VuFind\Auth\ILSAuthenticator; +use VuFind\Auth\Manager; +use VuFind\Db\Row\User; +use VuFind\ILS\Connection as ILSConnection; /** * ILS Authenticator Manager Test Class diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/LDAPTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/LDAPTest.php index 748a2e79a237b200deb6ceff81110b99871a1425..855ffe854b8a062449ff50d0cc8bf9cd87098181 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/LDAPTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/LDAPTest.php @@ -2,7 +2,7 @@ /** * LDAP authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\LDAP, Zend\Config\Config; + +use VuFind\Auth\LDAP; +use Zend\Config\Config; /** * LDAP authentication test class. @@ -51,7 +53,7 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase if (null === $config) { $config = $this->getAuthConfig(); } - $obj = clone($this->getAuthManager()->get('LDAP')); + $obj = clone $this->getAuthManager()->get('LDAP'); $obj->setConfig($config); return $obj; } @@ -78,10 +80,11 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase * Verify that missing host causes failure. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithMissingHost() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->LDAP->host); $this->getAuthObject($config)->getConfig(); @@ -91,10 +94,11 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase * Verify that missing port causes failure. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithMissingPort() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->LDAP->port); $this->getAuthObject($config)->getConfig(); @@ -104,10 +108,11 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase * Verify that missing baseDN causes failure. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithMissingBaseDN() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->LDAP->basedn); $this->getAuthObject($config)->getConfig(); @@ -117,10 +122,11 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase * Verify that missing UID causes failure. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithMissingUid() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->LDAP->username); $this->getAuthObject($config)->getConfig(); @@ -181,10 +187,11 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => '']); $this->getAuthObject()->authenticate($request); } @@ -193,10 +200,11 @@ class LDAPTest extends \VuFindTest\Unit\DbTestCase * Test login with blank password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => '']); $this->getAuthObject()->authenticate($request); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php index abbc3ba7a015935abe0ec9a58a95aa3be6f2f28a..a27c81cdde738ca7ce5cc7896ee4ef68a1d627a7 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php @@ -2,7 +2,7 @@ /** * Authentication manager test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,9 +26,13 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\Manager, VuFind\Auth\PluginManager, - VuFind\Db\Row\User as UserRow, VuFind\Db\Table\User as UserTable, - Zend\Config\Config, Zend\Session\SessionManager; + +use VuFind\Auth\Manager; +use VuFind\Auth\PluginManager; +use VuFind\Db\Row\User as UserRow; +use VuFind\Db\Table\User as UserTable; +use Zend\Config\Config; +use Zend\Session\SessionManager; /** * Authentication manager test class. @@ -510,7 +514,15 @@ class ManagerTest extends \VuFindTest\Unit\TestCase $pm = $this->getMockPluginManager(); } $cookies = new \VuFind\Cookie\CookieManager([]); - return new Manager($config, $userTable, $sessionManager, $pm, $cookies); + $csrf = new \VuFind\Validator\Csrf( + [ + 'session' => new \Zend\Session\Container('csrf', $sessionManager), + 'salt' => 'csrftest' + ] + ); + return new Manager( + $config, $userTable, $sessionManager, $pm, $cookies, $csrf + ); } /** @@ -544,7 +556,7 @@ class ManagerTest extends \VuFindTest\Unit\TestCase */ protected function getMockPluginManager() { - $pm = new PluginManager(); + $pm = new PluginManager($this->getServiceManager()); $mockChoice = $this->getMockBuilder('VuFind\Auth\ChoiceAuth') ->disableOriginalConstructor() ->getMock(); @@ -558,10 +570,10 @@ class ManagerTest extends \VuFindTest\Unit\TestCase $mockShib = $this->getMockBuilder('VuFind\Auth\Shibboleth') ->disableOriginalConstructor() ->getMock(); - $pm->setService('ChoiceAuth', $mockChoice); - $pm->setService('Database', $mockDb); - $pm->setService('MultiILS', $mockMulti); - $pm->setService('Shibboleth', $mockShib); + $pm->setService('VuFind\Auth\ChoiceAuth', $mockChoice); + $pm->setService('VuFind\Auth\Database', $mockDb); + $pm->setService('VuFind\Auth\MultiILS', $mockMulti); + $pm->setService('VuFind\Auth\Shibboleth', $mockShib); return $pm; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/MultiAuthTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/MultiAuthTest.php index bd31556b6b58e318a3cf1f7dcb05fbfdf08ab07c..9742ad92698819fd8b2b7df0d7604318a0e07c8b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/MultiAuthTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/MultiAuthTest.php @@ -2,7 +2,7 @@ /** * MultiAuth authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\MultiAuth, Zend\Config\Config; + +use VuFind\Auth\MultiAuth; +use Zend\Config\Config; /** * LDAP authentication test class. @@ -51,14 +53,9 @@ class MultiAuthTest extends \VuFindTest\Unit\DbTestCase if (null === $config) { $config = $this->getAuthConfig(); } - $serviceLocator = new \VuFind\Auth\PluginManager( - new \Zend\ServiceManager\Config( - [ - 'abstract_factories' => ['VuFind\Auth\PluginFactory'], - ] - ) - ); - $obj = clone($this->getAuthManager()->get('MultiAuth')); + $manager = $this->getAuthManager(); + $obj = clone $manager->get('MultiAuth'); + $obj->setPluginManager($manager); $obj->setConfig($config); return $obj; } @@ -82,10 +79,11 @@ class MultiAuthTest extends \VuFindTest\Unit\DbTestCase * Verify that missing host causes failure. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testWithMissingMethodOrder() { - $this->setExpectedException('VuFind\Exception\Auth'); $config = $this->getAuthConfig(); unset($config->MultiAuth->method_order); $this->getAuthObject($config)->getConfig(); @@ -113,11 +111,11 @@ class MultiAuthTest extends \VuFindTest\Unit\DbTestCase * Test login with handler configured to load a service which does not exist. * * @return void + * + * @expectedException Zend\ServiceManager\Exception\ServiceNotFoundException */ public function testLoginWithBadService() { - $this - ->setExpectedException('Zend\ServiceManager\Exception\ServiceNotFoundException'); $config = $this->getAuthConfig(); $config->MultiAuth->method_order = 'InappropriateService,Database'; @@ -131,11 +129,11 @@ class MultiAuthTest extends \VuFindTest\Unit\DbTestCase * as an arbitrary inappropriate class). * * @return void + * + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException */ public function testLoginWithBadClass() { - $this - ->setExpectedException('Zend\ServiceManager\Exception\RuntimeException'); $config = $this->getAuthConfig(); $config->MultiAuth->method_order = get_class($this) . ',Database'; @@ -147,10 +145,11 @@ class MultiAuthTest extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => '']); $this->getAuthObject()->authenticate($request); } @@ -159,10 +158,11 @@ class MultiAuthTest extends \VuFindTest\Unit\DbTestCase * Test login with blank password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => '']); $this->getAuthObject()->authenticate($request); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php index 6bc5439a7355fc0b2da9e1a3497a09db57f4ffcd..bf05faf91a833320e986af600ffaa48d030ba482 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Auth Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Auth; + use VuFind\Auth\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Auth\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/SIP2Test.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/SIP2Test.php index 374a634d2f462bf4b480a33cd4e2207d7228510a..47961b07b2111d3f666f1d92813b1d4e064a4fba 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/SIP2Test.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/SIP2Test.php @@ -2,7 +2,7 @@ /** * SIP2 authentication test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Auth; -use VuFind\Auth\SIP2, Zend\Config\Config; + +use VuFind\Auth\SIP2; +use Zend\Config\Config; /** * SIP2 authentication test class. @@ -51,7 +53,7 @@ class SIP2Test extends \VuFindTest\Unit\DbTestCase if (null === $config) { $config = $this->getAuthConfig(); } - $obj = clone($this->getAuthManager()->get('SIP2')); + $obj = clone $this->getAuthManager()->get('SIP2'); $obj->setConfig($config); return $obj; } @@ -94,10 +96,11 @@ class SIP2Test extends \VuFindTest\Unit\DbTestCase * Test login with blank username. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankUsername() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['username' => '']); $this->getAuthObject()->authenticate($request); } @@ -106,10 +109,11 @@ class SIP2Test extends \VuFindTest\Unit\DbTestCase * Test login with blank password. * * @return void + * + * @expectedException VuFind\Exception\Auth */ public function testLoginWithBlankPassword() { - $this->setExpectedException('VuFind\Exception\Auth'); $request = $this->getLoginRequest(['password' => '']); $this->getAuthObject()->authenticate($request); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7fc9a56232843a63e0bb9e703c72e00700adf145 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Eds autocomplete test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <jochen.lienhard@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Autocomplete; + +use VuFind\Autocomplete\Eds; + +/** + * Eds autocomplete test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <jochen.lienhard@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class EdsTest extends \VuFindTest\Unit\TestCase +{ + /** + * Get a mock backend + * + * @param string $id ID of fake backend. + * + * @return \VuFindSearch\Backend\EDS\Backend + */ + protected function getMockBackend($id = 'EDS') + { + return $this->getMockBuilder('VuFindSearch\Backend\EDS\Backend') + ->setMethods(['autocomplete']) + ->disableOriginalConstructor()->getMock(); + } + + /** + * Test getSuggestions. + * + * @return void + */ + public function testGetSuggestions() + { + $backend = $this->getMockBackend(); + $eds = new Eds($backend); + $backend->expects($this->once()) + ->method('autocomplete') + ->with($this->equalTo('query'), $this->equalTo('rawqueries')) + ->will($this->returnValue([1, 2, 3])); + $this->assertEquals([1, 2, 3], $eds->getSuggestions('query')); + } + + /** + * Test getSuggestions with non-default configuration. + * + * @return void + */ + public function testGetSuggestionsWithNonDefaultConfiguration() + { + $backend = $this->getMockBackend(); + $eds = new Eds($backend); + $eds->setConfig('holdings'); + $backend->expects($this->once()) + ->method('autocomplete') + ->with($this->equalTo('query'), $this->equalTo('holdings')) + ->will($this->returnValue([4, 5])); + $this->assertEquals([4, 5], $eds->getSuggestions('query')); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php index b7f329a1cd1ecf0ce6299e556859936b844eea7f..fd435c9047e3a50368acc365b1a2732c01c2c7be 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Autocomplete Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Autocomplete; + use VuFind\Autocomplete\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Autocomplete\AutocompleteInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c1e1e54721b92841288b2433c00a752e8dc37c27 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Solr autocomplete test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Autocomplete; + +use VuFind\Autocomplete\Solr; + +/** + * Solr autocomplete test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class SolrTest extends \VuFindTest\Unit\TestCase +{ + /** + * Get mock search options. + * + * @return \VuFind\Search\Solr\Options + */ + protected function getMockOptions() + { + return $this->getMockBuilder('VuFind\Search\Solr\Options') + ->disableOriginalConstructor()->getMock(); + } + + /** + * Get mock results plugin manager. + * + * @return \VuFind\Search\Results\PluginManager + */ + protected function getMockResults() + { + $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + ->disableOriginalConstructor() + ->setMethods(['getOptions']) + ->getMock(); + $results->expects($this->any())->method('getOptions') + ->will($this->returnValue($this->getMockOptions())); + return $results; + } + + /** + * Get mock results plugin manager. + * + * @return \VuFind\Search\Results\PluginManager + */ + protected function getMockResultsPluginManager() + { + $rpm = $this->getMockBuilder('VuFind\Search\Results\PluginManager') + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + $rpm->expects($this->any())->method('get') + ->will($this->returnValue($this->getMockResults())); + return $rpm; + } + + /** + * Test that configuration is parsed correctly. + * + * @return void + */ + public function testSetConfigDefaults() + { + $solr = new Solr($this->getMockResultsPluginManager()); + $solr->setConfig(''); + $this->assertEquals(null, $this->getProperty($solr, 'handler')); + $this->assertEquals(['title'], $this->getProperty($solr, 'displayField')); + $this->assertEquals(null, $this->getProperty($solr, 'sortField')); + $this->assertEquals([], $this->getProperty($solr, 'filters')); + } + + /** + * Test that configuration is parsed correctly. + * + * @return void + */ + public function testSetConfig() + { + $solr = new Solr($this->getMockResultsPluginManager()); + $solr->setConfig('Handler:Display:Sort:FF1:FV1:FF2:FV2'); + $this->assertEquals('Handler', $this->getProperty($solr, 'handler')); + $this->assertEquals(['Display'], $this->getProperty($solr, 'displayField')); + $this->assertEquals('Sort', $this->getProperty($solr, 'sortField')); + $expected = ['FF1:FV1', 'FF2:FV2']; + $this->assertEquals($expected, $this->getProperty($solr, 'filters')); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php index 0ba75894d6bab0eae4ea21cc370f3faed62a59a7..1b0080a408d0bdf4537ad213bfdfa07601050f6a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php @@ -2,7 +2,7 @@ /** * Tag autocomplete test class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Autocomplete; + use VuFind\Autocomplete\Tag; /** @@ -73,7 +74,7 @@ class TagTest extends \VuFindTest\Unit\DbTestCase ->with($this->equalTo('foo')) ->will($this->returnValue($tags)); $tableManager = $this->getMockBuilder('VuFind\Db\Table\PluginManager') - ->setMethods(['get'])->getMock(); + ->disableOriginalConstructor()->setMethods(['get'])->getMock(); $tableManager->expects($this->once())->method('get') ->with($this->equalTo('Tags')) ->will($this->returnValue($tagTable)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Cache/Storage/Adapter/NoCacheAdapterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Cache/Storage/Adapter/NoCacheAdapterTest.php index 8d516fc7749aa6bf1bfb0f501a274f0ab1bba297..f8aba7517616fa172edca0f9d89be955969347dc 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Cache/Storage/Adapter/NoCacheAdapterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Cache/Storage/Adapter/NoCacheAdapterTest.php @@ -3,7 +3,7 @@ /** * Unit tests for VuFind NoCacheAdapter. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Cache\Storage\Adapter; -use VuFind\Cache\Storage\Adapter\NoCacheAdapter; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase; +use VuFind\Cache\Storage\Adapter\NoCacheAdapter; /** * Unit tests for VuFind NoCacheAdapter. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ -class NoCacheAdapterTest extends PHPUnit_Framework_TestCase +class NoCacheAdapterTest extends TestCase { /** * Test that an item is not stored. @@ -52,6 +52,6 @@ class NoCacheAdapterTest extends PHPUnit_Framework_TestCase { $cache = new NoCacheAdapter(); $cache->setItem('key', 'value'); - $this->assertFalse((boolean)$cache->hasItem('key')); + $this->assertFalse((bool)$cache->hasItem('key')); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php index cbfc99e7775fbd1e4205062407b0e52355445868..b1f8057cc224137af57a4674be34ccf56a1609d6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php @@ -2,7 +2,7 @@ /** * Cart Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFind\Cookie\CookieManager; /** @@ -37,7 +38,7 @@ use VuFind\Cookie\CookieManager; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class CartTest extends \PHPUnit_Framework_TestCase +class CartTest extends \PHPUnit\Framework\TestCase { /** * Mock record loader diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php index 9d4c6b1650b62c1135757b13b66004371bef4d25..277f8fb3e4ce54b528513a7afe6a3f9a3f161b00 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php @@ -2,7 +2,7 @@ /** * Config Factory Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Config; + use VuFind\Config\Locator; /** @@ -133,9 +134,8 @@ class PluginFactoryTest extends \VuFindTest\Unit\TestCase */ protected function getConfig($name) { - return $this->factory->createServiceWithName( - $this->createMock('Zend\ServiceManager\ServiceLocatorInterface'), - $name, $name + return $this->factory->__invoke( + $this->createMock('Interop\Container\ContainerInterface'), $name ); } @@ -251,6 +251,8 @@ class PluginFactoryTest extends \VuFindTest\Unit\TestCase * Test configuration is read-only. * * @return void + * + * @expectedException Zend\Config\Exception\RuntimeException */ public function testReadOnlyConfig() { @@ -258,7 +260,6 @@ class PluginFactoryTest extends \VuFindTest\Unit\TestCase $this->markTestSkipped('Could not write test configurations.'); } $config = $this->getConfig('unit-test-parent'); - $this->setExpectedException('Zend\Config\Exception\RuntimeException'); $config->Section1->z = 'bad'; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php index 6f881748799f2f269b8b6b1ef61d6ec856f5e11b..d970a9ef65c1f557de1e0fd724d1b484e9819f1c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php @@ -3,7 +3,7 @@ /** * Config CacheDecorator test class file. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -39,7 +39,7 @@ use VuFind\Config\Reader\CacheDecorator; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class CacheDecoratorTest extends \PHPUnit_Framework_TestCase +class CacheDecoratorTest extends \PHPUnit\Framework\TestCase { /** * Read config from while, new file. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php index 43363b88f3f47438fb2ae84caf1a65a6954424f0..dcb6988f85dbd181f31a213ab495892d252d22fb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php @@ -2,7 +2,7 @@ /** * Config SearchSpecsReader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Config; -use VuFind\Config\Locator, VuFind\Config\SearchSpecsReader; + +use VuFind\Config\Locator; +use VuFind\Config\SearchSpecsReader; /** * Config SearchSpecsReader Test Class @@ -92,7 +94,7 @@ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase { // The searchspecs.yaml file should define author dismax fields (among many // other things). - $reader = $this->getServiceManager()->get('VuFind\SearchSpecsReader'); + $reader = $this->getServiceManager()->get('VuFind\Config\SearchSpecsReader'); $specs = $reader->get('searchspecs.yaml'); $this->assertTrue( isset($specs['Author']['DismaxFields']) @@ -107,7 +109,7 @@ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase */ public function testMissingFileRead() { - $reader = $this->getServiceManager()->get('VuFind\SearchSpecsReader'); + $reader = $this->getServiceManager()->get('VuFind\Config\SearchSpecsReader'); $specs = $reader->get('notreallyasearchspecs.yaml'); $this->assertEquals([], $specs); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php index b8562fc06ca6bb10d806d672373b4d9fcb763aa7..9308ea2af465fd9a25f7e28f3d8993c9dc4a69ef 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php @@ -2,7 +2,7 @@ /** * Config Upgrade Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Config; + use VuFind\Config\Upgrade; /** @@ -98,7 +99,7 @@ class UpgradeTest extends \VuFindTest\Unit\TestCase . "the default theme. Your config.ini [Site] theme setting " . "has been reset to the default: bootprint3. You may need to " . "reimplement your custom theme."; - } else if ((float)$version < 2.4) { + } elseif ((float)$version < 2.4) { $expectedWarnings[] = "WARNING: This version of VuFind does not support " . "the blueprint theme. Your config.ini [Site] theme setting " . "has been reset to the default: bootprint3. You may need to " diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/VersionTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/VersionTest.php index 755825ab06d7ebaa5cd7ad92648ee73082b27306..12ef1994a0a43e42176d99f06eadbd692d1e44c1 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/VersionTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/VersionTest.php @@ -2,7 +2,7 @@ /** * Version Reader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Config; + use VuFind\Config\Version; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/WriterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/WriterTest.php index 04849e68abb05c32680e97714c45386f5c0048ec..698bcbb26e47edb5ba469000ba2b8000ffc3d129 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/WriterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/WriterTest.php @@ -2,7 +2,7 @@ /** * Config Writer Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Config; + use VuFind\Config\Writer; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WikipediaTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WikipediaTest.php index b78bdfc8f5da564ddb450bd24d388828ad5d532b..35adc059d4899f3ddabecfb10ddfc13aae082502 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WikipediaTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WikipediaTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Wikipedia connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -42,7 +42,7 @@ use Zend\Http\Client as HttpClient; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class WikipediaTest extends \PHPUnit_Framework_TestCase +class WikipediaTest extends \PHPUnit\Framework\TestCase { /** * Test processing of English-language Jane Austen entry. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php index a5b13207d37668ee9ef2bab3b2db61ba42bc1ec1..c8f9b28931f86cc1b010c5f6b67825412568589a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php @@ -3,7 +3,7 @@ /** * Unit tests for WorldCat utility connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -42,7 +42,7 @@ use Zend\Http\Client as HttpClient; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class WorldCatUtilsTest extends \PHPUnit_Framework_TestCase +class WorldCatUtilsTest extends \PHPUnit\Framework\TestCase { /** * Test related identities diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php index 9b2c7a52ec97c7b54bd7cda463b9d335ef692537..1ec05109fdcc56c80e2685c1b725ce2995d866d8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Content AuthorNotes Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\AuthorNotes\Content; + use VuFind\Content\AuthorNotes\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Content\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/AmazonTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/AmazonTest.php index 73a52f41b72b71cc491c8d468cc1ca4cd36b6b2b..de7b0abea5d2fa531c7b30cc7e6c543b7303d9a5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/AmazonTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/AmazonTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Amazon cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; + use VuFindCode\ISBN; /** @@ -38,7 +39,7 @@ use VuFindCode\ISBN; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class AmazonTest extends \PHPUnit_Framework_TestCase +class AmazonTest extends \PHPUnit\Framework\TestCase { /** * Amazon parameters diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BooksiteTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BooksiteTest.php index 80a80a5853230bffd5c7a14c2f15225419342bac..ed043cd006849ec226e3a495f026573ec2ec6135 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BooksiteTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BooksiteTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Booksite cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; -use VuFindCode\ISBN, VuFind\Content\Covers\Booksite; + +use VuFind\Content\Covers\Booksite; +use VuFindCode\ISBN; /** * Unit tests for Booksite cover loader. @@ -38,7 +40,7 @@ use VuFindCode\ISBN, VuFind\Content\Covers\Booksite; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class BooksiteTest extends \PHPUnit_Framework_TestCase +class BooksiteTest extends \PHPUnit\Framework\TestCase { /** * Test cover loading diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BuchhandelTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BuchhandelTest.php index cc0d1892a2e3dd7dd9788a3aa7d458dddef1443e..df051fae5602460e69f2d0d7cf48d180b7910d7a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BuchhandelTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/BuchhandelTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Buchhandel cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,7 +28,9 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; -use VuFindCode\ISBN, VuFind\Content\Covers\Buchhandel; + +use VuFind\Content\Covers\Buchhandel; +use VuFindCode\ISBN; /** * Unit tests for Booksite cover loader. @@ -40,7 +42,7 @@ use VuFindCode\ISBN, VuFind\Content\Covers\Buchhandel; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class BuchhandelTest extends \PHPUnit_Framework_TestCase +class BuchhandelTest extends \PHPUnit\Framework\TestCase { /** * Test cover loading diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/ContentCafeTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/ContentCafeTest.php index 8a2c3b23d646cdd3231f1a47b80412cb70747b20..ead0d94022ecb19eddc798fd0d1c56b0ac9ff20d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/ContentCafeTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/ContentCafeTest.php @@ -3,7 +3,7 @@ /** * Unit tests for ContentCafe cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,10 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; -use VuFindCode\ISBN, VuFind\Content\Covers\ContentCafe, Zend\Config\Config; + +use VuFind\Content\Covers\ContentCafe; +use VuFindCode\ISBN; +use Zend\Config\Config; /** * Unit tests for ContentCafe cover loader. @@ -38,7 +41,7 @@ use VuFindCode\ISBN, VuFind\Content\Covers\ContentCafe, Zend\Config\Config; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class ContentCafeTest extends \PHPUnit_Framework_TestCase +class ContentCafeTest extends \PHPUnit\Framework\TestCase { /** * Test cover loading diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/LibraryThingTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/LibraryThingTest.php index 99c626ce9e5c8c64f4f6cb49647d7c8fad4e4017..d53bbcd701affe33b406a4dedd68a1e6cc3ed931 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/LibraryThingTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/LibraryThingTest.php @@ -3,7 +3,7 @@ /** * Unit tests for LibraryThing cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; -use VuFindCode\ISBN, VuFind\Content\Covers\LibraryThing; + +use VuFind\Content\Covers\LibraryThing; +use VuFindCode\ISBN; /** * Unit tests for LibraryThing cover loader. @@ -38,7 +40,7 @@ use VuFindCode\ISBN, VuFind\Content\Covers\LibraryThing; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class LibraryThingTest extends \PHPUnit_Framework_TestCase +class LibraryThingTest extends \PHPUnit\Framework\TestCase { /** * Test cover loading diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/OpenLibraryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/OpenLibraryTest.php index 4de1be7fcd3ba36b0bf963458b342fc9b79fbb51..986dffa281511aad91b2f0d514e7730ab2118985 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/OpenLibraryTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/OpenLibraryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for OpenLibrary cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; -use VuFindCode\ISBN, VuFind\Content\Covers\OpenLibrary; + +use VuFind\Content\Covers\OpenLibrary; +use VuFindCode\ISBN; /** * Unit tests for OpenLibrary cover loader. @@ -38,7 +40,7 @@ use VuFindCode\ISBN, VuFind\Content\Covers\OpenLibrary; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class OpenLibraryTest extends \PHPUnit_Framework_TestCase +class OpenLibraryTest extends \PHPUnit\Framework\TestCase { /** * Test cover loading diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php index 88ded6380e81d51f36746fc89f5fefa096bc5abf..b077ebb050e9654d3580a8fd2edf3f72390a488c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Content Covers Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Covers\Content; + use VuFind\Content\Covers\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Content\AbstractCover */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/SummonTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/SummonTest.php index f25c145d81e155cefd716b389a7a3479749029f4..3e07757dfd49868993b637d9ede092964c0b71c6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/SummonTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/SummonTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Summon cover loader. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org */ namespace VuFindTest\Content\Covers; -use VuFindCode\ISBN, VuFind\Content\Covers\Summon; + +use VuFind\Content\Covers\Summon; +use VuFindCode\ISBN; /** * Unit tests for Summon cover loader. @@ -38,7 +40,7 @@ use VuFindCode\ISBN, VuFind\Content\Covers\Summon; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class SummonTest extends \PHPUnit_Framework_TestCase +class SummonTest extends \PHPUnit\Framework\TestCase { /** * Test cover loading diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php index 4870d565fd1ff58fc2a2737bff872902195251a2..92f73643e687a4f80b1b8a241c4b62274714e798 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Content Excerpts Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Excerpts\Content; + use VuFind\Content\Excerpts\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Content\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php index c4654d323ae14ee7786ce3fe2ef51b241a21f52c..1d44017a85e7aafc4d0b0f37179141750aaad876 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Content Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Content; + use VuFind\Content\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Content\Loader */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php index f80c4d04e80e68561547c6f3ed0298018a3e95e6..9784550a2237db3a86e7d58182d555e3536cc1eb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Content Reviews Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Reviews\Content; + use VuFind\Content\Reviews\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Content\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php index 8cf9d49bfc48a7ff1b65fbd60554af5c536def6e..7ee166d6df4a8b0c6627f11e43bfe2809aaa8e83 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php @@ -3,7 +3,7 @@ /** * Followup controller plugin tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php index b806f5dc1487732e7643fd4121d4d25b6dcf567f..215a23b2b7d97430d27c905492e940a01966e7e4 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php @@ -3,7 +3,7 @@ /** * New items controller plugin tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -50,7 +50,7 @@ class NewItemsTest extends TestCase */ public function testGetBibIDsFromCatalog() { - $flash = $this->createMock('Zend\Mvc\Controller\Plugin\FlashMessenger'); + $flash = $this->createMock('Zend\Mvc\Plugin\FlashMessenger\FlashMessenger'); $config = new Config(['result_pages' => 10]); $newItems = new NewItems($config); $bibs = $newItems->getBibIDsFromCatalog( @@ -66,7 +66,7 @@ class NewItemsTest extends TestCase */ public function testGetBibIDsFromCatalogWithIDLimit() { - $flash = $this->createMock('Zend\Mvc\Controller\Plugin\FlashMessenger'); + $flash = $this->createMock('Zend\Mvc\Plugin\FlashMessenger\FlashMessenger'); $flash->expects($this->once())->method('addMessage') ->with($this->equalTo('too_many_new_items'), $this->equalTo('info')); $config = new Config(['result_pages' => 10]); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php index 123c589a64ade4499282835fa5940a4a02968452..ca66e6141c711db4b1084c71f80db7d626371972 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php @@ -3,7 +3,7 @@ /** * ResultScroller controller plugin tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -380,7 +380,8 @@ class ResultScrollerTest extends TestCase * * @return ResultScroller */ - protected function getMockResultScroller($results) { + protected function getMockResultScroller($results) + { $mockManager = $this->getMockBuilder('VuFind\Search\Results\PluginManager') ->disableOriginalConstructor()->getMock(); return new ResultScrollerMock($mockManager, $results); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Cookie/ContainerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Cookie/ContainerTest.php index 519fbe65feab0742f7225b4d2316478951e0a2d3..c6ada1dd2879be23649ae6cad48bdf94c7c07792 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Cookie/ContainerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Cookie/ContainerTest.php @@ -2,7 +2,7 @@ /** * Cookie Container Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Cookie; + use VuFind\Cookie\Container; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php index 2dbb5a0b7bdf11ba8cc25c50d3413236d3c60d5f..c93f7c84d03166ca670de56fa40bcb855b74b2e6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php @@ -2,7 +2,7 @@ /** * Cover Loader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,10 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Cover; + use VuFind\Cover\Loader; use VuFindTheme\ThemeInfo; use Zend\Config\Config; -use Zend\Http\Client\Adapter\Test as TestAdapter; /** * Cover Loader Test Class @@ -133,12 +133,12 @@ class LoaderTest extends \VuFindTest\Unit\TestCase * @param array $config Configuration * @param \VuFind\Content\Covers\PluginManager $manager Plugin manager (null to create mock) * @param ThemeInfo $theme Theme info object (null to create default) - * @param \Zend\Http\Client $client HTTP client (null to create TestAdapter) + * @param \VuFindHttp\HttpService $httpService HTTP client factory * @param array|bool $mock Array of functions to mock, or false for real object * * @return Loader */ - protected function getLoader($config = [], $manager = null, $theme = null, $client = null, $mock = false) + protected function getLoader($config = [], $manager = null, $theme = null, $httpService = null, $mock = false) { $config = new Config($config); if (null === $manager) { @@ -147,18 +147,16 @@ class LoaderTest extends \VuFindTest\Unit\TestCase if (null === $theme) { $theme = new ThemeInfo($this->getThemeDir(), $this->testTheme); } - if (null === $client) { - $adapter = new TestAdapter(); - $client = new \Zend\Http\Client(); - $client->setAdapter($adapter); + if (null === $httpService) { + $httpService = $this->getMockBuilder('VuFindHttp\HttpService')->getMock(); } if ($mock) { return $this->getMockBuilder(__NAMESPACE__ . '\MockLoader') ->setMethods($mock) - ->setConstructorArgs([$config, $manager, $theme, $client]) + ->setConstructorArgs([$config, $manager, $theme, $httpService]) ->getMock(); } - return new Loader($config, $manager, $theme, $client); + return new Loader($config, $manager, $theme, $httpService); } /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/RouterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/RouterTest.php index e2d97760fcf6dbbeed8b33a4f8a0fbce96c44744..98150dbbab96f30955595b761f51ffb307db0400 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/RouterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/RouterTest.php @@ -2,7 +2,7 @@ /** * Cover Router Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Cover; -use VuFind\Cover\Router, VuFindTest\RecordDriver\TestHarness; + +use VuFind\Cover\Router; +use VuFindTest\RecordDriver\TestHarness; /** * Cover Router Test Class diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/HMACTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/HMACTest.php index 6bfc5f08c3a11b6ecac0417523ff142e40b853ce..542b1411580b932ba01182e1d88bb295c90a89fd 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/HMACTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Crypt/HMACTest.php @@ -2,7 +2,7 @@ /** * HMAC Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Crypt; + use VuFind\Crypt\HMAC; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Date/ConverterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Date/ConverterTest.php deleted file mode 100644 index 65849a77803e52707388a69e7cb03deafd7e0a4a..0000000000000000000000000000000000000000 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Date/ConverterTest.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php -/** - * VuFindDate Test Class - * - * PHP version 5 - * - * Copyright (C) Villanova University 2011. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Tests - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Page - */ -namespace VuFindTest\Date; -use VuFind\Date\Converter, VuFind\Exception\Date as DateException, - Zend\Config\Config; - -/** - * VuFindDate Test Class - * - * @category VuFind - * @package Tests - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Page - */ -class ConverterTest extends \VuFindTest\Unit\TestCase -{ - protected $savedDateFormat = null; - protected $savedTimeFormat = null; - - /** - * Test citation generation - * - * @return void - */ - public function testDates() - { - // Get current default time zone - $real_zone = date_default_timezone_get(); - - // Try all the tests in different time zones to ensure consistency: - foreach (['America/New_York', 'Europe/Helsinki'] as $zone) { - date_default_timezone_set($zone); - $this->runTests(); - } - - // Restore original time zone - date_default_timezone_set($real_zone); - } - - /** - * Support method for testDates() - * - * @return void - */ - protected function runTests() - { - // Build an object to test with (using empty configuration to ensure default - // settings): - $date = new Converter(new Config([])); - - // Try some conversions: - $this->assertEquals( - '11-29-1973', $date->convertToDisplayDate('U', 123456879) - ); - $this->assertEquals( - '11-29-1973', $date->convertToDisplayDate('U', 123456879.1234) - ); - $this->assertEquals( - '11-29-1973--16:34', - $date->convertToDisplayDateAndTime('U', 123456879, '--') - ); - $this->assertEquals( - '16:34 11-29-1973', $date->convertToDisplayTimeAndDate('U', 123456879) - ); - $this->assertEquals( - '11-29-1973', $date->convertToDisplayDate('m-d-y', '11-29-73') - ); - $this->assertEquals( - '11-29-1973', $date->convertToDisplayDate('m-d-y', '11-29-1973') - ); - $this->assertEquals( - '11-29-1973', $date->convertToDisplayDate('m-d-y H:i', '11-29-73 23:01') - ); - $this->assertEquals( - '23:01', $date->convertToDisplayTime('m-d-y H:i', '11-29-73 23:01') - ); - $this->assertEquals( - '01-02-2001', $date->convertToDisplayDate('m-d-y', '01-02-01') - ); - $this->assertEquals( - '01-02-2001', $date->convertToDisplayDate('m-d-y', '01-02-2001') - ); - $this->assertEquals( - '01-02-2001', $date->convertToDisplayDate('m-d-y H:i', '01-02-01 05:11') - ); - $this->assertEquals( - '05:11', $date->convertToDisplayTime('m-d-y H:i', '01-02-01 05:11') - ); - $this->assertEquals( - '01-02-2001', $date->convertToDisplayDate('Y-m-d', '2001-01-02') - ); - $this->assertEquals( - '01-02-2001', - $date->convertToDisplayDate('Y-m-d H:i', '2001-01-02 05:11') - ); - $this->assertEquals( - '05:11', $date->convertToDisplayTime('Y-m-d H:i', '2001-01-02 05:11') - ); - $this->assertEquals( - '01-2001', $date->convertFromDisplayDate('m-Y', '01-02-2001') - ); - - // Check for proper handling of known problems: - try { - $bad = $date->convertToDisplayDate('U', 'invalid'); - $this->fail('Expected exception did not occur'); - } catch (DateException $e) { - $this->assertTrue( - (bool)stristr($e->getMessage(), 'failed to parse time string') - ); - } - try { - $bad = $date->convertToDisplayDate('d-m-Y', '31-02-2001'); - $this->fail('Expected exception did not occur'); - } catch (DateException $e) { - $this->assertTrue( - (bool)stristr($e->getMessage(), 'parsed date was invalid') - ); - } - } -} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php index d67afbfe1206c1432170b3b45329682ae97c2fc4..dc5b4e12575be21f19d9c2ebfa5ee3aad3387f90 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * DB Table Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Db\Table; + use VuFind\Db\Table\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Db\Table\Gateway */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/UserListTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/UserListTest.php index 2ad8d58afb39f702b93294fb964dae51546145d9..2eb49650aab0f38c545db55929335d9783ac0f4c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/UserListTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/UserListTest.php @@ -2,7 +2,7 @@ /** * UserList Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Db\Table; + use VuFind\Db\Table\UserList; /** @@ -44,10 +45,11 @@ class UserListTest extends \VuFindTest\Unit\DbTestCase * list. * * @return void + * + * @expectedException VuFind\Exception\LoginRequired */ public function testLoginRequiredToCreateList() { - $this->setExpectedException('VuFind\Exception\LoginRequired'); $table = $this->getTable('UserList'); $list = $table->getNew(false); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php index 856753b19aeb805a8be5064a161a77e498c6522b..e3f57eea0bd81724920daf7402ce7b4473e35f03 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php @@ -2,7 +2,7 @@ /** * Export Support Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; -use VuFind\Export, Zend\Config\Config; + +use VuFind\Export; +use Zend\Config\Config; /** * Export Support Test Class @@ -37,7 +39,7 @@ use VuFind\Export, Zend\Config\Config; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class ExportTest extends \PHPUnit_Framework_TestCase +class ExportTest extends \PHPUnit\Framework\TestCase { /** * Test bulk options using legacy (deprecated) configuration. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php index bd4cebbdbea0d0dcd3bc13622688f0cbc214ff7d..697e2a2acc69977be3015e133a1ea0bbb089303a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Hierarchy Driver Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Hierarchy\Driver; + use VuFind\Hierarchy\Driver\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Hierarchy\Driver\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php index 78a0fc391642a96c7ee867f2623bf56f61d90e97..5389a660b5f7df6e5030f318e1363d14d9244951 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Data Source Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Hierarchy\TreeDataSource; + use VuFind\Hierarchy\TreeDataSource\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Hierarchy\TreeDataSource\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php index d105bef9b35b230d8aa04b83592b03c45ac7ded2..425ce89c9a95577a47088196d90ac338110b8830 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Hierarchy Tree Renderer Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Hierarchy\TreeRenderer; + use VuFind\Hierarchy\TreeRenderer\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Hierarchy\TreeRenderer\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/ExtendedIniNormalizerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/ExtendedIniNormalizerTest.php index 882ef20efa9fec18f45b08cd57397bc2be0cdeee..2ab5db550b19fac4a1caeabbaa9993a07a268f05 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/ExtendedIniNormalizerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/ExtendedIniNormalizerTest.php @@ -2,7 +2,7 @@ /** * ExtendedIniNormalizer Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\I18n; + use VuFind\I18n\ExtendedIniNormalizer; /** @@ -69,7 +70,7 @@ class ExtendedIniNormalizerTest extends \VuFindTest\Unit\TestCase $full = $dir . '/' . $file; if ($file != '.' && $file != '..' && is_dir($full)) { $this->checkDirectory($normalizer, $full); - } else if (substr($file, -4) == '.ini') { + } elseif (substr($file, -4) == '.ini') { $this->assertEquals( $normalizer->normalizeFileToString($full), file_get_contents($full), diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniReaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniReaderTest.php index fd11881a9c62b2c0d1e37349e70b4247b0d48ec8..9a9ce5cede8234234db38ef1d96551b545580819 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniReaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniReaderTest.php @@ -2,7 +2,7 @@ /** * ExtendedIniReader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\I18n\Translator\Loader; + use VuFind\I18n\Translator\Loader\ExtendedIniReader; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniTest.php index cda7cacca4763d63c2291ade3bc57e329236185f..25799d6672db52d478ee649f6228921935c6979a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/I18n/Translator/Loader/ExtendedIniTest.php @@ -2,7 +2,7 @@ /** * ExtendedIni translation loader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\I18n\Translator\Loader; + use VuFind\I18n\Translator\Loader\ExtendedIni; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AlephTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AlephTest.php index ee3191ff7cb1f61a5f5387e877d57a15dad10518..8126dff8e8abc93f7d765c1a40ecb95a3cbf9e5b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AlephTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AlephTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Aleph; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Aleph; class AlephTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Aleph(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AmicusTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AmicusTest.php index cd56a798a0fa53ab50ccfb99d652898f82253164..9d0555b006ee2493c307c9bd5f1bb4319bd56524 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AmicusTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/AmicusTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Amicus; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Amicus; class AmicusTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Amicus(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DAIATest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DAIATest.php index 4d83a8d70ec9f31bb58a18190b1d8b45701bed95..c1850ee7d630506b6afce9a00fb9c818b208ee65 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DAIATest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DAIATest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,12 +27,13 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; -use VuFind\ILS\Driver\DAIA; +use InvalidArgumentException; + +use VuFind\ILS\Driver\DAIA; use Zend\Http\Client\Adapter\Test as TestAdapter; -use Zend\Http\Response as HttpResponse; -use InvalidArgumentException; +use Zend\Http\Response as HttpResponse; /** * ILS driver test @@ -136,9 +137,11 @@ class DAIATest extends \VuFindTest\Unit\ILSDriverTestCase ]; /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = $this->createConnector(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php index ffacb5242126412d7d930cce09180a6e853c2ac3..62b14e0da16d740023b945e69b06466adaadc4c0 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Demo; /** @@ -57,7 +58,9 @@ class DemoTest extends \VuFindTest\Unit\TestCase ->disableOriginalConstructor()->getMock(); $this->driver = new Demo( new \VuFind\Date\Converter(), $this->createMock('VuFindSearch\Service'), - function () use ($session) { return $session; } + function () use ($session) { + return $session; + } ); $this->driver->init(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/EvergreenTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/EvergreenTest.php index 094880a90740421c842fd23f229c25927d77e2ad..13a1f98a08ac58ebec6d6210e5b699cce55dbaff 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/EvergreenTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/EvergreenTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Evergreen; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Evergreen; class EvergreenTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Evergreen(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonTest.php index bb0ef44810e5cd1dcec0349b64bc7acc283b2ce1..195293e333f327e364997b1c4af58a476b20ee90 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Horizon; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Horizon; class HorizonTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Horizon(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonXMLAPITest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonXMLAPITest.php index 3ec7bca47f347473d704ded2f508b0d5c0fc6f3b..0f1dfba14a3f83e6339cec525bdeb741d20a83bc 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonXMLAPITest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/HorizonXMLAPITest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\HorizonXMLAPI; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\HorizonXMLAPI; class HorizonXMLAPITest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new HorizonXMLAPI(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/InnovativeTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/InnovativeTest.php index b884bba151c3c400721712d6ca266074ac43857d..bf4f803cf02fbe396a59d54a9b99b483339c1794 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/InnovativeTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/InnovativeTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Innovative; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Innovative; class InnovativeTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Innovative(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/KohaTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/KohaTest.php index 8f02d3abba37730b50e899bc956779991012cbd1..af5838dc422f0f48ce5a7afb95cc17a1c73ee602 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/KohaTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/KohaTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Koha; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Koha; class KohaTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Koha(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php index 63faa7a4df0f649b39b4fc5df6d749698a6399dc..2fa754c75815d0a4c502a2ed0b3bd37655d6440b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * Copyright (C) The National Library of Finland 2014-2016. @@ -28,7 +28,8 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; -use VuFind\ILS\Driver\MultiBackend, VuFind\Config\Reader as ConfigReader; + +use VuFind\ILS\Driver\MultiBackend; use Zend\Log\Writer\Mock; /** @@ -47,12 +48,14 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase * Test that driver complains about missing configuration. * * @return void + * + * @expectedException VuFind\Exception\ILS */ public function testMissingConfiguration() { - $this->setExpectedException('VuFind\Exception\ILS'); $test = new MultiBackend( - new \VuFind\Config\PluginManager(), $this->getMockILSAuthenticator(), + new \VuFind\Config\PluginManager($this->getServiceManager()), + $this->getMockILSAuthenticator(), $this->getMockSM() ); $test->init(); @@ -621,7 +624,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase public function testDefaultDriver() { //Case: The parameters let it know what driver to use - //Result: return the function results for that driver + //Result: return the function results for that driver $patron = $this->getPatron('username', 'institution'); $ILS = $this->getMockILS('Voyager', ['getMyTransactions', 'init']); @@ -646,7 +649,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $this->assertTrue($returnVal); //Case: There is a default driver set in the configuration - //Result: return the function results for that driver + //Result: return the function results for that driver // We need to clear patron login information so that MultiBackend has to // fall back on the defaultDriver implementation @@ -889,7 +892,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $patron['cat_username'] ); - $this->setExpectedException('VuFind\Exception\ILS'); + $this->expectException('VuFind\Exception\ILS'); $driver->patronLogin("bad", "info"); } @@ -917,9 +920,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $result = $driver->getMyTransactions($this->getPatron('username', 'd2')); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getMyTransactions( $this->getPatron('username', 'invalid') ); @@ -957,9 +959,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getRenewDetails( [ 'id' => 'invalid.loanid' @@ -1001,9 +1002,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->renewMyItems( ['patron' => $this->getPatron('username', 'invalid')] ); @@ -1033,9 +1033,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $result = $driver->getMyFines($this->getPatron('username', 'd2')); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getMyFines($this->getPatron('username', 'invalid')); } @@ -1063,9 +1062,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $result = $driver->getMyHolds($this->getPatron('username', 'd2')); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getMyHolds($this->getPatron('username', 'invalid')); } @@ -1103,9 +1101,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals([], $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getMyStorageRetrievalRequests( $this->getPatron('username', 'invalid') ); @@ -1281,9 +1278,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals([], $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getPickUpLocations( $this->getPatron('username', 'invalid'), ['id' => '1'] @@ -1330,9 +1326,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertFalse($result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getDefaultPickUpLocation( $this->getPatron('username', 'invalid'), ['id' => '1'] @@ -1379,9 +1374,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals([], $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getRequestGroups( '1', $this->getPatron('username', 'invalid') @@ -1428,9 +1422,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertFalse($result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getDefaultRequestGroup( $this->getPatron('username', 'invalid'), ['id' => '1'] @@ -1489,9 +1482,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $result ); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->placeHold( [ 'patron' => $this->getPatron('username', 'invalid'), @@ -1548,9 +1540,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->cancelHolds( [ 'patron' => $this->getPatron('username', 'invalid'), @@ -1586,9 +1577,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getCancelHoldDetails( ['id' => 'invalid.1', 'item_id' => 2] ); @@ -1646,9 +1636,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $result ); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->placeStorageRetrievalRequest( [ 'patron' => $this->getPatron('username', 'invalid'), @@ -1705,9 +1694,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->cancelStorageRetrievalRequests( [ 'patron' => $this->getPatron('username', 'invalid'), @@ -1743,9 +1731,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getCancelStorageRetrievalRequestDetails( ['id' => 'invalid.1', 'item_id' => 2] ); @@ -1798,9 +1785,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals(true, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->checkILLRequestIsValid( 'invalid.bibid', ['id' => 'invalid.itemid'], @@ -1845,9 +1831,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getILLPickupLibraries( '1', $this->getPatron('username', 'invalid') @@ -1893,9 +1878,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getILLPickupLocations( '1', '2', @@ -1954,9 +1938,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected2, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->placeILLRequest( [ 'patron' => $this->getPatron('username', 'invalid'), @@ -1993,9 +1976,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $result = $driver->getMyILLRequests($this->getPatron('username', 'd3')); $this->assertEquals([], $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getMyILLRequests( $this->getPatron('username', 'invalid') ); @@ -2049,9 +2031,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->cancelILLRequests( [ 'patron' => $this->getPatron('username', 'invalid'), @@ -2087,9 +2068,8 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ); $this->assertEquals($expected, $result); - $this->setExpectedException( - 'VuFind\Exception\ILS', 'No suitable backend driver found' - ); + $this->expectException('VuFind\Exception\ILS'); + $this->expectExceptionMessage('No suitable backend driver found'); $result = $driver->getCancelILLRequestDetails( ['id' => 'invalid.1', 'item_id' => 2] ); @@ -2142,7 +2122,6 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $this->setProperty($driver, 'ilsAuth', $mockAuth); $result = $driver->getConfig('Holds'); $this->assertEquals($expected1, $result); - } /** @@ -2170,40 +2149,40 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $this->setProperty($driver, 'drivers', $drivers); //Case: No driver info in params, but default driver has method - //Result: A return of false + //Result: A return of false $this->setProperty($driver, 'defaultDriver', 'testing3'); $methodReturn = $driver->supportsMethod('fail', null); $this->assertFalse($methodReturn); //Case: No driver info in params, though default driver has method - //Result: A return of true + //Result: A return of true $methodReturn = $driver->supportsMethod('getStatus', null); $this->assertTrue($methodReturn); $this->setProperty($driver, 'defaultDriver', null); //Case: Instance to use is in parameters but does not have method - //Result: A return of false + //Result: A return of false $patron = [$this->getPatron('username', 'testing3')]; $methodReturn = $driver->supportsMethod('fail', $patron); $this->assertFalse($methodReturn); //Case: Instance to use is in parameters and has method - //Result: A return of true + //Result: A return of true $methodReturn = $driver->supportsMethod('getStatus', $patron); $this->assertTrue($methodReturn); //Case: No parameters are given - //Result: A return of true + //Result: A return of true $methodReturn = $driver->supportsMethod('getStatus', null); $this->assertTrue($methodReturn); //Case: getLoginDrivers and getDefaultLoginDriver are always supported - //Result: A return of true + //Result: A return of true $methodReturn = $driver->supportsMethod('getLoginDrivers', null); $this->assertTrue($methodReturn); @@ -2251,9 +2230,9 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase function ($param) use ($voyager, $demo, $dummyILS) { if ($param == 'Voyager') { return $voyager; - } else if ($param == 'Demo') { + } elseif ($param == 'Demo') { return $demo; - } else if ($param == 'DummyILS') { + } elseif ($param == 'DummyILS') { return $dummyILS; } return null; @@ -2397,7 +2376,9 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase [ new \VuFind\Date\Converter(), $this->createMock('VuFindSearch\Service'), - function () use ($session) { return $session; } + function () use ($session) { + return $session; + } ] )->getMock(); } @@ -2422,7 +2403,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase ->setConstructorArgs([new \VuFind\Date\Converter()]) ->getMock(); } - } catch(\Exception $e) { + } catch (\Exception $e) { $mock = $this->getMockBuilder(__NAMESPACE__ . '\\' . $type . 'Mock') ->setMethods($methods)->getMock(); } @@ -2464,7 +2445,6 @@ class DummyILS extends \VuFind\ILS\Driver\AbstractBase */ public function init() { - return; } /** @@ -2541,66 +2521,87 @@ trait ILSMockTrait public function cancelHolds($cancelDetails) { } + public function cancelILLRequests($cancelDetails) { } + public function cancelStorageRetrievalRequests($cancelDetails) { } + public function checkRequestIsValid($id, $data, $patron) { } + public function checkILLRequestIsValid($id, $data, $patron) { } + public function checkStorageRetrievalRequestIsValid($id, $data, $patron) { } + public function getCancelHoldDetails($holdDetails) { } + public function getCancelILLRequestDetails($holdDetails) { } + public function getCancelStorageRetrievalRequestDetails($holdDetails) { } + public function getConfig($function, $params = null) { } + public function getDefaultPickUpLocation($patron = false, $holdDetails = null) { } + public function getDefaultRequestGroup($patron = false, $holdDetails = null) { } + public function getMyILLRequests($patron) { } + public function getILLPickUpLibraries($patron = false, $holdDetails = null) { } + public function getILLPickUpLocations($id, $pickupLib, $patron) { } + public function getPickUpLocations($patron = false, $holdDetails = null) { } + public function getRenewDetails($checkoutDetails) { } + public function getRequestGroups($bibId = null, $patron = null, $holdDetails = null) { } + public function placeHold($holdDetails) { } + public function placeILLRequest($holdDetails) { } + public function placeStorageRetrievalRequest($details) { } + public function renewMyItems($renewDetails) { } @@ -2616,7 +2617,9 @@ class VoyagerMock extends \VuFind\ILS\Driver\Voyager class VoyagerNoSupportMock extends \VuFind\ILS\Driver\Voyager { use ILSMockTrait; - public function supportsMethod(...$args) { + + public function supportsMethod(...$args) + { return false; } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NewGenLibTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NewGenLibTest.php index 8eb6ca46a7a4ddbb11bf79f15f713716a0232dd0..e7951bedd699afdebfcad9f59a38e19cb1f630a1 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NewGenLibTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NewGenLibTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\NewGenLib; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\NewGenLib; class NewGenLibTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new NewGenLib(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php index 6f33ccbed56bc96dedce3ecd99f5cf66104b09e6..b318f66506d28b3c4b3bd6eeb6d66f430e47ff45 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\NoILS; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php index c97d8f54ec7d6ae6408f5b391232fb144fadcd60..e4ad2042867f40ba7417253958ed4b7234684a4a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,12 +27,13 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; -use VuFind\ILS\Driver\PAIA; +use InvalidArgumentException; + +use VuFind\ILS\Driver\PAIA; use Zend\Http\Client\Adapter\Test as TestAdapter; -use Zend\Http\Response as HttpResponse; -use InvalidArgumentException; +use Zend\Http\Response as HttpResponse; /** * ILS driver test @@ -292,13 +293,13 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase protected $profileTestResult = [ 'firstname' => "Nobody", 'lastname' => "Nothing", - 'address1' => NULL, - 'address2' => NULL, - 'city' => NULL, - 'country' => NULL, - 'zip' => NULL, - 'phone' => NULL, - 'group' => NULL, + 'address1' => null, + 'address2' => null, + 'city' => null, + 'country' => null, + 'zip' => null, + 'phone' => null, + 'group' => null, 'expires' => "12-31-9999", 'statuscode' => 0, 'canWrite' => true @@ -321,9 +322,11 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = $this->createConnector(); } @@ -458,6 +461,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase $this->assertEquals(false, $result_expired); $this->assertEquals(false, $resultStorage_expired); } + /** * Test * @@ -538,14 +542,14 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase $this->assertEquals($this->renewTestResult, $result); - /* TODO: make me work - $conn_fail = $this->createConnector('renew_error.json'); - $connfail->setConfig($this->validConfig); - $conn_fail->init(); - $result_fail = $conn_fail->renewMyItems($renew_request); + /* TODO: make me work + $conn_fail = $this->createConnector('renew_error.json'); + $connfail->setConfig($this->validConfig); + $conn_fail->init(); + $result_fail = $conn_fail->renewMyItems($renew_request); - $this->assertEquals($this->failedRenewTestResult, $result_fail); - */ + $this->assertEquals($this->failedRenewTestResult, $result_fail); + */ } /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php index 391c3a00e82e13049336dfa0a8a93a9ab67514f0..5dd4651532613545fc8f93220b739e0fc9e8b329 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * ILS Driver Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\ILS\Driver\DriverInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PolarisTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PolarisTest.php index 874b61c48ecaeb5176b3e01d2f65630e14ba8391..ea4c93d9e40902af004f2861ba3b03e2140f1c10 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PolarisTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PolarisTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Polaris; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Polaris; class PolarisTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Polaris(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SampleTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SampleTest.php index 7111c3fa86f50ca62b666427938ff7dec40dd43b..c478eda1c110f54caba608e2a7ea890bfdee38ed 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SampleTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SampleTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Sample; /** @@ -39,12 +40,19 @@ use VuFind\ILS\Driver\Sample; */ class SampleTest extends \VuFindTest\Unit\TestCase { + /** + * Driver object. + * + * @var Sample + */ protected $driver; /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Sample(); $this->driver->init(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php index d5682b730702c7e92bb343bfeab15b684cbcb79b..5ffbe0d459a8d119018001d73a1e3220074c06c8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Symphony; /** @@ -39,12 +40,19 @@ use VuFind\ILS\Driver\Symphony; */ class SymphonyTest extends \VuFindTest\Unit\TestCase { + /** + * Driver object + * + * @var Symphony + */ protected $driver; /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $loader = $this->getMockBuilder('VuFind\Record\Loader') ->disableOriginalConstructor()->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/UnicornTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/UnicornTest.php index cc24dee02b44b4ba98a0a08c44ad668fa40cf22f..e99608f905cc79ff332b588eb27c78cab84c60ab 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/UnicornTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/UnicornTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Unicorn; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Unicorn; class UnicornTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Unicorn(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VirtuaTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VirtuaTest.php index 5e67b6222b978ce3fd9b8eb9dd90b837460831fe..f63a87060112340b5f2e02b4ed916a4ffd83d95e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VirtuaTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VirtuaTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Virtua; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Virtua; class VirtuaTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Virtua(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerRestfulTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerRestfulTest.php index ba32ce436471cc4e37e30199268a97321ec59ee2..7226c5c8da3d961cc6bc141e8d8e967980932caa 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerRestfulTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerRestfulTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\VoyagerRestful; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\VoyagerRestful; class VoyagerRestfulTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new VoyagerRestful(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerTest.php index 018363a1e2a0c822b13c71ad423d3c24b8e1f369..947c5c74b1a2043da070916154c00b903027c2a6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/VoyagerTest.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\Voyager; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\Voyager; class VoyagerTest extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new Voyager(new \VuFind\Date\Converter()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php index 1ef3834f39c67fb05c2b4e2e0160733a482156e6..799088000b19b1ffb3c03985fe7ae6b9745c1496 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/XCNCIP2Test.php @@ -2,7 +2,7 @@ /** * ILS driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\ILS\Driver; + use VuFind\ILS\Driver\XCNCIP2; /** @@ -40,9 +41,11 @@ use VuFind\ILS\Driver\XCNCIP2; class XCNCIP2Test extends \VuFindTest\Unit\ILSDriverTestCase { /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->driver = new XCNCIP2(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php index 44925b4fbd90bcee7bd949892ff684fbb6b1f517..1b96dd4f93256c9c7aa6c01fb502d99cda03dc0e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php @@ -2,7 +2,7 @@ /** * Logger Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Log; + use VuFind\Log\Logger; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php index 5ec74e6fa07163704f56e993fc3693c949a5fa52..e61579f131c3b6b226c0c38b2561d558c8c88f83 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php @@ -2,7 +2,7 @@ /** * Mailer Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Mailer; + use VuFind\Mailer\Mailer; use Zend\Mail\Address; use Zend\Mail\AddressList; @@ -124,6 +125,29 @@ class MailerTest extends \VuFindTest\Unit\TestCase $mailer->send($list, 'from@example.com', 'subject', 'body'); } + /** + * Test sending an email using a from address override. + * + * @return void + */ + public function testSendWithFromOverride() + { + $callback = function ($message) { + $fromString = $message->getFrom()->current()->toString(); + return '<to@example.com>' == $message->getTo()->current()->toString() + && '<me@example.com>' == $message->getReplyTo()->current()->toString() + && 'me <no-reply@example.com>' == $fromString + && 'body' == $message->getBody() + && 'subject' == $message->getSubject(); + }; + $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport->expects($this->once())->method('send')->with($this->callback($callback)); + $address = new Address('me@example.com'); + $mailer = new Mailer($transport); + $mailer->setFromAddressOverride('no-reply@example.com'); + $mailer->send('to@example.com', $address, 'subject', 'body'); + } + /** * Test bad to address. * @@ -139,6 +163,23 @@ class MailerTest extends \VuFindTest\Unit\TestCase $mailer->send('bad@bad', 'from@example.com', 'subject', 'body'); } + /** + * Test bad reply-to address. + * + * @return void + * + * @expectedException VuFind\Exception\Mail + * @expectedExceptionMessage Invalid Reply-To Email Address + */ + public function testBadReplyTo() + { + $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $mailer = new Mailer($transport); + $mailer->send( + 'good@good.com', 'from@example.com', 'subject', 'body', null, 'bad@bad' + ); + } + /** * Test empty to address. * @@ -291,6 +332,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase class MockEmailRenderer extends \Zend\View\Renderer\PhpRenderer { - public function partial($template, $driver) { + public function partial($template, $driver) + { } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php index a4c6ba9c288f0a67f1222d891c9c268d29b0328f..98e7bd1a61d379912573b380eef773691ba752e4 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php @@ -2,7 +2,7 @@ /** * IpAddressUtils Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Net; + use VuFind\Net\IpAddressUtils; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php index b1edff494fdcbc71c8be78e25bfc7448039052a9..028e50ba26121ac0b4defebee08d2f5c4feb354c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php @@ -3,7 +3,7 @@ /** * OAI-PMH server unit test. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php index 932a65e24cf71270db05c5dd6bfe92933e8f06d7..c7f5e3113a17f7470eefe6dd7b452fcff5ba0b45 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php @@ -2,7 +2,7 @@ /** * QR Code Loader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\QRCode; + use VuFind\QRCode\Loader; use VuFindTheme\ThemeInfo; use Zend\Config\Config; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php index a32069a75542b9bffe266633e969300ddedeb0d0..4d0644325249ea12b7a3081f6e1d46f0a7d5498c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php @@ -2,7 +2,7 @@ /** * CollectionSideFacets recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\CollectionSideFacets; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsDeferredTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsDeferredTest.php index 69c637449e36a6512a53e226db1d832c555dc34f..1605fe3e86ea493f7c47acfa6bc78e48be755d86 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsDeferredTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsDeferredTest.php @@ -2,7 +2,7 @@ /** * EuropeanaResultsDeferred recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php index ae9b739a6dfb830d4ed9667dabee55d0d6eaefe4..c62f2368b266cc4b21863f1d070b55758b3c1837 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php @@ -3,7 +3,7 @@ /** * EuropeanaResults tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php index 749ba70b36a7f538e9bd5fdff3299a54aa8d6bc9..738dfcdf999c03a1a74d0329d7fb9271f5bd47a4 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php @@ -2,7 +2,7 @@ /** * ExpandFacets recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\ExpandFacets; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php index d32819c3d7c7f1a528ba093f9844c212f8f0951d..2999bbd7c80b5cc1a9390527aa0d711edf9e9f26 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php @@ -2,7 +2,7 @@ /** * FacetCloud recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\FacetCloud; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php index d0e3a8d7cf969ccf370f31fca662360925f2a31f..526ef0baf8a1a79c5de805bd9114875989f3fcc5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php @@ -2,7 +2,7 @@ /** * FavoriteFacets recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\FavoriteFacets; /** @@ -51,6 +52,7 @@ class FavoriteFacetsTest extends \VuFindTest\Unit\TestCase $params->expects($this->exactly(0))->method('addFacet'); // no facets are expected in this case $this->getFavoriteFacets($results, 'disabled'); } + /** * Test facet initialization with enabled tags. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/OpenLibrarySubjectsDeferredTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/OpenLibrarySubjectsDeferredTest.php index 7eac330748e36dce791bda1aae71da1a696ed792..5a7548c970bc4c03b6359217ee1803ac6b146fa5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/OpenLibrarySubjectsDeferredTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/OpenLibrarySubjectsDeferredTest.php @@ -2,7 +2,7 @@ /** * OpenLibrarySubjectsDeferred recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php index 56fd205dd0dfa5c46fb5f126bb72fdc7acd6363d..3c652a665d8a1bca92d0dfb441739e929c1b2e15 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Recommend Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertFalse($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Recommend\RecommendInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php index 27486bcee97bf3da604819091496c2199e00b13c..380e48d15b4770b9fd570023f64694527512f833 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php @@ -3,7 +3,7 @@ /** * Random Recommend tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,6 +28,7 @@ */ namespace VuFindTest\Recommend; +use PHPUnit\Framework\Assert; use VuFind\Recommend\RandomRecommend as Random; use VuFindTest\Unit\TestCase as TestCase; @@ -65,31 +66,21 @@ class RandomRecommendTest extends TestCase //[backend]:[limit]:[display mode]:[random mode]:[minimumset]:[facet1]:[facetvalue1] $this->recommend->setConfig("SolrWeb:5:mixed:disregard:20:facet1:value1:facet2:value2"); $this->assertEquals( - "SolrWeb", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'backend' - ) + "SolrWeb", Assert::readAttribute($this->recommend, 'backend') ); $this->assertEquals( - "5", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'limit' - ) + "5", Assert::readAttribute($this->recommend, 'limit') ); $this->assertEquals( - "mixed", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'displayMode' - ) + "mixed", Assert::readAttribute($this->recommend, 'displayMode') ); $this->assertEquals( - "disregard", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'mode' - ) + "disregard", Assert::readAttribute($this->recommend, 'mode') ); $this->assertEquals( - "20", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'minimum' - ) + "20", Assert::readAttribute($this->recommend, 'minimum') ); - $filters = \PHPUnit_Framework_Assert::readAttribute($this->recommend, 'filters'); + $filters = Assert::readAttribute($this->recommend, 'filters'); $this->assertInternalType("array", $filters); $this->assertCount(2, $filters); $this->assertEquals("facet1:value1", $filters[0]); @@ -106,34 +97,22 @@ class RandomRecommendTest extends TestCase //[backend]:[limit]:[display mode]:[random mode]:[minimumset]:[facet1]:[facetvalue1] $this->recommend->setConfig(''); $this->assertEquals( - "Solr", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'backend' - ) + "Solr", Assert::readAttribute($this->recommend, 'backend') ); $this->assertEquals( - "10", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'limit' - ) + "10", Assert::readAttribute($this->recommend, 'limit') ); $this->assertEquals( - "standard", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'displayMode' - ) + "standard", Assert::readAttribute($this->recommend, 'displayMode') ); $this->assertEquals( - "retain", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'mode' - ) + "retain", Assert::readAttribute($this->recommend, 'mode') ); $this->assertEquals( - "0", \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'minimum' - ) + "0", Assert::readAttribute($this->recommend, 'minimum') ); $this->assertEquals( - [], \PHPUnit_Framework_Assert::readAttribute( - $this->recommend, 'filters' - ) + [], Assert::readAttribute($this->recommend, 'filters') ); } @@ -149,8 +128,8 @@ class RandomRecommendTest extends TestCase $recommend = new Random($service, $paramManager); // Use Solr since some Base components are abstract: - $params = $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr'); + $params = $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr'); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); $request = $this->createMock('\Zend\StdLib\Parameters'); @@ -181,14 +160,14 @@ class RandomRecommendTest extends TestCase ->with($this->equalTo("Solr")) ->will( $this->returnValue( - $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr') + $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr') ) ); // Use Solr since some Base components are abstract: - $params = $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr'); + $params = $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr'); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); $request = $this->createMock('\Zend\StdLib\Parameters'); @@ -214,8 +193,8 @@ class RandomRecommendTest extends TestCase $records = ["1", "2", "3", "4", "5"]; // Use Solr since some Base components are abstract: - $results = $this->getServiceManager()->get('VuFind\SearchResultsPluginManager') - ->get('Solr'); + $results = $this->getServiceManager() + ->get('VuFind\Search\Results\PluginManager')->get('Solr'); $params = $results->getParams(); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); @@ -252,8 +231,8 @@ class RandomRecommendTest extends TestCase $records = ["1", "2", "3", "4", "5"]; // Use Solr since some Base components are abstract: - $results = $this->getServiceManager()->get('VuFind\SearchResultsPluginManager') - ->get('Solr'); + $results = $this->getServiceManager() + ->get('VuFind\Search\Results\PluginManager')->get('Solr'); $params = $results->getParams(); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php index 1f93997559026819626cfdc4847acb81945337a2..8fe36e25349108d490710f84469041f89fb67ced 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php @@ -2,7 +2,7 @@ /** * SideFacets recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\SideFacets; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonBestBetsDeferredTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonBestBetsDeferredTest.php index 91972c247cbe955fce68c7f4e9e9e9c2744b81ad..fe3bd28894e735154f2e09f4777e1578c8e5136b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonBestBetsDeferredTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonBestBetsDeferredTest.php @@ -2,7 +2,7 @@ /** * SummonBestBetsDeferred recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonDatabasesDeferredTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonDatabasesDeferredTest.php index ae0a5d751ed3f35208528d92e446df6fbc599ba7..8adee83d664e50e9243ef2031ec8e323b8ad7c8f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonDatabasesDeferredTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonDatabasesDeferredTest.php @@ -2,7 +2,7 @@ /** * SummonDatabasesDeferred recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php index 6e14b1a74dd0f649435aaf61de33b82ee5b1360f..1b1de8f9cf5ff376a4e18a7a5c55bf5fb40dcfea 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php @@ -2,7 +2,7 @@ /** * SummonResultsDeferred recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php index d9ec2632b615be31c503123677c6f018a5f189be..ac31c69799a0bf57e527a6675d5eae8733ef85da 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php @@ -2,7 +2,7 @@ /** * SwitchQuery recommendation module Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Recommend; + use VuFind\Recommend\SwitchQuery; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php index 0a7ad313deafe95cadaf9daf1d3ef7fe9e4f3e5b..815f3bf47d778559aea76f08fbd2d874ea65be77 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php @@ -3,7 +3,7 @@ /** * Record cache tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2015. @@ -29,6 +29,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Record; + use VuFind\Record\Cache; use VuFindTest\Unit\TestCase as TestCase; @@ -229,7 +230,7 @@ class CacheTest extends TestCase /** * Create Record Table * - * @return PHPUnit_Framework_MockObject_MockObject + * @return PHPUnit\Framework\MockObject\MockObject */ protected function getRecordTable() { @@ -280,7 +281,7 @@ class CacheTest extends TestCase /** * Create a Record Factory Manager * - * @return PHPUnit_Framework_MockObject_MockObject + * @return PHPUnit\Framework\MockObject\MockObject */ protected function getRecordFactoryManager() { @@ -320,7 +321,7 @@ class CacheTest extends TestCase * @param string $id id * @param string $source source * - * @return PHPUnit_Framework_MockObject_MockObject + * @return PHPUnit\Framework\MockObject\MockObject */ protected function getDriver($id = 'test', $source = 'Solr') { diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php index 99ce6b1f5dde62c394aeef83faa2ee0ec8f28959..fa903566315d9fb8c57767fc8372de58a2c9373d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php @@ -3,7 +3,7 @@ /** * Record loader tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php index 1a4be2c7d2b6f8ed61a043bf2c4e609ee6f4e137..8d52579f18c0455c1275c20e1285e48941ee37ca 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php @@ -3,7 +3,7 @@ /** * Record router tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,8 +30,8 @@ namespace VuFindTest\Record; use VuFind\Record\Router; use VuFind\RecordDriver\AbstractBase as RecordDriver; -use Zend\Config\Config; use VuFindTest\Unit\TestCase as TestCase; +use Zend\Config\Config; /** * Record router tests. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php index fa79725ceef269280005e96156a9083c2b8e68e1..27e833205ced22be4226e19f1283730bf5aab439 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * RecordDriver Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\RecordDriver; + use VuFind\RecordDriver\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertFalse($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\RecordDriver\AbstractBase */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/Response/PublicationDetailsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/Response/PublicationDetailsTest.php index c40859a6edaf2169e98d6d5d877cce75497107e0..07f812d2b4c49f5d7c7aaa5b5f4a2f07544c4880 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/Response/PublicationDetailsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/Response/PublicationDetailsTest.php @@ -2,7 +2,7 @@ /** * SolrMarc Record Driver Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\RecordDriver\Response; + use VuFind\RecordDriver\Response\PublicationDetails; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php index 6ebdcf2735add745b6f130749201c2428c44e3e4..e01a7450d3888b728af5c373104eb44e171d7198 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php @@ -2,7 +2,7 @@ /** * SolrDefault Record Driver Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\RecordDriver; + use VuFind\RecordDriver\SolrDefault; /** @@ -100,6 +101,22 @@ class SolrDefaultTest extends \VuFindTest\Unit\TestCase $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rft.creator=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.format=Thingie&rft.language=Italian', $driver->getOpenUrl()); } + /** + * Test Dublin Core conversion. + * + * @return void + */ + public function testDublinCore() + { + $expected = <<<XML +<?xml version="1.0"?> +<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd"><dc:title>La congiura dei Principi Napoletani 1701 : (prima e seconda stesura) /</dc:title><dc:creator>Vico, Giambattista, 1668-1744.</dc:creator><dc:creator>Pandolfi, Claudia.</dc:creator><dc:language>Italian</dc:language><dc:language>Latin</dc:language><dc:publisher>Centro di Studi Vichiani,</dc:publisher><dc:date>1992</dc:date><dc:subject>Naples (Kingdom) History Spanish rule, 1442-1707 Sources</dc:subject></oai_dc:dc> + +XML; + $xml = $this->getDriver()->getXML('oai_dc'); + $this->assertEquals($expected, $xml); + } + /** * Get a record driver with fake data. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php index 458895121e37ba84733e5442504361d3e768e27c..471e108c182430e839bcd11c86e69362a685d6cb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrMarcTest.php @@ -2,7 +2,7 @@ /** * SolrMarc Record Driver Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php index bd1877dff58c4a4d7a785e4369d40858d952001c..df6b526337ba404d5bf6285dbafa4931c64a99f6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * RecordTab Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\RecordTab; + use VuFind\RecordTab\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\RecordTab\TabInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php index 2eef50e1130576a05ad22ccc9f6bfee6aaf68613..8c15dd964e5aa6e628773729ca1fbc7a7cfa1236 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Related Items Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Related; + use VuFind\Related\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertFalse($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Related\RelatedInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php index 34f5c0af8ec18aa9099fd9e09d27d852f8c3552f..2edd9b59ea248e33edc85f9b05f2da9d966e9237 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php @@ -2,7 +2,7 @@ /** * Similar Related Items Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Related; + use VuFind\Related\Similar; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php index d1bc18b54a829a0d74b71ead4be6eadf5ddd1426..83ca8b1ec20c00494349aff1d6daf22a51763dae 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php @@ -2,7 +2,7 @@ /** * WorldCat Similar Related Items Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Related; -use VuFind\Related\WorldCatSimilar, VuFindSearch\Query\Query; + +use VuFind\Related\WorldCatSimilar; +use VuFindSearch\Query\Query; /** * WorldCat Similar Related Items Test Class diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Reserves/CsvReaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Reserves/CsvReaderTest.php index 062644f74306105759143d8b96aae6e9249ea3e1..59e451a69825c181984b29b7887ba056bf76f1da 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Reserves/CsvReaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Reserves/CsvReaderTest.php @@ -2,7 +2,7 @@ /** * Course Reserves CSV Loader Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Reserves; + use VuFind\Reserves\CsvReader; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/EzbTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/EzbTest.php index 69245635a804513da0f0b0b62c6da82d1d916302..d2ac85651ba6135dedec207a266d7c693d101c9a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/EzbTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/EzbTest.php @@ -2,7 +2,7 @@ /** * Ezb resolver driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Leipzig University Library 2015. * @@ -27,12 +27,13 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Resolver\Driver; -use VuFind\Resolver\Driver\Ezb; +use InvalidArgumentException; + +use VuFind\Resolver\Driver\Ezb; use Zend\Http\Client\Adapter\Test as TestAdapter; -use Zend\Http\Response as HttpResponse; -use InvalidArgumentException; +use Zend\Http\Response as HttpResponse; /** * Ezb resolver driver test diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php index 0bf83a09dd5eb44cbc8cdfc8ee2eec2c19938e8a..26d7391bfcc32ce625b4994ef28730465ab41920 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Resolver\Driver Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Resolver\Driver; + use VuFind\Resolver\Driver\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Resolver\Driver\DriverInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/RediTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/RediTest.php index fd520b6a1c390e1d8fa8c9cd8d57b9e19f7d5248..eb2e8505644eaee7b43c4885966ff7104de0fe8b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/RediTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/RediTest.php @@ -2,7 +2,7 @@ /** * Redi resolver driver test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Leipzig University Library 2015. * @@ -27,12 +27,13 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\Resolver\Driver; -use VuFind\Resolver\Driver\Redi; +use InvalidArgumentException; + +use VuFind\Resolver\Driver\Redi; use Zend\Http\Client\Adapter\Test as TestAdapter; -use Zend\Http\Response as HttpResponse; -use InvalidArgumentException; +use Zend\Http\Response as HttpResponse; /** * Redi resolver driver test diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php index 7cb020fc6a24da2540d991d29369dec533094d4f..acaedee09ac977c6397084b8cc0fc8c79114219f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php @@ -2,7 +2,7 @@ /** * Dynamic Role Provider Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Role; -use VuFind\Role\DynamicRoleProvider, VuFind\Role\PermissionProvider\PluginManager; + +use VuFind\Role\DynamicRoleProvider; +use VuFind\Role\PermissionProvider\PluginManager; /** * Dynamic Role Provider Test Class @@ -99,7 +101,7 @@ class DynamicRoleProviderTest extends \VuFindTest\Unit\TestCase */ protected function getFakePluginManager() { - $pm = new PluginManager(); + $pm = new PluginManager($this->getServiceManager()); foreach (['a', 'b', 'c'] as $name) { $pm->setService($name, $this->createMock('VuFind\Role\PermissionProvider\PermissionProviderInterface')); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionDeniedManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionDeniedManagerTest.php index ee0156e004700b3128caedd62d285f351f612969..146ca15572a2bea12baedb7cc8e8aad757955d78 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionDeniedManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionDeniedManagerTest.php @@ -2,7 +2,7 @@ /** * PermissionManager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Role; + use VuFind\Role\PermissionDeniedManager; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php index 7c08377cdf6d60f71a85669a88a39f19ea694cba..64cd58bbf554fdd60814980e7ce460a3774e9d0b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php @@ -2,7 +2,7 @@ /** * PermissionManager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Role; + use VuFind\Role\PermissionManager; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php index faf6d89679bf72f431150d2f1b1448dc4cf6e81a..7827ba0082c2c61207bbd29ea1d68562e9ca68d5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * PermissionProvider Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Role\PermissionProvider; + use VuFind\Role\PermissionProvider\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Role\PermissionProvider\PermissionProviderInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php index 43171a8ee6017db3ff17f1f76592d649ae7e85c0..e16a476718adb39bc27f72a4bd0794d051ebd2a3 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php @@ -2,7 +2,7 @@ /** * PermissionProvider ServerParam Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Role\PermissionProvider; + use VuFind\Role\PermissionProvider\ServerParam; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ShibbolethTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ShibbolethTest.php index 660ce4ab1668f9d91503d39800fd2a1dcce0bfdd..7e1d337f421285e73963fded8cf2299917cda80f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ShibbolethTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ShibbolethTest.php @@ -2,7 +2,7 @@ /** * PermissionProvider Shibboleth Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Role\PermissionProvider; + use VuFind\Role\PermissionProvider\Shibboleth; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php index 43aa098d9333b2a148e2b248c66eaccb61b98552..276bc22651b8ac4a30cc129a883be4516fffbd60 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php @@ -2,7 +2,7 @@ /** * PermissionProvider User Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -46,8 +46,7 @@ class UserTest extends \VuFindTest\Unit\TestCase ['username','mbeh'], ['email','markus.beh@ub.uni-freiburg.de'], ['college', 'Albert Ludwigs Universität Freiburg'] - ] - , + ], 'testuser2' => [ ['username','mbeh2'], @@ -152,5 +151,4 @@ class UserTest extends \VuFindTest\Unit\TestCase return $user; } - } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php index 6e907193f2d8e017b0a43f0f51e675c4fd6d9820..a6e1eb12ebe61d6f846cbe7e112d3436c04a8991 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php @@ -2,7 +2,7 @@ /** * SMS test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindTest\SMS; + use VuFind\SMS\Clickatell; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/BackendManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/BackendManagerTest.php index 5ff43c7e8f18acb3581823daf7e696e7de0fa3e1..a12d93cdc5a5c864de9925e6b5148782e5e27ac4 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/BackendManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/BackendManagerTest.php @@ -3,7 +3,7 @@ /** * BackendManager unit tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php index 727aaa12de5b959b476d0e48b903ad9c538205ec..2ff6af9f3c189f6402a984ef65a3070a0d76f8ce 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php @@ -2,7 +2,7 @@ /** * Base Search Object Parameters Test * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -49,8 +49,8 @@ class ParamsTest extends \VuFindTest\Unit\TestCase public function testSpellingReplacements() { // Use Solr since some Base components are abstract: - $params = $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr'); + $params = $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr'); // Key test: word boundaries: $params->setBasicSearch('go good googler'); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/MemoryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/MemoryTest.php index cbad1b53f6f7f3962254df0fcfa6754da6ac2367..e360068fa1eb4cb6a0502736463fefe017093e5c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/MemoryTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/MemoryTest.php @@ -3,7 +3,7 @@ /** * Memory unit tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php index 8bde975351c7756792aa08419245ef1423da5f65..3bca379c3238acdf1eee6b14bd4b1feeb0d8e9b6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Search\Options Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Search\Options; + use VuFind\Search\Options\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Search\Base\Options */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php index 7922e568eeb151baa4e711221c25e931e1622b88..152c368493d2da0763f9eba34bf5b9d291cc292c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Search\Params Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Search\Params; + use VuFind\Search\Params\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertFalse($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Search\Base\Params */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php index 020d4d4301cb802c9a6abbd0bbd171866d7fb447..4d4840d1802f51937fa819f1e50bb5cae2048be1 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for OnCampus listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -28,11 +28,11 @@ */ namespace VuFindTest\Search\Primo; -use VuFindSearch\ParamBag; +use VuFind\Search\Primo\InjectOnCampusListener; use VuFindSearch\Backend\Primo\Backend; use VuFindSearch\Backend\Primo\Connector; -use VuFind\Search\Primo\InjectOnCampusListener; +use VuFindSearch\ParamBag; use VuFindTest\Unit\TestCase; use Zend\EventManager\Event; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php index 29d7093d7243d7ffeced300d4a461265d0269e12..6bc48e2bcf52a94e87478c5d7bb72d6439895470 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Primo Permission Handler. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -31,9 +31,6 @@ namespace VuFindTest\Search\Primo; use VuFind\Search\Primo\PrimoPermissionHandler; use VuFindTest\Unit\TestCase; -use ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; - /** * Unit tests for Primo Permission Handler. * @@ -809,7 +806,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerMemberAuthNotSuccessfullCallback($param) { + public function handlerMemberAuthNotSuccessfullCallback($param) + { if ($param == 'primo.MEMBER') { return false; } @@ -822,7 +820,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerMemberAuthSuccessfullCallback($param) { + public function handlerMemberAuthSuccessfullCallback($param) + { if ($param == 'primo.MEMBER') { return true; } @@ -835,7 +834,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerDefaultAuthSuccessfullCallback($param) { + public function handlerDefaultAuthSuccessfullCallback($param) + { if ($param == 'primo.defaultRule') { return true; } @@ -848,7 +848,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerDefaultAuthNotSuccessfullCallback($param) { + public function handlerDefaultAuthNotSuccessfullCallback($param) + { if ($param == 'primo.defaultRule') { return false; } @@ -861,7 +862,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerMemberIsOnCampusCallback($param) { + public function handlerMemberIsOnCampusCallback($param) + { if ($param == 'primo.defaultRule') { return false; } @@ -880,7 +882,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerMemberIsNotOnCampusCallback($param) { + public function handlerMemberIsNotOnCampusCallback($param) + { if ($param == 'primo.defaultRule') { return false; } @@ -899,7 +902,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerIsNotAMemberCallback($param) { + public function handlerIsNotAMemberCallback($param) + { if ($param == 'primo.defaultRule') { return false; } @@ -915,7 +919,8 @@ class PrimoPermissionHandlerTest extends TestCase * * @return void */ - public function handlerIsOnDefaultCampusCallback($param) { + public function handlerIsOnDefaultCampusCallback($param) + { if ($param == 'primo.defaultRule') { return true; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/QueryAdapterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/QueryAdapterTest.php index 238a939f3493d8fc3ccdc308cb3f22585a0dee45..ecaf3401bc53088057d9ba46faad73e632927090 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/QueryAdapterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/QueryAdapterTest.php @@ -3,7 +3,7 @@ /** * QueryAdapter unit tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php index 7b825458c66e1ae5b0df5fa61c4310f027a23df7..a68acf7ffce638c3147de38a2f3b75720ad5e8f6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Search\Results Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Search\Results; + use VuFind\Search\Results\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertFalse($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Search\Base\Results */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php index fffe9d1aa6ccb9fb196448b91efd72ad6e38dcab..fe4933a71f68d91f4624d0e5e83e1405e8be0360 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php @@ -3,7 +3,7 @@ /** * SearchTabsHelper unit tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2016. * @@ -132,9 +132,9 @@ class SearchTabsHelperTest extends TestCase { $helper = $this->getSearchTabsHelper(); $this->assertEquals( - $this->tabConfig['default_unfiltered'], $helper->getTabConfig() + $this->tabConfig['default_unfiltered'], $helper->getTabConfig() ); - $this->assertEquals($this->filterConfig, $helper->getTabFilterConfig()); + $this->assertEquals($this->filterConfig, $helper->getTabFilterConfig()); } /** @@ -241,7 +241,7 @@ class SearchTabsHelperTest extends TestCase ->method('get') ->will( $this->returnCallback( - function($backend) use ($mockSolr, $mockPrimo) { + function ($backend) use ($mockSolr, $mockPrimo) { switch ($backend) { case 'Solr': return $mockSolr; case 'Primo': return $mockPrimo; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php index 321294694bbb059ce7ed19d05c7245dea7daa7b9..37c9013d35144c29b9cd167f710a8b0aacc7f8e8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Conditional Filter listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -28,18 +28,15 @@ */ namespace VuFindTest\Search\Solr; -use VuFindSearch\ParamBag; +use VuFind\Search\Solr\InjectConditionalFilterListener; use VuFindSearch\Backend\Solr\Backend; use VuFindSearch\Backend\Solr\Connector; use VuFindSearch\Backend\Solr\HandlerMap; -use VuFind\Search\Solr\InjectConditionalFilterListener; +use VuFindSearch\ParamBag; use VuFindTest\Unit\TestCase; use Zend\EventManager\Event; -use ZfcRbac\Service\AuthorizationServiceAwareInterface, - ZfcRbac\Service\AuthorizationServiceAwareTrait; - /** * Unit tests for Conditional Filter listener. * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php index 52a6580cc6dc17ff58c20f8a5951fce3f56e9830..faa65233eee3fec33b1f4a4981edcfddaeb58e86 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for FilterFieldConversionListener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Search\Solr; -use VuFindSearch\ParamBag; - use VuFind\Search\Solr\FilterFieldConversionListener; + +use VuFindSearch\ParamBag; use VuFindTest\Unit\TestCase; use Zend\EventManager\Event; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php index d72b6add0771c94b7593ddd4d701731f5264a5cf..81c61ad31d1f2d6ad481dd68ab90ce7fea06dd4a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Hide Facet Value Listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Search\Solr; -use VuFindTest\Unit\TestCase; use VuFind\Search\Solr\HideFacetValueListener; use VuFindSearch\Backend\Solr\Response\Json\Facets; +use VuFindTest\Unit\TestCase; use Zend\EventManager\Event; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php index 741d0226bcfda527175c7fa2cdf1219b950c877a..4df48e826fc7172470409706f29fb4ea9f8c593c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Hierarchical Facet Helper. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2014. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Search\Solr; -use VuFindTest\Unit\TestCase; use VuFind\Search\Solr\HierarchicalFacetHelper; +use VuFindTest\Unit\TestCase; /** * Unit tests for Hierarchical Facet Helper. @@ -179,7 +179,6 @@ class HierarchicalFacetHelperTest extends TestCase $facetList[0]['children'][0]['value'], '1/Book/BookPart/' ); $this->assertEquals($facetList[0]['children'][0]['isApplied'], true); - } /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php index be777ed198c358a89137335eafd6660943ef9feb..82d1773dfed84c67f5215a2c408bfefbfa500137 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for multiindex listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,12 +28,12 @@ */ namespace VuFindTest\Search\Solr; -use VuFindSearch\ParamBag; +use VuFind\Search\Solr\MultiIndexListener; use VuFindSearch\Backend\Solr\Backend; use VuFindSearch\Backend\Solr\Connector; use VuFindSearch\Backend\Solr\HandlerMap; -use VuFind\Search\Solr\MultiIndexListener; +use VuFindSearch\ParamBag; use VuFindTest\Unit\TestCase; use Zend\EventManager\Event; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php index a639d2f350b432422084e80e0bf08215e8cc737c..b62166ad154836bfb00dc817ef61fb412cb5217f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php @@ -3,7 +3,7 @@ /** * Unit tests for spelling processor. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -130,8 +130,8 @@ class SpellingProcessorTest extends TestCase { $spelling = $this->getFixture('spell1'); $query = $this->getFixture('query1'); - $params = $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr'); + $params = $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr'); $params->setBasicSearch($query->getString(), $query->getHandler()); $sp = new SpellingProcessor(); $this->assertEquals( @@ -192,8 +192,8 @@ class SpellingProcessorTest extends TestCase { $spelling = $this->getFixture('spell1'); $query = $this->getFixture('query1'); - $params = $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr'); + $params = $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr'); $params->setBasicSearch($query->getString(), $query->getHandler()); $config = new Config(['expand' => false, 'phrase' => true]); $sp = new SpellingProcessor($config); @@ -428,8 +428,8 @@ class SpellingProcessorTest extends TestCase { $spelling = $this->getFixture('spell' . $testNum); $query = $this->getFixture('query' . $testNum); - $params = $this->getServiceManager()->get('VuFind\SearchParamsPluginManager') - ->get('Solr'); + $params = $this->getServiceManager() + ->get('VuFind\Search\Params\PluginManager')->get('Solr'); $this->setProperty($params, 'query', $query); $sp = new SpellingProcessor(new Config($config)); $suggestions = $sp->getSuggestions($spelling, $query); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V3/ErrorListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V3/ErrorListenerTest.php index 6e8920b52ac7ec1d3afa1a6d8142eb611c21ac73..be88993963857e8ed805bed4b98f20575600e6f1 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V3/ErrorListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V3/ErrorListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR 3.x error listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,16 +28,16 @@ */ namespace VuFindTest\Search\Solr\V3; -use VuFind\Search\Solr\V3\ErrorListener; +use PHPUnit\Framework\TestCase; + +use RuntimeException; +use VuFind\Search\Solr\V3\ErrorListener; use VuFindSearch\Backend\Exception\HttpErrorException; use Zend\EventManager\Event; -use Zend\Http\Response; - -use PHPUnit_Framework_TestCase as TestCase; -use RuntimeException; +use Zend\Http\Response; /** * Unit tests for SOLR 3.x error listener. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V4/ErrorListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V4/ErrorListenerTest.php index b50ffea986fbeb13c8c71a5c75f50008610c0537..32ccb0f284a87025f0aed5f0671ada90593d1bfc 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V4/ErrorListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/V4/ErrorListenerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR 3.x error listener. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,16 +28,16 @@ */ namespace VuFindTest\Search\Solr\V4; -use VuFind\Search\Solr\V4\ErrorListener; +use PHPUnit\Framework\TestCase; + +use RuntimeException; +use VuFind\Search\Solr\V4\ErrorListener; use VuFindSearch\Backend\Exception\HttpErrorException; use Zend\EventManager\Event; -use Zend\Http\Response; - -use PHPUnit_Framework_TestCase as TestCase; -use RuntimeException; +use Zend\Http\Response; /** * Unit tests for SOLR 3.x error listener. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php index 006ab35c103c9f9e4d7c24ad805362becd5f802e..ab512b0b5ed8994d961150de9285903cc50d3f40 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php @@ -3,7 +3,7 @@ /** * UrlQueryHelper unit tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Search; -use VuFind\Search\UrlQueryHelper; use VuFind\Search\Factory\UrlQueryHelperFactory; -use VuFindTest\Unit\TestCase as TestCase; +use VuFind\Search\UrlQueryHelper; use VuFindSearch\Query\Query; +use VuFindTest\Unit\TestCase as TestCase; /** * UrlQueryHelper unit tests. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php index 0fb4100a32d354a3d942b0aaf671b80d4e9c7994..e6633f6b39bbf850b881bc6a1beb5d39d8da85b2 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php @@ -2,7 +2,7 @@ /** * Session Plugin Manager Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Session; + use VuFind\Session\PluginManager; /** @@ -46,8 +47,10 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase */ public function testShareByDefault() { - $pm = new PluginManager(null); - $this->assertTrue($pm->shareByDefault()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } /** @@ -55,12 +58,14 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase * * @return void * - * @expectedException Zend\ServiceManager\Exception\RuntimeException - * @expectedExceptionMessage Plugin ArrayObject does not belong to Zend\Session\SaveHandler\SaveHandlerInterface + * @expectedException Zend\ServiceManager\Exception\InvalidServiceException + * @expectedExceptionMessage Plugin ArrayObject does not belong to VuFind\Session\HandlerInterface */ public function testExpectedInterface() { - $pm = new PluginManager(null); - $pm->validatePlugin(new \ArrayObject()); + $pm = new PluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); + $pm->validate(new \ArrayObject()); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/SimpleXMLTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/SimpleXMLTest.php index 65b8f4c14450dd530948847ec6c989ca06509570..6470981168d1d26dabf153fff456db5fdfbda48b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/SimpleXMLTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/SimpleXMLTest.php @@ -2,7 +2,7 @@ /** * SimpleXML Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFindTest; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class SimpleXMLTest extends \PHPUnit_Framework_TestCase +class SimpleXMLTest extends \PHPUnit\Framework\TestCase { /** * Test attaching one item to another. diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapIndexTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapIndexTest.php index 75382ef7628f02e9ccf9d852ebffd224a6ef4b02..4f0672545c2f3745ed1d0fd0895f82526bb0e6f0 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapIndexTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapIndexTest.php @@ -2,7 +2,7 @@ /** * SitemapIndex Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Sitemap; + use VuFind\Sitemap\SitemapIndex; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapTest.php index ec24d5490fa9fd5821528d8f2c717d66704f8393..e4a80b6f487c8cf29b276ef66685b373883c408c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Sitemap/SitemapTest.php @@ -2,7 +2,7 @@ /** * Sitemap Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Sitemap; + use VuFind\Sitemap\Sitemap; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/UtilsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/UtilsTest.php index 139169c31c03f2940946d5e93ed85021bab756fe..c644b2f38eddf97b8ac3f84b522269b47cb09377 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/UtilsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/UtilsTest.php @@ -2,7 +2,7 @@ /** * Solr Utils Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Solr; + use VuFind\Solr\Utils; /** @@ -89,7 +90,7 @@ class UtilsTest extends \VuFindTest\Unit\TestCase '2nd Quarter 2004' => '2004-01-01', 'Nov 2009 and Dec 2009' => '2009-01-01', ]; - + foreach ($tests as $in => $out) { $this->assertEquals( $out === null ? null : $out . 'T00:00:00Z', // append standard time value unless null diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php index 3ee5363536499ea7991cced454a0304fa975c940..430dd1fb8021df6346e3ae2e0d80dfce7a5258ae 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php @@ -2,7 +2,7 @@ /** * Solr Writer Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Solr; -use VuFind\Db\Table\ChangeTracker, VuFind\Search\BackendManager; + +use VuFind\Db\Table\ChangeTracker; +use VuFind\Search\BackendManager; use VuFind\Solr\Writer; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/TagsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/TagsTest.php index b630d3f3402c54f319706a3ff554a770687db265..8d35f5a9666c3eeecab4caabec09ad07f7487eb5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/TagsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/TagsTest.php @@ -2,7 +2,7 @@ /** * Tags Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFindTest; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class TagsTest extends \PHPUnit_Framework_TestCase +class TagsTest extends \PHPUnit\Framework\TestCase { /** * Tag parser @@ -46,9 +46,11 @@ class TagsTest extends \PHPUnit_Framework_TestCase protected $parser; /** - * Constructor + * Standard setup method. + * + * @return void */ - public function __construct() + public function setUp() { $this->parser = new \VuFind\Tags(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Validator/CsrfTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Validator/CsrfTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8c68d3ed90b6d42c47840b468c1dcab9117f165c --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Validator/CsrfTest.php @@ -0,0 +1,91 @@ +<?php +/** + * CSRF Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Validator; + +use VuFind\Validator\Csrf; +use Zend\Session\Container; + +/** + * CSRF Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class CsrfTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test counting behavior. + * + * @return void + */ + public function testCounting() + { + $csrf = new Csrf(['session' => new Container('csrftest1')]); + $this->assertEquals(0, $csrf->getTokenCount()); + $csrf->getHash(); + $this->assertEquals(1, $csrf->getTokenCount()); + $csrf->getHash(true); + $this->assertEquals(2, $csrf->getTokenCount()); + } + + /** + * Test trimming behavior. + * + * @return void + */ + public function testTrimming() + { + $csrf = new Csrf(['session' => new Container('csrftest2')]); + // Try trimming an empty list: + $csrf->trimTokenList(5); + $this->assertEquals(0, $csrf->getTokenCount()); + + // Now populate the list: + $firstToken = $csrf->getHash(); + $secondToken = $csrf->getHash(true); + $thirdToken = $csrf->getHash(true); + $this->assertEquals(3, $csrf->getTokenCount()); + + // All tokens are valid now! + $this->assertTrue($csrf->isValid($firstToken)); + $this->assertTrue($csrf->isValid($secondToken)); + $this->assertTrue($csrf->isValid($thirdToken)); + + // Trim the list down to one: + $csrf->trimTokenList(1); + $this->assertEquals(1, $csrf->getTokenCount()); + + // Now only the latest token is valid: + $this->assertFalse($csrf->isValid($firstToken)); + $this->assertFalse($csrf->isValid($secondToken)); + $this->assertTrue($csrf->isValid($thirdToken)); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php index 63ce5593b3229d34cf0d5b9aabe769b19b858686..4f943bfbf844e83e9f8033a897d0054306e4b0da 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php @@ -2,7 +2,7 @@ /** * Cart view helper Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -36,7 +36,7 @@ namespace VuFindTest\View\Helper\Root; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class CartTest extends \PHPUnit_Framework_TestCase +class CartTest extends \PHPUnit\Framework\TestCase { /** * Test the helper diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CitationTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CitationTest.php index f88d76f240be2f62e5edca3221075e3b5d99f89c..f236b42995f916c4dde65a4cf4310e203a4b335c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CitationTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CitationTest.php @@ -2,7 +2,7 @@ /** * CitationBuilder Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; + use VuFind\View\Helper\Root\Citation; /** @@ -40,7 +41,7 @@ use VuFind\View\Helper\Root\Citation; class CitationTest extends \VuFindTest\Unit\ViewHelperTestCase { /** - * Sample citations -- each element of this array contains three elements -- + * Sample citations -- each element of this array contains three elements -- * the raw input data and the expected apa/mla output citations. * * @var array @@ -309,26 +310,6 @@ class CitationTest extends \VuFindTest\Unit\ViewHelperTestCase // @codingStandardsIgnoreEnd ]; - /** - * Setup test case. - * - * Mark test skipped if short_open_tag is not enabled. The partial - * uses short open tags. This directive is PHP_INI_PERDIR, - * i.e. can only be changed via php.ini or a per-directory - * equivalent. The testCitations() will fail if the test is run on - * a system with short_open_tag disabled in the system-wide php - * ini-file. - * - * @return void - */ - protected function setup() - { - parent::setup(); - if (!ini_get('short_open_tag')) { - $this->markTestSkipped('Test requires short_open_tag to be enabled'); - } - } - /** * Test citation generation * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/GoogleAnalyticsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/GoogleAnalyticsTest.php index 4281c00141d2a22953078ed44ed7fec776787de1..c634a51f1f9da7f1ca2b67f14b37cb83e39ce65d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/GoogleAnalyticsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/GoogleAnalyticsTest.php @@ -2,7 +2,7 @@ /** * GoogleAnalytics view helper Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; + use VuFind\View\Helper\Root\GoogleAnalytics; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php index 5c02af1a1c1b36edb35a4e84eb3e5dc375170e2e..21b141d395c405d82ac02b0821d4f5e8734c59fb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php @@ -2,7 +2,7 @@ /** * OpenUrl Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,7 +27,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; -use VuFind\View\Helper\Root\OpenUrl, Zend\Config\Config, InvalidArgumentException; + +use VuFind\View\Helper\Root\OpenUrl; +use Zend\Config\Config; /** * OpenUrl Test Class @@ -296,7 +298,7 @@ class OpenUrlTest extends \VuFindTest\Unit\ViewHelperTestCase $mockContext = $this->getMockContext(); } $mockPm = $this->getMockBuilder('VuFind\Resolver\Driver\PluginManager') - ->getMock(); + ->disableOriginalConstructor()->getMock(); $openUrl = new OpenUrl($mockContext, $rules, $mockPm, new Config($config)); $openUrl->setView($this->getPhpRenderer()); return $openUrl; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php index 76154cf2f8e60db45ffafd83494fc9ec5279ff15..ba64a52f0df32fc502e8767f9b6c44d74740b91c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php @@ -2,7 +2,7 @@ /** * Permission view helper Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; + use VuFind\View\Helper\Root\Permission; /** @@ -98,24 +99,26 @@ class PermissionTest extends \VuFindTest\Unit\ViewHelperTestCase * Test the template display * * @return void + * + * @expectedException Zend\View\Exception\RuntimeException */ public function testTemplateDisplay() { // Template does not exist, expect an exception, though - $this->setExpectedException('Zend\View\Exception\RuntimeException'); - - $mockPmd = $this->getMockPmd([ + $mockPmd = $this->getMockPmd( + [ 'deniedTemplateBehavior' => [ 'action' => 'showTemplate', 'value' => 'record/displayLogicTest', 'params' => [], ], - ]); + ] + ); $helper = new Permission($this->getMockPm(false), $mockPmd); $helper->setView($this->getMockView()); - $displayBlock = $helper->getAlternateContent('permissionDeniedTemplate'); + $helper->getAlternateContent('permissionDeniedTemplate'); } /** @@ -149,7 +152,8 @@ class PermissionTest extends \VuFindTest\Unit\ViewHelperTestCase * * @return \VuFind\Role\PermissionDeniedManager */ - protected function getMockPmd($config = false) { + protected function getMockPmd($config = false) + { $mockPmd = $this->getMockBuilder('\VuFind\Role\PermissionDeniedManager') ->setConstructorArgs([$this->permissionDeniedConfig]) ->getMock(); @@ -165,7 +169,8 @@ class PermissionTest extends \VuFindTest\Unit\ViewHelperTestCase * * @return \VuFind\Role\PermissionManager */ - protected function getMockPm($isAuthorized = false) { + protected function getMockPm($isAuthorized = false) + { $mockPm = $this->getMockBuilder('\VuFind\Role\PermissionManager') ->disableOriginalConstructor() ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php index 4466b8fd6db46ef2bb3a67ecefcb50b335c4a664..d6d9c7e847fd5bf739c4c4affdf1682e1d5db4b7 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php @@ -2,7 +2,7 @@ /** * RecordDataFormatter spec builder Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root\RecordDataFormatter; + use VuFind\View\Helper\Root\RecordDataFormatter\SpecBuilder; /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php index 23bdb096d76ee3d98f7b22717b8e084c4c6225ae..0e2622b0258bdfe5aa260d26bf33cd6358abca88 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php @@ -2,7 +2,7 @@ /** * RecordDataFormatter Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; + use VuFind\View\Helper\Root\RecordDataFormatter; use VuFind\View\Helper\Root\RecordDataFormatterFactory; @@ -40,26 +41,6 @@ use VuFind\View\Helper\Root\RecordDataFormatterFactory; */ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase { - /** - * Setup test case. - * - * Mark test skipped if short_open_tag is not enabled. The partial - * uses short open tags. This directive is PHP_INI_PERDIR, - * i.e. can only be changed via php.ini or a per-directory - * equivalent. The test will fail if the test is run on - * a system with short_open_tag disabled in the system-wide php - * ini-file. - * - * @return void - */ - protected function setup() - { - parent::setup(); - if (!ini_get('short_open_tag')) { - $this->markTestSkipped('Test requires short_open_tag to be enabled'); - } - } - /** * Get view helpers needed by test. * @@ -69,12 +50,16 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase { $context = new \VuFind\View\Helper\Root\Context(); return [ - 'auth' => new \VuFind\View\Helper\Root\Auth($this->getMockBuilder('VuFind\Auth\Manager')->disableOriginalConstructor()->getMock()), + 'auth' => new \VuFind\View\Helper\Root\Auth( + $this->getMockBuilder('VuFind\Auth\Manager')->disableOriginalConstructor()->getMock(), + $this->getMockBuilder('VuFind\Auth\ILSAuthenticator')->disableOriginalConstructor()->getMock() + ), 'context' => $context, 'openUrl' => new \VuFind\View\Helper\Root\OpenUrl($context, [], $this->getMockBuilder('VuFind\Resolver\Driver\PluginManager')->disableOriginalConstructor()->getMock()), 'proxyUrl' => new \VuFind\View\Helper\Root\ProxyUrl(), 'record' => new \VuFind\View\Helper\Root\Record(), 'recordLink' => new \VuFind\View\Helper\Root\RecordLink($this->getMockBuilder('VuFind\Record\Router')->disableOriginalConstructor()->getMock()), + 'searchOptions' => new \VuFind\View\Helper\Root\SearchOptions(new \VuFind\Search\Options\PluginManager($this->getServiceManager())), 'searchTabs' => $this->getMockBuilder('VuFind\View\Helper\Root\SearchTabs')->disableOriginalConstructor()->getMock(), 'transEsc' => new \VuFind\View\Helper\Root\TransEsc(), 'translate' => new \VuFind\View\Helper\Root\Translate(), @@ -138,17 +123,19 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase { // Build the formatter: $factory = new RecordDataFormatterFactory(); - $formatter = $factory->__invoke(); + $formatter = $factory->__invoke( + $this->getServiceManager(), RecordDataFormatter::class + ); // Create a view object with a set of helpers: $helpers = $this->getViewHelpers(); $view = $this->getPhpRenderer($helpers); // Mock out the router to avoid errors: - $match = new \Zend\Mvc\Router\RouteMatch([]); + $match = new \Zend\Router\RouteMatch([]); $match->setMatchedRouteName('foo'); $view->plugin('url') - ->setRouter($this->createMock('Zend\Mvc\Router\RouteStackInterface')) + ->setRouter($this->createMock('Zend\Router\RouteStackInterface')) ->setRouteMatch($match); // Inject the view object into all of the helpers: @@ -160,6 +147,39 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase return $formatter; } + /** + * Find a result in the results array. + * + * @param string $needle Result to look up. + * @param array $haystack Result set. + * + * @return mixed + */ + protected function findResult($needle, $haystack) + { + foreach ($haystack as $current) { + if ($current['label'] == $needle) { + return $current; + } + } + return null; + } + + /** + * Extract labels from a results array. + * + * @param array $results Results to process. + * + * @return array + */ + protected function getLabels($results) + { + $callback = function ($c) { + return $c['label']; + }; + return array_map($callback, $results); + } + /** * Test citation generation * @@ -171,9 +191,101 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase $spec = $formatter->getDefaults('core'); $spec['Building'] = [ 'dataMethod' => 'getBuilding', 'pos' => 0, 'context' => ['foo' => 1], - 'translationTextDomain' => 'prefix_' + 'translationTextDomain' => 'prefix_', + ]; + $spec['MultiTest'] = [ + 'dataMethod' => 'getFormats', + 'renderType' => 'Multi', + 'pos' => 1000, + 'multiFunction' => function ($data) { + return [ + [ + 'label' => 'Multi Data', + 'values' => $data, + ], + [ + 'label' => 'Multi Count', + 'values' => count($data), + ], + ]; + } + ]; + $spec['MultiEmptyArrayTest'] = [ + 'dataMethod' => true, + 'renderType' => 'Multi', + 'pos' => 2000, + 'multiFunction' => function () { + return []; + } + ]; + $spec['MultiNullTest'] = [ + 'dataMethod' => true, + 'renderType' => 'Multi', + 'pos' => 2000, + 'multiFunction' => function () { + return null; + } + ]; + $spec['MultiNullInArrayWithZeroTest'] = [ + 'dataMethod' => true, + 'renderType' => 'Multi', + 'pos' => 2000, + 'allowZero' => false, + 'multiFunction' => function () { + return [ + [ + 'label' => 'Null', + 'values' => null, + ], + [ + 'label' => 'ZeroBlocked', + 'values' => 0, + ] + ]; + } + ]; + $spec['MultiNullInArrayWithZeroAllowedTest'] = [ + 'dataMethod' => true, + 'renderType' => 'Multi', + 'pos' => 2000, + 'allowZero' => true, + 'multiFunction' => function () { + return [ + [ + 'label' => 'Null', + 'values' => null, + ], + [ + 'label' => 'ZeroAllowed', + 'values' => 0, + ] + ]; + } + ]; + $spec['MultiWithSortPos'] = [ + 'dataMethod' => true, + 'renderType' => 'Multi', + 'pos' => 0, + 'multiFunction' => function () { + return [ + [ + 'label' => 'b', + 'values' => 'b', + 'options' => ['pos' => 3000] + ], + [ + 'label' => 'a', + 'values' => 'a', + 'options' => ['pos' => 3000] + ], + [ + 'label' => 'c', + 'values' => 'c', + 'options' => ['pos' => 2999] + ], + ]; + } ]; - $expected = [ 'Building' => 'prefix_0', 'Published in' => '0', @@ -184,28 +296,43 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase 'Published' => 'Centro di Studi Vichiani, 1992', 'Edition' => 'Fictional edition.', 'Series' => 'Vico, Giambattista, 1668-1744. Works. 1982 ;', + 'Multi Count' => 1, + 'Multi Data' => 'Book', 'Subjects' => 'Naples (Kingdom) History Spanish rule, 1442-1707 Sources', 'Online Access' => 'http://fictional.com/sample/url', 'Tags' => 'Add Tag No Tags, Be the first to tag this record!', + 'ZeroAllowed' => 0, + 'c' => 'c', + 'a' => 'a', + 'b' => 'b', ]; $driver = $this->getDriver(); $results = $formatter->getData($driver, $spec); // Check for expected array keys - $this->assertEquals(array_keys($expected), array_keys($results)); + $this->assertEquals(array_keys($expected), $this->getLabels($results)); // Check for expected text (with markup stripped) foreach ($expected as $key => $value) { $this->assertEquals( $value, - trim(preg_replace('/\s+/', ' ', strip_tags($results[$key]['value']))) + trim( + preg_replace( + '/\s+/', ' ', + strip_tags($this->findResult($key, $results)['value']) + ) + ) ); } // Check for exact markup in representative example: - $this->assertEquals('Italian<br />Latin', $results['Language']['value']); + $this->assertEquals( + 'Italian<br />Latin', $this->findResult('Language', $results)['value'] + ); // Check for context in Building: - $this->assertEquals(['foo' => 1], $results['Building']['context']); + $this->assertEquals( + ['foo' => 1], $this->findResult('Building', $results)['context'] + ); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php index ae657c33920cec2897632ec2815d4f1b44c04514..2ee0ba6e3d8da4c9759420ebcf21893751950210 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php @@ -2,7 +2,7 @@ /** * Record view helper Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; -use VuFind\View\Helper\Root\Record, Zend\View\Exception\RuntimeException; + +use VuFind\View\Helper\Root\Record; +use Zend\View\Exception\RuntimeException; /** * Record view helper Test Class @@ -37,7 +39,7 @@ use VuFind\View\Helper\Root\Record, Zend\View\Exception\RuntimeException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class RecordTest extends \PHPUnit_Framework_TestCase +class RecordTest extends \PHPUnit\Framework\TestCase { /** * Test attempting to display a template that does not exist. @@ -45,7 +47,7 @@ class RecordTest extends \PHPUnit_Framework_TestCase * @return void * * @expectedException Zend\View\Exception\RuntimeException - * @expectedExceptionMessage Cannot find core.phtml template for record driver: VuFind\RecordDriver\SolrMarc + * @expectedExceptionMessage Cannot find RecordDriver/AbstractBase/core.phtml template for class: VuFind\RecordDriver\SolrMarc */ public function testMissingTemplate() { @@ -69,7 +71,9 @@ class RecordTest extends \PHPUnit_Framework_TestCase $record->getView()->resolver()->expects($this->at(0))->method('resolve') ->with($this->equalTo('RecordDriver/SolrMarc/collection-record.phtml')) ->will($this->returnValue(false)); - $this->setSuccessTemplate($record, 'RecordDriver/SolrDefault/collection-record.phtml', 'success', 1); + $this->setSuccessTemplate( + $record, 'RecordDriver/SolrDefault/collection-record.phtml', 'success', 1, 3 + ); $this->assertEquals('success', $record->getCollectionBriefRecord()); } @@ -181,7 +185,9 @@ class RecordTest extends \PHPUnit_Framework_TestCase // that one fail so we can load the parent class' template instead: $record->getView()->resolver()->expects($this->at(0))->method('resolve') ->will($this->returnValue(false)); - $this->setSuccessTemplate($record, 'RecordDriver/AbstractBase/list-entry.phtml', 'success', 1, 1); + $this->setSuccessTemplate( + $record, 'RecordDriver/AbstractBase/list-entry.phtml', 'success', 1, 3 + ); $this->assertEquals('success', $record->getListEntry(null, $user)); } @@ -250,8 +256,11 @@ class RecordTest extends \PHPUnit_Framework_TestCase public function testGetLink() { $context = $this->getMockContext(); + $callback = function ($arr) { + return $arr['lookfor'] === 'foo'; + }; $context->expects($this->once())->method('apply') - ->with($this->equalTo(['lookfor' => 'foo'])) + ->with($this->callback($callback)) ->will($this->returnValue(['bar' => 'baz'])); $context->expects($this->once())->method('restore') ->with($this->equalTo(['bar' => 'baz'])); @@ -271,17 +280,17 @@ class RecordTest extends \PHPUnit_Framework_TestCase { $context = $this->getMockContext(); $context->expects($this->at(1))->method('renderInContext') - ->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'count' => 0, 'prefix' => 'bar', 'formAttr' => 'foo'])) + ->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'number' => 1, 'prefix' => 'bar', 'formAttr' => 'foo'])) ->will($this->returnValue('success')); $context->expects($this->at(2))->method('renderInContext') - ->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'count' => 1, 'prefix' => 'bar', 'formAttr' => 'foo'])) + ->with($this->equalTo('record/checkbox.phtml'), $this->equalTo(['id' => 'Solr|000105196', 'number' => 2, 'prefix' => 'bar', 'formAttr' => 'foo'])) ->will($this->returnValue('success')); $record = $this->getRecord( $this->loadRecordFixture('testbug1.json'), [], $context ); // We run the test twice to ensure that checkbox incrementing works properly: - $this->assertEquals('success', $record->getCheckbox('bar', 'foo')); - $this->assertEquals('success', $record->getCheckbox('bar', 'foo')); + $this->assertEquals('success', $record->getCheckbox('bar', 'foo', 1)); + $this->assertEquals('success', $record->getCheckbox('bar', 'foo', 2)); } /** @@ -490,21 +499,18 @@ class RecordTest extends \PHPUnit_Framework_TestCase if (null === $context) { $context = $this->getMockContext(); } - $view = $this->createMock('Zend\View\Renderer\PhpRenderer'); - if ($url) { - $url = $this->getMockUrl($url); - } - if (false !== $serverurl) { - $serverurl = $this->getMockServerUrl(); - } + $view = $this->getMockBuilder('Zend\View\Renderer\PhpRenderer') + ->disableOriginalConstructor() + ->setMethods(['render', 'plugin', 'resolver']) + ->getMock(); $pluginCallback = function ($helper) use ($context, $url, $serverurl) { switch ($helper) { case 'context': return $context; case 'serverurl': - return $serverurl; + return $serverurl ? $this->getMockServerUrl() : false; case 'url': - return $url; + return $url ? $this->getMockUrl($url) : $url; case 'searchTabs': return $this->getMockSearchTabs(); default: @@ -624,8 +630,9 @@ class RecordTest extends \PHPUnit_Framework_TestCase * * @return void */ - protected function setSuccessTemplate($record, $tpl, $response = 'success', $resolveAt = 0, $renderAt = 1) - { + protected function setSuccessTemplate($record, $tpl, $response = 'success', + $resolveAt = 0, $renderAt = 2 + ) { $expectResolve = $resolveAt === '*' ? $this->any() : $this->at($resolveAt); $record->getView()->resolver()->expects($expectResolve)->method('resolve') ->with($this->equalTo($tpl)) diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php index 0eb8852ef2fbe231256039e8f84b1404d2f449cf..7a8ea1f96568639c255a74be9caca1c584d2304f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php @@ -2,7 +2,7 @@ /** * SafeMoneyFormat view helper Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; + use VuFind\View\Helper\Root\SafeMoneyFormat; /** @@ -37,7 +38,7 @@ use VuFind\View\Helper\Root\SafeMoneyFormat; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class SafeMoneyFormatTest extends \PHPUnit_Framework_TestCase +class SafeMoneyFormatTest extends \PHPUnit\Framework\TestCase { /** * Locale (for restoration after testing) diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php index 63f4d8d279ac9ccbf337766f2537392338052db0..f058cb96a5cbe2931694c304ecace313b4a4bc19 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php @@ -2,7 +2,7 @@ /** * Translate view helper Test Class (and by extension, the TranslatorAwareTrait) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper\Root; -use VuFind\View\Helper\Root\Translate; + use VuFind\I18n\TranslatableString; +use VuFind\View\Helper\Root\Translate; /** * Translate view helper Test Class (and by extension, the TranslatorAwareTrait) @@ -38,7 +39,7 @@ use VuFind\I18n\TranslatableString; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class TranslateTest extends \PHPUnit_Framework_TestCase +class TranslateTest extends \PHPUnit\Framework\TestCase { /** * Test translation without a loaded translator @@ -249,8 +250,7 @@ class TranslateTest extends \PHPUnit_Framework_TestCase protected function getMockTranslator($translations) { $callback = function ($str, $domain) use ($translations) { - return isset($translations[$domain][$str]) - ? $translations[$domain][$str] : $str; + return $translations[$domain][$str] ?? $str; }; $translator = $this->createMock('Zend\I18n\Translator\TranslatorInterface'); $translator->expects($this->any())->method('translate') diff --git a/module/VuFindAdmin/Module.php b/module/VuFindAdmin/Module.php index 91d6e58637425f8103942a95e09bb68304d00776..e93b241e9335630fbbfabcf6cdf94f5a26d45d6b 100644 --- a/module/VuFindAdmin/Module.php +++ b/module/VuFindAdmin/Module.php @@ -2,7 +2,7 @@ /** * VuFind Admin Tools module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,6 @@ * @link https://vufind.org/wiki/development */ namespace VuFindAdmin; -use Zend\ModuleManager\ModuleManager, - Zend\Mvc\MvcEvent; /** * VuFind Admin Tools module. diff --git a/module/VuFindAdmin/config/module.config.php b/module/VuFindAdmin/config/module.config.php index e22e45d90f7331f5dbec73d79f4aeeaabe9b8cb4..bf80ea81a8aded7eb5bb918becdb7ac77a69470f 100644 --- a/module/VuFindAdmin/config/module.config.php +++ b/module/VuFindAdmin/config/module.config.php @@ -4,18 +4,24 @@ namespace VuFindAdmin\Module\Configuration; $config = [ 'controllers' => [ 'factories' => [ - 'admin' => 'VuFindAdmin\Controller\Factory::getAdminController', - 'adminconfig' => 'VuFindAdmin\Controller\Factory::getConfigController', - 'adminsocial' => 'VuFindAdmin\Controller\Factory::getSocialstatsController', - 'adminmaintenance' => 'VuFindAdmin\Controller\Factory::getMaintenanceController', - 'adminstatistics' => 'VuFindAdmin\Controller\Factory::getStatisticsController', - 'admintags' => 'VuFindAdmin\Controller\Factory::getTagsController', + 'VuFindAdmin\Controller\AdminController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindAdmin\Controller\ConfigController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindAdmin\Controller\MaintenanceController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindAdmin\Controller\SocialstatsController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindAdmin\Controller\TagsController' => 'VuFind\Controller\AbstractBaseFactory', + ], + 'aliases' => [ + 'Admin' => 'VuFindAdmin\Controller\AdminController', + 'AdminConfig' => 'VuFindAdmin\Controller\ConfigController', + 'AdminMaintenance' => 'VuFindAdmin\Controller\MaintenanceController', + 'AdminSocial' => 'VuFindAdmin\Controller\SocialstatsController', + 'AdminTags' => 'VuFindAdmin\Controller\TagsController', ], ], 'router' => [ 'routes' => [ 'admin' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/Admin', 'defaults' => [ @@ -26,7 +32,7 @@ $config = [ 'may_terminate' => true, 'child_routes' => [ 'disabled' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/Disabled', 'defaults' => [ @@ -36,7 +42,7 @@ $config = [ ] ], 'config' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/Config[/:action]', 'defaults' => [ @@ -46,7 +52,7 @@ $config = [ ] ], 'maintenance' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/Maintenance[/:action]', 'defaults' => [ @@ -56,7 +62,7 @@ $config = [ ] ], 'social' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/Social[/:action]', 'defaults' => [ @@ -65,18 +71,8 @@ $config = [ ] ] ], - 'statistics' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', - 'options' => [ - 'route' => '/Statistics[/:action]', - 'defaults' => [ - 'controller' => 'AdminStatistics', - 'action' => 'Home', - ] - ] - ], 'tags' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'options' => [ 'route' => '/Tags[/:action]', 'defaults' => [ diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php index b24fe4248f3551debcebeffa1aa846d10eca6c58..6f8288f0b14384e9dbbab0a7bf1344f712e7c3f1 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php @@ -2,7 +2,7 @@ /** * VuFind Admin Controller Base * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Page */ namespace VuFindAdmin\Controller; + use Zend\Mvc\MvcEvent; use Zend\ServiceManager\ServiceLocatorInterface; diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php index 07c6cc9fd603ffbb84026b0c446b337ce4424b27..5c431ad8c173dccf907d8924fe53a49acc31b1b8 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php @@ -2,7 +2,7 @@ /** * Admin Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -58,7 +58,7 @@ class AdminController extends AbstractAdmin $config = $this->getConfig(); $xml = false; if (isset($config->Index->url)) { - $response = $this->serviceLocator->get('VuFind\Http') + $response = $this->serviceLocator->get('VuFindHttp\HttpService') ->get($config->Index->url . '/admin/cores'); $xml = $response->isSuccess() ? $response->getBody() : false; } diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php index b585e5f5ec12e04fb09d5a1c64c388a35bcc83b9..75a829e8d4647da8a352758c558b3c41801c3d6d 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php @@ -2,7 +2,7 @@ /** * Admin Configuration Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -70,7 +70,8 @@ class ConfigController extends AbstractAdmin // Reload config now that it has been edited (otherwise, old setting // will persist in cache): - $this->serviceLocator->get('VuFind\Config')->reload('config'); + $this->serviceLocator->get('VuFind\Config\PluginManager') + ->reload('config'); } else { $this->flashMessenger()->addMessage( 'Could not enable auto-configuration; check permissions on ' @@ -79,5 +80,4 @@ class ConfigController extends AbstractAdmin } return $this->forwardTo('AdminConfig', 'Home'); } - } diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php deleted file mode 100644 index b7273fa3d9a057b931be92b0530db4dda6f16246..0000000000000000000000000000000000000000 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Factory for controllers. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFindAdmin\Controller; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for controllers. - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct a generic controller. - * - * @param string $name Name of table to construct (fully qualified - * class name, or else a class name within the current namespace) - * @param ServiceManager $sm Service manager - * - * @return object - */ - public static function getGenericController($name, ServiceManager $sm) - { - // Prepend the current namespace unless we receive a FQCN: - $class = (strpos($name, '\\') === false) - ? __NAMESPACE__ . '\\' . $name : $name; - if (!class_exists($class)) { - throw new \Exception('Cannot construct ' . $class); - } - return new $class($sm->getServiceLocator()); - } - - /** - * Construct a generic controller. - * - * @param string $name Method name being called - * @param array $args Method arguments - * - * @return object - */ - public static function __callStatic($name, $args) - { - // Strip "get" from method name to get name of class; pass first argument - // on assumption that it should be the ServiceManager object. - return static::getGenericController( - substr($name, 3), isset($args[0]) ? $args[0] : null - ); - } -} diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php index 885b98f10c2ef6e3b83ebb0ecaa1bd1cda722c9f..bd5349a7223fe3a7d51079d3d6f9fe52d368aea6 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php @@ -2,7 +2,7 @@ /** * Admin Maintenance Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -46,7 +46,7 @@ class MaintenanceController extends AbstractAdmin public function homeAction() { $view = $this->createViewModel(); - $view->caches = $this->serviceLocator->get('VuFind\CacheManager') + $view->caches = $this->serviceLocator->get('VuFind\Cache\Manager') ->getCacheList(); $view->setTemplate('admin/maintenance/home'); return $view; @@ -59,7 +59,7 @@ class MaintenanceController extends AbstractAdmin */ public function clearcacheAction() { - $cacheManager = $this->serviceLocator->get('VuFind\CacheManager'); + $cacheManager = $this->serviceLocator->get('VuFind\Cache\Manager'); foreach ($this->params()->fromQuery('cache', []) as $cache) { $cacheManager->getCache($cache)->flush(); } diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/SocialstatsController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/SocialstatsController.php index 067ee3bd2fa6989c162facabc55c44ec0996918d..4d80265e5fef41064d76a6340d8d76c186fac44a 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/SocialstatsController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/SocialstatsController.php @@ -2,7 +2,7 @@ /** * Admin Social Statistics Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/TagsController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/TagsController.php index b5e26167315369dda30dd12c7f743daf4008daeb..7e1bb3c5cd1ac6775b03d75bf2c904fe24afa66a 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/TagsController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/TagsController.php @@ -2,7 +2,7 @@ /** * Admin Tag Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -86,7 +86,7 @@ class TagsController extends AbstractAdmin $view = $this->createViewModel(); $view->setTemplate('admin/tags/manage'); - $view->type = !is_null($this->params()->fromPost('type', null)) + $view->type = null !== $this->params()->fromPost('type', null) ? $this->params()->fromPost('type') : $this->params()->fromQuery('type', null); $view->uniqueTags = $this->getUniqueTags()->toArray(); @@ -147,8 +147,8 @@ class TagsController extends AbstractAdmin // Delete All if ("manage" == $origin - || !is_null($this->getRequest()->getPost('deleteFilter')) - || !is_null($this->getRequest()->getQuery('deleteFilter')) + || null !== $this->getRequest()->getPost('deleteFilter') + || null !== $this->getRequest()->getQuery('deleteFilter') ) { if (false === $confirm) { return $this->confirmTagsDeleteByFilter($tags, $originUrl, $newUrl); @@ -157,7 +157,7 @@ class TagsController extends AbstractAdmin } else { // Delete by ID // Fail if we have nothing to delete: - $ids = is_null($this->getRequest()->getPost('deletePage')) + $ids = null === $this->getRequest()->getPost('deletePage') ? $this->params()->fromPost('ids') : $this->params()->fromPost('idsAll'); @@ -170,7 +170,6 @@ class TagsController extends AbstractAdmin return $this->confirmTagsDelete($ids, $originUrl, $newUrl); } $delete = $tags->deleteByIdArray($ids); - } if (0 == $delete) { diff --git a/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php b/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php index 1058e786afe7143a48cfa7185020ff9496e696f8..7a6cfb73c2c8c6d5996b4d1c4bd35413e23c487d 100644 --- a/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php +++ b/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Socialstats controller. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * diff --git a/module/VuFindApi/Module.php b/module/VuFindApi/Module.php index b439aec4319007fd62624e8e0b8b5263581ab7b3..71851a7c866085b8527bbb0f5d829fa5a0ccebe4 100644 --- a/module/VuFindApi/Module.php +++ b/module/VuFindApi/Module.php @@ -2,7 +2,7 @@ /** * VuFind Api module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2016. * @@ -26,8 +26,6 @@ * @link https://vufind.org/wiki/development */ namespace VuFindApi; -use Zend\ModuleManager\ModuleManager, - Zend\Mvc\MvcEvent; /** * VuFind Api module. diff --git a/module/VuFindApi/config/module.config.php b/module/VuFindApi/config/module.config.php index 8214f56894a0de267144ba4b6866097ee2b2df52..8b72c900a323a453107c2fa4440c24f655b3db03 100644 --- a/module/VuFindApi/config/module.config.php +++ b/module/VuFindApi/config/module.config.php @@ -4,14 +4,18 @@ namespace VuFindApi\Module\Configuration; $config = [ 'controllers' => [ 'factories' => [ - 'api' => 'VuFindApi\Controller\Factory::getApiController', - 'searchapi' => 'VuFindApi\Controller\Factory::getSearchApiController', - ] + 'VuFindApi\Controller\ApiController' => 'VuFindApi\Controller\Factory::getApiController', + 'VuFindApi\Controller\SearchApiController' => 'VuFindApi\Controller\Factory::getSearchApiController', + ], + 'aliases' => [ + 'Api' => 'VuFindApi\Controller\ApiController', + 'SearchApi' => 'VuFindApi\Controller\SearchApiController', + ], ], 'router' => [ 'routes' => [ 'apiHome' => [ - 'type' => 'Zend\Mvc\Router\Http\Segment', + 'type' => 'Zend\Router\Http\Segment', 'verb' => 'get,post,options', 'options' => [ 'route' => '/api[/v1][/]', @@ -22,7 +26,7 @@ $config = [ ], ], 'searchApiv1' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'verb' => 'get,post,options', 'options' => [ 'route' => '/api/v1/search', @@ -33,7 +37,7 @@ $config = [ ] ], 'recordApiv1' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'verb' => 'get,post,options', 'options' => [ 'route' => '/api/v1/record', diff --git a/module/VuFindApi/src/VuFindApi/Controller/ApiController.php b/module/VuFindApi/src/VuFindApi/Controller/ApiController.php index 9f2d0e475f85a4e4261a7d12bddcf746f1eeee13..a7e0730f8db35fcc59bb53f911fc7d93a195bf6b 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/ApiController.php +++ b/module/VuFindApi/src/VuFindApi/Controller/ApiController.php @@ -2,7 +2,7 @@ /** * API Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015-2016. * diff --git a/module/VuFindApi/src/VuFindApi/Controller/ApiInterface.php b/module/VuFindApi/src/VuFindApi/Controller/ApiInterface.php index f6121c3bed45bd2eb90d8b4b3b09fde2afcb4dd1..05a7a7eb5b4081906853151dd6b8c7b80d00dd6f 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/ApiInterface.php +++ b/module/VuFindApi/src/VuFindApi/Controller/ApiInterface.php @@ -2,7 +2,7 @@ /** * Additional functionality for API controllers. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library 2015. * diff --git a/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php b/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php index 6b29bc2ab1ab29c9ede0157164ce8818bd073da1..fd056266039895a4530c7bd630c1aa1d1b9c9f7a 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php +++ b/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php @@ -2,7 +2,7 @@ /** * Additional functionality for API controllers. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library 2015-2016. * diff --git a/module/VuFindApi/src/VuFindApi/Controller/Factory.php b/module/VuFindApi/src/VuFindApi/Controller/Factory.php index ba17300bc31356863d89e68f3ce5a75db1ce0db0..0f95d5a5d1bed4677812c6e985b84c62fb4fa121 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/Factory.php +++ b/module/VuFindApi/src/VuFindApi/Controller/Factory.php @@ -2,7 +2,7 @@ /** * Factory for controllers. * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindApi\Controller; + use VuFindApi\Formatter\FacetFormatter; use VuFindApi\Formatter\RecordFormatter; use Zend\ServiceManager\ServiceManager; @@ -53,7 +54,7 @@ class Factory public static function getApiController(ServiceManager $sm) { $controller = new ApiController($sm); - $controller->addApi($sm->get('SearchApi')); + $controller->addApi($sm->get('ControllerManager')->get('SearchApi')); return $controller; } @@ -66,9 +67,9 @@ class Factory */ public static function getSearchApiController(ServiceManager $sm) { - $recordFields = $sm->getServiceLocator() - ->get('VuFind\YamlReader')->get('SearchApiRecordFields.yaml'); - $helperManager = $sm->getServiceLocator()->get('ViewHelperManager'); + $recordFields = $sm->get('VuFind\Config\YamlReader') + ->get('SearchApiRecordFields.yaml'); + $helperManager = $sm->get('ViewHelperManager'); $rf = new RecordFormatter($recordFields, $helperManager); $controller = new SearchApiController($sm, $rf, new FacetFormatter()); return $controller; diff --git a/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php b/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php index 273aa20ba4df0c17aec7452972a0ae76c543f5f9..1794a75f42790481a973aedafd06a31a94d1e719 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php +++ b/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php @@ -2,7 +2,7 @@ /** * Search API Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015-2016. * @@ -189,7 +189,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch return $this->output([], self::STATUS_ERROR, 400, 'Missing id'); } - $loader = $this->serviceLocator->get('VuFind\RecordLoader'); + $loader = $this->serviceLocator->get('VuFind\Record\Loader'); try { if (is_array($request['id'])) { $results = $loader->loadBatchForSource($request['id']); @@ -253,7 +253,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch ? $facetConfig->SpecialFacets->hierarchical->toArray() : []; - $runner = $this->serviceLocator->get('VuFind\SearchRunner'); + $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); try { $results = $runner->run( $request, @@ -261,7 +261,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch function ($runner, $params, $searchId) use ( $hierarchicalFacets, $request, $requestedFields ) { - foreach (isset($request['facet']) ? $request['facet'] : [] + foreach ($request['facet'] ?? [] as $facet ) { if (!isset($hierarchicalFacets[$facet])) { @@ -269,7 +269,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch } } if ($requestedFields) { - $limit = isset($request['limit']) ? $request['limit'] : 20; + $limit = $request['limit'] ?? 20; $params->setLimit($limit); } else { $params->setLimit(0); @@ -297,7 +297,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch $response['records'] = $records; } - $requestedFacets = isset($request['facet']) ? $request['facet'] : []; + $requestedFacets = $request['facet'] ?? []; $hierarchicalFacetData = $this->getHierarchicalFacetData( array_intersect($requestedFacets, $hierarchicalFacets) ); @@ -333,7 +333,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch $facetResults = $results->getFullFieldFacets($facets, false, -1, 'count'); $facetHelper = $this->serviceLocator - ->get('VuFind\HierarchicalFacetHelper'); + ->get('VuFind\Search\Solr\HierarchicalFacetHelper'); $facetList = []; foreach ($facets as $facet) { diff --git a/module/VuFindApi/src/VuFindApi/Formatter/BaseFormatter.php b/module/VuFindApi/src/VuFindApi/Formatter/BaseFormatter.php index 2902ec31084b28168a716631c58e33363220e31d..96588b0bfa4f6ce0e51b61199e6ce91493c15f50 100644 --- a/module/VuFindApi/src/VuFindApi/Formatter/BaseFormatter.php +++ b/module/VuFindApi/src/VuFindApi/Formatter/BaseFormatter.php @@ -2,7 +2,7 @@ /** * Base formatter for API responses * - * PHP Version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015-2016. * @@ -61,7 +61,7 @@ class BaseFormatter || $value === null || $value === '' ) { unset($array[$key]); - } else if (is_bool($value) || $value === 'true' || $value === 'false') { + } elseif (is_bool($value) || $value === 'true' || $value === 'false') { $array[$key] = $value === true || $value === 'true' ? 1 : 0; } } diff --git a/module/VuFindApi/src/VuFindApi/Formatter/FacetFormatter.php b/module/VuFindApi/src/VuFindApi/Formatter/FacetFormatter.php index 4657863090f5179f9f69caf71f73e38ab2fcfbb1..55b70c11a433e956b07be6c1b89b59403fba13b6 100644 --- a/module/VuFindApi/src/VuFindApi/Formatter/FacetFormatter.php +++ b/module/VuFindApi/src/VuFindApi/Formatter/FacetFormatter.php @@ -2,7 +2,7 @@ /** * Facet formatter for API responses * - * PHP Version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015-2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindApi\Formatter; + use VuFind\Search\Base\Results; /** @@ -53,7 +54,7 @@ class FacetFormatter extends BaseFormatter foreach ($request['facetFilter'] as $filter) { list($facetField, $regex) = explode(':', $filter, 2); $regex = trim($regex); - if (substr($regex, 0, 1) == '"') { + if (substr($regex, 0, 1) == '"') { $regex = substr($regex, 1); } if (substr($regex, -1, 1) == '"') { diff --git a/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatter.php b/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatter.php index 110e4d0f40e1653b3e5592e318573e883942eb50..0394f6e91ba9a6d7f791fcef61e862c4092e3f38 100644 --- a/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatter.php +++ b/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatter.php @@ -2,7 +2,7 @@ /** * Record formatter for API responses * - * PHP Version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2015-2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindApi\Formatter; + use VuFind\I18n\TranslatableString; use Zend\View\HelperPluginManager; @@ -113,7 +114,7 @@ class RecordFormatter extends BaseFormatter return $xml; } $rawData = $record->tryMethod('getRawData'); - return isset($rawData['fullrecord']) ? $rawData['fullrecord'] : null; + return $rawData['fullrecord'] ?? null; } /** @@ -155,7 +156,7 @@ class RecordFormatter extends BaseFormatter */ protected function getURLs($record) { - $recordHelper = $this->helperManager->get('Record'); + $recordHelper = $this->helperManager->get('record'); return $recordHelper($record)->getLinkDetails(); } diff --git a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php index 3dc10384df6b1797a1e5433168c42609de21e8ef..2f3c71b3e59fd070ef9dd38725dc2ae1a3a2e7b7 100644 --- a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php +++ b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php @@ -3,7 +3,7 @@ /** * Unit tests for facet formatter. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * diff --git a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php index 61f1406840874104bd41f6a7d26a21ba45c8d72b..686cfb894d947102266bc96b5d32bc80eaf862fe 100644 --- a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php +++ b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php @@ -3,7 +3,7 @@ /** * Unit tests for record formatter. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -27,8 +27,9 @@ * @link https://vufind.org */ namespace VuFindTest\Formatter; -use VuFindApi\Formatter\RecordFormatter; + use VuFind\I18n\TranslatableString; +use VuFindApi\Formatter\RecordFormatter; /** * Unit tests for record formatter. @@ -77,7 +78,9 @@ class RecordFormatterTest extends \VuFindTest\Unit\TestCase */ protected function getHelperPluginManager() { - $hm = new \Zend\View\HelperPluginManager(); + $hm = new \Zend\View\HelperPluginManager( + $this->createMock('Interop\Container\ContainerInterface') + ); $hm->setService('translate', new \VuFind\View\Helper\Root\Translate()); $mockRecordLink = $this->getMockBuilder('VuFind\View\Helper\Root\RecordLink') diff --git a/module/VuFindConsole/Module.php b/module/VuFindConsole/Module.php index e3e35e1b498247dd27f23fd0abf3ebb2d6475526..bbcbf4f931733ae4d14e43a50783c623719c3bcd 100644 --- a/module/VuFindConsole/Module.php +++ b/module/VuFindConsole/Module.php @@ -2,7 +2,7 @@ /** * ZF2 module definition for the VuFind console module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development */ namespace VuFindConsole; + use Zend\Console\Adapter\AdapterInterface as Console; /** @@ -78,6 +79,8 @@ class Module implements \Zend\ModuleManager\Feature\ConsoleUsageProviderInterfac * @param Console $console Console adapter * * @return string|null + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getConsoleBanner(Console $console) { @@ -98,6 +101,7 @@ class Module implements \Zend\ModuleManager\Feature\ConsoleUsageProviderInterfac return [ 'compile theme' => 'Flatten a theme hierarchy for improved performance', 'generate dynamicroute' => 'Add a dynamic route', + 'generate extendclass' => 'Subclass a service, w/ lookup by class name', 'generate extendservice' => 'Override a service with a new child class', 'generate nontabrecordaction' => 'Add routes for non-tab record action', 'generate recordroute' => 'Add a record route', diff --git a/module/VuFindConsole/config/module.config.php b/module/VuFindConsole/config/module.config.php index 086187c098d03cdc6336a90403b6cd4bc6174374..bdadb5389c7d8d86c9fe590e9cffd7c5d693e39e 100644 --- a/module/VuFindConsole/config/module.config.php +++ b/module/VuFindConsole/config/module.config.php @@ -4,13 +4,22 @@ namespace VuFindConsole\Module\Configuration; $config = [ 'controllers' => [ 'factories' => [ - 'compile' => 'VuFindConsole\Controller\Factory::getCompileController', - 'generate' => 'VuFindConsole\Controller\Factory::getGenerateController', - 'harvest' => 'VuFindConsole\Controller\Factory::getHarvestController', - 'import' => 'VuFindConsole\Controller\Factory::getImportController', - 'language' => 'VuFindConsole\Controller\Factory::getLanguageController', - 'redirect' => 'VuFindConsole\Controller\Factory::getRedirectController', - 'util' => 'VuFindConsole\Controller\Factory::getUtilController', + 'VuFindConsole\Controller\CompileController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\GenerateController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\HarvestController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\ImportController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\LanguageController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\RedirectController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\UtilController' => 'VuFind\Controller\AbstractBaseFactory', + ], + 'aliases' => [ + 'compile' => 'VuFindConsole\Controller\CompileController', + 'generate' => 'VuFindConsole\Controller\GenerateController', + 'harvest' => 'VuFindConsole\Controller\HarvestController', + 'import' => 'VuFindConsole\Controller\ImportController', + 'language' => 'VuFindConsole\Controller\LanguageController', + 'redirect' => 'VuFindConsole\Controller\RedirectController', + 'util' => 'VuFindConsole\Controller\UtilController', ], ], 'console' => [ @@ -29,6 +38,11 @@ $config = [ ], ], ], + 'service_manager' => [ + 'factories' => [ + 'VuFindConsole\Generator\GeneratorTools' => 'VuFindConsole\Generator\GeneratorToolsFactory', + ], + ], 'view_manager' => [ // CLI tools are admin-oriented, so we should always output full errors: 'display_exceptions' => true, @@ -38,13 +52,14 @@ $config = [ $routes = [ 'compile/theme' => 'compile theme [--force] [<source>] [<target>]', 'generate/dynamicroute' => 'generate dynamicroute [<name>] [<newController>] [<newAction>] [<module>]', + 'generate/extendclass' => 'generate extendclass [--extendfactory] [<class>] [<target>]', 'generate/extendservice' => 'generate extendservice [<source>] [<target>]', 'generate/nontabrecordaction' => 'generate nontabrecordaction [<newAction>] [<module>]', 'generate/recordroute' => 'generate recordroute [<base>] [<newController>] [<module>]', 'generate/staticroute' => 'generate staticroute [<name>] [<module>]', 'generate/theme' => 'generate theme [<themename>]', 'generate/thememixin' => 'generate thememixin [<name>]', - // harvest/harvest_oai is too complex to represent here; we need to rely on default-route + 'harvest/harvest_oai' => 'harvest harvest_oai [...params]', 'harvest/merge-marc' => 'harvest merge-marc [<dir>]', 'import/import-xsl' => 'import import-xsl [--test-only] [--index=] [<xml>] [<properties>]', 'import/webcrawl' => 'import webcrawl [--test-only] [--index=]', @@ -55,7 +70,7 @@ $routes = [ 'util/cleanup_record_cache' => 'util (cleanuprecordcache|cleanup_record_cache) [--help|-h]', 'util/commit' => 'util commit [<core>]', 'util/createHierarchyTrees' => 'util createHierarchyTrees [--skip-xml|-sx] [--skip-json|-sj] [--help|-h]', - // util/cssBuilder relies on default-route because it has an arbitrary number of parameters + 'util/cssBuilder' => 'util cssBuilder [...themes]', 'util/deletes' => 'util deletes [--verbose] [<filename>] [<format>] [<index>]', 'util/expire_external_sessions' => 'util expire_external_sessions [--help|-h] [--batch=] [--sleep=] [<daysOld>]', 'util/expire_searches' => 'util expire_searches [--help|-h] [--batch=] [--sleep=] [<daysOld>]', diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php b/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php index aff3bfdc4e45150c39e5d63791ed4ae61957f2f0..78322f74f8bc04ce00ec985a281f6b95bf80d4fb 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php @@ -3,7 +3,7 @@ * VuFind controller base class (defines some methods that can be shared by other * controllers). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,9 +27,10 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; -use Zend\Console\Console, - Zend\ServiceManager\ServiceLocatorInterface, - Zend\Mvc\Controller\AbstractActionController; + +use Zend\Console\Console; +use Zend\Mvc\Controller\AbstractActionController; +use Zend\ServiceManager\ServiceLocatorInterface; /** * VuFind controller base class (defines some methods that can be shared by other @@ -55,7 +56,7 @@ class AbstractBase extends AbstractActionController throw new \Exception('Access denied to command line tools.'); } - $this->setServiceLocator($sm); + $this->serviceLocator = $sm; // Switch the context back to the original working directory so that // relative paths work as expected. (This constant is set in @@ -116,7 +117,7 @@ class AbstractBase extends AbstractActionController */ public function getConfig($id = 'config') { - return $this->serviceLocator->get('VuFind\Config')->get($id); + return $this->serviceLocator->get('VuFind\Config\PluginManager')->get($id); } /** @@ -126,7 +127,7 @@ class AbstractBase extends AbstractActionController */ public function getILS() { - return $this->serviceLocator->get('VuFind\ILSConnection'); + return $this->serviceLocator->get('VuFind\ILS\Connection'); } /** @@ -138,7 +139,7 @@ class AbstractBase extends AbstractActionController */ public function getTable($table) { - return $this->serviceLocator->get('VuFind\DbTablePluginManager') + return $this->serviceLocator->get('VuFind\Db\Table\PluginManager') ->get($table); } } diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php b/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php index 4836a632f2fea2b76d4a85fc39ebb3019a86bea4..d2696c70ec9ea62c54780d9e25b628f4b2d2eec1 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php @@ -2,7 +2,7 @@ /** * Compile Controller Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; + use Zend\Console\Console; /** diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php b/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php deleted file mode 100644 index 110efc00109a66fb0b7d2722ab11ff66ec03a386..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Factory for controllers. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFindConsole\Controller; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for controllers. - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct a generic controller. - * - * @param string $name Name of table to construct (fully qualified - * class name, or else a class name within the current namespace) - * @param ServiceManager $sm Service manager - * - * @return object - */ - public static function getGenericController($name, ServiceManager $sm) - { - // Prepend the current namespace unless we receive a FQCN: - $class = (strpos($name, '\\') === false) - ? __NAMESPACE__ . '\\' . $name : $name; - if (!class_exists($class)) { - throw new \Exception('Cannot construct ' . $class); - } - return new $class($sm->getServiceLocator()); - } - - /** - * Construct a generic controller. - * - * @param string $name Method name being called - * @param array $args Method arguments - * - * @return object - */ - public static function __callStatic($name, $args) - { - // Strip "get" from method name to get name of class; pass first argument - // on assumption that it should be the ServiceManager object. - return static::getGenericController( - substr($name, 3), isset($args[0]) ? $args[0] : null - ); - } -} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php b/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php index db7ecc6fa2d3e3be580c9c69813d55dc75df8595..01252e446f5e7e0dd1a2122af42365468bb82b2f 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php @@ -2,7 +2,7 @@ /** * CLI Controller Module (language tools) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,10 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; -use Zend\Code\Generator\ClassGenerator; -use Zend\Code\Generator\MethodGenerator; -use Zend\Code\Generator\FileGenerator; -use Zend\Code\Reflection\ClassReflection; + use Zend\Console\Console; /** @@ -76,8 +73,9 @@ class GenerateController extends AbstractBase } // Create backup of configuration - $configPath = $this->getModuleConfigPath($module); - $this->backUpFile($configPath); + $generator = $this->getGeneratorTools(); + $configPath = $generator->getModuleConfigPath($module); + $generator->backUpFile($configPath); // Append the route $config = include $configPath; @@ -85,71 +83,81 @@ class GenerateController extends AbstractBase $routeGenerator->addDynamicRoute($config, $route, $controller, $action); // Write updated configuration - $this->writeModuleConfig($configPath, $config); + $generator->writeModuleConfig($configPath, $config); return $this->getSuccessResponse(); } /** - * Extend an existing service + * Extend an existing class * * @return \Zend\Console\Response */ - public function extendserviceAction() + public function extendclassAction() { // Display help message if parameters missing: $request = $this->getRequest(); - $source = $request->getParam('source'); + $class = $request->getParam('class'); $target = $request->getParam('target'); - if (empty($source) || empty($target)) { + $extendFactory = $request->getParam('extendfactory'); + + if (empty($class) || empty($target)) { Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate extendservice' - . ' [config_path] [target_module]' + 'Usage: ' . $request->getScriptName() . ' generate extendclass' + . ' [--extendfactory] [class_name] [target_module]' ); Console::writeLine( - "\tconfig_path - the path to the service in the framework config" + "\t--extendfactory - optional switch; when set, subclass " + . 'the factory; otherwise, use existing factory' + ); + Console::writeLine( + "\tclass_name - the name of the class you wish to extend" ); - Console::writeLine("\t\te.g. controllers/invokables/generate"); Console::writeLine( "\ttarget_module - the module where the new class will be generated" ); return $this->getFailureResponse(); } - $parts = explode('/', $source); - $partCount = count($parts); - if ($partCount < 3) { - Console::writeLine("Config path too short."); + try { + $this->getGeneratorTools()->extendClass( + $this->serviceLocator, $class, $target, $extendFactory + ); + } catch (\Exception $e) { + Console::writeLine($e->getMessage()); return $this->getFailureResponse(); } - $sourceType = $parts[$partCount - 2]; - $supportedTypes = ['factories', 'invokables']; - if (!in_array($sourceType, $supportedTypes)) { + return $this->getSuccessResponse(); + } + + /** + * Extend an existing service + * + * @return \Zend\Console\Response + */ + public function extendserviceAction() + { + // Display help message if parameters missing: + $request = $this->getRequest(); + $source = $request->getParam('source'); + $target = $request->getParam('target'); + if (empty($source) || empty($target)) { Console::writeLine( - 'Unsupported service type; supported values: ' - . implode(', ', $supportedTypes) + 'Usage: ' . $request->getScriptName() . ' generate extendservice' + . ' [config_path] [target_module]' + ); + Console::writeLine( + "\tconfig_path - the path to the service in the framework config" + ); + Console::writeLine("\t\te.g. controllers/invokables/generate"); + Console::writeLine( + "\ttarget_module - the module where the new class will be generated" ); - return $this->getFailureResponse(); - } - - $config = $this->retrieveConfig($parts); - if (!$config) { - Console::writeLine("{$source} not found in configuration."); return $this->getFailureResponse(); } try { - switch ($sourceType) { - case 'factories': - $newConfig = $this->cloneFactory($config, $target); - break; - case 'invokables': - $newConfig = $this->createSubclassInModule($config, $target); - break; - default: - throw new \Exception('Reached unreachable code!'); - } - $this->writeNewConfig($parts, $newConfig, $target); + $this->getGeneratorTools()->extendService($source, $target); } catch (\Exception $e) { Console::writeLine($e->getMessage()); return $this->getFailureResponse(); @@ -183,8 +191,9 @@ class GenerateController extends AbstractBase } // Create backup of configuration - $configPath = $this->getModuleConfigPath($module); - $this->backUpFile($configPath); + $generator = $this->getGeneratorTools(); + $configPath = $generator->getModuleConfigPath($module); + $generator->backUpFile($configPath); // Load the route config $config = include $configPath; @@ -209,7 +218,7 @@ class GenerateController extends AbstractBase } // Write updated configuration - $this->writeModuleConfig($configPath, $config); + $generator->writeModuleConfig($configPath, $config); return $this->getSuccessResponse(); } @@ -242,8 +251,9 @@ class GenerateController extends AbstractBase } // Create backup of configuration - $configPath = $this->getModuleConfigPath($module); - $this->backUpFile($configPath); + $generator = $this->getGeneratorTools(); + $configPath = $generator->getModuleConfigPath($module); + $generator->backUpFile($configPath); // Append the route $config = include $configPath; @@ -251,7 +261,7 @@ class GenerateController extends AbstractBase $routeGenerator->addRecordRoute($config, $base, $controller); // Write updated configuration - $this->writeModuleConfig($configPath, $config); + $generator->writeModuleConfig($configPath, $config); return $this->getSuccessResponse(); } @@ -280,8 +290,9 @@ class GenerateController extends AbstractBase } // Create backup of configuration - $configPath = $this->getModuleConfigPath($module); - $this->backUpFile($configPath); + $generator = $this->getGeneratorTools(); + $configPath = $generator->getModuleConfigPath($module); + $generator->backUpFile($configPath); // Append the route $config = include $configPath; @@ -289,7 +300,7 @@ class GenerateController extends AbstractBase $routeGenerator->addStaticRoute($config, $route); // Write updated configuration - $this->writeModuleConfig($configPath, $config); + $generator->writeModuleConfig($configPath, $config); return $this->getSuccessResponse(); } @@ -348,322 +359,12 @@ class GenerateController extends AbstractBase } /** - * Create a new subclass and factory to override a factory-generated - * service. + * Get generator tools * - * @param mixed $factory Factory configuration for class to extend - * @param string $module Module in which to create the new factory - * - * @return string - * @throws \Exception + * @return \VuFindConsole\Generator\GeneratorTools */ - protected function cloneFactory($factory, $module) + protected function getGeneratorTools() { - // Make sure we can figure out how to handle the factory; it should - // either be a [controller, method] array or a "controller::method" - // string; anything else will cause a problem. - $parts = is_string($factory) ? explode('::', $factory) : $factory; - if (!is_array($parts) || count($parts) != 2 || !class_exists($parts[0]) - || !method_exists($parts[0], $parts[1]) - ) { - throw new \Exception('Unexpected factory configuration format.'); - } - list($factoryClass, $factoryMethod) = $parts; - $newFactoryClass = $this->generateLocalClassName($factoryClass, $module); - if (!class_exists($newFactoryClass)) { - $this->createClassInModule($newFactoryClass, $module); - $skipBackup = true; - } else { - $skipBackup = false; - } - if (method_exists($newFactoryClass, $factoryMethod)) { - throw new \Exception("$newFactoryClass::$factoryMethod already exists."); - } - - $oldReflection = new ClassReflection($factoryClass); - $newReflection = new ClassReflection($newFactoryClass); - - $generator = ClassGenerator::fromReflection($newReflection); - $method = MethodGenerator::fromReflection( - $oldReflection->getMethod($factoryMethod) - ); - $this->createServiceClassAndUpdateFactory( - $method, $oldReflection->getNamespaceName(), $module - ); - $generator->addMethodFromGenerator($method); - $this->writeClass($generator, $module, true, $skipBackup); - - return $newFactoryClass . '::' . $factoryMethod; - } - - /** - * Given a factory method, extend the class being constructed and create - * a new factory for the subclass. - * - * @param MethodGenerator $method Method to modify - * @param string $ns Namespace of old factory - * @param string $module Module in which to make changes - * - * @return void - * @throws \Exception - */ - protected function createServiceClassAndUpdateFactory(MethodGenerator $method, - $ns, $module - ) { - $body = $method->getBody(); - $regex = '/new\s+([\w\\\\]*)\s*\(/m'; - preg_match_all($regex, $body, $matches); - $classNames = $matches[1]; - $count = count($classNames); - if ($count != 1) { - throw new \Exception("Found $count class names; expected 1."); - } - $className = $classNames[0]; - // Figure out fully qualified name for purposes of createSubclassInModule(): - $fqClassName = (substr($className, 0, 1) != '\\') - ? "$ns\\$className" : $className; - $newClass = $this->createSubclassInModule($fqClassName, $module); - $body = preg_replace( - '/new\s+' . addslashes($className) . '\s*\(/m', - 'new \\' . $newClass . '(', - $body - ); - $method->setBody($body); - } - - /** - * Determine the name of a local replacement class within the specified - * module. - * - * @param string $class Name of class to extend/replace - * @param string $module Module in which to create the new class - * - * @return string - * @throws \Exception - */ - protected function generateLocalClassName($class, $module) - { - // Determine the name of the new class by exploding the old class and - // replacing the namespace: - $parts = explode('\\', trim($class, '\\')); - if (count($parts) < 2) { - throw new \Exception('Expected a namespaced class; found ' . $class); - } - $parts[0] = $module; - return implode('\\', $parts); - } - - /** - * Extend a specified class within a specified module. Return the name of - * the new subclass. - * - * @param string $class Name of class to create - * @param string $module Module in which to create the new class - * @param string $parent Parent class (null for no parent) - * - * @return void - * @throws \Exception - */ - protected function createClassInModule($class, $module, $parent = null) - { - $generator = new ClassGenerator($class, null, null, $parent); - return $this->writeClass($generator, $module); - } - - /** - * Write a class to disk. - * - * @param ClassGenerator $classGenerator Representation of class to write - * @param string $module Module in which to write class - * @param bool $allowOverwrite Allow overwrite of existing file? - * @param bool $skipBackup Should we skip backing up the file? - * - * @return void - * @throws \Exception - */ - protected function writeClass(ClassGenerator $classGenerator, $module, - $allowOverwrite = false, $skipBackup = false - ) { - // Use the class name parts from the previous step to determine a path - // and filename, then create the new path. - $parts = explode('\\', $classGenerator->getNamespaceName()); - array_unshift($parts, 'module', $module, 'src'); - $this->createTree($parts); - - // Generate the new class: - $generator = FileGenerator::fromArray(['classes' => [$classGenerator]]); - $filename = $classGenerator->getName() . '.php'; - $fullPath = APPLICATION_PATH . '/' . implode('/', $parts) . '/' . $filename; - if (file_exists($fullPath)) { - if ($allowOverwrite) { - if (!$skipBackup) { - $this->backUpFile($fullPath); - } - } else { - throw new \Exception("$fullPath already exists."); - } - } - if (!file_put_contents($fullPath, $generator->generate())) { - throw new \Exception("Problem writing to $fullPath."); - } - Console::writeLine("Saved file: $fullPath"); - } - - /** - * Extend a specified class within a specified module. Return the name of - * the new subclass. - * - * @param string $class Name of class to extend - * @param string $module Module in which to create the new class - * - * @return string - * @throws \Exception - */ - protected function createSubclassInModule($class, $module) - { - // Normalize leading backslashes; in some contexts we will - // have them and in others we may not. - $class = trim($class, '\\'); - $newClass = $this->generateLocalClassName($class, $module); - $this->createClassInModule($newClass, $module, "\\$class"); - return $newClass; - } - - /** - * Create a directory tree. - * - * @param array $path Array of subdirectories to create relative to - * APPLICATION_PATH - * - * @return void - * @throws \Exception - */ - protected function createTree($path) - { - $fullPath = APPLICATION_PATH; - foreach ($path as $part) { - $fullPath .= '/' . $part; - if (!file_exists($fullPath)) { - if (!mkdir($fullPath)) { - throw new \Exception("Problem creating $fullPath"); - } - } - if (!is_dir($fullPath)) { - throw new \Exception("$fullPath is not a directory!"); - } - } - } - - /** - * Create a backup of a file. - * - * @param string $filename File to back up - * - * @return void - * @throws \Exception - */ - protected function backUpFile($filename) - { - $backup = $filename . '.' . time() . '.bak'; - if (!copy($filename, $backup)) { - throw new \Exception("Problem generating backup file: $backup"); - } - Console::writeLine("Created backup: $backup"); - } - - /** - * Get the path to the module configuration; throw an exception if it is - * missing. - * - * @param string $module Module name - * - * @return string - * @throws \Exception - */ - protected function getModuleConfigPath($module) - { - $configPath = APPLICATION_PATH . "/module/$module/config/module.config.php"; - if (!file_exists($configPath)) { - throw new \Exception("Cannot find $configPath"); - } - return $configPath; - } - - /** - * Write a module configuration. - * - * @param string $configPath Path to write to - * @param string $config Configuration array to write - * - * @return void - * @throws \Exception - */ - protected function writeModuleConfig($configPath, $config) - { - $generator = FileGenerator::fromArray( - [ - 'body' => 'return ' . var_export($config, true) . ';' - ] - ); - if (!file_put_contents($configPath, $generator->generate())) { - throw new \Exception("Cannot write to $configPath"); - } - Console::writeLine("Successfully updated $configPath"); - } - /** - * Update the configuration of a target module. - * - * @param array $path Representation of path in config array - * @param string $setting New setting to write into config - * @param string $module Module in which to write the configuration - * - * @return void - * @throws \Exception - */ - protected function writeNewConfig($path, $setting, $module) - { - // Create backup of configuration - $configPath = $this->getModuleConfigPath($module); - $this->backUpFile($configPath); - - $config = include $configPath; - $current = & $config; - $finalStep = array_pop($path); - foreach ($path as $step) { - if (!is_array($current)) { - throw new \Exception('Unexpected non-array: ' . $current); - } - if (!isset($current[$step])) { - $current[$step] = []; - } - $current = & $current[$step]; - } - if (!is_array($current)) { - throw new \Exception('Unexpected non-array: ' . $current); - } - $current[$finalStep] = $setting; - - // Write updated configuration - $this->writeModuleConfig($configPath, $config); - } - - /** - * Retrieve a value from the application configuration (or return false - * if the path is not found). - * - * @param array $path Path to walk through configuration - * - * @return mixed - */ - protected function retrieveConfig(array $path) - { - $config = $this->serviceLocator->get('config'); - foreach ($path as $part) { - if (!isset($config[$part])) { - return false; - } - $config = $config[$part]; - } - return $config; + return $this->serviceLocator->get('VuFindConsole\Generator\GeneratorTools'); } } diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php b/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php index b5bd7aa9d09bbc559be2ef5ad4f2ca9e4db0cc36..fdbb12bff3105c95bf906d751f2f4cbafc6995b0 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php @@ -2,7 +2,7 @@ /** * CLI Controller Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; -use VuFindHarvest\OaiPmh\HarvesterConsoleRunner, Zend\Console\Console; + +use VuFindHarvest\OaiPmh\HarvesterConsoleRunner; +use Zend\Console\Console; /** * This controller handles various command-line tools @@ -75,13 +77,15 @@ class HarvestController extends AbstractBase // Get default options, add the default --ini setting if missing: $opts = HarvesterConsoleRunner::getDefaultOptions(); + $opts->setArguments($this->getRequest()->getParam('params')); if (!$opts->getOption('ini')) { $ini = \VuFind\Config\Locator::getConfigPath('oai.ini', 'harvest'); $opts->addArguments(['--ini=' . $ini]); } // Get the default VuFind HTTP client: - $client = $this->serviceLocator->get('VuFind\Http')->createClient(); + $client = $this->serviceLocator->get('VuFindHttp\HttpService') + ->createClient(); // Run the job! $runner = new HarvesterConsoleRunner( diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php b/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php index a9c75d86e25b24354f2ad0c4bf1a0d6836eec6b0..8c1ff0c2c23a0296d44b9471515af4669f1db46d 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php @@ -2,7 +2,7 @@ /** * CLI Controller Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; -use VuFind\XSLT\Importer, Zend\Console\Console; + +use VuFind\XSLT\Importer; +use Zend\Console\Console; /** * This controller handles various command-line tools @@ -138,7 +140,7 @@ class ImportController extends AbstractBase $testMode = $request->getParam('test-only') ? true : false; $index = $request->getParam('index', 'SolrWeb'); - $configLoader = $this->serviceLocator->get('VuFind\Config'); + $configLoader = $this->serviceLocator->get('VuFind\Config\PluginManager'); $crawlConfig = $configLoader->get('webcrawl'); // Get the time we started indexing -- we'll delete records older than this diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php b/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php index deb3d9f91a1022491e77de8f064fc15ab7b42b9b..c130d346ea61f90938c6bdb31a173829c0f3569c 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php @@ -2,7 +2,7 @@ /** * CLI Controller Module (language tools) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,9 +26,10 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; -use VuFind\I18n\ExtendedIniNormalizer, - VuFind\I18n\Translator\Loader\ExtendedIniReader, - Zend\Console\Console; + +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; +use Zend\Console\Console; /** * This controller handles various command-line tools for dealing with language files @@ -278,7 +279,7 @@ class LanguageController extends AbstractBase $normalizer = new ExtendedIniNormalizer(); if (is_dir($target)) { $normalizer->normalizeDirectory($target); - } else if (is_file($target)) { + } elseif (is_file($target)) { $normalizer->normalizeFile($target); } else { Console::writeLine("{$target} does not exist."); diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php b/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php index ac40a88c278fa275c73589fa837a8193a61725ba..23dca95fb007d01ba6fda90be027177b0bf7c00c 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php @@ -2,7 +2,7 @@ /** * Redirect Controller * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; + use Zend\Console\Console; use Zend\Mvc\Application; @@ -38,7 +39,7 @@ use Zend\Mvc\Application; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ -class RedirectController extends \Zend\Mvc\Controller\AbstractActionController +class RedirectController extends AbstractBase { /** * Get a usage message with the help of the RouteNotFoundStrategy. diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php b/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php index 905d4ae6fc76d06eb4e301c4b05b2397488e8614..d221d2cf2595ec733727589a80fc52ba0abea4bf 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php @@ -2,7 +2,7 @@ /** * CLI Controller Module * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,14 +26,17 @@ * @link https://vufind.org/wiki/development:plugins:controllers Wiki */ namespace VuFindConsole\Controller; -use File_MARC, File_MARCXML, VuFind\Sitemap\Generator as Sitemap; + +use File_MARC; +use File_MARCXML; use VuFind\Config\Locator as ConfigLocator; use VuFind\Config\Writer as ConfigWriter; +use VuFind\Sitemap\Generator as Sitemap; use VuFindSearch\Backend\Solr\Document\UpdateDocument; use VuFindSearch\Backend\Solr\Record\SerializableRecord; use Zend\Console\Console; -use Zend\Crypt\Symmetric\Openssl, - Zend\Crypt\BlockCipher as BlockCipher; +use Zend\Crypt\BlockCipher as BlockCipher; +use Zend\Crypt\Symmetric\Openssl; /** * This controller handles various command-line tools @@ -204,14 +207,11 @@ class UtilController extends AbstractBase 'id' => $id, 'bib_id' => [], 'instructor_id' => $instructor_id, - 'instructor' => isset($instructors[$instructor_id]) - ? $instructors[$instructor_id] : '', + 'instructor' => $instructors[$instructor_id] ?? '', 'course_id' => $course_id, - 'course' => isset($courses[$course_id]) - ? $courses[$course_id] : '', + 'course' => $courses[$course_id] ?? '', 'department_id' => $department_id, - 'department' => isset($departments[$department_id]) - ? $departments[$department_id] : '' + 'department' => $departments[$department_id] ?? '' ]; } $index[$id]['bib_id'][] = $record['BIB_ID']; @@ -278,7 +278,7 @@ class UtilController extends AbstractBase public function sitemapAction() { // Build sitemap and display appropriate warnings if needed: - $configLoader = $this->serviceLocator->get('VuFind\Config'); + $configLoader = $this->serviceLocator->get('VuFind\Config\PluginManager'); $generator = new Sitemap( $this->serviceLocator->get('VuFind\Search\BackendManager'), $configLoader->get('config')->Site->url, $configLoader->get('sitemap') @@ -422,7 +422,7 @@ class UtilController extends AbstractBase return $this->getFailureResponse(); } - $recordTable = $this->serviceLocator->get('VuFind\DbTablePluginManager') + $recordTable = $this->serviceLocator->get('VuFind\Db\Table\PluginManager') ->get('Record'); $count = $recordTable->cleanup(); @@ -564,7 +564,7 @@ class UtilController extends AbstractBase if (!is_array($result)) { Console::writeLine("Could not obtain suppressed record list from ILS."); return $this->getFailureResponse(); - } else if (empty($result)) { + } elseif (empty($result)) { Console::writeLine("No suppressed records to delete."); return $this->getSuccessResponse(); } @@ -748,9 +748,9 @@ class UtilController extends AbstractBase } $skipJson = $request->getParam('skip-json') || $request->getParam('sj'); $skipXml = $request->getParam('skip-xml') || $request->getParam('sx'); - $recordLoader = $this->serviceLocator->get('VuFind\RecordLoader'); + $recordLoader = $this->serviceLocator->get('VuFind\Record\Loader'); $hierarchies = $this->serviceLocator - ->get('VuFind\SearchResultsPluginManager')->get('Solr') + ->get('VuFind\Search\Results\PluginManager')->get('Solr') ->getFullFieldFacets(['hierarchy_top_id']); if (!isset($hierarchies['hierarchy_top_id']['data']['list'])) { $hierarchies['hierarchy_top_id']['data']['list'] = []; @@ -808,23 +808,22 @@ class UtilController extends AbstractBase */ public function cssbuilderAction() { - $opts = new \Zend\Console\Getopt([]); $compiler = new \VuFindTheme\LessCompiler(true); - $cacheManager = $this->serviceLocator->get('VuFind\CacheManager'); + $cacheManager = $this->serviceLocator->get('VuFind\Cache\Manager'); $cacheDir = $cacheManager->getCacheDir() . 'less/'; $compiler->setTempPath($cacheDir); - $compiler->compile(array_unique($opts->getRemainingArgs())); + $compiler->compile(array_unique($this->getRequest()->getParam('themes'))); return $this->getSuccessResponse(); } /** * Abstract delete method. * - * @param string $tableName Table to operate on. - * @param string $successString String for reporting success. - * @param string $failString String for reporting failure. - * @param int $minAge Minimum age allowed for expiration (also used - * as default value). + * @param string $tableName Table to operate on. + * @param string $successString String for reporting success. + * @param string $failString String for reporting failure. + * @param int|float $minAge Minimum age allowed for expiration in days + * (also used as default value). * * @return mixed */ @@ -834,7 +833,7 @@ class UtilController extends AbstractBase $request = $this->getRequest(); // Use command line value as expiration age, or default to $minAge. - $daysOld = intval($request->getParam('daysOld', $minAge)); + $daysOld = floatval($request->getParam('daysOld', $minAge)); // Use command line values for batch size and sleep time if specified. $batchSize = $request->getParam('batch', 1000); @@ -969,7 +968,7 @@ class UtilController extends AbstractBase } // Now do the database rewrite: - $userTable = $this->serviceLocator->get('VuFind\DbTablePluginManager') + $userTable = $this->serviceLocator->get('VuFind\Db\Table\PluginManager') ->get('User'); $users = $userTable->select( function ($select) { diff --git a/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php b/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php new file mode 100644 index 0000000000000000000000000000000000000000..82732035c29dadd84ec704ecc7e43c342fca06e4 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php @@ -0,0 +1,618 @@ +<?php +/** + * Generator tools. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFindConsole\Generator; + +use Interop\Container\ContainerInterface; +use Zend\Code\Generator\ClassGenerator; +use Zend\Code\Generator\FileGenerator; +use Zend\Code\Generator\MethodGenerator; +use Zend\Code\Reflection\ClassReflection; +use Zend\Console\Console; + +/** + * Generator tools. + * + * @category VuFind + * @package Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GeneratorTools +{ + /** + * Zend Framework configuration + * + * @var array + */ + protected $config; + + /** + * Constructor. + * + * @param array $config Zend Framework configuration + */ + public function __construct(array $config) + { + $this->config = $config; + } + + /** + * Extend a class defined somewhere in the service manager or its child + * plugin managers. + * + * @param ContainerInterface $container Service manager + * @param string $class Class name to extend + * @param string $target Target module in which to create new + * service + * @param bool $extendFactory Should we extend the factory? + * + * @return bool + * @throws \Exception + */ + public function extendClass(ContainerInterface $container, $class, $target, + $extendFactory = false + ) { + // Set things up differently depending on whether this is a top-level + // service or a class in a plugin manager. + $cm = $container->get('ControllerManager'); + $cpm = $container->get('ControllerPluginManager'); + $delegators = []; + if ($container->has($class)) { + $factory = $this->getFactoryFromContainer($container, $class); + $configPath = ['service_manager']; + } elseif ($factory = $this->getFactoryFromContainer($cm, $class)) { + $configPath = ['controllers']; + } elseif ($factory = $this->getFactoryFromContainer($cpm, $class)) { + $configPath = ['controller_plugins']; + } elseif ($pm = $this->getPluginManagerContainingClass($container, $class)) { + $apmFactory = new \VuFind\ServiceManager\AbstractPluginManagerFactory(); + $pmKey = $apmFactory->getConfigKey(get_class($pm)); + $factory = $this->getFactoryFromContainer($pm, $class); + $configPath = ['vufind', 'plugin_managers', $pmKey]; + $delegators = $this->getDelegatorsFromContainer($pm, $class); + } + + // No factory found? Throw an error! + if (empty($factory)) { + throw new \Exception('Could not find factory for ' . $class); + } + + // Create the custom subclass. + $newClass = $this->createSubclassInModule($class, $target); + + // Create the custom factory only if requested. + $newFactory = $extendFactory + ? $this->cloneFactory($factory, $target) : $factory; + + // Finalize the local module configuration -- create a factory for the + // new class, and set up the new class as an alias for the old class. + $factoryPath = array_merge($configPath, ['factories', $newClass]); + $this->writeNewConfig($factoryPath, $newFactory, $target); + $aliasPath = array_merge($configPath, ['aliases', $class]); + // Don't back up the config twice -- the first backup from the previous + // write operation is sufficient. + $this->writeNewConfig($aliasPath, $newClass, $target, false); + + // Clone/configure delegator factories as needed. + if (!empty($delegators)) { + $newDelegators = []; + foreach ($delegators as $delegator) { + $newDelegators[] = $extendFactory + ? $this->cloneFactory($delegator, $target) : $delegator; + } + $delegatorPath = array_merge($configPath, ['delegators', $newClass]); + $this->writeNewConfig($delegatorPath, $newDelegators, $target, false); + } + + return true; + } + + /** + * Get a list of factories in the provided container. + * + * @param ContainerInterface $container Container to inspect + * + * @return array + */ + protected function getAllFactoriesFromContainer(ContainerInterface $container) + { + // There is no "getFactories" method, so we need to use reflection: + $reflectionProperty = new \ReflectionProperty($container, 'factories'); + $reflectionProperty->setAccessible(true); + return $reflectionProperty->getValue($container); + } + + /** + * Get a factory from the provided container (or null if undefined). + * + * @param ContainerInterface $container Container to inspect + * @param string $class Class whose factory we want + * + * @return string + */ + protected function getFactoryFromContainer(ContainerInterface $container, $class) + { + $factories = $this->getAllFactoriesFromContainer($container); + return $factories[$class] ?? null; + } + + /** + * Get a list of delegators in the provided container. + * + * @param ContainerInterface $container Container to inspect + * + * @return array + */ + protected function getAllDelegatorsFromContainer(ContainerInterface $container) + { + // There is no "getDelegators" method, so we need to use reflection: + $reflectionProperty = new \ReflectionProperty($container, 'delegators'); + $reflectionProperty->setAccessible(true); + return $reflectionProperty->getValue($container); + } + + /** + * Get delegators from the provided container (or empty array if undefined). + * + * @param ContainerInterface $container Container to inspect + * @param string $class Class whose delegators we want + * + * @return array + */ + protected function getDelegatorsFromContainer(ContainerInterface $container, + $class + ) { + $delegators = $this->getAllDelegatorsFromContainer($container); + return $delegators[$class] ?? []; + } + + /** + * Search all plugin managers for one containing the requested class (or return + * null if none found). + * + * @param ContainerInterface $container Service manager + * @param string $class Class to search for + * + * @return ContainerInterface + */ + protected function getPluginManagerContainingClass(ContainerInterface $container, + $class + ) { + $factories = $this->getAllFactoriesFromContainer($container); + foreach (array_keys($factories) as $service) { + if (substr($service, -13) == 'PluginManager') { + $pm = $container->get($service); + if (null !== $this->getFactoryFromContainer($pm, $class)) { + return $pm; + } + } + } + return null; + } + + /** + * Extend a service defined in module.config.php. + * + * @param string $source Configuration path to use as source + * @param string $target Target module in which to create new service + * + * @return bool + * @throws \Exception + */ + public function extendService($source, $target) + { + $parts = explode('/', $source); + $partCount = count($parts); + if ($partCount < 3) { + throw new \Exception('Config path too short.'); + } + $sourceType = $parts[$partCount - 2]; + + $supportedTypes = ['factories', 'invokables']; + if (!in_array($sourceType, $supportedTypes)) { + throw new \Exception( + 'Unsupported service type; supported values: ' + . implode(', ', $supportedTypes) + ); + } + + $config = $this->retrieveConfig($parts); + if (!$config) { + throw new \Exception("{$source} not found in configuration."); + } + + switch ($sourceType) { + case 'factories': + $this->createSubclassInModule($parts[$partCount - 1], $target); + $newConfig = $this->cloneFactory($config, $target); + break; + case 'invokables': + $newConfig = $this->createSubclassInModule($config, $target); + break; + default: + throw new \Exception('Reached unreachable code!'); + } + $this->writeNewConfig($parts, $newConfig, $target); + return true; + } + + /** + * Create a new subclass and factory to override a factory-generated + * service. + * + * @param mixed $factory Factory configuration for class to extend + * @param string $module Module in which to create the new factory + * + * @return string + * @throws \Exception + */ + protected function cloneFactory($factory, $module) + { + // If the factory is a stand-alone class, it's simple to clone: + if (class_exists($factory)) { + return $this->createSubclassInModule($factory, $module); + } + + // Make sure we can figure out how to handle the factory; it should + // either be a [controller, method] array or a "controller::method" + // string; anything else will cause a problem. + $parts = is_string($factory) ? explode('::', $factory) : $factory; + if (!is_array($parts) || count($parts) != 2 || !class_exists($parts[0]) + || !is_callable($parts) + ) { + throw new \Exception('Unexpected factory configuration format.'); + } + list($factoryClass, $factoryMethod) = $parts; + $newFactoryClass = $this->generateLocalClassName($factoryClass, $module); + if (!class_exists($newFactoryClass)) { + $this->createSubclassInModule($factoryClass, $module); + $skipBackup = true; + } else { + $skipBackup = false; + } + + $oldReflection = new ClassReflection($factoryClass); + $newReflection = new ClassReflection($newFactoryClass); + + try { + $newMethod = $newReflection->getMethod($factoryMethod); + if ($newMethod->getDeclaringClass()->getName() == $newFactoryClass) { + throw new \Exception( + "$newFactoryClass::$factoryMethod already exists." + ); + } + + $generator = ClassGenerator::fromReflection($newReflection); + $method = MethodGenerator::fromReflection( + $oldReflection->getMethod($factoryMethod) + ); + $this->updateFactory( + $method, $oldReflection->getNamespaceName(), $module + ); + $generator->addMethodFromGenerator($method); + $this->writeClass($generator, $module, true, $skipBackup); + } catch (\ReflectionException $e) { + // If a parent factory has a __callStatic method, the method we are + // trying to rewrite may not exist. In that case, we can just inherit + // __callStatic and ignore the error. Any other exception should be + // treated as a fatal error. + if (method_exists($factoryClass, '__callStatic')) { + Console::writeLine('Error: ' . $e->getMessage()); + Console::writeLine( + '__callStatic in parent factory; skipping method generation.' + ); + } else { + throw $e; + } + } + + return $newFactoryClass . '::' . $factoryMethod; + } + + /** + * Given a factory method, extend the class being constructed and create + * a new factory for the subclass. + * + * @param MethodGenerator $method Method to modify + * @param string $ns Namespace of old factory + * @param string $module Module in which to make changes + * + * @return void + * @throws \Exception + */ + protected function updateFactory(MethodGenerator $method, + $ns, $module + ) { + $body = $method->getBody(); + $regex = '/new\s+([\w\\\\]*)\s*\(/m'; + preg_match_all($regex, $body, $matches); + $classNames = $matches[1]; + $count = count($classNames); + if ($count != 1) { + throw new \Exception("Found $count class names; expected 1."); + } + $className = $classNames[0]; + // Figure out fully qualified name for purposes of createSubclassInModule(): + $fqClassName = (substr($className, 0, 1) != '\\') + ? "$ns\\$className" : $className; + $newClass = $this->generateLocalClassName($fqClassName, $module); + $body = preg_replace( + '/new\s+' . addslashes($className) . '\s*\(/m', + 'new \\' . $newClass . '(', + $body + ); + $method->setBody($body); + } + + /** + * Determine the name of a local replacement class within the specified + * module. + * + * @param string $class Name of class to extend/replace + * @param string $module Module in which to create the new class + * + * @return string + * @throws \Exception + */ + protected function generateLocalClassName($class, $module) + { + // Determine the name of the new class by exploding the old class and + // replacing the namespace: + $parts = explode('\\', trim($class, '\\')); + if (count($parts) < 2) { + throw new \Exception('Expected a namespaced class; found ' . $class); + } + $parts[0] = $module; + return implode('\\', $parts); + } + + /** + * Extend a specified class within a specified module. Return the name of + * the new subclass. + * + * @param string $class Name of class to create + * @param string $module Module in which to create the new class + * @param string $parent Parent class (null for no parent) + * + * @return void + * @throws \Exception + */ + protected function createClassInModule($class, $module, $parent = null) + { + $generator = new ClassGenerator($class, null, null, $parent); + return $this->writeClass($generator, $module); + } + + /** + * Write a class to disk. + * + * @param ClassGenerator $classGenerator Representation of class to write + * @param string $module Module in which to write class + * @param bool $allowOverwrite Allow overwrite of existing file? + * @param bool $skipBackup Should we skip backing up the file? + * + * @return void + * @throws \Exception + */ + protected function writeClass(ClassGenerator $classGenerator, $module, + $allowOverwrite = false, $skipBackup = false + ) { + // Use the class name parts from the previous step to determine a path + // and filename, then create the new path. + $parts = explode('\\', $classGenerator->getNamespaceName()); + array_unshift($parts, 'module', $module, 'src'); + $this->createTree($parts); + + // Generate the new class: + $generator = FileGenerator::fromArray(['classes' => [$classGenerator]]); + $filename = $classGenerator->getName() . '.php'; + $fullPath = APPLICATION_PATH . '/' . implode('/', $parts) . '/' . $filename; + if (file_exists($fullPath)) { + if ($allowOverwrite) { + if (!$skipBackup) { + $this->backUpFile($fullPath); + } + } else { + throw new \Exception("$fullPath already exists."); + } + } + // TODO: this is a workaround for an apparent bug in Zend\Code which + // omits the leading backslash on "extends" statements when rewriting + // existing classes. Can we remove this after a future Zend\Code upgrade? + $code = str_replace( + 'extends VuFind\\', 'extends \\VuFind\\', $generator->generate() + ); + if (!file_put_contents($fullPath, $code)) { + throw new \Exception("Problem writing to $fullPath."); + } + Console::writeLine("Saved file: $fullPath"); + } + + /** + * Extend a specified class within a specified module. Return the name of + * the new subclass. + * + * @param string $class Name of class to extend + * @param string $module Module in which to create the new class + * + * @return string + * @throws \Exception + */ + protected function createSubclassInModule($class, $module) + { + // Normalize leading backslashes; in some contexts we will + // have them and in others we may not. + $class = trim($class, '\\'); + $newClass = $this->generateLocalClassName($class, $module); + $this->createClassInModule($newClass, $module, "\\$class"); + return $newClass; + } + + /** + * Create a directory tree. + * + * @param array $path Array of subdirectories to create relative to + * APPLICATION_PATH + * + * @return void + * @throws \Exception + */ + protected function createTree($path) + { + $fullPath = APPLICATION_PATH; + foreach ($path as $part) { + $fullPath .= '/' . $part; + if (!file_exists($fullPath)) { + if (!mkdir($fullPath)) { + throw new \Exception("Problem creating $fullPath"); + } + } + if (!is_dir($fullPath)) { + throw new \Exception("$fullPath is not a directory!"); + } + } + } + + /** + * Create a backup of a file. + * + * @param string $filename File to back up + * + * @return void + * @throws \Exception + */ + public function backUpFile($filename) + { + $backup = $filename . '.' . time() . '.bak'; + if (!copy($filename, $backup)) { + throw new \Exception("Problem generating backup file: $backup"); + } + Console::writeLine("Created backup: $backup"); + } + + /** + * Get the path to the module configuration; throw an exception if it is + * missing. + * + * @param string $module Module name + * + * @return string + * @throws \Exception + */ + public function getModuleConfigPath($module) + { + $configPath = APPLICATION_PATH . "/module/$module/config/module.config.php"; + if (!file_exists($configPath)) { + throw new \Exception("Cannot find $configPath"); + } + return $configPath; + } + + /** + * Write a module configuration. + * + * @param string $configPath Path to write to + * @param string $config Configuration array to write + * + * @return void + * @throws \Exception + */ + public function writeModuleConfig($configPath, $config) + { + $generator = FileGenerator::fromArray( + [ + 'body' => 'return ' . var_export($config, true) . ';' + ] + ); + if (!file_put_contents($configPath, $generator->generate())) { + throw new \Exception("Cannot write to $configPath"); + } + Console::writeLine("Successfully updated $configPath"); + } + + /** + * Update the configuration of a target module. + * + * @param array $path Representation of path in config array + * @param string $setting New setting to write into config + * @param string $module Module in which to write the configuration + * @param bool $backup Should we back up the existing config? + * + * @return void + * @throws \Exception + */ + protected function writeNewConfig($path, $setting, $module, $backup = true) + { + // Create backup of configuration + $configPath = $this->getModuleConfigPath($module); + if ($backup) { + $this->backUpFile($configPath); + } + + $config = include $configPath; + $current = & $config; + $finalStep = array_pop($path); + foreach ($path as $step) { + if (!is_array($current)) { + throw new \Exception('Unexpected non-array: ' . $current); + } + if (!isset($current[$step])) { + $current[$step] = []; + } + $current = & $current[$step]; + } + if (!is_array($current)) { + throw new \Exception('Unexpected non-array: ' . $current); + } + $current[$finalStep] = $setting; + + // Write updated configuration + $this->writeModuleConfig($configPath, $config); + } + + /** + * Retrieve a value from the application configuration (or return false + * if the path is not found). + * + * @param array $path Path to walk through configuration + * + * @return mixed + */ + protected function retrieveConfig(array $path) + { + $config = $this->config; + foreach ($path as $part) { + if (!isset($config[$part])) { + return false; + } + $config = $config[$part]; + } + return $config; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorToolsFactory.php b/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorToolsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..27cebaa3fda504aaeb3accb87ddf06cd04c5a8ea --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorToolsFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Generator tools factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFindConsole\Generator; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Generator tools factory. + * + * @category VuFind + * @package Generator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GeneratorToolsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get('config')); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php b/module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php index 0ac97026acdc2fcb22ea05c43ca1fb330fd7f48b..60279b85378337af169e431ef50301562b36acae 100644 --- a/module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php +++ b/module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php @@ -2,7 +2,7 @@ /** * Route Generator Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php index dfff0982e36910f6ad83c91e32629bbf78aa3144..171e28bb07980df7bdb9f0fa2fe3d62c6b3926c4 100644 --- a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php @@ -3,7 +3,7 @@ /** * Route generator tests. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindConsoleTest\Route; + use VuFindConsole\Route\RouteGenerator; /** @@ -38,7 +39,7 @@ use VuFindConsole\Route\RouteGenerator; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ -class CacheTest extends \PHPUnit_Framework_TestCase +class CacheTest extends \PHPUnit\Framework\TestCase { /** * Test route generation diff --git a/module/VuFindDevTools/Module.php b/module/VuFindDevTools/Module.php index 377e9494b75d29e2e8f3a3871f7b22951f876b75..f6e0298c06dbf526d8eccf26fde6452874cfe717 100644 --- a/module/VuFindDevTools/Module.php +++ b/module/VuFindDevTools/Module.php @@ -2,7 +2,7 @@ /** * VuFind Developer Tools module. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,6 @@ * @link https://vufind.org/wiki/development */ namespace VuFindDevTools; -use Zend\ModuleManager\ModuleManager, - Zend\Mvc\MvcEvent; /** * VuFind Developer Tools module. diff --git a/module/VuFindDevTools/config/module.config.php b/module/VuFindDevTools/config/module.config.php index dbe3315d235f2adca33c57fbd21bcb13364d1d24..9f95f2d302d9f853875f2714e5bdd5b7b8c00ec4 100644 --- a/module/VuFindDevTools/config/module.config.php +++ b/module/VuFindDevTools/config/module.config.php @@ -4,13 +4,16 @@ namespace VuFindLocalTemplate\Module\Configuration; $config = [ 'controllers' => [ 'factories' => [ - 'devtools' => 'VuFindDevTools\Controller\Factory::getDevtoolsController', + 'VuFindDevTools\Controller\DevtoolsController' => 'VuFind\Controller\AbstractBaseFactory', + ], + 'aliases' => [ + 'DevTools' => 'VuFindDevTools\Controller\DevtoolsController', ], ], 'router' => [ 'routes' => [ 'devtools-deminify' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/devtools/deminify', 'defaults' => [ @@ -20,7 +23,7 @@ $config = [ ] ], 'devtools-home' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/devtools/home', 'defaults' => [ @@ -30,7 +33,7 @@ $config = [ ] ], 'devtools-language' => [ - 'type' => 'Zend\Mvc\Router\Http\Literal', + 'type' => 'Zend\Router\Http\Literal', 'options' => [ 'route' => '/devtools/language', 'defaults' => [ diff --git a/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php b/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php index abb7ed21b3d02df1902b64d6f0d3c0850d57db97..204527b24ee67c6f5500597ae0beb5d26fed4182 100644 --- a/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php +++ b/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php @@ -2,7 +2,7 @@ /** * Development Tools Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/indexing:alphabetical_heading_browse Wiki */ namespace VuFindDevTools\Controller; + use VuFind\I18n\Translator\Loader\ExtendedIni; use VuFindDevTools\LanguageHelper; @@ -77,7 +78,7 @@ class DevtoolsController extends \VuFind\Controller\AbstractBase } if (isset($view->min) && $view->min) { $view->results = $view->min->deminify( - $this->serviceLocator->get('VuFind\SearchResultsPluginManager') + $this->serviceLocator->get('VuFind\Search\Results\PluginManager') ); } if (isset($view->results) && $view->results) { diff --git a/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php b/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php deleted file mode 100644 index 694fd97f14a6089368daaaed9fc097478eacfb93..0000000000000000000000000000000000000000 --- a/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Factory for controllers. - * - * PHP version 5 - * - * Copyright (C) Villanova University 2014. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFindDevTools\Controller; -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for controllers. - * - * @category VuFind - * @package Controller - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct a generic controller. - * - * @param string $name Name of table to construct (fully qualified - * class name, or else a class name within the current namespace) - * @param ServiceManager $sm Service manager - * - * @return object - */ - public static function getGenericController($name, ServiceManager $sm) - { - // Prepend the current namespace unless we receive a FQCN: - $class = (strpos($name, '\\') === false) - ? __NAMESPACE__ . '\\' . $name : $name; - if (!class_exists($class)) { - throw new \Exception('Cannot construct ' . $class); - } - return new $class($sm->getServiceLocator()); - } - - /** - * Construct a generic controller. - * - * @param string $name Method name being called - * @param array $args Method arguments - * - * @return object - */ - public static function __callStatic($name, $args) - { - // Strip "get" from method name to get name of class; pass first argument - // on assumption that it should be the ServiceManager object. - return static::getGenericController( - substr($name, 3), isset($args[0]) ? $args[0] : null - ); - } -} diff --git a/module/VuFindDevTools/src/VuFindDevTools/LanguageHelper.php b/module/VuFindDevTools/src/VuFindDevTools/LanguageHelper.php index c0aa7f91d9e4957f4ebf6a26a79d442addc0693b..7f1c0ff333fe0e39f47915c2089de1394c88b44e 100644 --- a/module/VuFindDevTools/src/VuFindDevTools/LanguageHelper.php +++ b/module/VuFindDevTools/src/VuFindDevTools/LanguageHelper.php @@ -2,7 +2,7 @@ /** * Language Helper for Development Tools Controller * - * PHP Version 5 + * PHP version 7 * * Copyright (C) Villanova University 2015. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/indexing:alphabetical_heading_browse Wiki */ namespace VuFindDevTools; + use VuFind\I18n\Translator\Loader\ExtendedIni; use Zend\Config\Config; use Zend\I18n\Translator\TextDomain; @@ -159,7 +160,7 @@ class LanguageHelper if (isset($this->config->Languages->$lang)) { return $this->config->Languages->$lang; } - switch($lang) { + switch ($lang) { case 'en-gb': return 'British English'; case 'pt-br': diff --git a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php index d81a66ac999205fd0532d20fada0e1185fb6f3e1..9ff1aeb5fcbabf032cf849bcb3c8d7f71a091fa6 100644 --- a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php +++ b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for DevTools controller. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * diff --git a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php index f0acfed76559bfef41f73223422198792d5605e7..02a09bd4bdc67a7b87b24e9a3a9394ebd05c6507 100644 --- a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php +++ b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php @@ -3,7 +3,7 @@ /** * Unit tests for language helper. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * diff --git a/module/VuFindLocalTemplate/Module.php b/module/VuFindLocalTemplate/Module.php index 10afcf2716bb94c193ac47cb8ad1f9acacecd56c..b28d9363f32fdf9d079abae759abb05f0c48d7e7 100644 --- a/module/VuFindLocalTemplate/Module.php +++ b/module/VuFindLocalTemplate/Module.php @@ -2,7 +2,7 @@ /** * Template for ZF2 module for storing local overrides. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,8 +26,9 @@ * @link https://vufind.org/wiki/development */ namespace VuFindLocalTemplate; -use Zend\ModuleManager\ModuleManager, - Zend\Mvc\MvcEvent; + +use Zend\ModuleManager\ModuleManager; +use Zend\Mvc\MvcEvent; /** * Template for ZF2 module for storing local overrides. diff --git a/module/VuFindSearch/Module.php b/module/VuFindSearch/Module.php index 819807ca6e57f01cd4af32c88ce577cc17860757..9810779d622ebebfaaa0ab32a3e673b7f7608058 100644 --- a/module/VuFindSearch/Module.php +++ b/module/VuFindSearch/Module.php @@ -3,7 +3,7 @@ /** * ZF2 module definition for the VF2 search service. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -31,7 +31,7 @@ namespace VuFindSearch; /** * ZF2 module definition for the VF2 search service. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php b/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php index 8a9961464ba835e350f806ac6ee2cbd053ab639b..dff9533c693b39fb267c011a44cc1b6cbd5f5bda 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php @@ -2,7 +2,7 @@ /** * Abstract backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,8 +27,8 @@ */ namespace VuFindSearch\Backend; -use VuFindSearch\Response\RecordCollectionInterface; use VuFindSearch\Response\RecordCollectionFactoryInterface; +use VuFindSearch\Response\RecordCollectionInterface; use Zend\Log\LoggerAwareInterface; diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/AbstractHandlerMap.php b/module/VuFindSearch/src/VuFindSearch/Backend/AbstractHandlerMap.php index 627b3ec10b6005848c58c5b9dd9a87500cb08a43..47b484b5f561cd8f48f687062dc5467b648113d1 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/AbstractHandlerMap.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/AbstractHandlerMap.php @@ -3,7 +3,7 @@ /** * Base class for search backend handler maps. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BackendInterface.php b/module/VuFindSearch/src/VuFindSearch/Backend/BackendInterface.php index 64d0df1e74fce1eba027cdb6011a4c2b2f349dbe..9fbbfd2682e1f0b11d1ffec4a9bbfb26a1dcdfcc 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BackendInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BackendInterface.php @@ -3,7 +3,7 @@ /** * Search backend interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend; -use VuFindSearch\Query\AbstractQuery; use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; /** * Search backend interface definition. @@ -81,5 +81,4 @@ interface BackendInterface * @return \VuFindSearch\Response\RecordCollectionInterface */ public function retrieve($id, ParamBag $params = null); - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Backend.php index 1bedd4c0e00a827024e4ef06904ed38e4b46e19d..b290a37c105a60f18db620926a06fe4700d20ea2 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Backend.php @@ -2,7 +2,7 @@ /** * BrowZine backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -27,15 +27,15 @@ */ namespace VuFindSearch\Backend\BrowZine; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; + +use VuFindSearch\Backend\Exception\BackendException; use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Response\RecordCollectionInterface; use VuFindSearch\Response\RecordCollectionFactoryInterface; - -use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\Backend\Exception\BackendException; +use VuFindSearch\Response\RecordCollectionInterface; /** * BrowZine backend. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Connector.php index 35616196965ddb039a7983291d0e2112bb1dcf79..3a76f84b107d6135b307f7e8f0ba08daae9fda2f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Connector.php @@ -3,7 +3,7 @@ /** * BrowZine connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -27,6 +27,7 @@ * @link https://vufind.org */ namespace VuFindSearch\Backend\BrowZine; + use Zend\Http\Client as HttpClient; /** diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/QueryBuilder.php index 9301eb551118267a1f24ba58bcc5412cb1fd44cf..f96174ebbf69968e88b2631982d7d01fbe4b870f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/QueryBuilder.php @@ -3,7 +3,7 @@ /** * BrowZine QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,10 +28,10 @@ */ namespace VuFindSearch\Backend\BrowZine; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\Query; /** * BrowZine QueryBuilder. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollection.php index 89c92307210aa62160acbe06f2d24dbea6ab990d..533b90b89d6106825104b9ebcbcd8f33ff4ab59d 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollection.php @@ -3,7 +3,7 @@ /** * BrowZine record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php index 2a9303dc90b060d70d64eefb3e90639802cff7b0..d9a55f933b4f3dbb28d2618ffdef1f92fbdc86ee 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,9 +28,9 @@ */ namespace VuFindSearch\Backend\BrowZine\Response; -use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Exception\InvalidArgumentException; use VuFindSearch\Backend\Solr\Response\Json\Record; +use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple factory for record collection. @@ -72,7 +72,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $recordFactory = function ($i) { return new Record($i); }; - } else if (!is_callable($recordFactory)) { + } elseif (!is_callable($recordFactory)) { throw new InvalidArgumentException('Record factory must be callable.'); } $this->recordFactory = $recordFactory; @@ -104,5 +104,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Exception.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/ApiException.php similarity index 74% rename from module/VuFindSearch/src/VuFindSearch/Backend/EDS/Exception.php rename to module/VuFindSearch/src/VuFindSearch/Backend/EDS/ApiException.php index 0a1245b013e8ae61204c6131d99be3406330d67a..ef3d169aabf5c59df89f494687c1b725cc3d9c11 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Exception.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/ApiException.php @@ -1,8 +1,8 @@ <?php /** - * EBSCO EdsApi Exception class + * EBSCO API Exception class * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -25,17 +25,19 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ +namespace VuFindSearch\Backend\EDS; /** - * EBSCO EdsApi Exception class + * EBSCO API Exception class * * @category EBSCOIndustries * @package EBSCO * @author Michelle Milton <mmilton@epnet.com> + * @author Cornelius Amzar <cornelius.amzar@bsz-bw.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class EbscoEdsApiException extends \VuFindSearch\Backend\Exception\BackendException +class ApiException extends \VuFindSearch\Backend\Exception\BackendException { /** * Error message details returned from the API @@ -53,14 +55,10 @@ class EbscoEdsApiException extends \VuFindSearch\Backend\Exception\BackendExcept { if (is_array($apiErrorMessage)) { $this->setApiError($apiErrorMessage); - parent::__construct( - isset($this->apiErrorDetails['Description']) - ? $this->apiErrorDetails['Description'] : '' - ); + parent::__construct($this->apiErrorDetails['Description'] ?? ''); } else { parent::__construct($apiErrorMessage); } - } /** @@ -72,20 +70,27 @@ class EbscoEdsApiException extends \VuFindSearch\Backend\Exception\BackendExcept */ protected function setApiError($message) { - //AuthErrorMessages if (isset($message['ErrorCode'])) { + // AuthErrorMessages $this->apiErrorDetails['ErrorCode'] = $message['ErrorCode']; $this->apiErrorDetails['Description'] = $message['Reason']; $this->apiErrorDetails['DetailedDescription'] = $message['AdditionalDetail']; - } - - //EDSAPI error messages - if (isset($message['ErrorNumber'])) { + } elseif (isset($message['ErrorNumber'])) { + // EDSAPI error messages $this->apiErrorDetails['ErrorCode'] = $message['ErrorNumber']; $this->apiErrorDetails['Description'] = $message['ErrorDescription']; $this->apiErrorDetails['DetailedDescription'] = $message['DetailedErrorDescription']; + } elseif (is_array($message['errors'] ?? null) + && count($message['errors']) > 0 + ) { + // Array of errors + $this->apiErrorDetails['ErrorCode'] = $message['errors'][0]['code']; + $this->apiErrorDetails['Description'] = $message['errors'][0]['message']; + } else { + $this->apiErrorDetails['ErrorCode'] = null; + $this->apiErrorDetails['Description'] = 'unrecognized error'; } } @@ -116,8 +121,7 @@ class EbscoEdsApiException extends \VuFindSearch\Backend\Exception\BackendExcept */ public function getApiErrorCode() { - return isset($this->apiErrorDetails) - ? $this->apiErrorDetails['ErrorCode'] : ''; + return $this->apiErrorDetails['ErrorCode'] ?? ''; } /** @@ -127,8 +131,7 @@ class EbscoEdsApiException extends \VuFindSearch\Backend\Exception\BackendExcept */ public function getApiErrorDescription() { - return isset($this->apiErrorDetails) - ? $this->apiErrorDetails['Description'] : ''; + return $this->apiErrorDetails['Description'] ?? ''; } /** @@ -138,7 +141,6 @@ class EbscoEdsApiException extends \VuFindSearch\Backend\Exception\BackendExcept */ public function getApiDetailedErrorDescription() { - return (isset($this->apiErrorDetails)) - ? $this->apiErrorDetails['DetailedDescription'] : ''; + return $this->apiErrorDetails['DetailedDescription'] ?? ''; } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php index 455048dd800bf06338b563a0b589482b077f6ab8..e22d9da23123fe5fe9898c316cb4bb333a161662 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php @@ -2,7 +2,7 @@ /** * EDS API Backend * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -22,25 +22,20 @@ * @category VuFind * @package Search * @author Michelle Milton <mmilton@epnet.com> + * @author Cornelius Amzar <cornelius.amzar@bsz-bw.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ namespace VuFindSearch\Backend\EDS; use Exception; - +use VuFindSearch\Backend\AbstractBackend; use VuFindSearch\Backend\EDS\Zend2 as ApiClient; - -use VuFindSearch\Query\AbstractQuery; - +use VuFindSearch\Backend\Exception\BackendException; use VuFindSearch\ParamBag; - -use VuFindSearch\Response\RecordCollectionInterface; +use VuFindSearch\Query\AbstractQuery; use VuFindSearch\Response\RecordCollectionFactoryInterface; - -use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\Backend\Exception\BackendException; - +use VuFindSearch\Response\RecordCollectionInterface; use Zend\Cache\Storage\Adapter\AbstractAdapter as CacheAdapter; use Zend\Config\Config; use Zend\Session\Container as SessionContainer; @@ -183,7 +178,7 @@ class Backend extends AbstractBackend $this->defaultProfile = $this->profile; } - /** + /** * Perform a search and return record collection. * * @param AbstractQuery $query Search query @@ -191,7 +186,7 @@ class Backend extends AbstractBackend * @param int $limit Search limit * @param ParamBag $params Search backend parameters * - *@return \VuFindSearch\Response\RecordCollectionInterface + * @return \VuFindSearch\Response\RecordCollectionInterface **/ public function search(AbstractQuery $query, $offset, $limit, ParamBag $params = null @@ -232,7 +227,7 @@ class Backend extends AbstractBackend try { $response = $this->client ->search($searchModel, $authenticationToken, $sessionToken); - } catch (\EbscoEdsApiException $e) { + } catch (ApiException $e) { // if the auth or session token was invalid, try once more switch ($e->getApiErrorCode()) { case 104: @@ -247,7 +242,7 @@ class Backend extends AbstractBackend } $response = $this->client ->search($searchModel, $authenticationToken, $sessionToken); - } catch(Exception $e) { + } catch (Exception $e) { throw new BackendException($e->getMessage(), $e->getCode(), $e); } break; @@ -255,7 +250,7 @@ class Backend extends AbstractBackend $response = []; break; } - } catch(Exception $e) { + } catch (Exception $e) { $this->debugPrint("Exception found: " . $e->getMessage()); throw new BackendException($e->getMessage(), $e->getCode(), $e); } @@ -294,7 +289,7 @@ class Backend extends AbstractBackend $response = $this->client->retrieve( $an, $dbId, $authenticationToken, $sessionToken, $hlTerms ); - } catch (\EbscoEdsApiException $e) { + } catch (ApiException $e) { // if the auth or session token was invalid, try once more switch ($e->getApiErrorCode()) { case 104: @@ -310,7 +305,7 @@ class Backend extends AbstractBackend $response = $this->client->retrieve( $an, $dbId, $authenticationToken, $sessionToken, $hlTerms ); - } catch(Exception $e) { + } catch (Exception $e) { throw new BackendException($e->getMessage(), $e->getCode(), $e); } break; @@ -389,6 +384,20 @@ class Backend extends AbstractBackend $this->queryBuilder = $queryBuilder; } + /** + * Get popular terms using the autocomplete API. + * + * @param string $query Simple query string + * @param string $domain Autocomplete type (e.g. 'rawqueries' or 'holdings') + * + * @return array of terms + */ + public function autocomplete($query, $domain = 'rawqueries') + { + return $this->client + ->autocomplete($query, $domain, $this->getAutocompleteData()); + } + /// Internal API /** @@ -422,10 +431,8 @@ class Backend extends AbstractBackend } $authTokenData = $this->cache->getItem('edsAuthenticationToken'); if (isset($authTokenData)) { - $currentToken = isset($authTokenData['token']) - ? $authTokenData['token'] : ''; - $expirationTime = isset($authTokenData['expiration']) - ? $authTokenData['expiration'] : 0; + $currentToken = $authTokenData['token'] ?? ''; + $expirationTime = $authTokenData['expiration'] ?? 0; $this->debugPrint( 'Cached Authentication data: ' . "$currentToken, expiration time: $expirationTime" @@ -445,7 +452,7 @@ class Backend extends AbstractBackend if (!empty($username) && !empty($password)) { $this->debugPrint( 'Calling Authenticate with username: ' - . "$username, password: $password, orgid: $orgId " + . "$username, password: XXXXXXXX, orgid: $orgId " ); $results = $this->client->authenticate($username, $password, $orgId); $token = $results['AuthToken']; @@ -456,6 +463,59 @@ class Backend extends AbstractBackend return $token; } + /** + * Obtain the autocomplete authentication to use with the EDS API from cache + * if it exists. If not, then generate a new set. + * + * @param bool $isInvalid whether or not the the current autocomplete data + * is invalid and should be regenerated + * + * @return array autocomplete data + */ + protected function getAutocompleteData($isInvalid = false) + { + // Autocomplete is currently unsupported with IP authentication + if ($this->ipAuth) { + return null; + } + if ($isInvalid) { + $this->cache->setItem('edsAutocomplete', null); + } + $autocompleteData = $this->cache->getItem('edsAutocomplete'); + if (!empty($autocompleteData)) { + $currentToken = $autocompleteData['token'] ?? ''; + $expirationTime = $autocompleteData['expiration'] ?? 0; + + // Check to see if the token expiration time is greater than the current + // time. If the token is expired or within 5 minutes of expiring, + // generate a new one. + if (!empty($currentToken) && (time() <= ($expirationTime - (60 * 5)))) { + return $autocompleteData; + } + } + + $username = $this->userName; + $password = $this->password; + if (!empty($username) && !empty($password)) { + $results = $this->client + ->authenticate($username, $password, $this->orgId, ['autocomplete']); + $autoresult = $results['Autocomplete'] ?? []; + if (isset($autoresult['Token']) && isset($autoresult['TokenTimeOut']) + && isset($autoresult['CustId']) && isset($autoresult['Url']) + ) { + $token = $autoresult['Token']; + $expiration = $autoresult['TokenTimeOut'] + time(); + $custid = $autoresult['CustId']; + $url = $autoresult['Url']; + + $autocompleteData = compact('token', 'expiration', 'url', 'custid'); + // store token, expiration, url and custid in cache. + $this->cache->setItem('edsAutocomplete', $autocompleteData); + } + } + return $autocompleteData; + } + /** * Print a message if debug is enabled. * @@ -537,7 +597,7 @@ class Backend extends AbstractBackend try { $authToken = $this->getAuthenticationToken(); $results = $this->client->createSession($profile, $isGuest, $authToken); - } catch(\EbscoEdsApiException $e) { + } catch (ApiException $e) { $errorCode = $e->getApiErrorCode(); $desc = $e->getApiErrorDescription(); $this->debugPrint( @@ -549,7 +609,7 @@ class Backend extends AbstractBackend $authToken = $this->getAuthenticationToken(true); $results = $this->client ->createSession($this->profile, $isGuest, $authToken); - } catch(Exception $e) { + } catch (Exception $e) { throw new BackendException( $e->getMessage(), $e->getCode(), @@ -579,26 +639,24 @@ class Backend extends AbstractBackend } try { $response = $this->client->info($authenticationToken, $sessionToken); - } catch (\EbscoEdsApiException $e) { + } catch (ApiException $e) { if ($e->getApiErrorCode() == 104) { try { $authenticationToken = $this->getAuthenticationToken(true); $response = $this->client ->info($authenticationToken, $sessionToken); - } catch(Exception $e) { + } catch (Exception $e) { throw new BackendException( $e->getMessage(), $e->getCode(), $e ); - } } else { $response = []; } } return $response; - } /** diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Base.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Base.php index d4d10ad132fd977e5f12ad290a45d54f4818d1da..344c442c00bf1b28a009f493bd6e1cbf255e33e7 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Base.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Base.php @@ -2,7 +2,7 @@ /** * EBSCO Search API abstract base class * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -22,13 +22,12 @@ * @category EBSCOIndustries * @package EBSCO * @author Michelle Milton <mmilton@epnet.com> + * @author Cornelius Amzar <cornelius.amzar@bsz-bw.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://edswiki.ebscohost.com/EDS_API_Documentation */ namespace VuFindSearch\Backend\EDS; -require_once dirname(__FILE__) . '/Exception.php'; - /** * EBSCO Search API abstract base class * @@ -38,7 +37,7 @@ require_once dirname(__FILE__) . '/Exception.php'; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://edswiki.ebscohost.com/EDS_API_Documentation */ -abstract class EdsApi_REST_Base +abstract class Base { /** * A boolean value determining whether to print debug information @@ -99,7 +98,7 @@ abstract class EdsApi_REST_Base { if (is_array($settings)) { foreach ($settings as $key => $value) { - switch($key) { + switch ($key) { case 'debug': $this->debug = $value; break; @@ -190,7 +189,6 @@ abstract class EdsApi_REST_Base $url = $this->edsApiHost . '/retrieve'; $headers = $this->setTokens($authenticationToken, $sessionToken); return $this->call($url, $headers, $qs); - } /** @@ -212,22 +210,66 @@ abstract class EdsApi_REST_Base return $this->call($url, $headers, $qs); } + /** + * Parse autocomplete response from API in an array of terms + * + * @param array $msg Response from API + * + * @return array of terms + */ + protected function parseAutocomplete($msg) + { + $result = []; + if (isset($msg["terms"]) && is_array($msg["terms"])) { + foreach ($msg["terms"] as $value) { + $result[] = $value["term"]; + } + } + return $result; + } + + /** + * Execute an EdsApi autocomplete + * + * @param string $query Search term + * @param string $type Autocomplete type (e.g. 'rawqueries' or 'holdings') + * @param array $data Autocomplete API details (from authenticating with + * 'autocomplete' option set -- requires token, custid and url keys). + * @param bool $raw Should we return the results raw (true) or processed + * (false)? + * + * @return array An array of autocomplete terns as returned from the api + */ + public function autocomplete($query, $type, $data, $raw = false) + { + // build request + $url = $data['url'] . '?idx=' . urlencode($type) . + '&token=' . urlencode($data['token']) . + '&filters=[{"name"%3A"custid"%2C"values"%3A["' . + urlencode($data['custid']) . '"]}]&term=' . urlencode($query); + $this->debugPrint("Autocomplete URL: " . $url); + $response = $this->call($url, null, null, 'GET', null); + return $raw ? $response : $this->parseAutocomplete($response); + } + /** * Generate an authentication token with a valid EBSCO EDS Api account * * @param string $username username associated with an EBSCO EdsApi account * @param string $password password associated with an EBSCO EdsApi account * @param string $orgid Organization id the request is initiated from + * @param array $params optional params (autocomplete) * * @return array */ - public function authenticate($username = null, $password = null, $orgid = null) - { + public function authenticate($username = null, $password = null, + $orgid = null, $params = null + ) { $this->debugPrint( - "Authenticating: username: $username, password: $password, orgid: $orgid" + "Authenticating: username: $username, password: XXXXXXX, orgid: $orgid" ); $url = $this->authHost . '/uidauth'; - $org = isset($orgid) ? $orgid : $this->orgId; + $org = $orgid ?? $this->orgId; $authInfo = []; if (isset($username)) { $authInfo['UserId'] = $username; @@ -238,8 +280,11 @@ abstract class EdsApi_REST_Base if (isset($org)) { $authInfo['orgid'] = $org; } + if (isset($params)) { + $authInfo['Options'] = $params; + } $messageBody = json_encode($authInfo); - return $this->call($url, null, null, 'POST', $messageBody); + return $this->call($url, null, null, 'POST', $messageBody); } /** @@ -289,7 +334,7 @@ abstract class EdsApi_REST_Base * @param string $message Message to POST if $method is POST * @param string $messageFormat Format of request $messageBody and responses * - * @throws \EbscoEdsApiException + * @throws ApiException * @return object EDS API response (or an Error object). */ protected function call($baseUrl, $headerParams, $params = [], @@ -324,7 +369,7 @@ abstract class EdsApi_REST_Base * * @param array $input The raw response from Summon * - * @throws EbscoEdsApiException + * @throws ApiException * @return array The processed response from EDS API */ protected function process($input) @@ -332,14 +377,14 @@ abstract class EdsApi_REST_Base //process response. try { $result = json_decode($input, true); - } catch(Exception $e) { - throw new EbscoEdsApiException( + } catch (\Exception $e) { + throw new ApiException( 'An error occurred when processing EDS Api response: ' . $e->getMessage() ); } if (!isset($result)) { - throw new EbscoEdsApiException('Unknown error processing response'); + throw new ApiException('Unknown error processing response'); } return $result; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php index fb2611633e51b405a6cba3dc9fbc118ae582fac5..7c5e702ab5dfaccc98038e9c0a09cc58b895a98f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php @@ -3,7 +3,7 @@ /** * EDS API Querybuilder * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -28,10 +28,10 @@ */ namespace VuFindSearch\Backend\EDS; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * EDS API Querybuilder @@ -138,6 +138,5 @@ class QueryBuilder } } return $groups; - } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollection.php index badf94d65a3eb612190c12285919ed39dd06f5bc..1bcf50be8b406d73a6ec6ce76500b842bff9a6e1 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollection.php @@ -3,7 +3,7 @@ /** * EDS API record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -27,6 +27,7 @@ * @link https://vufind.org */ namespace VuFindSearch\Backend\EDS\Response; + use VuFindSearch\Response\AbstractRecordCollection; /** @@ -111,7 +112,6 @@ class RecordCollection extends AbstractRecordCollection 'count' => $availableFacetValue['Count'], 'displayText' => $availableFacetValue['Value'] ]; - } $vufindFacet['counts'] = $values; $vufindFacetList[$facet['Id']] = $vufindFacet; @@ -140,5 +140,4 @@ class RecordCollection extends AbstractRecordCollection } return 0; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php index 46b5b8eaae0965767cc4ce65c4029f4ebfe0a262..17ecf8d0992165b1d15c2d44f661bb081dd9e106 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php @@ -2,7 +2,7 @@ /** * Factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -27,8 +27,8 @@ */ namespace VuFindSearch\Backend\EDS\Response; -use VuFindSearch\Response\RecordCollectionFactoryInterface; use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Factory for record collection. @@ -100,7 +100,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface ) { // Format of the search response $records = $response['SearchResult']['Data']['Records']; - } else if (isset($response['Records'])) { // Format of the retrieve response + } elseif (isset($response['Records'])) { // Format of the retrieve response $records = $response['Records']; } @@ -109,5 +109,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/SearchRequestModel.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/SearchRequestModel.php index 7cbf63bc11a23cbbb118ab8d249f809f13c57938..bc69974b1f702cb21bd637c598c0e6ebf47b3e6b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/SearchRequestModel.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/SearchRequestModel.php @@ -2,7 +2,7 @@ /** * EBSCO EDS API Search Model * - * PHP version 5 + * PHP version 7 * * Copyright (C) Serials Solutions 2011. * @@ -26,6 +26,7 @@ * @link https://vufind.org */ namespace VuFindSearch\Backend\EDS; + /** * EBSCO EDS API Search Model * @@ -171,17 +172,17 @@ class SearchRequestModel public function setParameters($parameters = []) { foreach ($parameters as $key => $values) { - switch($key) { + switch ($key) { case 'filters': $cnt = 1; foreach ($values as $filter) { if (substr($filter, 0, 6) == 'LIMIT|') { $this->addLimiter(substr($filter, 6)); - } else if (substr($filter, 0, 7) == 'EXPAND:') { + } elseif (substr($filter, 0, 7) == 'EXPAND:') { $this->addExpander(substr($filter, 7)); - } else if (substr($filter, 0, 11) == 'SEARCHMODE:') { + } elseif (substr($filter, 0, 11) == 'SEARCHMODE:') { $this->searchMode = substr($filter, 11, null); - } else if (substr($filter, 0, 15) == 'PublicationDate') { + } elseif (substr($filter, 0, 15) == 'PublicationDate') { $this->addLimiter($this->formatDateLimiter($filter)); } else { $this->addFilter("$cnt,$filter"); @@ -379,8 +380,8 @@ class SearchRequestModel { return addcslashes($value, ":,"); } - - /** + + /** * Escape characters that may be present in the action parameter syntax * * @param string $value The value to escape diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php index 899f01527f52e781f0bf4a32cef06a090421cc53..9d115379964b62b139c7dd887c1f92926892ff02 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php @@ -2,7 +2,7 @@ /** * EBSCO EDS API Zend2 Framework implementation * - * PHP version 5 + * PHP version 7 * * Copyright (C) EBSCO Industries 2013 * @@ -22,15 +22,15 @@ * @category EBSCOIndustries * @package EBSCO * @author Michelle Milton <mmilton@epnet.com> + * @author Cornelius Amzar <cornelius.amzar@bsz-bw.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ namespace VuFindSearch\Backend\EDS; -require_once dirname(__FILE__) . '/Base.php'; +use Zend\Http\Client\Adapter\Curl as CurlAdapter; use Zend\Http\Client as Zend2HttpClient; use Zend\Log\LoggerAwareInterface; -use Zend\Http\Client\Adapter\Curl as CurlAdapter; /** * EBSCO EDS API Zend2 Framework implementation @@ -41,13 +41,13 @@ use Zend\Http\Client\Adapter\Curl as CurlAdapter; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class Zend2 extends EdsApi_REST_Base implements LoggerAwareInterface +class Zend2 extends Base implements LoggerAwareInterface { use \VuFind\Log\LoggerAwareTrait; - /** + /** * The HTTP Request object to execute EDS API transactions - * + * * @var Zend2HttpClient */ protected $client; @@ -119,7 +119,7 @@ class Zend2 extends EdsApi_REST_Base implements LoggerAwareInterface * @param string $messageBody Message body to for HTTP Request * @param string $messageFormat Format of request $messageBody and respones * - * @throws EbscoEdsApiException + * @throws ApiException * @return string HTTP response body */ protected function httpRequest($baseUrl, $method, $queryString, $headers, @@ -141,7 +141,7 @@ class Zend2 extends EdsApi_REST_Base implements LoggerAwareInterface $this->client->setEncType($messageFormat); $result = $this->client->send(); if (!$result->isSuccess()) { - throw new \EbscoEdsApiException(json_decode($result->getBody(), true)); + throw new ApiException(json_decode($result->getBody(), true)); } return $result->getBody(); } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php index 3713b8b201a90a8222e6890ed483795134b8a22c..341d74e183a453bec19068f2eb0416ea583f95f4 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php @@ -3,7 +3,7 @@ /** * EIT backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Julia Bauder 2013. * @@ -28,14 +28,14 @@ */ namespace VuFindSearch\Backend\EIT; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; use VuFindSearch\ParamBag; -use VuFindSearch\Response\RecordCollectionInterface; +use VuFindSearch\Query\AbstractQuery; use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Backend\AbstractBackend; +use VuFindSearch\Response\RecordCollectionInterface; /** * EIT backend. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Connector.php index 5c04d244518b1f3b8cbf3bec1a3f7d31fd43faf6..e3c2782d727e3dba6c7ba0852b01c9632d8a6e0e 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Connector.php @@ -3,7 +3,7 @@ /** * Central class for connecting to EIT resources used by VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Julia Bauder 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\EIT; -use VuFindSearch\ParamBag; use VuFindSearch\Backend\Exception\HttpErrorException; +use VuFindSearch\ParamBag; use Zend\Http\Request; @@ -124,7 +124,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface return [ 'docs' => $finalDocs, 'offset' => $offset, - 'total' => (integer)$xml->Hits + 'total' => (int)$xml->Hits ]; } @@ -189,7 +189,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface return $body; } - /** + /** * Retrieve a specific record. * * @param string $id Record ID to retrieve @@ -215,7 +215,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface return [ 'docs' => $finalDocs, 'offset' => 0, - 'total' => (integer)$xml->Hits + 'total' => (int)$xml->Hits ]; } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/QueryBuilder.php index af72d7e048be7ce415b496b453c5b630e9fcacc4..5d50d637661662a9e9e53363ad5b0715d3a7c32b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/QueryBuilder.php @@ -2,7 +2,7 @@ /** * EIT QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,11 +30,12 @@ */ namespace VuFindSearch\Backend\EIT; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; + /** * EIT QueryBuilder. * Largely copied from the WorldCat QueryBuilder diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollection.php index 27b7dd4a83dd073306008f4d443764517622a3a4..817462b11894f23efce8a5a9add7df0a8f8ef8d6 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollection.php @@ -3,7 +3,7 @@ /** * EIT record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -90,7 +90,6 @@ class RecordCollection extends AbstractRecordCollection */ public function getOffset() { - return isset($this->response['offset']) ? $this->response['offset'] : 0; } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php index 9c664eb06934c51d6cb000c99322470d69db845f..9c237a60adb076b7c418bc7d2266b66e03a2d535 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php @@ -2,7 +2,7 @@ /** * Simple XML-based factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\EIT\Response\XML; -use VuFindSearch\Response\RecordCollectionFactoryInterface; use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple XML-based factory for record collection. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/BackendException.php b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/BackendException.php index b4cc48f9c845eb55c1ecadaae40dd85bced61ca0..32cdb3173c0c1d0317135ca240513a992f49daf7 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/BackendException.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/BackendException.php @@ -3,7 +3,7 @@ /** * BackendException. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/HttpErrorException.php b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/HttpErrorException.php index 1ba4267818765822cf595d017ae1caa1cc840313..22fbd2efd596eec44e2338261f801c86313adf47 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/HttpErrorException.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/HttpErrorException.php @@ -3,7 +3,7 @@ /** * HTTP error exception. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,10 +28,10 @@ */ namespace VuFindSearch\Backend\Exception; -use Zend\Http\Response; - use Exception; +use Zend\Http\Response; + /** * HTTP error exception. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RemoteErrorException.php b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RemoteErrorException.php index 4d3f0c60a40b04e224b6f7396de8eacd66b15ec1..8c20712c3b39da713cee71dc3909f3efb4028763 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RemoteErrorException.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RemoteErrorException.php @@ -3,7 +3,7 @@ /** * RemoteErrorException. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RequestErrorException.php b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RequestErrorException.php index e9aae62f7dfec1facfd98ef0853201305f05d56c..d26495d9757960ee2578e79d7adbfd36c6b98d48 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RequestErrorException.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Exception/RequestErrorException.php @@ -3,7 +3,7 @@ /** * RequestErrorException. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Backend.php index f1a0dc992b49465fa1fd454db534898a175f475d..55bced2e1377bbcfd4710f87761630a2f552bbac 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Backend.php @@ -3,7 +3,7 @@ /** * LibGuides backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,15 +28,15 @@ */ namespace VuFindSearch\Backend\LibGuides; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; + +use VuFindSearch\Backend\Exception\BackendException; use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Response\RecordCollectionInterface; use VuFindSearch\Response\RecordCollectionFactoryInterface; - -use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\Backend\Exception\BackendException; +use VuFindSearch\Response\RecordCollectionInterface; /** * LibGuides backend. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Connector.php index ed682f18ae74d32a556856bccb5639ce28d35bc1..6dd9f6e3a9b95427ab754669abedf847d4fa730c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Connector.php @@ -3,7 +3,7 @@ /** * LibGuides connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,6 +28,7 @@ * @link https://vufind.org */ namespace VuFindSearch\Backend\LibGuides; + use Zend\Http\Client as HttpClient; /** diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/QueryBuilder.php index e2bffeefa86e6a1c1a1618099d0f0bd2d55eef93..ec7d77314e9451a464d259bee8ecc4472048578f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/QueryBuilder.php @@ -3,7 +3,7 @@ /** * LibGuides QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -29,11 +29,11 @@ */ namespace VuFindSearch\Backend\LibGuides; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * LibGuides QueryBuilder. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollection.php index 45fe90e83539e4a91245853ca8cdd4fb59882156..247c6c045166a2641d91de18d4e35bbb76274db8 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollection.php @@ -3,7 +3,7 @@ /** * LibGuides record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php index ed199ac3f9bc378ce5d3ab032300606811c54fbe..ede139d3023dd96bdb063e9c21bbbd768c82cbf5 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindSearch\Backend\LibGuides\Response; -use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Exception\InvalidArgumentException; use VuFindSearch\Backend\Solr\Response\Json\Record; +use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple factory for record collection. @@ -72,7 +72,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $recordFactory = function ($i) { return new Record($i); }; - } else if (!is_callable($recordFactory)) { + } elseif (!is_callable($recordFactory)) { throw new InvalidArgumentException('Record factory must be callable.'); } $this->recordFactory = $recordFactory; @@ -90,7 +90,6 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface */ public function factory($response) { - if (!is_array($response)) { throw new InvalidArgumentException( sprintf( @@ -105,5 +104,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Backend.php index 6fb0796bee8fc7fd1c05f638ed0079245150ddd4..61f782743cad0f2a98f42c8e5773c956c4a2782c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Backend.php @@ -2,7 +2,7 @@ /** * Pazpar2 backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,14 +27,14 @@ */ namespace VuFindSearch\Backend\Pazpar2; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; use VuFindSearch\ParamBag; -use VuFindSearch\Response\RecordCollectionInterface; +use VuFindSearch\Query\AbstractQuery; use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Backend\AbstractBackend; +use VuFindSearch\Response\RecordCollectionInterface; /** * Pazpar2 backend. @@ -263,6 +263,6 @@ class Backend extends AbstractBackend protected function getSearchProgress() { $statResponse = $this->connector->stat(); - return (float) $statResponse->progress; + return (float)$statResponse->progress; } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Connector.php index ac7aa37d6580d061ceda796c0c2545cff14d9e08..62b702c49e42a8146204f054479d578d43b29bab 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Connector.php @@ -2,7 +2,7 @@ /** * Central class for connecting to Pazpar2 resources used by VuFind. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2011. * @@ -27,8 +27,8 @@ */ namespace VuFindSearch\Backend\Pazpar2; -use VuFindSearch\ParamBag; use VuFindSearch\Backend\Exception\HttpErrorException; +use VuFindSearch\ParamBag; use Zend\Http\Request; @@ -122,7 +122,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface } // Don't change input when manipulating parameters: - $params = (null === $data) ? new ParamBag() : clone($data); + $params = (null === $data) ? new ParamBag() : clone $data; // Add session and command: if ($this->session) { diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/QueryBuilder.php index 47ee9ca3b9822e14463e6c7ddc7d52779519043c..3b826916bdfe1def8c28f6d736a873f872b7aa75 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/QueryBuilder.php @@ -3,7 +3,7 @@ /** * Pazpar2 QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,11 +30,11 @@ */ namespace VuFindSearch\Backend\Pazpar2; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * Pazpar2 QueryBuilder. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/Record.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/Record.php index 6caa851c9ca46f4000154cf30a8853f74e5a65fa..ce7203784e151c61c42e9079a4f601acacd68229 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/Record.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/Record.php @@ -3,7 +3,7 @@ /** * Simple Pazpar2 record. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\Pazpar2\Response; -use VuFindSearch\Response\RecordInterface; use SimpleXMLElement; +use VuFindSearch\Response\RecordInterface; /** * Simple Pazpar2 record. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollection.php index e507cf82221142b20ba36d1dbabff9ec9229d97e..cd1bf53106d5ffa8829a44d4eb08f6a9dc82e29d 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollection.php @@ -3,7 +3,7 @@ /** * Pazpar2 record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php index 2a5c6b4e6762ed88c6882a771013a63a80b7422f..c8acc2dac29c2ceef86315815042106bd4e4651e 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\Pazpar2\Response; -use VuFindSearch\Response\RecordCollectionFactoryInterface; use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple factory for record collection. @@ -71,7 +71,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $recordFactory = function ($i) { return new Record($i); }; - } else if (!is_callable($recordFactory)) { + } elseif (!is_callable($recordFactory)) { throw new InvalidArgumentException('Record factory must be callable.'); } $this->recordFactory = $recordFactory; @@ -97,5 +97,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Backend.php index 7d80edbc45afd8e1131fb881a9191fb414501f5f..6ce7e1baffc9487629b768234c174d18e880b596 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Backend.php @@ -3,7 +3,7 @@ /** * Primo backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,15 +28,15 @@ */ namespace VuFindSearch\Backend\Primo; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; + +use VuFindSearch\Backend\Exception\BackendException; use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Response\RecordCollectionInterface; use VuFindSearch\Response\RecordCollectionFactoryInterface; - -use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\Backend\Exception\BackendException; +use VuFindSearch\Response\RecordCollectionInterface; /** * Primo Central backend. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php index a4c4ade7f841f120938eb00c989fb069177f1870..56d22647e0c0c7639610e84faf834f4cd3758d7d 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php @@ -3,7 +3,7 @@ /** * Primo Central connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -32,6 +32,7 @@ * @link https://vufind.org */ namespace VuFindSearch\Backend\Primo; + use Zend\Http\Client as HttpClient; /** @@ -324,8 +325,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface $qs[] = "loc=adaptor,primo_central_multiple_fe"; if ($this->debug) { - print "URL: " . implode('&', $qs); - + echo "URL: " . implode('&', $qs); } // Send Request diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/QueryBuilder.php index 7cb3aa649203874eca607c0349c04952198b4872..bdb78b3d9dace0e53ee4afce9f6e4f52d6109e39 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/QueryBuilder.php @@ -3,7 +3,7 @@ /** * Primo Central QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,11 +30,11 @@ */ namespace VuFindSearch\Backend\Primo; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * Primo Central QueryBuilder. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php index 0ecb31a9e8398bb0d3b9270977a150952ac39364..7fa90f450c40bd37c59624b0502e9b383f6e1a1b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php @@ -3,7 +3,7 @@ /** * Primo Central record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php index 25b83040a3573dca365cd5bf962cef2f24c3cc47..210ca51b7b82e2b5f2b2cd3793d81a9b6bacb8d1 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindSearch\Backend\Primo\Response; -use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Exception\InvalidArgumentException; use VuFindSearch\Backend\Solr\Response\Json\Record; +use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple factory for record collection. @@ -72,7 +72,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $recordFactory = function ($i) { return new Record($i); }; - } else if (!is_callable($recordFactory)) { + } elseif (!is_callable($recordFactory)) { throw new InvalidArgumentException('Record factory must be callable.'); } $this->recordFactory = $recordFactory; @@ -90,7 +90,6 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface */ public function factory($response) { - if (!is_array($response)) { throw new InvalidArgumentException( sprintf( @@ -105,5 +104,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php index b63786426f2bd9bf0f765eb70c48a8bf4f813742..272cf6d92daf18e93da0ac0c2783fb5875730534 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php @@ -2,7 +2,7 @@ /** * SRU Search Interface * - * PHP version 5 + * PHP version 7 * * Copyright (C) Andrew Nagy 2008. * @@ -27,10 +27,10 @@ */ namespace VuFindSearch\Backend\SRU; -use VuFindSearch\Backend\Exception\HttpErrorException; +use VuFind\XSLT\Processor as XSLTProcessor; use VuFindSearch\Backend\Exception\BackendException; -use VuFind\XSLT\Processor as XSLTProcessor; +use VuFindSearch\Backend\Exception\HttpErrorException; /** * SRU Search Interface @@ -128,10 +128,10 @@ class Connector implements \Zend\Log\LoggerAwareInterface { $options = ['operation' => 'scan', 'scanClause' => $clause]; - if (!is_null($pos)) { + if (null !== $pos) { $options['responsePosition'] = $pos; } - if (!is_null($maxTerms)) { + if (null !== $maxTerms) { $options['maximumTerms'] = $maxTerms; } @@ -160,10 +160,10 @@ class Connector implements \Zend\Log\LoggerAwareInterface 'query' => $query, 'startRecord' => ($start) ? $start : 1, 'recordSchema' => $schema]; - if (!is_null($limit)) { + if (null !== $limit) { $options['maximumRecords'] = $limit; } - if (!is_null($sortBy)) { + if (null !== $sortBy) { $options['sortKeys'] = $sortBy; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php index acbd44f240efcab625d2f5a516c0f9e743241a41..1c7414c5f2e8cd6d703ee005fb4b0564081cfc3f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php @@ -2,7 +2,7 @@ /** * SOLR backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -27,25 +27,25 @@ */ namespace VuFindSearch\Backend\Solr; -use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\Query; - -use VuFindSearch\ParamBag; +use VuFindSearch\Backend\AbstractBackend; +use VuFindSearch\Backend\Exception\BackendException; -use VuFindSearch\Response\RecordCollectionInterface; -use VuFindSearch\Response\RecordCollectionFactoryInterface; +use VuFindSearch\Backend\Exception\RemoteErrorException; use VuFindSearch\Backend\Solr\Response\Json\Terms; +use VuFindSearch\Exception\InvalidArgumentException; -use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\Feature\SimilarInterface; -use VuFindSearch\Feature\RetrieveBatchInterface; use VuFindSearch\Feature\RandomInterface; -use VuFindSearch\Backend\Exception\BackendException; -use VuFindSearch\Backend\Exception\RemoteErrorException; +use VuFindSearch\Feature\RetrieveBatchInterface; +use VuFindSearch\Feature\SimilarInterface; +use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Query\Query; +use VuFindSearch\Response\RecordCollectionFactoryInterface; + +use VuFindSearch\Response\RecordCollectionInterface; /** * SOLR backend. @@ -420,8 +420,7 @@ class Backend extends AbstractBackend sprintf('JSON decoding error: %s -- %s', $error, $json) ); } - $qtime = isset($response['responseHeader']['QTime']) - ? $response['responseHeader']['QTime'] : 'n/a'; + $qtime = $response['responseHeader']['QTime'] ?? 'n/a'; $this->log('debug', 'Deserialized SOLR response', ['qtime' => $qtime]); return $response; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php index dc07c896f85f2d9fc7165b3a6558a4d7fff2a5d2..5b175d86f62b281922ba269c1e897aa145b03666 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php @@ -3,7 +3,7 @@ /** * SOLR connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,21 +30,23 @@ */ namespace VuFindSearch\Backend\Solr; -use VuFindSearch\Query\Query; - -use VuFindSearch\ParamBag; +use InvalidArgumentException; +use VuFindSearch\Backend\Exception\BackendException; use VuFindSearch\Backend\Exception\HttpErrorException; +use VuFindSearch\Backend\Exception\RemoteErrorException; use VuFindSearch\Backend\Exception\RequestErrorException; use VuFindSearch\Backend\Solr\Document\AbstractDocument; -use Zend\Http\Request; -use Zend\Http\Client as HttpClient; +use VuFindSearch\ParamBag; + +use VuFindSearch\Query\Query; use Zend\Http\Client\Adapter\AdapterInterface; use Zend\Http\Client\Adapter\Exception\TimeoutException; +use Zend\Http\Client as HttpClient; -use InvalidArgumentException; +use Zend\Http\Request; /** * SOLR connector. @@ -376,6 +378,26 @@ class Connector implements \Zend\Log\LoggerAwareInterface || $ex instanceof RequestErrorException; } + /** + * If an unexpected exception type was received, wrap it in a generic + * BackendException to standardize upstream handling. + * + * @param \Exception $ex Exception + * + * @return \Exception + */ + protected function forceToBackendException($ex) + { + // Don't wrap specific backend exceptions.... + if ($ex instanceof RemoteErrorException + || $ex instanceof RequestErrorException + || $ex instanceof HttpErrorException + ) { + return $ex; + } + return new BackendException('Problem connecting to Solr.', null, $ex); + } + /** * Try all Solr URLs until we find one that works (or throw an exception). * @@ -404,15 +426,15 @@ class Connector implements \Zend\Log\LoggerAwareInterface return $this->send($client); } catch (\Exception $ex) { if ($this->isRethrowableSolrException($ex)) { - throw $ex; + throw $this->forceToBackendException($ex); } $exception = $ex; } } - // If we got this far, everything failed -- throw the most recent - // exception caught above. - throw $exception; + // If we got this far, everything failed -- throw a BackendException with + // the most recent exception caught above set as the previous exception. + throw $this->forceToBackendException($exception); } /** diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/AbstractDocument.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/AbstractDocument.php index 6c42383ca7fcca16d6195ecc161f1dcd8d284990..28ee02fcdef23b6a1c5999eb47e7de17c9c07888 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/AbstractDocument.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/AbstractDocument.php @@ -3,7 +3,7 @@ /** * SOLR delete document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/CommitDocument.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/CommitDocument.php index 27b7f1e35904ff0990ab2a179ced3e314b7fd23e..1ade7508787e410a56d812e648f06926fab4cee9 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/CommitDocument.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/CommitDocument.php @@ -3,7 +3,7 @@ /** * SOLR commit document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -88,5 +88,4 @@ class CommitDocument extends AbstractDocument $writer->endDocument(); return $writer->flush(); } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/DeleteDocument.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/DeleteDocument.php index 2eb386ae439eda19cb68f70923d858691ebaeeaa..2ae3e5d30b9e0b8d631965c59e0bd9e2feb88143 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/DeleteDocument.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/DeleteDocument.php @@ -3,7 +3,7 @@ /** * SOLR delete document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/OptimizeDocument.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/OptimizeDocument.php index 523ad174bad4f194e7c87ca936444b877ede4058..89d02668b8ec04d9f2ee83c7b747b376679ea5b7 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/OptimizeDocument.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/OptimizeDocument.php @@ -3,7 +3,7 @@ /** * SOLR optimize document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -104,5 +104,4 @@ class OptimizeDocument extends AbstractDocument $writer->endDocument(); return $writer->flush(); } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/RawXMLDocument.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/RawXMLDocument.php index 5e05267d5a0cce92ab0fbe13356049c601403a75..738994cd7de816498074346d9d1d8d3fc8e1964f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/RawXMLDocument.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/RawXMLDocument.php @@ -3,7 +3,7 @@ /** * SOLR "raw XML" document class for manual overrides. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/UpdateDocument.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/UpdateDocument.php index 65927e789796fc7382f23390120e6a402dc231be..7bd500fbb55de6106d5e4828d5860b18576b993d 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/UpdateDocument.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Document/UpdateDocument.php @@ -3,7 +3,7 @@ /** * SOLR update document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\Solr\Document; -use VuFindSearch\Backend\Solr\Record\SerializableRecordInterface; use SplObjectStorage; +use VuFindSearch\Backend\Solr\Record\SerializableRecordInterface; use XMLWriter; /** @@ -116,5 +116,4 @@ class UpdateDocument extends AbstractDocument ) { $this->records->attach($record, $indexAttr); } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/HandlerMap.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/HandlerMap.php index 9cfc3e4d1b0ae45ae8b0974c0d9793d8c0072e7c..71c554216ce47960c6a49528da3d15c52e7fe3ae 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/HandlerMap.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/HandlerMap.php @@ -3,7 +3,7 @@ /** * SOLR backend handler map. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,12 +28,12 @@ */ namespace VuFindSearch\Backend\Solr; -use VuFindSearch\ParamBag; -use VuFindSearch\Backend\AbstractHandlerMap; - use InvalidArgumentException; use RuntimeException; +use VuFindSearch\Backend\AbstractHandlerMap; +use VuFindSearch\ParamBag; + /** * SOLR backend handler map. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php index a508351c94937f875acbc92276b26dbdcabd3d15..39c4ecbf140350c0054297eb103895067bcb9546 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php @@ -3,7 +3,7 @@ /** * Lucene query syntax helper class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. @@ -487,11 +487,21 @@ class LuceneSyntaxHelper { // Freestanding hyphens and slashes can cause problems: $lookahead = self::$insideQuotes; + // remove freestanding hyphens $input = preg_replace( - '/(\s+[-\/]$|\s+[-\/]\s+|^[-\/]\s+)' . $lookahead . '/', + '/(\s+[-]$|\s+[-]\s+|^[-]\s+)' . $lookahead . '/', + ' ', $input + ); + // wrap quotes on standalone slashes + $input = preg_replace( + '/(\s+[\/]\s+)' . $lookahead . '/', + ' "/" ', $input + ); + // remove trailing and leading slashes + $input = preg_replace( + '/(\s+[\/]$|^[\/]\s+)' . $lookahead . '/', ' ', $input ); - // A proximity of 1 is illegal and meaningless -- remove it: $input = preg_replace('/~1(\.0*)?$/', '', $input); $input = preg_replace('/~1(\.0*)?\s+' . $lookahead . '/', ' ', $input); @@ -520,6 +530,7 @@ class LuceneSyntaxHelper $input = preg_replace('/(\:[:\s]+|[:\s]+:)' . $lookahead . '/', ' ', $input); return trim($input, ':'); } + /** * Prepare input to be used in a SOLR query. * @@ -538,7 +549,7 @@ class LuceneSyntaxHelper // If the user has entered a lone BOOLEAN operator, convert it to lowercase // so it is treated as a word (otherwise it will trigger a fatal error): - switch(trim($input)) { + switch (trim($input)) { case 'OR': return 'or'; case 'AND': @@ -587,7 +598,7 @@ class LuceneSyntaxHelper || $this->caseSensitiveBooleans === "0" ) { return $this->allBools; - } else if ($this->caseSensitiveBooleans === true + } elseif ($this->caseSensitiveBooleans === true || $this->caseSensitiveBooleans === 1 || $this->caseSensitiveBooleans === "1" ) { diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php index 3367b23e789a8fc3fc1bb2e66385d1e23c2773ab..de532a8e8899be807f7d414b4ededfa7e11c4134 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php @@ -3,7 +3,7 @@ /** * SOLR QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,11 +30,11 @@ */ namespace VuFindSearch\Backend\Solr; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * SOLR QueryBuilder. @@ -71,11 +71,13 @@ class QueryBuilder implements QueryBuilderInterface protected $exactSpecs = []; /** - * Should we create the hl.q parameter when appropriate? + * Solr fields to highlight. Also serves as a flag for whether to perform + * highlight-specific behavior; if the field list is empty, highlighting is + * skipped. * - * @var bool + * @var string */ - protected $createHighlightingQuery = false; + protected $fieldsToHighlight = ''; /** * Should we create the spellcheck.q parameter when appropriate? @@ -133,11 +135,14 @@ class QueryBuilder implements QueryBuilderInterface $finalQuery = $this->reduceQueryGroup($query); } else { // Clone the query to avoid modifying the original user-visible query - $finalQuery = clone($query); + $finalQuery = clone $query; $finalQuery->setString($this->getNormalizedQueryString($query)); } $string = $finalQuery->getString() ?: '*:*'; + // Highlighting is enabled if we have a field list set. + $highlight = !empty($this->fieldsToHighlight); + if ($handler = $this->getSearchHandler($finalQuery->getHandler(), $string)) { if (!$handler->hasExtendedDismax() && $this->getLuceneHelper()->containsAdvancedLuceneSyntax($string) @@ -149,11 +154,11 @@ class QueryBuilder implements QueryBuilderInterface // If a boost was added, we don't want to highlight based on // the boost query, so we should use the non-boosted version: - if ($this->createHighlightingQuery && $oldString != $string) { + if ($highlight && $oldString != $string) { $params->set('hl.q', $oldString); } } - } else if ($handler->hasDismax()) { + } elseif ($handler->hasDismax()) { $params->set('qf', implode(' ', $handler->getDismaxFields())); $params->set('qt', $handler->getDismaxHandler()); foreach ($handler->getDismaxParams() as $param) { @@ -166,6 +171,11 @@ class QueryBuilder implements QueryBuilderInterface $string = $handler->createSimpleQueryString($string); } } + // Set an appropriate highlight field list when applicable: + if ($highlight) { + $filter = $handler ? $handler->getAllFields() : []; + $params->add('hl.fl', $this->getFieldsToHighlight($filter)); + } $params->set('q', $string); return $params; @@ -179,10 +189,49 @@ class QueryBuilder implements QueryBuilderInterface * @param bool $enable Should highlighting query generation be enabled? * * @return void + * + * @deprecated */ public function setCreateHighlightingQuery($enable) { - $this->createHighlightingQuery = $enable; + // This is deprecated, but use it to manipulate the highlighted field + // list for backward compatibility. + $this->fieldsToHighlight = $enable ? '*' : ''; + } + + /** + * Get list of fields to highlight, filtered by array. + * + * @param array $filter Field list to use as a filter. + * + * @return string + */ + protected function getFieldsToHighlight(array $filter = []) + { + // No filter? Return unmodified default: + if (empty($filter)) { + return $this->fieldsToHighlight; + } + // Account for possibility of comma OR space delimiters: + $fields = array_map('trim', preg_split('/[, ]/', $this->fieldsToHighlight)); + // Wildcard in field list? Return filter as-is; otherwise, use intersection. + $list = in_array('*', $fields) ? $filter : array_intersect($fields, $filter); + return implode(',', $list); + } + + /** + * Set list of fields to highlight, if any (or '*' for all). Set to an + * empty string (the default) to completely disable highlighting-related + * functionality. + * + * @param string $list Highlighting field list + * + * @return QueryBuilder + */ + public function setFieldsToHighlight($list) + { + $this->fieldsToHighlight = $list; + return $this; } /** @@ -351,7 +400,7 @@ class QueryBuilder implements QueryBuilderInterface } if ($advanced && $handler) { return $handler->createAdvancedQueryString($string); - } else if ($handler) { + } elseif ($handler) { return $handler->createSimpleQueryString($string); } else { return $string; diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php index 275bfa9eafb3c9803d5832f50014f775336404ab..e34db5487b3ed44c55c17a1054d5f216628df40f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php @@ -3,7 +3,7 @@ /** * SOLR QueryBuilder interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,8 +30,8 @@ */ namespace VuFindSearch\Backend\Solr; -use VuFindSearch\Query\AbstractQuery; use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; /** * SOLR QueryBuilder interface definition. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecord.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecord.php index 3b3fe2eaa0db8b22488998c16c49a479f04bfd9b..28351487aa4a55fdc927eab136ec0d1b6bc171d5 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecord.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecord.php @@ -3,7 +3,7 @@ /** * SOLR serializable record definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecordInterface.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecordInterface.php index bc2255688726d7fc9b9297bcd6d280f6805fff31..8ec39b82aac60611b758edb8ea36fd41dceb547c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecordInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Record/SerializableRecordInterface.php @@ -3,7 +3,7 @@ /** * SOLR serializable record interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Facets.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Facets.php index f5f39b7ca4873bc764d0ae4ad7ae0a31a479cc62..b21281fd93db55473241bdac6cd7077fcee0e3fe 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Facets.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Facets.php @@ -3,7 +3,7 @@ /** * SOLR facet information. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/NamedList.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/NamedList.php index 8d2314e054c06477f73d832551240dbf316e1779..5dd1f48718b2739351e4d28e3546ad429ae3cb4b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/NamedList.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/NamedList.php @@ -3,7 +3,7 @@ /** * SOLR NamedList with parameter json.nl=arrarr. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,7 +28,8 @@ */ namespace VuFindSearch\Backend\Solr\Response\Json; -use Countable, Iterator; +use Countable; +use Iterator; /** * SOLR NamedList with parameter json.nl=arrarr. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Record.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Record.php index 2370f011961ba7084e776010ce1a9583ed32422f..c7031a42fbbbb742c83170f33732b54ce704e70b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Record.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Record.php @@ -3,7 +3,7 @@ /** * Simple, schema-less SOLR record. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php index 39c9e2a6f590aa14fc60be556b441d733ea235c1..bebfe7a7baccc5c1c5579f07cc00b663a0639d2f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php @@ -3,7 +3,7 @@ /** * Simple JSON-based record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -169,9 +169,7 @@ class RecordCollection extends AbstractRecordCollection protected function getSpellcheckQuery() { $params = $this->getSolrParameters(); - return isset($params['spellcheck.q']) - ? $params['spellcheck.q'] - : (isset($params['q']) ? $params['q'] : ''); + return $params['spellcheck.q'] ?? ($params['q'] ?? ''); } /** diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php index 8378112f19524a805ee546fcc73b6815d197b4f1..6ffd4eff6c4cf0642d7be5bd6febdf5757871927 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple JSON-based factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\Solr\Response\Json; -use VuFindSearch\Response\RecordCollectionFactoryInterface; use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple JSON-based factory for record collection. @@ -102,5 +102,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php index 0c34d88359466d981b4e78968bf2643bd94b4327..12d1a70b4495f95f8b1bc5e3de78e5c91a59bbe1 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php @@ -3,7 +3,7 @@ /** * SOLR spellcheck information. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindSearch\Backend\Solr\Response\Json; -use IteratorAggregate; use ArrayObject; use Countable; +use IteratorAggregate; /** * SOLR spellcheck information. @@ -200,7 +200,6 @@ class Spellcheck implements IteratorAggregate, Countable */ public function compareTermLength($a, $b) { - return (strlen($b) - strlen($a)); + return strlen($b) - strlen($a); } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Terms.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Terms.php index e9a640ad3d3932547f66806304501ec7c2e7ecd1..340d285be9365e0f4f1c16d20a98b592a20459d4 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Terms.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Terms.php @@ -3,7 +3,7 @@ /** * SOLR Terms component. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php index 9ff6012e2b306bcb0ff153915cac6fc5245b2e28..0083c7573d6315365af84c4176a4b62b209e7a83 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php @@ -3,7 +3,7 @@ /** * VuFind SearchHandler. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -82,7 +82,7 @@ class SearchHandler public function __construct(array $spec, $defaultDismaxHandler = 'dismax') { foreach (self::$configKeys as $key) { - $this->specs[$key] = isset($spec[$key]) ? $spec[$key] : []; + $this->specs[$key] = $spec[$key] ?? []; } // Set dismax handler to default if not specified: if (empty($this->specs['DismaxHandler'])) { @@ -140,7 +140,7 @@ class SearchHandler list($name, $value) = $param; if ($name === 'bq') { $boostQuery[] = $value; - } else if ($name === 'bf') { + } elseif ($name === 'bf') { // BF parameter may contain multiple space-separated functions // with individual boosts. We need to parse this into _val_ // query components: @@ -197,6 +197,25 @@ class SearchHandler return $this->hasDismax() && ('edismax' == $this->getDismaxHandler()); } + /** + * Get a list of all Solr fields searched by this handler. + * + * @return array + */ + public function getAllFields() + { + // If we have non-Dismax rules, the keys are the field names. + $queryFields = array_keys($this->mungeRules()); + + // If we have Dismax fields, we need to strip off boost values. + $callback = function ($f) { + return current(explode('^', $f)); + }; + $dismaxFields = array_map($callback, $this->getDismaxFields()); + + return array_unique(array_merge($queryFields, $dismaxFields)); + } + /** * Return defined dismax fields. * @@ -456,7 +475,7 @@ class SearchHandler ')'; // ...and add a weight if we have one $weight = $sw[1]; - if (!is_null($weight) && $weight && $weight > 0) { + if (null !== $weight && $weight && $weight > 0) { $sstring .= '^' . $weight; } // push it onto the stack of clauses @@ -470,7 +489,7 @@ class SearchHandler // Add the weight if we have one. Yes, I know, it's redundant // code. $weight = $spec[1]; - if (!is_null($weight) && $weight && $weight > 0) { + if (null !== $weight && $weight && $weight > 0) { $sstring .= '^' . $weight; } // ..and push it on the stack of clauses diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilder.php index b9a47fcd2257f76efd22f7b9a04f43aab7a2b800..8392d581028ed35d7618e450738e176442754704 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilder.php @@ -3,7 +3,7 @@ /** * SOLR SimilarBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilderInterface.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilderInterface.php index 1397e45cd96b70f7146ddf40604cd43acc555d85..0c0099c3b8cbf28751c10fdc6974340058e41803 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilderInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SimilarBuilderInterface.php @@ -3,7 +3,7 @@ /** * SOLR SimilarBuilder interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Backend.php index 42fd418f52c0c483b00570a9c30d8bb743fc8e92..91ff5c4c0b4797e9da9f89f817a78668c344ab2b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Backend.php @@ -3,7 +3,7 @@ /** * Summon backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -29,20 +29,20 @@ namespace VuFindSearch\Backend\Summon; use SerialsSolutions\Summon\Zend2 as Connector; -use SerialsSolutions_Summon_Query as SummonQuery; use SerialsSolutions_Summon_Exception as SummonException; +use SerialsSolutions_Summon_Query as SummonQuery; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\ParamBag; +use VuFindSearch\Backend\Exception\BackendException; use VuFindSearch\Feature\RetrieveBatchInterface; -use VuFindSearch\Response\RecordCollectionInterface; -use VuFindSearch\Response\RecordCollectionFactoryInterface; +use VuFindSearch\ParamBag; +use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Backend\AbstractBackend; -use VuFindSearch\Backend\Exception\BackendException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; +use VuFindSearch\Response\RecordCollectionInterface; /** * Summon backend. @@ -173,9 +173,16 @@ class Backend extends AbstractBackend implements RetrieveBatchInterface 'pageSize' => $pageSize ] ); - $next = $this->createRecordCollection( - $this->connector->query($query) - ); + try { + $batch = $this->connector->query($query); + } catch (SummonException $e) { + throw new BackendException( + $e->getMessage(), + $e->getCode(), + $e + ); + } + $next = $this->createRecordCollection($batch); if (!$results) { $results = $next; } else { @@ -266,7 +273,7 @@ class Backend extends AbstractBackend implements RetrieveBatchInterface $params = $params->getArrayCopy(); // Extract the query: - $query = isset($params['query'][0]) ? $params['query'][0] : null; + $query = $params['query'][0] ?? null; unset($params['query']); // Convert the options: diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php index 64eda65801effe9a8f9260b3e59bed0672313544..f78bb8dfcef975e0e5d2addb240e4f5a745c0d88 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php @@ -3,7 +3,7 @@ /** * Summon QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -32,11 +32,11 @@ namespace VuFindSearch\Backend\Summon; use VuFindSearch\Backend\Solr\LuceneSyntaxHelper; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * Summon QueryBuilder. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollection.php index 261262cacfc89a8960b7549762c5104638f959ae..65d0598260f7d45cf136e9bdbeddb6f255ce6c3d 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollection.php @@ -3,7 +3,7 @@ /** * WorldCat record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php index 77f11892b9a30dc33744804e59ada85f8bf01365..fbfb17f124b3ac8d33be1d525b2973027ae6dadc 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindSearch\Backend\Summon\Response; -use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Exception\InvalidArgumentException; use VuFindSearch\Backend\Solr\Response\Json\Record; +use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple factory for record collection. @@ -72,7 +72,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $recordFactory = function ($i) { return new Record($i); }; - } else if (!is_callable($recordFactory)) { + } elseif (!is_callable($recordFactory)) { throw new InvalidArgumentException('Record factory must be callable.'); } $this->recordFactory = $recordFactory; @@ -104,5 +104,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php index d0ea632f6e3a98df315e2e5e7ad80f6f18725757..71984d09ad6ede4fc6f4ebf568e964010b5fe078 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php @@ -3,7 +3,7 @@ /** * WorldCat backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,14 +28,14 @@ */ namespace VuFindSearch\Backend\WorldCat; -use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\Backend\AbstractBackend; use VuFindSearch\ParamBag; -use VuFindSearch\Response\RecordCollectionInterface; +use VuFindSearch\Query\AbstractQuery; use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Backend\AbstractBackend; +use VuFindSearch\Response\RecordCollectionInterface; /** * WorldCat backend. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php index 056dfe3f8debca9b77899b68d051890eb8656f40..13c9eed6c796ad21c624a42e612a935d8e27ce4e 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php @@ -2,7 +2,7 @@ /** * Class for accessing OCLC WorldCat search API * - * PHP version 5 + * PHP version 7 * * Copyright (C) Andrew Nagy 2008. * @@ -27,6 +27,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFindSearch\Backend\WorldCat; + use VuFindSearch\ParamBag; /** @@ -89,6 +90,10 @@ class Connector extends \VuFindSearch\Backend\SRU\Connector } $uri = "http://www.worldcat.org/webservices/catalog/content/libraries/{$id}" . "?wskey={$this->wskey}&servicelevel=full&frbrGrouping=$grouping"; + if (isset($this->options['latLon'])) { + list($lat, $lon) = explode(',', $this->options['latLon']); + $uri .= '&lat=' . urlencode($lat) . '&lon=' . urlencode($lon); + } $this->client->setUri($uri); $this->debug('Connect: ' . $uri); $result = $this->client->setMethod('POST')->send(); @@ -159,7 +164,7 @@ class Connector extends \VuFindSearch\Backend\SRU\Connector return [ 'docs' => $finalDocs, 'offset' => $offset, - 'total' => isset($xml->numberOfRecords) ? (int)$xml->numberOfRecords : 0 + 'total' => (int)($xml->numberOfRecords ?? 0) ]; } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php index 94b288d2269d6863b5e54a05220b1007913ec75c..11f7a591f82f72177ff237d01d19536dce08d89c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php @@ -3,7 +3,7 @@ /** * WorldCat QueryBuilder. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -30,11 +30,11 @@ */ namespace VuFindSearch\Backend\WorldCat; +use VuFindSearch\ParamBag; use VuFindSearch\Query\AbstractQuery; -use VuFindSearch\Query\QueryGroup; use VuFindSearch\Query\Query; -use VuFindSearch\ParamBag; +use VuFindSearch\Query\QueryGroup; /** * WorldCat QueryBuilder. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/Record.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/Record.php index 8e1c89a7ba4ca70ef1a048b08fae55dcdcb48d3d..af01cf87cf42536f87148ea3fbcffaa8fd2bf856 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/Record.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/Record.php @@ -3,7 +3,7 @@ /** * Simple WorldCat record. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindSearch\Backend\WorldCat\Response\XML; -use VuFindSearch\Response\RecordInterface; use File_MARC_Record; +use VuFindSearch\Response\RecordInterface; /** * Simple WorldCat record. diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php index 7b9f0e6139dfd412509724325dcf7d15f60f617e..047a5b0c42d7dd425fe4fa928f3f9892f03cc883 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php @@ -3,7 +3,7 @@ /** * WorldCat record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php index fb2e6ab810912514220b06b29def488579895a2a..38a35c4f428f5bd9f72c9f59b21875f07a7d814e 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php @@ -3,7 +3,7 @@ /** * Simple XML-based factory for record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindSearch\Backend\WorldCat\Response\XML; -use VuFindSearch\Response\RecordCollectionFactoryInterface; -use VuFindSearch\Exception\InvalidArgumentException; use File_MARCXML; +use VuFindSearch\Exception\InvalidArgumentException; +use VuFindSearch\Response\RecordCollectionFactoryInterface; /** * Simple XML-based factory for record collection. @@ -72,7 +72,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $marc = new File_MARCXML($i, File_MARCXML::SOURCE_STRING); return new Record($marc->next()); }; - } else if (!is_callable($recordFactory)) { + } elseif (!is_callable($recordFactory)) { throw new InvalidArgumentException('Record factory must be callable.'); } $this->recordFactory = $recordFactory; @@ -104,5 +104,4 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } return $collection; } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Exception/ExceptionInterface.php b/module/VuFindSearch/src/VuFindSearch/Exception/ExceptionInterface.php index 14701b521db538585073001801f8c3b5c6192874..dac6bd8498e0631917fc79661c369ecf48be842c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Exception/ExceptionInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Exception/ExceptionInterface.php @@ -3,7 +3,7 @@ /** * Marker interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Exception/InvalidArgumentException.php b/module/VuFindSearch/src/VuFindSearch/Exception/InvalidArgumentException.php index 374a3ce91bc68f3a4df7ed99bfa26ad3aa5c9576..cab86c7b6161dc724fa8bbad0d92cf3ad3dd1a3a 100644 --- a/module/VuFindSearch/src/VuFindSearch/Exception/InvalidArgumentException.php +++ b/module/VuFindSearch/src/VuFindSearch/Exception/InvalidArgumentException.php @@ -3,7 +3,7 @@ /** * Invalid argument exception. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Exception/RuntimeException.php b/module/VuFindSearch/src/VuFindSearch/Exception/RuntimeException.php index a6f86dc11f0b3e55006843e4be7480bca1837b15..b0923144e7224ab4e9d2b8696ba302d542c46b0f 100644 --- a/module/VuFindSearch/src/VuFindSearch/Exception/RuntimeException.php +++ b/module/VuFindSearch/src/VuFindSearch/Exception/RuntimeException.php @@ -3,7 +3,7 @@ /** * Generic RuntimeException. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Feature/RandomInterface.php b/module/VuFindSearch/src/VuFindSearch/Feature/RandomInterface.php index c8cb9723dd0762ba085680c8734050c7a615c411..22515463bfdce6fb95f0d63996501964e614eb25 100644 --- a/module/VuFindSearch/src/VuFindSearch/Feature/RandomInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Feature/RandomInterface.php @@ -3,7 +3,7 @@ /** * Random record retrieval feature interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php b/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php index 240bf8f936b690a6bd98d3092f2a2ee717832d4d..e6ddc9f9cc6092bc33f6e969554b24eb98305a6d 100644 --- a/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php @@ -3,7 +3,7 @@ /** * Optional backend feature: retrieve batch of records. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Feature/SimilarInterface.php b/module/VuFindSearch/src/VuFindSearch/Feature/SimilarInterface.php index 2a2d29a28ba7c8744d7bcfb854da7979d4d19897..f4c316e3d449fd4f1845555133c81dd7841bc521 100644 --- a/module/VuFindSearch/src/VuFindSearch/Feature/SimilarInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Feature/SimilarInterface.php @@ -3,7 +3,7 @@ /** * MLT feature interface definition. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/ParamBag.php b/module/VuFindSearch/src/VuFindSearch/ParamBag.php index 2c53545c1b3d6f94130e57152a7c8ca180e164f9..8aed52dcda71d7cc47e101d1b2e7202b6277751f 100644 --- a/module/VuFindSearch/src/VuFindSearch/ParamBag.php +++ b/module/VuFindSearch/src/VuFindSearch/ParamBag.php @@ -3,7 +3,7 @@ /** * Parameter bag. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -76,6 +76,16 @@ class ParamBag implements \Countable return isset($this->params[$name]) ? $this->params[$name] : null; } + /** + * Count parameters in internal array. Needed for Countable interface. + * + * @return int + */ + public function count() + { + return count($this->params); + } + /** * Return true if the bag contains any value(s) for the specified parameter. * @@ -238,14 +248,4 @@ class ParamBag implements \Countable } return $request; } - - /** - * Get a count of parameters set in the bag. - * - * @return int - */ - public function count() - { - return count($this->params); - } } diff --git a/module/VuFindSearch/src/VuFindSearch/Query/AbstractQuery.php b/module/VuFindSearch/src/VuFindSearch/Query/AbstractQuery.php index 62e8f73f2e0548a341572de0eaa53f17caa7bf35..86155e9606b31bac6d795a1ddec0c72aaba50759 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/AbstractQuery.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/AbstractQuery.php @@ -3,7 +3,7 @@ /** * Abstract base class of user query components. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Query/Query.php b/module/VuFindSearch/src/VuFindSearch/Query/Query.php index 6c92e181d813cde6aa5d58aacbf1e370de9f1904..2e3d3731461945ec08cc730c89230ca643ac5551 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/Query.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/Query.php @@ -3,7 +3,7 @@ /** * A single/simple query. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php b/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php index 35eb6645cae4f6a12152b365f5070a3a8709e780..3a03e2af1c8c5da1170eaada30679cadd980cbe9 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php @@ -3,7 +3,7 @@ /** * A group of single/simples queries, joined by boolean operator. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -106,7 +106,7 @@ class QueryGroup extends AbstractQuery { $new = []; foreach ($this->queries as $q) { - $new[] = clone($q); + $new[] = clone $q; } $this->queries = $new; } diff --git a/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php b/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php index c9b48abe601b2a2fa8f8b16a44a2b80675ee1bc3..f2458f7bc651636c51ebcd9c7c1e33bce0a8dd44 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php @@ -3,7 +3,7 @@ /** * Common methods that must be shared by all query objects. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php index 0ac5ae10d0ae06feaa21185e83a7be70d26a064d..b8c48657f1b614be3ca5ca8ee703f6f57a699c5b 100644 --- a/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php @@ -3,7 +3,7 @@ /** * Abstract record collection (implements some shared low-level functionality). * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -223,5 +223,4 @@ abstract class AbstractRecordCollection implements RecordCollectionInterface { return count($this->records); } - } diff --git a/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionFactoryInterface.php b/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionFactoryInterface.php index d6d46e250ff8d13410d93f445db14f5b24afc7e8..72ed1fda31cb920badb335b84b1c3ac367994ab5 100644 --- a/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionFactoryInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionFactoryInterface.php @@ -2,7 +2,7 @@ /** * Search backend record collection factory interface. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionInterface.php b/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionInterface.php index ef08511ee593637f2ed44f5608c8a218d235bf1b..df0bd063b1a60abdfc81d9fb7ea1749b6a204fe7 100644 --- a/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Response/RecordCollectionInterface.php @@ -3,7 +3,7 @@ /** * Search backend search response interface file. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -102,5 +102,4 @@ interface RecordCollectionInterface extends \Countable, \Iterator * @return void */ public function add(RecordInterface $record); - } diff --git a/module/VuFindSearch/src/VuFindSearch/Response/RecordInterface.php b/module/VuFindSearch/src/VuFindSearch/Response/RecordInterface.php index d9a66fc356910eda41d3e836712652ba45dd028d..50c12c4a81e6fab1e4a0089b51397a814ee6617c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Response/RecordInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Response/RecordInterface.php @@ -3,7 +3,7 @@ /** * Record interface file. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -56,5 +56,4 @@ interface RecordInterface * @return string */ public function getSourceIdentifier(); - } diff --git a/module/VuFindSearch/src/VuFindSearch/Service.php b/module/VuFindSearch/src/VuFindSearch/Service.php index 775b6733a933a667f72214c5b043456799978663..3e8a79269b653f4b0d9da875de68937f6a6105ec 100644 --- a/module/VuFindSearch/src/VuFindSearch/Service.php +++ b/module/VuFindSearch/src/VuFindSearch/Service.php @@ -3,7 +3,7 @@ /** * Search service. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -29,13 +29,13 @@ namespace VuFindSearch; use VuFindSearch\Backend\BackendInterface; -use VuFindSearch\Feature\RetrieveBatchInterface; -use VuFindSearch\Feature\RandomInterface; use VuFindSearch\Backend\Exception\BackendException; +use VuFindSearch\Feature\RandomInterface; +use VuFindSearch\Feature\RetrieveBatchInterface; use VuFindSearch\Response\RecordCollectionInterface; -use Zend\EventManager\EventManagerInterface; use Zend\EventManager\EventManager; +use Zend\EventManager\EventManagerInterface; /** * Search service. @@ -186,7 +186,7 @@ class Service } if (!$response) { $response = $next; - } else if ($record = $next->first()) { + } elseif ($record = $next->first()) { $response->add($record); } } @@ -242,7 +242,7 @@ class Service } elseif ($total_records < $limit) { // Result set smaller than limit? Get everything and shuffle: try { - $response = $backend->search($query, 0, $limit, $params); + $response = $backend->search($query, 0, $limit, $params); } catch (BackendException $e) { $this->triggerError($e, $args); throw $e; @@ -269,7 +269,7 @@ class Service } if (!$response) { $response = $currentBatch; - } else if ($record = $currentBatch->first()) { + } elseif ($record = $currentBatch->first()) { $response->add($record); } } @@ -356,7 +356,7 @@ class Service if (!isset($this->backends[$backend])) { $response = $this->getEventManager()->triggerUntil( function ($o) { - return ($o instanceof BackendInterface); + return $o instanceof BackendInterface; }, self::EVENT_RESOLVE, $this, @@ -413,5 +413,4 @@ class Service { $this->getEventManager()->trigger(self::EVENT_POST, $response, $args); } - } diff --git a/module/VuFindSearch/tests/unit-tests/fixtures/eds/response/autocomplete b/module/VuFindSearch/tests/unit-tests/fixtures/eds/response/autocomplete new file mode 100644 index 0000000000000000000000000000000000000000..cc7a47cb0b5ce1dd6bc6e1002f0ceaf2772f67e9 --- /dev/null +++ b/module/VuFindSearch/tests/unit-tests/fixtures/eds/response/autocomplete @@ -0,0 +1 @@ +a:3:{s:4:"type";s:20:"autoCompleteResponse";s:14:"processingTime";i:1;s:5:"terms";a:10:{i:0;a:4:{s:4:"term";s:5:"black";s:15:"highlightedTerm";s:74:"<span class="userprovided">bla</span><span class="autocompleted">ck</span>";s:5:"score";i:16295;s:6:"domain";s:10:"rawqueries";}i:1;a:4:{s:4:"term";s:18:"black lives matter";s:15:"highlightedTerm";s:87:"<span class="userprovided">bla</span><span class="autocompleted">ck lives matter</span>";s:5:"score";i:11493;s:6:"domain";s:10:"rawqueries";}i:2;a:4:{s:4:"term";s:11:"black women";s:15:"highlightedTerm";s:80:"<span class="userprovided">bla</span><span class="autocompleted">ck women</span>";s:5:"score";i:6295;s:6:"domain";s:10:"rawqueries";}i:3;a:4:{s:4:"term";s:10:"black hole";s:15:"highlightedTerm";s:79:"<span class="userprovided">bla</span><span class="autocompleted">ck hole</span>";s:5:"score";i:3387;s:6:"domain";s:10:"rawqueries";}i:4;a:4:{s:4:"term";s:11:"black death";s:15:"highlightedTerm";s:80:"<span class="userprovided">bla</span><span class="autocompleted">ck death</span>";s:5:"score";i:3371;s:6:"domain";s:10:"rawqueries";}i:5;a:4:{s:4:"term";s:14:"black panthers";s:15:"highlightedTerm";s:83:"<span class="userprovided">bla</span><span class="autocompleted">ck panthers</span>";s:5:"score";i:3148;s:6:"domain";s:10:"rawqueries";}i:6;a:4:{s:4:"term";s:9:"black men";s:15:"highlightedTerm";s:78:"<span class="userprovided">bla</span><span class="autocompleted">ck men</span>";s:5:"score";i:2205;s:6:"domain";s:10:"rawqueries";}i:7;a:4:{s:4:"term";s:27:"black lives matter movement";s:15:"highlightedTerm";s:96:"<span class="userprovided">bla</span><span class="autocompleted">ck lives matter movement</span>";s:5:"score";i:2117;s:6:"domain";s:10:"rawqueries";}i:8;a:4:{s:4:"term";s:19:"black panther party";s:15:"highlightedTerm";s:88:"<span class="userprovided">bla</span><span class="autocompleted">ck panther party</span>";s:5:"score";i:2016;s:6:"domain";s:10:"rawqueries";}i:9;a:4:{s:4:"term";s:12:"black plague";s:15:"highlightedTerm";s:81:"<span class="userprovided">bla</span><span class="autocompleted">ck plague</span>";s:5:"score";i:1367;s:6:"domain";s:10:"rawqueries";}}} diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/AbstractHandlerMapTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/AbstractHandlerMapTest.php index 570eb5551a4dd9c270d31a2b6106944a0918b56a..20faa251abb148bd51487a55c0ec7c711985e836 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/AbstractHandlerMapTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/AbstractHandlerMapTest.php @@ -3,7 +3,7 @@ /** * Unit tests for handler map base class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend; -use VuFindSearch\ParamBag; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase as TestCase; +use VuFindSearch\ParamBag; /** * Unit tests for handler map base class. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php index 7494f84d19d73232bd3c857277ce236d7903bc66..3101bdbc0a840dbcc96701e4a9aac2c960df2e96 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for BrowZine backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,14 +28,14 @@ */ namespace VuFindTest\Backend\BrowZine; +use InvalidArgumentException; use VuFindSearch\Backend\BrowZine\Backend; use VuFindSearch\Backend\BrowZine\Connector; use VuFindSearch\Backend\BrowZine\QueryBuilder; use VuFindSearch\Backend\BrowZine\Response\RecordCollectionFactory; +use VuFindSearch\Query\Query; use Zend\Http\Client\Adapter\Test as TestAdapter; use Zend\Http\Client as HttpClient; -use VuFindSearch\Query\Query; -use InvalidArgumentException; /** * Unit tests for BrowZine backend. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/QueryBuilderTest.php index b3f98e7da5399a2c05ed0471c3bde1e8c6e84111..571b7ee3e478336d7d9cd0be85416d83c931a0f2 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for BrowZine query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\BrowZine; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\BrowZine\QueryBuilder; use VuFindSearch\Query\Query; use VuFindSearch\Query\QueryGroup; -use PHPUnit_Framework_TestCase; /** * Unit tests for BrowZine query builder @@ -42,7 +42,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test basic query parsing diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionFactoryTest.php index a9664e223722a69dd0a2b9168cfdb28d59b15f44..17d2c81f0f30f9ded8dee46645f17d4136fbe931 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for BrowZine record collection factory. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\BrowZine\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\BrowZine\Response\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for BrowZine record collection factory. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test that the factory creates a collection. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionTest.php index 72fdfbb4402082fe1dfabac0364b320eb8c0cdee..98e6cc4f62061ea5ff42058acb606b1cc050f420 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/Response/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for BrowZine record collection * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\BrowZine\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\BrowZine\Response\RecordCollection; -use PHPUnit_Framework_TestCase; /** * Unit tests for BrowZine record collection @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test defaults when given empty data. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php index 83f7c2f51619ab5c2b93f42fe099fe4cd6f36f69..bb9a13e053c3939696ca4cee0ee8c9eefbb1f1c9 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EDS backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\EDS; +use InvalidArgumentException; use VuFindSearch\Backend\EDS\Backend; use VuFindSearch\Query\Query; -use InvalidArgumentException; /** * Unit tests for EDS backend. @@ -43,6 +43,39 @@ use InvalidArgumentException; */ class BackendTest extends \VuFindTest\Unit\TestCase { + /** + * Test performing an autocomplete + * + * @return void + */ + public function testAutocomplete() + { + $conn = $this->getConnectorMock(['call']); + $expectedUri = 'http://foo?idx=rawdata&token=auth1234' + . '&filters=[{"name"%3A"custid"%2C"values"%3A["foo"]}]&term=bla'; + $conn->expects($this->once()) + ->method('call') + ->with($this->equalTo($expectedUri)) + ->will($this->returnValue($this->loadResponse('autocomplete'))); + + $back = $this->getBackend( + $conn, $this->getRCFactory(), null, null, [], ['getAutocompleteData'] + ); + $autocompleteData = [ + 'custid' => 'foo', 'url' => 'http://foo', 'token' => 'auth1234' + ]; + $back->expects($this->any()) + ->method('getAutocompleteData') + ->will($this->returnValue($autocompleteData)); + + $coll = $back->autocomplete('bla', 'rawdata'); + // check count + $this->assertCount(10, $coll); + foreach ($coll as $value) { + $this->assertEquals('bla', substr($value, 0, 3)); + } + } + /** * Test retrieving a record. * @@ -245,7 +278,6 @@ class BackendTest extends \VuFindTest\Unit\TestCase }; return new \VuFindSearch\Backend\EDS\Response\RecordCollectionFactory($callback); } - } class BackendMock extends \VuFindSearch\Backend\EDS\Backend diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/QueryBuilderTest.php index e88d4154a07e803a677c1806aac0a34ae70575c4..cb9377b26143a2a3b6fe379f217603547441ef92 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EDS query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\EDS; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\EDS\QueryBuilder; -use PHPUnit_Framework_TestCase; /** * Unit tests for EDS query builder @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test query parsing. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionFactoryTest.php index f91c11918944d059ab645f896fdd78148c5fda00..979587af30676e1d5b6174f268958f2ffd377caa 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EDS record collection factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\EDS\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\EDS\Response\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for EDS record collection factory @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test constructor exceptions. @@ -67,7 +67,6 @@ class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase { $factory = new RecordCollectionFactory( function () { - } ); $factory->factory('bad'); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionTest.php index 47732f573ea5393445f4a2e4d9ed8435aa8b8727..84c9b64fccdcc1f9d5e91274ddc50a4560f83430 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/Response/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EDS record collection * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\EDS\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\EDS\Response\RecordCollection; -use PHPUnit_Framework_TestCase; /** * Unit tests for EDS record collection @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test defaults when given empty data. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php index 8445ea96657de54f16f67eaf572f2086c2705f1a..58781cf8de6f49e5400e2be1baee234877d27db2 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EIT backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\EIT; +use InvalidArgumentException; use VuFindSearch\Backend\EIT\Backend; use VuFindSearch\Backend\EIT\QueryBuilder; use VuFindSearch\Query\Query; -use InvalidArgumentException; /** * Unit tests for EIT backend. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/QueryBuilderTest.php index f14d9ae673d1fb7cb8e0246e508a3642a8f1bb13..9ef3d5ac234ad0e4f8ad11ca92aac538bbf7ebca 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EIT query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\EIT; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\EIT\QueryBuilder; -use PHPUnit_Framework_TestCase; /** * Unit tests for EIT query builder @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test query parsing. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionFactoryTest.php index b7ba078f7da6786cb48c24a4644e3de9e8327149..bca3ea77c1e3a1fdbb2892f8f31e78018e618ce0 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EIT record collection factory. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\EIT\Response\XML; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\EIT\Response\XML\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for EIT record collection factory. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test constructor exception. @@ -67,7 +67,6 @@ class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase { $fact = new RecordCollectionFactory( function () { - } ); $coll = $fact->factory('garbage'); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionTest.php index b761feffc347e175b26b3e50528a42014cf39520..c07c4f21235d40658672f6f8b75394c6e7196c4a 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/Response/XML/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for EIT record collection * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\EIT\Response\XML; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\EIT\Response\XML\RecordCollection; -use PHPUnit_Framework_TestCase; /** * Unit tests for EIT record collection @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test defaults when given empty data. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php index 2a10b3af1827e52d51e7d03d3ff2128c9f347a57..eba17deea2c77c4a7d955eb4c7a0630c2515b028 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for LibGuides backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,15 +28,15 @@ */ namespace VuFindTest\Backend\LibGuides; +use InvalidArgumentException; use VuFindSearch\Backend\LibGuides\Backend; use VuFindSearch\Backend\LibGuides\Connector; use VuFindSearch\Backend\LibGuides\QueryBuilder; use VuFindSearch\Backend\LibGuides\Response\RecordCollectionFactory; -use Zend\Http\Client\Adapter\Test as TestAdapter; -use Zend\Http\Client as HttpClient; use VuFindSearch\ParamBag; use VuFindSearch\Query\Query; -use InvalidArgumentException; +use Zend\Http\Client\Adapter\Test as TestAdapter; +use Zend\Http\Client as HttpClient; /** * Unit tests for LibGuides backend. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/QueryBuilderTest.php index f52be91b602f8ad204d58d975b4c5413d3856e9b..e1ec7c1c2c21b76201805b9a63b84f86a1f0416f 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for LibGuides query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\LibGuides; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\LibGuides\QueryBuilder; use VuFindSearch\Query\Query; use VuFindSearch\Query\QueryGroup; -use PHPUnit_Framework_TestCase; /** * Unit tests for LibGuides query builder @@ -42,7 +42,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test basic query parsing diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionFactoryTest.php index 519a7f5e38626b019112cfcc239814bfad50f87a..45ded837971b6b242176674adccb1a6cb3f69793 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for LibGuides record collection factory. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\LibGuides\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\LibGuides\Response\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for LibGuides record collection factory. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test that the factory creates a collection. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionTest.php index 3a0fec7e81cfe0fe81d5a3f9fad58526fed45902..1efe7770c5f07c408529a4ef6569e0bcb4f17427 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/Response/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for LibGuides record collection * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\LibGuides\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\LibGuides\Response\RecordCollection; -use PHPUnit_Framework_TestCase; /** * Unit tests for LibGuides record collection @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test defaults when given empty data. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php index 21ac2db7599af46f5073582130c3be02c6185ee8..493e22d6e1afb0ff10843d4d18918fc23098c7b7 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Pazpar2 backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\Pazpar2; -use VuFindSearch\Query\Query; +use InvalidArgumentException; use VuFindSearch\Backend\Pazpar2\Backend; +use VuFindSearch\Query\Query; use VuFindTest\Unit\TestCase; -use InvalidArgumentException; /** * Unit tests for Pazpar2 backend. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php index 48c1aaf06ce41e3eea34221a62e846280324c8f8..1b585f160f4ef0e0f75a927e9879c769a5e5c5e5 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Primo backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\Primo; +use InvalidArgumentException; use VuFindSearch\Backend\Primo\Backend; use VuFindSearch\ParamBag; use VuFindSearch\Query\Query; -use InvalidArgumentException; /** * Unit tests for Primo backend. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php index a36e58485e55ba2d01ff4f5e888099888dd5174e..01af661861a7416a86074ac570ffcfb083391b0f 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Primo connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,14 +28,14 @@ */ namespace VuFindTest\Backend\Primo; +use InvalidArgumentException; + +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Primo\Connector; use Zend\Http\Client\Adapter\Test as TestAdapter; use Zend\Http\Client as HttpClient; -use PHPUnit_Framework_TestCase; -use InvalidArgumentException; - /** * Unit tests for Primo connector. * @@ -45,7 +45,7 @@ use InvalidArgumentException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class ConnectorTest extends PHPUnit_Framework_TestCase +class ConnectorTest extends TestCase { /** * Test default timeout value diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/QueryBuilderTest.php index 080d421e27bf04c886bb783d741a8ea70eff2e71..3fa3f4ee044b2c70bf63caa7a2c74202bb5ccbcc 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Primo query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\Primo; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Primo\QueryBuilder; use VuFindSearch\Query\Query; use VuFindSearch\Query\QueryGroup; -use PHPUnit_Framework_TestCase; /** * Unit tests for Primo query builder @@ -42,7 +42,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test basic query parsing diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionFactoryTest.php index 57bbcd70b1f941f26bdcc656480bd21a94225992..3c7443ae189cf931bec39975fc3fe88756c093a2 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Primo record collection factory. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Primo\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Primo\Response\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for Primo record collection factory. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test constructor exception. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionTest.php index 3feb618e2cdffecee337bf57a04f05a12c41f6cd..a52ac2c5edd380a3f18bb7665506efa8f8e3db97 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/Response/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Primo record collection * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Primo\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Primo\Response\RecordCollection; -use PHPUnit_Framework_TestCase; /** * Unit tests for Primo record collection @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test defaults when given empty data. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php index 3aaf2144ae9ac8bca50be3979f24ef6d7803235a..4f08bd25501ac3aaed7ff0684ef98007b1c60dea 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,15 +28,15 @@ */ namespace VuFindTest\Backend\Solr; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Exception\RemoteErrorException; use VuFindSearch\Backend\Solr\Backend; use VuFindSearch\Backend\Solr\HandlerMap; + use VuFindSearch\ParamBag; use VuFindSearch\Query\Query; - use Zend\Http\Response; -use PHPUnit_Framework_TestCase; -use InvalidArgumentException; /** * Unit tests for SOLR backend. @@ -47,7 +47,7 @@ use InvalidArgumentException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class BackendTest extends PHPUnit_Framework_TestCase +class BackendTest extends TestCase { /** * Test retrieving a record. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php index 173736cd15e200ec02abe98caeb6b50726aa9c85..c62ba7436c40fc0c79058f5eec633a17f7589ce3 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,15 +28,15 @@ */ namespace VuFindTest\Backend\Solr; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; + use VuFindSearch\Backend\Solr\Connector; use VuFindSearch\Backend\Solr\HandlerMap; use Zend\Http\Client\Adapter\Test as TestAdapter; use Zend\Http\Client as HttpClient; -use PHPUnit_Framework_TestCase; -use InvalidArgumentException; - /** * Unit tests for SOLR connector. * @@ -46,7 +46,7 @@ use InvalidArgumentException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class ConnectorTest extends PHPUnit_Framework_TestCase +class ConnectorTest extends TestCase { /** * Current response. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/CommitDocumentTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/CommitDocumentTest.php index ce07259ba25a94e7c296982578874a7156ebff32..aae50bb645cb490353f5b2f19e64db0ff99e1ab0 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/CommitDocumentTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/CommitDocumentTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR commit document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr\Document; -use VuFindSearch\Backend\Solr\Document\CommitDocument; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase; +use VuFindSearch\Backend\Solr\Document\CommitDocument; /** * Unit tests for SOLR update document class. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class CommitDocumentTest extends PHPUnit_Framework_TestCase +class CommitDocumentTest extends TestCase { /** * Test creation of XML document. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/DeleteDocumentTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/DeleteDocumentTest.php index 862764d51be601358a353641cf4f3910cb677718..51a4b9784c75c799d47d068db5fcd7269acac2b5 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/DeleteDocumentTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/DeleteDocumentTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR delete document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr\Document; -use VuFindSearch\Backend\Solr\Document\DeleteDocument; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase; +use VuFindSearch\Backend\Solr\Document\DeleteDocument; /** * Unit tests for SOLR delete document class. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class DeleteDocumentTest extends PHPUnit_Framework_TestCase +class DeleteDocumentTest extends TestCase { /** * Test creation of XML document. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/OptimizeDocumentTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/OptimizeDocumentTest.php index d05715917f304703cac28630fa2ed0e6289f6673..48e39020fa8f9e3f649395451f36386b3470fd11 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/OptimizeDocumentTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/OptimizeDocumentTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR optimize document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr\Document; -use VuFindSearch\Backend\Solr\Document\OptimizeDocument; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase; +use VuFindSearch\Backend\Solr\Document\OptimizeDocument; /** * Unit tests for SOLR update document class. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class OptimizeDocumentTest extends PHPUnit_Framework_TestCase +class OptimizeDocumentTest extends TestCase { /** * Test creation of XML document. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/RawXMLDocumentTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/RawXMLDocumentTest.php index 0e352327b7d60976c280a2e959c1ba223d952dc8..c434909a8e1f9a9379d8ec8c52def8adf1b2b3e8 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/RawXMLDocumentTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/RawXMLDocumentTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR raw XML document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr\Document; -use VuFindSearch\Backend\Solr\Document\RawXMLDocument; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase; +use VuFindSearch\Backend\Solr\Document\RawXMLDocument; /** * Unit tests for SOLR raw XML document class. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RawXMLDocumentTest extends PHPUnit_Framework_TestCase +class RawXMLDocumentTest extends TestCase { /** * Test creation of XML document. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/UpdateDocumentTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/UpdateDocumentTest.php index 16696c5d3daa642da4c75dac43ed10909ac19a2b..4652296a5a019914cf9db051ae8425d481a75d21 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/UpdateDocumentTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Document/UpdateDocumentTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR update document class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr\Document; -use VuFindSearch\Backend\Solr\Document\UpdateDocument; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase; +use VuFindSearch\Backend\Solr\Document\UpdateDocument; /** * Unit tests for SOLR update document class. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class UpdateDocumentTest extends PHPUnit_Framework_TestCase +class UpdateDocumentTest extends TestCase { /** * Test creation of XML document. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/HandlerMapTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/HandlerMapTest.php index 18ec025e71c45d5caa11b6036e0eefaa2faef43c..d57e9e5ba01847b154c175219a6dd0d7661a5553 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/HandlerMapTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/HandlerMapTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR HandlerMap. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,12 +28,12 @@ */ namespace VuFindTest\Backend\Solr; -use VuFindSearch\Backend\Solr\HandlerMap; +use InvalidArgumentException; -use PHPUnit_Framework_TestCase as TestCase; +use PHPUnit\Framework\TestCase; -use InvalidArgumentException; use RuntimeException; +use VuFindSearch\Backend\Solr\HandlerMap; /** * Unit tests for SOLR HandlerMap. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php index 356094b739e8fa75c4c0285b630cda30e279c47b..2880025416c5f89e6e64ce3b904855894b0dc0af 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Lucene syntax helper * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -337,10 +337,11 @@ class LuceneSyntaxHelperTest extends \VuFindTest\Unit\TestCase '"this : that"' => '"this : that"', '::::::::::::::::::::' => '', ]; - foreach ($tests as $input => $expected) - $this->assertEquals( - $expected, $lh->normalizeSearchString($input) - ); + foreach ($tests as $input => $expected) { + $this->assertEquals( + $expected, $lh->normalizeSearchString($input) + ); + } } /** @@ -371,9 +372,10 @@ class LuceneSyntaxHelperTest extends \VuFindTest\Unit\TestCase 'index:{start TO end}' => '{start TO end}', 'es\\"caped field:test' => 'es\\"caped test' ]; - foreach ($tests as $input => $expected) - $this->assertEquals( - $expected, $lh->extractSearchTerms($input) - ); + foreach ($tests as $input => $expected) { + $this->assertEquals( + $expected, $lh->extractSearchTerms($input) + ); + } } } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php index d9560012a96de0a688e430d37829b227bd7a5415..30ea55f23d75edf133dc8ac0508224143e0b857b 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr; +use VuFindSearch\Backend\Solr\QueryBuilder; use VuFindSearch\Query\Query; use VuFindSearch\Query\QueryGroup; -use VuFindSearch\Backend\Solr\QueryBuilder; /** * Unit tests for SOLR query builder @@ -75,10 +75,9 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase ['^10', '10'], // invalid boosts ['test^ test^6', 'test test6'], // invalid boosts ['test^1 test^2', 'test^1 test^2'], // valid boosts - ['this / that', 'this that'], // freestanding slash + ['this / that', 'this "/" that'], // freestanding slash ['/ this', 'this'], // leading slash ['title /', 'title'], // trailing slash - ['this - that', 'this that'], // freestanding hyphen ['- this', 'this'], // leading hyphen ['title -', 'title'], // trailing hyphen ['AND', 'and'], // freestanding operator @@ -346,17 +345,18 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase } /** - * Test generation with highlighting + * Test generation with highlighting, using the setCreateHighlightingQuery() + * method. * * @return void */ - public function testHighlighting() + public function testSetCreateHighlightingQuery() { $qb = new QueryBuilder( [ 'test' => [ 'DismaxFields' => ['test1'], - 'DismaxParams' => [['bq', 'boost']] + 'DismaxParams' => [['bq', 'boost']], ] ] ); @@ -365,23 +365,94 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase // No hl.q if highlighting query disabled: $qb->setCreateHighlightingQuery(false); - $response = $qb->build($q); - $hlQ = $response->get('hl.q'); - $this->assertEquals(null, $hlQ[0]); + $response1 = $qb->build($q); + $hlQ1 = $response1->get('hl.q'); + $this->assertEquals(null, $hlQ1[0]); // hl.q if highlighting query enabled: $qb->setCreateHighlightingQuery(true); + $response2 = $qb->build($q); + $hlQ2 = $response2->get('hl.q'); + $this->assertEquals('*:*', $hlQ2[0]); + } + + /** + * Test hl.q edge case: when we are in dismax (not edismax) mode, and a boost + * is set, and a query contains advanced syntax, VuFind manipulates the query + * to trigger the boost and sets hl.q to prevent the highlighter from matching + * the wrong words. + * + * @return void + */ + public function testHlQ() + { + $qb = new QueryBuilder( + [ + 'test' => [ + 'DismaxFields' => ['test'], + 'DismaxHandler' => 'dismax', + 'DismaxParams' => [['bq', 'boost']], + ] + ] + ); + + $q = new Query('my friend*', 'test'); + + $qb->setFieldsToHighlight('*'); $response = $qb->build($q); - $hlQ = $response->get('hl.q'); - $this->assertEquals('*:*', $hlQ[0]); + $hlq = $response->get('hl.q'); + $q = $response->get('q'); + $this->assertEquals('(my friend*)', $hlq[0]); + $this->assertEquals('((my friend*)) AND (*:* OR boost)', $q[0]); + } + + /** + * Test generation with highlighting, using the setFieldsToHighlight() method. + * + * @return void + */ + public function testSetFieldsToHighlight() + { + $qb = new QueryBuilder( + [ + 'test' => [ + 'QueryFields' => ['test1' => []], + 'DismaxFields' => ['test2', 'test3^10000'], + ] + ] + ); + + $q = new Query('my friend', 'test'); + + // Map of field whitelist to expected hl.fl output. + $tests = [ + // No hl.fl if highlight field list is empty: + '' => null, + // hl.fl set when whitelist is wildcard: + '*' => 'test1,test2,test3', + // No hl.fl if whitelist doesn't match handler list: + 'test4,test5' => null, + // hl.fl contains intersection of whitelist and handler list + // (testing with a comma-separated whitelist) + 'test1,test2,test6' => 'test1,test2', + // hl.fl contains intersection of whitelist and handler list + // (testing with a space-separated whitelist) + 'test1 test3 test5' => 'test1,test3', + ]; + foreach ($tests as $input => $output) { + $qb->setFieldsToHighlight($input); + $response = $qb->build($q); + $hlfl = $response->get('hl.fl'); + $this->assertEquals($output, $hlfl[0]); + } } /** - * Test generation with spelling + * Test generation with spelling, using the setCreateSpellingQuery() method. * * @return void */ - public function testSpelling() + public function testSetCreateSpellingQuery() { $qb = new QueryBuilder( [ @@ -396,15 +467,15 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase // No spellcheck.q if spellcheck query disabled: $qb->setCreateSpellingQuery(false); - $response = $qb->build($q); - $spQ = $response->get('spellcheck.q'); - $this->assertEquals(null, $spQ[0]); + $response1 = $qb->build($q); + $spQ1 = $response1->get('spellcheck.q'); + $this->assertEquals(null, $spQ1[0]); // spellcheck.q if spellcheck query enabled: $qb->setCreateSpellingQuery(true); - $response = $qb->build($q); - $spQ = $response->get('spellcheck.q'); - $this->assertEquals('my friend', $spQ[0]); + $response2 = $qb->build($q); + $spQ2 = $response2->get('spellcheck.q'); + $this->assertEquals('my friend', $spQ2[0]); } /** diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Record/SerializableRecordTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Record/SerializableRecordTest.php index 96502d7bf2543dd441ebc529e43f35a0ae7da23c..b58b252950ac25412c9fb7f1d929ed04a766920c 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Record/SerializableRecordTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Record/SerializableRecordTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR serializable record. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -39,7 +39,7 @@ use VuFindSearch\Backend\Solr\Record\SerializableRecord; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class SerializableRecordTest extends \PHPUnit_Framework_TestCase +class SerializableRecordTest extends \PHPUnit\Framework\TestCase { /** * Test field retrieval diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/FacetsTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/FacetsTest.php index 5ed80ee0133233924eb1beacd1320a7619fb4b20..a6aca9c108d028d8ef8c469aaf6cb802d68a1db7 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/FacetsTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/FacetsTest.php @@ -3,7 +3,7 @@ /** * Unit tests for facet information. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Solr\Json\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\Response\Json\Facets; -use PHPUnit_Framework_TestCase as TestCase; /** * Unit tests for facet information. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/NamedListTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/NamedListTest.php index 9c988ff0fa091c6694eb365a046e4f07ff84072a..698b362d45a1a08cb6fca97ca3edd7c4759631b0 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/NamedListTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/NamedListTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR NamedList. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Solr\Json\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\Response\Json\NamedList; -use PHPUnit_Framework_TestCase as TestCase; /** * Unit tests for SOLR NamedList. @@ -52,7 +52,7 @@ class NamedListTest extends TestCase $list = new NamedList([['first term', 'info'], ['second term', 'info']]); $keys = []; foreach ($list as $key => $value) { - $keys [] = $key; + $keys[] = $key; } $this->assertEquals(['first term', 'second term'], $keys); } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionFactoryTest.php index b0d6917766051bd64c5110109b983795ad406e5d..87a84a7a8b68a84b07c99ae65649241e8cc292db 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for simple JSON-based record collection factory. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Solr\Json\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for simple JSON-based record collection factory. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test that the factory creates a collection. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php index 51eaf142b6bcd2cfd6c073164aa81f448c83b6d4..5bc0eeacd0299a3690ae1e519bd08fac4008e7f2 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for simple JSON-based record collection. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Backend\Solr\Json\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\Response\Json\RecordCollection; use VuFindTest\RecordDriver\TestHarness; -use PHPUnit_Framework_TestCase; /** * Unit tests for simple JSON-based record collection. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test that the object returns appropriate defaults for missing elements. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php index 1910336b25fa1dcdaee35cdc6ca4cf9099d511a0..ffc63f597bcfdffbec237cea4b81edab6a6142a1 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php @@ -3,7 +3,7 @@ /** * Unit tests for spellcheck information. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Solr\Json\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\Response\Json\Spellcheck; -use PHPUnit_Framework_TestCase as TestCase; /** * Unit tests for spellcheck information. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/TermsTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/TermsTest.php index 6bacfc600ef4936e8374b5c4f77be0a69663e7ec..ae2a6afa52d454e47b2ffab94ad35c44e27f2f32 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/TermsTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/TermsTest.php @@ -3,7 +3,7 @@ /** * Unit tests for terms information. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Solr\Json\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\Response\Json\Terms; -use PHPUnit_Framework_TestCase as TestCase; /** * Unit tests for terms information. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php index cecbd0fdef01edd5d77ed562a0db8bffb635e461..206ffc7e6cc41c948be5e987f49eed97d842e76f 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR search handler. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Solr; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Solr\SearchHandler; -use PHPUnit_Framework_TestCase; /** * Unit tests for SOLR search handler. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class SearchHandlerTest extends PHPUnit_Framework_TestCase +class SearchHandlerTest extends TestCase { /** * Test creating simple dismax query. @@ -65,6 +65,7 @@ class SearchHandlerTest extends PHPUnit_Framework_TestCase $hndl = new SearchHandler($spec); $this->assertEquals('(id:("escaped\"quote" OR not OR quoted OR "basic phrase"))', $hndl->createSimpleQueryString('"escaped\"quote" not quoted "basic phrase"')); } + /** * Test toArray() method. * diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SimilarBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SimilarBuilderTest.php index 1f07a0694f5b97986c8ce9ea065af23f29de5237..6d1c6b32f90355a2f7f058e2176c8cbf62b8af75 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SimilarBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SimilarBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for SOLR similar records query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * Copyright (C) The National Library of Finland 2016. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php index 691079adf27e57b82242d3cfafdc1aa2b9a5109b..d1d25f0135fec6bab3911c7ab18d07d9e32878c3 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Summon Backend class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,15 +28,15 @@ */ namespace VuFindSearch\Backend\Summon; -use VuFindSearch\ParamBag; -use VuFindSearch\Query\Query; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; use SerialsSolutions_Summon_Exception as SummonException; use SerialsSolutions_Summon_Query as SummonQuery; -use PHPUnit_Framework_TestCase as TestCase; +use VuFindSearch\ParamBag; -use InvalidArgumentException; +use VuFindSearch\Query\Query; /** * Unit tests for Summon Backend class. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/QueryBuilderTest.php index 1e6c5d5e6a6699ee3f120350d28be382cf3ba485..189960ab2a7f3cbd7b083c0e5a209221a8ba1436 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Summon query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Summon; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Summon\QueryBuilder; -use PHPUnit_Framework_TestCase; /** * Unit tests for Summon query builder @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test query parsing. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionFactoryTest.php index 65d363568a5556a63d68f29276e73161d6874a7c..6c151d2398086d11cb75bfe4a01467436b2d67f9 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Summon record collection factory * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Summon\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Summon\Response\RecordCollectionFactory; -use PHPUnit_Framework_TestCase; /** * Unit tests for Summon record collection factory @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends TestCase { /** * Test constructor exceptions. @@ -67,7 +67,6 @@ class RecordCollectionFactoryTest extends PHPUnit_Framework_TestCase { $factory = new RecordCollectionFactory( function () { - } ); $factory->factory('bad'); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionTest.php index 87ad7a3018c9be102c2b101cd4e8d57d96c0af1a..bd4e740b1fd2d7e2baa37b10b9109740e11aaa8d 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/Response/RecordCollectionTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Summon record collection * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\Summon\Response; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\Summon\Response\RecordCollection; -use PHPUnit_Framework_TestCase; /** * Unit tests for Summon record collection @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionTest extends PHPUnit_Framework_TestCase +class RecordCollectionTest extends TestCase { /** * Test defaults when given empty data. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php index d17a2b55c00da8df71ea6eba25bf435942c7adbc..305ec9ba18cebca2dc762433af00d5cbf38533a0 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php @@ -3,7 +3,7 @@ /** * Unit tests for WorldCat backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,10 +28,10 @@ */ namespace VuFindTest\Backend\WorldCat; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\WorldCat\Backend; use VuFindSearch\Query\Query; -use PHPUnit_Framework_TestCase; -use InvalidArgumentException; /** * Unit tests for WorldCat backend. @@ -42,7 +42,7 @@ use InvalidArgumentException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class BackendTest extends PHPUnit_Framework_TestCase +class BackendTest extends TestCase { /** * Test retrieving a record. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php index b0985a55221457ec1b9aedb4ca73d5d44cc78e04..276c42c256d7932c339d90396c7eb31c930ba677 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php @@ -3,7 +3,7 @@ /** * Unit tests for WorldCat connector. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -40,7 +40,7 @@ use VuFindSearch\ParamBag; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class ConnectorTest extends \PHPUnit_Framework_TestCase +class ConnectorTest extends \PHPUnit\Framework\TestCase { /** * Test "get holdings" diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/QueryBuilderTest.php index 90be8a707517c669afb7e4c55dfa4bdeac7f0d08..2358ed3e0b57602bcb8ae7f12b0ce75445cfa3a2 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/QueryBuilderTest.php @@ -3,7 +3,7 @@ /** * Unit tests for WorldCat query builder * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Backend\WorldCat; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\WorldCat\QueryBuilder; -use PHPUnit_Framework_TestCase; /** * Unit tests for WorldCat query builder @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends TestCase { /** * Test query parsing. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/Response/XML/RecordCollectionFactoryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/Response/XML/RecordCollectionFactoryTest.php index 9f5334f65bc69fee104f856b1d82a8b2fec92117..e17574701328461c17392ae1a29a5f1a8ead7b7d 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/Response/XML/RecordCollectionFactoryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/Response/XML/RecordCollectionFactoryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for WorldCat backend. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -39,7 +39,7 @@ use VuFindSearch\Backend\WorldCat\Response\XML\RecordCollectionFactory; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class RecordCollectionFactoryTest extends \PHPUnit_Framework_TestCase +class RecordCollectionFactoryTest extends \PHPUnit\Framework\TestCase { /** * Test bad callback. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/ParamBagTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/ParamBagTest.php index 80a3b26160112e3e86a79529a749c9b8d3f9536d..e785b6041b90c1b5bdaf6eef07ab6d504f48d78d 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/ParamBagTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/ParamBagTest.php @@ -3,7 +3,7 @@ /** * Unit tests for ParamBag. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest; -use VuFindSearch\ParamBag; +use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_TestCase as TestCase; +use VuFindSearch\ParamBag; /** * Unit tests for ParamBag. diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php index 8b91ac0200d5738bb1bb8c84d68a1c3b258d5b99..93062e6b4ee2411001e265182d8ec0ce84846e90 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php @@ -3,7 +3,7 @@ /** * Unit tests for QueryGroup class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,9 +28,9 @@ */ namespace VuFindTest\Query; +use PHPUnit\Framework\TestCase; use VuFindSearch\Query\Query; use VuFindSearch\Query\QueryGroup; -use PHPUnit_Framework_TestCase; /** * Unit tests for QueryGroup class. @@ -41,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryGroupTest extends PHPUnit_Framework_TestCase +class QueryGroupTest extends TestCase { /** * Test containsTerm() method @@ -95,7 +95,7 @@ class QueryGroupTest extends PHPUnit_Framework_TestCase public function testClone() { $q = $this->getSampleQueryGroup(); - $qClone = clone($q); + $qClone = clone $q; $q->replaceTerm('query', 'question'); $qClone->setOperator('AND'); $this->assertEquals('test question multi word question', $q->getAllTerms()); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php index 7678581b8fb0f81521d5d124a2c566512359e22e..2d8400af355dac0dba9b70ff7248c9a0ed2f5880 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php @@ -3,7 +3,7 @@ /** * Unit tests for Query class. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,8 +28,8 @@ */ namespace VuFindTest\Query; +use PHPUnit\Framework\TestCase; use VuFindSearch\Query\Query; -use PHPUnit_Framework_TestCase; /** * Unit tests for Query class. @@ -40,7 +40,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org */ -class QueryTest extends PHPUnit_Framework_TestCase +class QueryTest extends TestCase { /** * Test containsTerm() method diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php index 7f2035071e4c896c7573a835965a8635f9d810aa..283e3de01ae858fcaf118ec8f5687b7f80a1f525 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php @@ -3,7 +3,7 @@ /** * Unit tests for search service. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -28,17 +28,17 @@ */ namespace VuFindTest; -use VuFindSearch\Service; -use VuFindSearch\ParamBag; +use PHPUnit\Framework\TestCase; use VuFindSearch\Backend\BackendInterface; use VuFindSearch\Backend\Exception\BackendException; -use VuFindSearch\Feature\RetrieveBatchInterface; use VuFindSearch\Feature\RandomInterface; +use VuFindSearch\Feature\RetrieveBatchInterface; use VuFindSearch\Feature\SimilarInterface; +use VuFindSearch\ParamBag; use VuFindSearch\Query\Query; use VuFindSearch\Response\AbstractRecordCollection; -use PHPUnit_Framework_TestCase as TestCase; +use VuFindSearch\Service; /** * Unit tests for search service. diff --git a/module/VuFindTheme/Module.php b/module/VuFindTheme/Module.php index 5af6790aace8b4631e0c0ddecf7fe31aea07629a..d353e85c645ef0f98225616f4140a6d69789a5f6 100644 --- a/module/VuFindTheme/Module.php +++ b/module/VuFindTheme/Module.php @@ -2,7 +2,7 @@ /** * ZF2 module definition for the VuFind theme system. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2013. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development */ namespace VuFindTheme; + use Zend\ServiceManager\ServiceManager; /** @@ -66,16 +67,16 @@ class Module 'factories' => [ 'VuFindTheme\MixinGenerator' => 'VuFindTheme\Module::getMixinGenerator', + 'VuFindTheme\Mobile' => + 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFindTheme\ResourceContainer' => + 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFindTheme\ThemeCompiler' => 'VuFindTheme\Module::getThemeCompiler', 'VuFindTheme\ThemeGenerator' => 'VuFindTheme\Module::getThemeGenerator', 'VuFindTheme\ThemeInfo' => 'VuFindTheme\Module::getThemeInfo', ], - 'invokables' => [ - 'VuFindTheme\Mobile' => 'VuFindTheme\Mobile', - 'VuFindTheme\ResourceContainer' => 'VuFindTheme\ResourceContainer', - ], ]; } @@ -88,14 +89,20 @@ class Module { return [ 'factories' => [ - 'headlink' => 'VuFindTheme\View\Helper\Factory::getHeadLink', - 'headscript' => 'VuFindTheme\View\Helper\Factory::getHeadScript', - 'headthemeresources' => + 'VuFindTheme\View\Helper\HeadThemeResources' => 'VuFindTheme\View\Helper\Factory::getHeadThemeResources', - 'imagelink' => 'VuFindTheme\View\Helper\Factory::getImageLink', - 'inlinescript' => + 'VuFindTheme\View\Helper\ImageLink' => + 'VuFindTheme\View\Helper\Factory::getImageLink', + 'Zend\View\Helper\HeadLink' => + 'VuFindTheme\View\Helper\Factory::getHeadLink', + 'Zend\View\Helper\HeadScript' => + 'VuFindTheme\View\Helper\Factory::getHeadScript', + 'Zend\View\Helper\InlineScript' => 'VuFindTheme\View\Helper\Factory::getInlineScript', - 'mobileurl' => 'VuFindTheme\View\Helper\Factory::getMobileUrl', + ], + 'aliases' => [ + 'headThemeResources' => 'VuFindTheme\View\Helper\HeadThemeResources', + 'imageLink' => 'VuFindTheme\View\Helper\ImageLink', ], ]; } diff --git a/module/VuFindTheme/src/VuFindTheme/AbstractThemeUtility.php b/module/VuFindTheme/src/VuFindTheme/AbstractThemeUtility.php index d0e1e70a4ca4eea18642935f43fc6ce418fe1f14..7a505b7393df88c00522b840037970605b3c09a6 100644 --- a/module/VuFindTheme/src/VuFindTheme/AbstractThemeUtility.php +++ b/module/VuFindTheme/src/VuFindTheme/AbstractThemeUtility.php @@ -2,7 +2,7 @@ /** * Abstract base class to hold shared logic for theme utilities. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -96,7 +96,7 @@ abstract class AbstractThemeUtility if (!$this->copyDir("$src/$current", "$dest/$current")) { return false; } - } else if (!file_exists("$dest/$current") + } elseif (!file_exists("$dest/$current") && !copy("$src/$current", "$dest/$current") ) { return $this->setLastError( @@ -126,7 +126,7 @@ abstract class AbstractThemeUtility if (!$this->deleteDir("$path/$current")) { return false; } - } else if (!unlink("$path/$current")) { + } elseif (!unlink("$path/$current")) { return $this->setLastError("Cannot delete $path/$current"); } } diff --git a/module/VuFindTheme/src/VuFindTheme/Initializer.php b/module/VuFindTheme/src/VuFindTheme/Initializer.php index 161a6c7dbaf1188dbbeb7cf2a766a0d5d500a632..bbfba623472055f27fc66730c9174604001855e1 100644 --- a/module/VuFindTheme/src/VuFindTheme/Initializer.php +++ b/module/VuFindTheme/src/VuFindTheme/Initializer.php @@ -2,7 +2,7 @@ /** * VuFind Theme Initializer * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org Main Site */ namespace VuFindTheme; + use Zend\Config\Config; +use Zend\Console\Console; use Zend\Mvc\MvcEvent; use Zend\Mvc\View\Http\InjectTemplateListener as BaseInjectTemplateListener; use Zend\Stdlib\RequestInterface as Request; @@ -84,6 +86,13 @@ class Initializer */ protected $cookieManager; + /** + * A static flag used to determine if the theme has been initialized + * + * @var bool + */ + protected static $themeInitialized = false; + /** * Constructor * @@ -178,6 +187,12 @@ class Initializer */ public function init() { + // Make sure to initialize the theme just once + if (self::$themeInitialized) { + return; + } + self::$themeInitialized = true; + // Determine the current theme: $currentTheme = $this->pickTheme($this->event->getRequest()); @@ -215,6 +230,9 @@ class Initializer { // Load standard configuration options: $standardTheme = $this->config->theme; + if (Console::isConsole()) { + return $standardTheme; + } $mobileTheme = $this->mobile->enabled() ? $this->config->mobile_theme : false; @@ -268,10 +286,12 @@ class Initializer protected function sendThemeOptionsToView() { // Get access to the view model: - $viewModel = $this->serviceManager->get('viewmanager')->getViewModel(); + if (!Console::isConsole()) { + $viewModel = $this->serviceManager->get('ViewManager')->getViewModel(); - // Send down the view options: - $viewModel->setVariable('themeOptions', $this->getThemeOptions()); + // Send down the view options: + $viewModel->setVariable('themeOptions', $this->getThemeOptions()); + } } /** @@ -411,11 +431,11 @@ class Initializer if (!empty($pathStack)) { try { - $translator = $this->serviceManager->get('VuFind\Translator'); + $translator = $this->serviceManager->get('Zend\Mvc\I18n\Translator'); $pm = $translator->getPluginManager(); - $pm->get('extendedini')->addToPathStack($pathStack); - } catch (\Zend\Mvc\Exception\BadMethodCallException $e) { + $pm->get('ExtendedIni')->addToPathStack($pathStack); + } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $e) { // This exception likely indicates that translation is disabled, // so we can't proceed. return; @@ -424,13 +444,13 @@ class Initializer // Override the default cache with a theme-specific cache to avoid // key collisions in a multi-theme environment. try { - $cacheManager = $this->serviceManager->get('VuFind\CacheManager'); + $cacheManager = $this->serviceManager->get('VuFind\Cache\Manager'); $cacheName = $cacheManager->addLanguageCacheForTheme($theme); $translator->setCache($cacheManager->getCache($cacheName)); } catch (\Exception $e) { // Don't let a cache failure kill the whole application, but make // note of it: - $logger = $this->serviceManager->get('VuFind\Logger'); + $logger = $this->serviceManager->get('VuFind\Log\Logger'); $logger->debug( 'Problem loading cache: ' . get_class($e) . ' exception: ' . $e->getMessage() diff --git a/module/VuFindTheme/src/VuFindTheme/InjectTemplateListener.php b/module/VuFindTheme/src/VuFindTheme/InjectTemplateListener.php index b074c98ec320f9d01d90107e85a98ab0a18302fd..afce4b21732e62351c63048de156f43a111f4f8f 100644 --- a/module/VuFindTheme/src/VuFindTheme/InjectTemplateListener.php +++ b/module/VuFindTheme/src/VuFindTheme/InjectTemplateListener.php @@ -2,7 +2,7 @@ /** * VuFind "Inject Template" Listener * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -54,17 +54,18 @@ class InjectTemplateListener extends \Zend\Mvc\View\Http\InjectTemplateListener } /** - * Determine the top-level namespace of the controller + * Strip namespace part off controller name for compatibility with theme + * system. * - * @param string $controller Controller name + * @param string $controller controller FQCN * - * @return string - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @return string|false template name or false if controller was not matched */ - protected function deriveModuleNamespace($controller) + public function mapController($controller) { - // Namespaces just make the theme system more confusing; ignore them: - return ''; + $initial = parent::mapController($controller); + $parts = explode('/', $initial); + array_shift($parts); + return implode('/', $parts); } } diff --git a/module/VuFindTheme/src/VuFindTheme/LessCompiler.php b/module/VuFindTheme/src/VuFindTheme/LessCompiler.php index 68f2e6eaa29c56aa8964f0e5e52d88b178ae4192..3b6c2923e20204e8f7847848f6839ffad65f50b7 100644 --- a/module/VuFindTheme/src/VuFindTheme/LessCompiler.php +++ b/module/VuFindTheme/src/VuFindTheme/LessCompiler.php @@ -2,7 +2,7 @@ /** * Class to compile LESS into CSS within a theme. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org Main Site */ namespace VuFindTheme; + use Zend\Console\Console; /** @@ -160,7 +161,7 @@ class LessCompiler $base = (isset($configArr['extends'])) ? $this->getAllLessFiles($configArr['extends']) : []; - $current = isset($configArr['less']) ? $configArr['less'] : []; + $current = $configArr['less'] ?? []; return array_merge($base, $current); } diff --git a/module/VuFindTheme/src/VuFindTheme/Minify/CSS.php b/module/VuFindTheme/src/VuFindTheme/Minify/CSS.php index 9fc2e5353f83cc139639ce71bd5cf5bda25364ea..95547c4a01034af74ddebcf2504cb73e93be2af0 100644 --- a/module/VuFindTheme/src/VuFindTheme/Minify/CSS.php +++ b/module/VuFindTheme/src/VuFindTheme/Minify/CSS.php @@ -2,7 +2,7 @@ /** * CSS minifier extension * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2017. * diff --git a/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php b/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php index dd69996b62dd2a0ff2dc2b12f5059ef9f49de2cd..55070c5982df1eb70a31243b0f5c165d3eb54a2d 100644 --- a/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php +++ b/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php @@ -2,7 +2,7 @@ /** * CSS path converter extension * - * PHP version 5 + * PHP version 7 * * Copyright (C) The National Library of Finland 2017. * diff --git a/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php b/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php index 957820399658542c0a0c51388bd6bcc886521f15..e7cc4019f570d7a36d3c8f770c2090a14cf42c20 100644 --- a/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php +++ b/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php @@ -2,7 +2,7 @@ /** * Class to generate a new mixin from a template. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -27,6 +27,7 @@ * @link https://vufind.org Main Site */ namespace VuFindTheme; + use Zend\Console\Console; /** diff --git a/module/VuFindTheme/src/VuFindTheme/Mobile.php b/module/VuFindTheme/src/VuFindTheme/Mobile.php index c1b47f2ced9ce90177537306c881d38f5721df1d..cb54654ba6bd53a6702b44817971340dccc70ec5 100644 --- a/module/VuFindTheme/src/VuFindTheme/Mobile.php +++ b/module/VuFindTheme/src/VuFindTheme/Mobile.php @@ -2,7 +2,7 @@ /** * Mobile Device Detection Wrapper * - * PHP version 5 + * PHP version 7 * * This file is a wrapper around the mobileesp library for browser detection. * We chose mobileesp as VuFind's default option because it is fairly robust @@ -32,6 +32,7 @@ * @link https://github.com/ahand/mobileesp MobileESP Project */ namespace VuFindTheme; + use uagent_info; /** diff --git a/module/VuFindTheme/src/VuFindTheme/ResourceContainer.php b/module/VuFindTheme/src/VuFindTheme/ResourceContainer.php index c0573975002d964d9318cde443855ba3ebdc4cf0..5094cc56a10704614539b2fd8149f217511ed2ac 100644 --- a/module/VuFindTheme/src/VuFindTheme/ResourceContainer.php +++ b/module/VuFindTheme/src/VuFindTheme/ResourceContainer.php @@ -2,7 +2,7 @@ /** * VuFind Theme Public Resource Handler (for CSS, JS, etc.) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindTheme/src/VuFindTheme/ThemeCompiler.php b/module/VuFindTheme/src/VuFindTheme/ThemeCompiler.php index 7070d65a2cecff8211800b0e4dc3d1a26e53b542..fbb1ef6083a76a2340e0ec36d06ac3c3ccff100b 100644 --- a/module/VuFindTheme/src/VuFindTheme/ThemeCompiler.php +++ b/module/VuFindTheme/src/VuFindTheme/ThemeCompiler.php @@ -2,7 +2,7 @@ /** * Class to compile a theme hierarchy into a single flat theme. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -122,7 +122,7 @@ class ThemeCompiler extends AbstractThemeUtility // Call this function recursively to deal with the helpers // sub-array: $dest[$key] = $this - ->mergeConfig($value, isset($dest[$key]) ? $dest[$key] : []); + ->mergeConfig($value, $dest[$key] ?? []); break; case 'mixins': // Omit mixin settings entirely @@ -132,7 +132,7 @@ class ThemeCompiler extends AbstractThemeUtility // trump new incoming ones: if (!isset($dest[$key])) { $dest[$key] = $value; - } else if (is_array($dest[$key])) { + } elseif (is_array($dest[$key])) { $dest[$key] = array_merge($value, $dest[$key]); } break; diff --git a/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php b/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php index 32d01e35004f806ee3db3551e8120819cc6811ea..8ae59b2d347013374d24d059932989be25062f8c 100644 --- a/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php +++ b/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php @@ -2,7 +2,7 @@ /** * Class to generate a new theme from a template and reconfigure VuFind to use it. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -27,6 +27,7 @@ * @link https://vufind.org Main Site */ namespace VuFindTheme; + use VuFind\Config\Locator as ConfigLocator; use VuFind\Config\Writer as ConfigWriter; use Zend\Config\Config; diff --git a/module/VuFindTheme/src/VuFindTheme/ThemeInfo.php b/module/VuFindTheme/src/VuFindTheme/ThemeInfo.php index 70f76d5141490e44bfa420fba89677fbbbb641a2..d677c3f5baa5d0a5ecc96244f27e35941bf7ac84 100644 --- a/module/VuFindTheme/src/VuFindTheme/ThemeInfo.php +++ b/module/VuFindTheme/src/VuFindTheme/ThemeInfo.php @@ -2,7 +2,7 @@ /** * Class to represent currently-selected theme and related information. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -212,9 +212,8 @@ class ThemeInfo while (!empty($currentTheme)) { $currentThemeSet = array_merge( - (array) $currentTheme, - isset($allThemeInfo[$currentTheme]['mixins']) - ? $allThemeInfo[$currentTheme]['mixins'] : [] + (array)$currentTheme, + $allThemeInfo[$currentTheme]['mixins'] ?? [] ); foreach ($currentThemeSet as $theme) { foreach ($allPaths as $currentPath) { diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/ConcatTrait.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/ConcatTrait.php index abd4e0a6ca4fe37cc01de38fe6f7734520994189..c6b5037d9c3a3cb9100821e0d6a20565689fb9ee 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/ConcatTrait.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/ConcatTrait.php @@ -3,7 +3,7 @@ * Trait to add asset pipeline functionality (concatenation / minification) to * a HeadLink/HeadScript-style view helper. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2016. * Copyright (C) The National Library of Finland 2017. @@ -29,6 +29,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFindTheme\View\Helper; + use VuFindTheme\ThemeInfo; /** @@ -74,7 +75,7 @@ trait ConcatTrait * * @param stdClass $item Element object * @param string $path New path string - + * * @return void */ abstract protected function setResourceFilePath($item, $path); diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php index 093c00663424cd46ca2b9d12ab4ffa0c6d4b57e1..662e5dfeeee2f413f54d029580ddbfe8c37602a7 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php @@ -2,7 +2,7 @@ /** * Factory for VuFindTheme view helpers. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFindTheme\View\Helper; + use Zend\ServiceManager\ServiceManager; /** @@ -50,7 +51,7 @@ class Factory */ protected static function getPipelineConfig(ServiceManager $sm) { - $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $config = $sm->get('VuFind\Config\PluginManager')->get('config'); $default = false; if (isset($config['Site']['asset_pipeline'])) { $settings = array_map( @@ -61,9 +62,9 @@ class Factory $parts = array_map('trim', explode(':', $setting)); if (APPLICATION_ENV === $parts[0]) { return $parts[1]; - } else if (count($parts) == 1) { + } elseif (count($parts) == 1) { $default = $parts[0]; - } else if ($parts[0] === '*') { + } elseif ($parts[0] === '*') { $default = $parts[1]; } } @@ -81,7 +82,7 @@ class Factory public static function getHeadLink(ServiceManager $sm) { return new HeadLink( - $sm->getServiceLocator()->get('VuFindTheme\ThemeInfo'), + $sm->get('VuFindTheme\ThemeInfo'), Factory::getPipelineConfig($sm) ); } @@ -96,7 +97,7 @@ class Factory public static function getHeadScript(ServiceManager $sm) { return new HeadScript( - $sm->getServiceLocator()->get('VuFindTheme\ThemeInfo'), + $sm->get('VuFindTheme\ThemeInfo'), Factory::getPipelineConfig($sm) ); } @@ -111,7 +112,7 @@ class Factory public static function getHeadThemeResources(ServiceManager $sm) { return new HeadThemeResources( - $sm->getServiceLocator()->get('VuFindTheme\ResourceContainer') + $sm->get('VuFindTheme\ResourceContainer') ); } @@ -125,7 +126,7 @@ class Factory public static function getImageLink(ServiceManager $sm) { return new ImageLink( - $sm->getServiceLocator()->get('VuFindTheme\ThemeInfo') + $sm->get('VuFindTheme\ThemeInfo') ); } @@ -139,22 +140,8 @@ class Factory public static function getInlineScript(ServiceManager $sm) { return new InlineScript( - $sm->getServiceLocator()->get('VuFindTheme\ThemeInfo'), + $sm->get('VuFindTheme\ThemeInfo'), Factory::getPipelineConfig($sm) ); } - - /** - * Construct the MobileUrl helper. - * - * @param ServiceManager $sm Service manager. - * - * @return MobileUrl - */ - public static function getMobileUrl(ServiceManager $sm) - { - return new MobileUrl( - $sm->getServiceLocator()->get('VuFindTheme\Mobile') - ); - } } diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php index c9478a4ae3d52b35e0a82900a5453e34dbc6ac47..60168d4d6dc776d963badfb2b74f3239e871f06c 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php @@ -2,7 +2,7 @@ /** * Head link view helper (extended for VuFind's theme system) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFindTheme\View\Helper; + use VuFindTheme\ThemeInfo; /** diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php index 09e53bb7cd6d6912f0a1bd49fbfafe67b09e44f5..d2328c7795637cfbbf6430686ce91691d1522306 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php @@ -2,7 +2,7 @@ /** * Head script view helper (extended for VuFind's theme system) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development Wiki */ namespace VuFindTheme\View\Helper; + use VuFindTheme\ThemeInfo; /** diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php index acb1a40d449d914575c2a78cf1f4749d5e54b093..38a143406590d4372c8e31cdb7b60c761e1edaf2 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php @@ -2,7 +2,7 @@ /** * View helper for loading theme-related resources in the header. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -97,7 +97,7 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper protected function addMetaTags() { // Set up encoding: - $headMeta = $this->getView()->plugin('headmeta'); + $headMeta = $this->getView()->plugin('headMeta'); $headMeta()->prependHttpEquiv( 'Content-Type', 'text/html; charset=' . $this->container->getEncoding() ); @@ -117,12 +117,18 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper protected function addLinks() { // Convenient shortcut to view helper: - $headLink = $this->getView()->plugin('headlink'); + $headLink = $this->getView()->plugin('headLink'); // Load CSS (make sure we prepend them in the appropriate order; theme // resources should load before extras added by individual templates): foreach (array_reverse($this->container->getCss()) as $current) { $parts = $this->parseSetting($current); + // Special case for media with paretheses + // ie. (min-width: 768px) + if (count($parts) > 1 && substr($parts[1], 0, 1) == '(') { + $parts[1] .= ':' . $parts[2]; + array_splice($parts, 2, 1); + } $headLink()->prependStylesheet( trim($parts[0]), isset($parts[1]) ? trim($parts[1]) : 'all', @@ -144,11 +150,13 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper // If we have a favicon, load it now: $favicon = $this->container->getFavicon(); if (!empty($favicon)) { - $imageLink = $this->getView()->plugin('imagelink'); - $headLink([ - 'href' => $imageLink($favicon), - 'type' => 'image/x-icon', 'rel' => 'shortcut icon' - ]); + $imageLink = $this->getView()->plugin('imageLink'); + $headLink( + [ + 'href' => $imageLink($favicon), + 'type' => 'image/x-icon', 'rel' => 'shortcut icon' + ] + ); } } @@ -160,7 +168,7 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper protected function addScripts() { // Load Javascript (same ordering considerations as CSS, above): - $headScript = $this->getView()->plugin('headscript'); + $headScript = $this->getView()->plugin('headScript'); foreach (array_reverse($this->container->getJs()) as $current) { $parts = $this->parseSetting($current); $headScript()->prependFile( diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLink.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLink.php index 6d7c8004dcf78862a207340bac632a64d432de10..21b54fe403dae31543c2aeb7a0229e416c5b02d6 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLink.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLink.php @@ -2,7 +2,7 @@ /** * Image link view helper (extended for VuFind's theme system) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -68,7 +68,7 @@ class ImageLink extends \Zend\View\Helper\AbstractHelper $relPath = 'images/' . $image; $currentTheme = $this->themeInfo->findContainingTheme($relPath); - if (is_null($currentTheme)) { + if (null === $currentTheme) { return null; } diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/InlineScript.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/InlineScript.php index ec9fc2596ff2e35065b6979b9fd90fcb028d21b1..2ab8754b90da1bad0d8601e57498eccfdc0488e9 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/InlineScript.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/InlineScript.php @@ -2,7 +2,7 @@ /** * Inline script view helper (extended for VuFind's theme system) * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/child/theme.config.php b/module/VuFindTheme/tests/unit-tests/fixtures/themes/child/theme.config.php index d79f66278bdf43e41dc3c1382f48b1b511776fc0..cd7c97ff86b6c7bc51e701becdcb0ac40194a9f3 100644 --- a/module/VuFindTheme/tests/unit-tests/fixtures/themes/child/theme.config.php +++ b/module/VuFindTheme/tests/unit-tests/fixtures/themes/child/theme.config.php @@ -7,7 +7,7 @@ return [ 'factories' => [ 'foo' => 'fooOverrideFactory', ], - 'invokables' => [ + 'aliases' => [ 'xyzzy' => 'Xyzzy', ] ], diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/LessCompilerTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/LessCompilerTest.php index fdb265750d8cc98a24ef68af13bf43c5c1d555c3..733fcdeaf8ce01f40b800c5c8195597424fe0da3 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/LessCompilerTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/LessCompilerTest.php @@ -2,7 +2,7 @@ /** * LessCompiler Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFindTheme\LessCompiler; /** @@ -58,9 +59,9 @@ class LessCompilerTest extends Unit\TestCase $temp = sys_get_temp_dir(); $testDest = $temp . '/vufind_less_comp_test/'; // Create directory structure, recursively - mkdir($testDest . 'themes/child/less', 0777, true); - mkdir($testDest . 'themes/empty', 0777, true); - mkdir($testDest . 'themes/parent/css', 0777, true); + mkdir($testDest . 'themes/child/less', 0777, true); + mkdir($testDest . 'themes/empty', 0777, true); + mkdir($testDest . 'themes/parent/css', 0777, true); mkdir($testDest . 'themes/parent/less/relative', 0777, true); file_put_contents( $testDest . 'themes/empty/theme.config.php', diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeCompilerTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeCompilerTest.php index dcaedb3b18b572f8c25dc4786fe8db03575cf873..1638647c8dce8ce40761d78aadfc4194422a42da 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeCompilerTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeCompilerTest.php @@ -2,7 +2,7 @@ /** * ThemeCompiler Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2017. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFindTheme\ThemeCompiler; use VuFindTheme\ThemeInfo; @@ -61,16 +62,6 @@ class ThemeCompilerTest extends Unit\TestCase */ protected $targetPath; - /** - * Constructor - */ - public function __construct() - { - $this->fixturePath = realpath(__DIR__ . '/../../fixtures/themes'); - $this->info = new ThemeInfo($this->fixturePath, 'parent'); - $this->targetPath = $this->info->getBaseDir() . '/compiled'; - } - /** * Standard setup method. * @@ -78,6 +69,9 @@ class ThemeCompilerTest extends Unit\TestCase */ public function setUp() { + $this->fixturePath = realpath(__DIR__ . '/../../fixtures/themes'); + $this->info = new ThemeInfo($this->fixturePath, 'parent'); + $this->targetPath = $this->info->getBaseDir() . '/compiled'; // Give up if the target directory already exists: if (is_dir($this->targetPath)) { return $this->markTestSkipped('compiled theme already exists.'); @@ -127,7 +121,7 @@ class ThemeCompilerTest extends Unit\TestCase 'foo' => 'fooOverrideFactory', 'bar' => 'barFactory', ], - 'invokables' => [ + 'aliases' => [ 'xyzzy' => 'Xyzzy', ] ], @@ -185,7 +179,7 @@ class ThemeCompilerTest extends Unit\TestCase 'foo' => 'fooOverrideFactory', 'bar' => 'barFactory', ], - 'invokables' => [ + 'aliases' => [ 'xyzzy' => 'Xyzzy', ] ], diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInfoTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInfoTest.php index 0cf095e7eea96691f7e14ef637de353d4dc9d821..cebb80d60eec6002fa37aa99e3532ac8c0061cfe 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInfoTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInfoTest.php @@ -2,7 +2,7 @@ /** * ThemeInfo Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFindTheme\ThemeInfo; /** @@ -49,7 +50,7 @@ class ThemeInfoTest extends Unit\TestCase /** * Constructor */ - public function __construct() + public function setUp() { $this->fixturePath = realpath(__DIR__ . '/../../fixtures/themes'); } diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInjectTemplateListenerTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInjectTemplateListenerTest.php index 8e59fc159d55b2d6bb41c4dc9a000562ef0cd8c7..58b7c1d59b508c07083ec532221e9ce4a8336574 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInjectTemplateListenerTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeInjectTemplateListenerTest.php @@ -2,7 +2,7 @@ /** * InjectTemplateListener Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFindTheme\InjectTemplateListener; /** @@ -48,7 +49,7 @@ class ThemeInjectTemplateListenerTest extends Unit\TestCase { $l = new InjectTemplateListener(); $this->assertEquals( - '', $this->callMethod($l, 'deriveModuleNamespace', ['dummy']) + 'search', $l->mapController('VuFind\Controller\SearchController') ); } diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php index 10b71d33c7485b865d7d4f4d399a0ad03fd50dfe..505d41a63228e6ec899c4ac3a64df03cab1ccd25 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php @@ -2,7 +2,7 @@ /** * Mobile Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFindTheme\Mobile; /** diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeResourceContainerTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeResourceContainerTest.php index f0abc5a2c41ff101f57dbc1d076fbdd8e1aa534b..8ca368b10c9dfeb6c9e3276652d04590dd959bd8 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeResourceContainerTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeResourceContainerTest.php @@ -2,7 +2,7 @@ /** * ResourceContainer Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,6 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest; + use VuFindTheme\ResourceContainer; /** diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php index d741648849cc082d3a531b1241df93753d60be2a..64e078a3934afcf6d21c4779142c85f92de34afe 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php @@ -2,7 +2,7 @@ /** * HeadThemeResources view helper Test Class * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * @@ -26,7 +26,9 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\View\Helper; -use VuFindTheme\ResourceContainer, VuFindTheme\View\Helper\HeadThemeResources; + +use VuFindTheme\ResourceContainer; +use VuFindTheme\View\Helper\HeadThemeResources; /** * HeadThemeResources view helper Test Class @@ -92,13 +94,13 @@ class HeadThemeResourcesTest extends \VuFindTest\Unit\TestCase { $view = $this->createMock('Zend\View\Renderer\PhpRenderer'); $view->expects($this->at(0))->method('plugin') - ->with($this->equalTo('headmeta')) + ->with($this->equalTo('headMeta')) ->will($this->returnValue($this->getMockHeadMeta())); $view->expects($this->at(1))->method('plugin') - ->with($this->equalTo('headlink')) + ->with($this->equalTo('headLink')) ->will($this->returnValue($this->getMockHeadLink())); $view->expects($this->at(2))->method('plugin') - ->with($this->equalTo('headscript')) + ->with($this->equalTo('headScript')) ->will($this->returnValue($this->getMockHeadScript())); return $view; } diff --git a/package.json b/package.json index f203ca6e97058f29854f84dc75a1324c1dedb9f8..37cec6f6f153ea9e1194acb3e86fabf0fed4b4f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vufind", - "version": "4.1.3", + "version": "5.0.1", "description": "Dev tools to handle css preprocessing, js magic, and compression", "repository": { "type": "git", @@ -10,11 +10,11 @@ "url": "https://vufind.org/jira" }, "dependencies": { - "grunt": "^0.4.5", + "grunt": "^1.0.0", "grunt-cli": "^1.2.0", + "grunt-sass": "^1.0.0", "grunt-contrib-less": "1.3.0", "grunt-less-to-sass": "0.0.10", - "grunt-sass": "^1.0.0", "jit-grunt": "^0.9.1" }, "devDependencies": { diff --git a/packages/DEBIAN/changelog b/packages/DEBIAN/changelog index a3c8ec22eae76ade764e19051ae3e36ae13c0d75..e0b2844b70a4f9e8c3d4c31838505348c4559460 100644 --- a/packages/DEBIAN/changelog +++ b/packages/DEBIAN/changelog @@ -1,3 +1,15 @@ +vufind 5.0.1 distribution; urgency=low + + * VuFind 5.0.1 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> We 10 Oct 2018 08:31:52 UTC + +vufind 5.0 distribution; urgency=low + + * VuFind 5.0 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Mo 16 Jul 2018 11:15:42 UTC + vufind 4.1.3 distribution; urgency=low * VuFind 4.1.3 release (see http://vufind.org/wiki/changelog for details) diff --git a/packages/DEBIAN/control b/packages/DEBIAN/control index 377359e674c53f660b54653f853fcc105c9fc0cc..2ddebeb5ac02f26979b7e89f85ca5f2dba949670 100644 --- a/packages/DEBIAN/control +++ b/packages/DEBIAN/control @@ -1,22 +1,23 @@ Package: vufind -Version: 4.1.3 +Version: 5.0.1 Section: World Wide Web Priority: Optional Architecture: all Depends: apache2, - openjdk-8-jdk | default-jdk, + default-jdk, libapache2-mod-php5 | libapache2-mod-php, libapache2-mod-php5 | php-mbstring, mysql-server | virtual-mysql-server-core, php-pear, - php5 | php, - php5-dev | php-dev, - php5-gd | php-gd, - php5-intl | php-intl, - php5-json | php-json, - php5-ldap | php-ldap, - php5-mysql | php-mysql, - php5-xsl | php-xml + php, + php-dev, + php-gd, + php-intl, + php-json, + php-ldap, + php-mysql, + php-soap, + php-xml Maintainer: VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Homepage: http://vufind.org/ Description: A library resource discovery portal diff --git a/public/index.php b/public/index.php index 6967aaf957aeee755527c2d756ee1e77c3a4a29a..50aa986dc3b00130a468a5d12e7e2e3cc056f7a0 100644 --- a/public/index.php +++ b/public/index.php @@ -1,14 +1,10 @@ <?php -use Zend\Loader\AutoloaderFactory; -use Zend\ServiceManager\ServiceManager; -use Zend\Mvc\Service\ServiceManagerConfig; - // If the XHProf profiler is enabled, set it up now: $xhprof = getenv('VUFIND_PROFILER_XHPROF'); if (!empty($xhprof)) { if (extension_loaded('xhprof')) { xhprof_enable(); - } else if (extension_loaded('tideways')) { + } elseif (extension_loaded('tideways')) { tideways_enable(); } else { $xhprof = false; @@ -57,7 +53,7 @@ chdir(APPLICATION_PATH); // Ensure vendor/ is on include_path; some PEAR components may not load correctly // otherwise (i.e. File_MARC may cause a "Cannot redeclare class" error by pulling // from the shared PEAR directory instead of the local copy): -$pathParts = array(); +$pathParts = []; $pathParts[] = APPLICATION_PATH . '/vendor'; $pathParts[] = get_include_path(); set_include_path(implode(PATH_SEPARATOR, $pathParts)); @@ -67,18 +63,8 @@ if (file_exists('vendor/autoload.php')) { $loader = include 'vendor/autoload.php'; } -// Support for ZF2_PATH environment variable -if ($zf2Path = getenv('ZF2_PATH')) { - if (isset($loader)) { - $loader->add('Zend', $zf2Path . '/Zend'); - } else { - include $zf2Path . '/Zend/Loader/AutoloaderFactory.php'; - AutoloaderFactory::factory(); - } -} - if (!class_exists('Zend\Loader\AutoloaderFactory')) { - throw new RuntimeException('Unable to load ZF2.'); + throw new RuntimeException('Unable to load Zend Framework autoloader.'); } // Run the application! diff --git a/solr.bat b/solr.bat index eb6c9b4a403f6469d74f70c06005a7a23f5aa06b..fbce82ff4b61b5db258cdb604a8b55d9d8bcb75e 100644 --- a/solr.bat +++ b/solr.bat @@ -40,17 +40,20 @@ rem Unrecognized action -- display help text if "!%1!"=="!!" goto usage rem Set VUFIND_HOME (if not already set) -if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound -rem VUFIND_HOME not set -- try to call env.bat to +if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound +rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat rem If env.bat doesn't exist, the user hasn't run the installer yet. -echo ERROR: env.bat does not exist -- could not set up environment. +echo WARNING: env.bat does not exist -- trying default environment settings. echo Please run "php install.php" to correct this problem. -goto end +rem Extract path from current batch file and trim trailing slash: +set VUFIND_HOME=%~dp0% +set VUFIND_HOME=%VUFIND_HOME:~0,-1% +goto vufindhomefound :useenvbat call env > nul -if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound +if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound echo You need to set the VUFIND_HOME environmental variable before running this script. goto end :vufindhomefound diff --git a/tests/data/geo_titles.mrc b/tests/data/geo_titles.mrc new file mode 100644 index 0000000000000000000000000000000000000000..f852d24721d1ff58b7762bff7dd9435d27ee7831 --- /dev/null +++ b/tests/data/geo_titles.mrc @@ -0,0 +1 @@ +00740naaa 2200109zu 4500001001000000005001500010008004100025034005600066100002300122245040200145520008300547 778812320160513104939730000s1973 coAa 0 0 EL d0 aadW1220000eW1190000fN450000gN420000zSite Z21401 aHurldabood, Millie10aResults of 2008 exploration program and GEMCOM modeling of Sn-In-Zn-Cu and WO<3`-MoS<2`-Bi zones on the Mount Pleasant Mine property, Charlotte County, southwestern New Brunswick--Résultats du programme d'exploration de 2008 et modélisations GEMCOM des zones de Sn-In-Zn-Cu et de WO<3`-MoS<2`-Bi sur la properiété mini3Ì€re du Mont Pleasant, comté de Charlotte, sud-ouest du Nouveau-Brunswick aAn exploration of salt domes, loess deposits in areas impacted by hurricanes. 00390naaa 2200109zu 4500001001000000005001500010008004100025034005500066100002100121245005000142520008800192 778812420160513104939730000s1973 coAa 0 0 EL d0 aadW1220000eW1210000fN470000gN460000zSite 14311 aWinrow, Sanjuana10aFracture Folds Australian Granitic Structures aA discussion of fracture folding in granitic formations in the Australian outback. 00293naaa 2200097zu 4500001001000000005001500010008004100025034005600066100001500122520005800137 778812520160513104939730000s1973 coAa 0 0 EL d0 aadW1190000eW1190000fN460000gN460000zPort Kelly1 aRudd, Jann aExpedition results from Port Kelly core logging trip.00325naaa 2200097zu 4500001001000000005001500010008004100025034005800066100002300124520008000147 778812720160513104939730000s1973 coAa 0 0 EL d0 aadW1230000eW1220000fN470000gN460000zSite ABD13431 aHurldabood, Millie aAn exploration of clay and silica lenses in the southwestern United States. \ No newline at end of file diff --git a/tests/phpcs.xml b/tests/phpcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..a6cfb5c198a232b90681a9fe55ef35bb7d3d59c5 --- /dev/null +++ b/tests/phpcs.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<ruleset name="VuFind Coding Standards" namespace="VuFind\PHPCS"> + <description>Coding standards for VuFind.</description> + <file>../module</file> + <exclude-pattern>module/[^/]+/config/*</exclude-pattern> + <exclude-pattern>module/[^/]+/tests/*</exclude-pattern> + <arg name="extensions" value="php"/> + <rule ref="PEAR"> + <exclude name="PEAR.Commenting.FunctionComment.ParamCommentAlignment" /> + </rule> +</ruleset> diff --git a/tests/vufind.php_cs b/tests/vufind.php_cs new file mode 100644 index 0000000000000000000000000000000000000000..8920407197e1a5c6c3b16265bd796e8fe41ecd89 --- /dev/null +++ b/tests/vufind.php_cs @@ -0,0 +1,84 @@ +<?php + +$finder = PhpCsFixer\Finder::create() + ->in(__DIR__ . '/../config') + ->in(__DIR__ . '/../module') + ->in(__DIR__ . '/../public'); + +$rules = [ + 'align_multiline_comment' => true, + 'array_syntax' => ['syntax' => 'short'], + 'binary_operator_spaces' => [ + 'default' => 'single_space', + 'operators' => ['=' => null, '=>' => null], + ], + 'blank_line_after_namespace' => true, + 'braces' => true, + 'cast_spaces' => ['space' => 'none'], + 'concat_space' => ['spacing' => 'one'], + 'elseif' => true, + 'encoding' => true, + 'ereg_to_preg' => true, + 'full_opening_tag' => true, + 'function_declaration' => true, + 'function_typehint_space' => true, + 'indentation_type' => true, + 'is_null' => true, + 'line_ending' => true, + 'linebreak_after_opening_tag' => true, + 'lowercase_cast' => true, + 'lowercase_constants' => true, + 'lowercase_keywords' => true, + 'magic_constant_casing' => true, + 'method_argument_space' => true, + 'method_separation' => true, + 'native_function_casing' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_blank_lines_before_namespace' => true, + 'no_closing_tag' => true, + 'no_empty_comment' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_consecutive_blank_lines' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_around_offset' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unneeded_curly_braces' => true, + 'no_unused_imports' => true, + 'no_useless_return' => true, + 'no_whitespace_before_comma_in_array' => true, + 'no_whitespace_in_blank_line' => true, + 'non_printable_character' => true, + 'ordered_imports' => true, + 'phpdoc_no_access' => true, + 'single_blank_line_at_eof' => true, + 'single_class_element_per_statement' => true, + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'short_scalar_cast' => true, + 'standardize_not_equals' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'ternary_operator_spaces' => true, + 'ternary_to_null_coalescing' => true, + 'visibility_required' => true, +]; + +$cacheDir = __DIR__ . '/../.php_cs_cache'; +if (!is_dir($cacheDir)) { + mkdir($cacheDir); +} + +return PhpCsFixer\Config::create() + ->setCacheFile($cacheDir . '/.code.cache') + ->setRiskyAllowed(true) + ->setRules($rules) + ->setFinder($finder); diff --git a/tests/vufind_templates.php_cs b/tests/vufind_templates.php_cs new file mode 100644 index 0000000000000000000000000000000000000000..df6942b654c9e40c083391e345a34059e8f773d9 --- /dev/null +++ b/tests/vufind_templates.php_cs @@ -0,0 +1,78 @@ +<?php + +$finder = PhpCsFixer\Finder::create()->in(__DIR__ . '/../themes') + ->name('*.phtml'); + +$rules = [ + 'align_multiline_comment' => true, + 'array_syntax' => ['syntax' => 'short'], + 'binary_operator_spaces' => [ + 'default' => 'single_space', + ], + 'blank_line_after_namespace' => true, + //'braces' => true, // disabled because we don't want to create inconsistent indentation, but useful to normalize control structure spacing + 'cast_spaces' => ['space' => 'none'], + 'concat_space' => ['spacing' => 'one'], + 'elseif' => true, + 'encoding' => true, + 'ereg_to_preg' => true, + 'full_opening_tag' => true, + 'function_declaration' => true, + 'function_typehint_space' => true, + 'indentation_type' => true, + 'is_null' => true, + 'line_ending' => true, + 'lowercase_cast' => true, + 'lowercase_constants' => true, + 'lowercase_keywords' => true, + 'magic_constant_casing' => true, + 'method_argument_space' => true, + 'method_separation' => true, + 'native_function_casing' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_blank_lines_before_namespace' => true, + 'no_empty_comment' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_consecutive_blank_lines' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_around_offset' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_trailing_whitespace' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unneeded_curly_braces' => true, + 'no_unused_imports' => true, + 'no_useless_return' => true, + 'no_whitespace_before_comma_in_array' => true, + 'no_whitespace_in_blank_line' => true, + 'non_printable_character' => true, + 'ordered_imports' => true, + 'phpdoc_no_access' => true, + 'single_blank_line_at_eof' => true, + 'single_class_element_per_statement' => true, + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'short_scalar_cast' => true, + 'standardize_not_equals' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + //'ternary_operator_spaces' => true, // disabled due to bug in php-cs-fixer 2.7.1 + 'ternary_to_null_coalescing' => true, + 'visibility_required' => true, +]; + +$cacheDir = __DIR__ . '/../.php_cs_cache'; +if (!is_dir($cacheDir)) { + mkdir($cacheDir); +} + +return PhpCsFixer\Config::create() + ->setCacheFile($cacheDir . '/.template.cache') + ->setRiskyAllowed(true) + ->setRules($rules) + ->setFinder($finder); diff --git a/themes/bootprint3/less/bootprint.less b/themes/bootprint3/less/bootprint.less index 17c6aa73401b07ca9b41f67be8a7cff797f98936..596a442afc5345420933d5ce00b3c27d0743b470 100644 --- a/themes/bootprint3/less/bootprint.less +++ b/themes/bootprint3/less/bootprint.less @@ -83,6 +83,11 @@ footer ul { padding-left: 30px; } [id^=list].list-group .col-sm-9 { margin: 0; } /* --- Channels --- */ +.channel { + max-width: 96%; + margin-left: 2%; + margin-right: 2%; +} .channel, .channel-title.no-results { margin-bottom: 2rem; } .channel-title { margin-top: 1rem; } @@ -150,7 +155,6 @@ ul.random.image li img { margin: 0 auto; } .facet .badge a { color: #fff; } .facet.facetOR { padding-left: 1rem; } - .facet.excludable { padding-right: 0; } } .slider-container .slider-handle { background: @brand-primary; @@ -165,5 +169,5 @@ ul.random.image li img { margin: 0 auto; } color: #fff; &:hover { color: @brand-danger; } } -.jstree-facet .jstree-ocl::before { margin-left: -5px; } +.jstree-facet .facet { padding: 0; } .jstree-node.active { background-color: @active-orange; } diff --git a/themes/bootprint3/scss/bootprint.scss b/themes/bootprint3/scss/bootprint.scss index 68f8f561eeb52fab32cb0989badfe2c7b144f749..c9b8e98ed7ba427cc052ea553c2e8342418411a0 100644 --- a/themes/bootprint3/scss/bootprint.scss +++ b/themes/bootprint3/scss/bootprint.scss @@ -21,6 +21,11 @@ body { /* --- Layout --- */ .main .container { padding-top: 1rem; } +.main .sidebar { background-color: #fff; } +body.offcanvas .sidebar { + padding-left: 1rem; + padding-right: 1rem; +} @media (min-width: 768px) { .main .sidebar { padding: 0; } } @@ -159,5 +164,5 @@ ul.random.image li img { margin: 0 auto; } color: #fff; &:hover { color: $brand-danger; } } -.jstree-facet .jstree-ocl::before { margin-left: -5px; } +.jstree-facet .facet { padding: 0; } .jstree-node.active { background-color: $active-orange; } diff --git a/themes/bootprint3/theme.config.php b/themes/bootprint3/theme.config.php index 018a473aeb6395abb5ed5a8c63dbb2661fd46f33..2f5a8373cd19585855303f5d660dc033e9828a92 100644 --- a/themes/bootprint3/theme.config.php +++ b/themes/bootprint3/theme.config.php @@ -1,4 +1,4 @@ <?php -return array( +return [ 'extends' => 'bootstrap3' -); +]; diff --git a/themes/bootstrap3/css/geofeatures.css b/themes/bootstrap3/css/geofeatures.css new file mode 100644 index 0000000000000000000000000000000000000000..efad959cfea345d3b9d95b79a6b0afdd08e4f594 --- /dev/null +++ b/themes/bootstrap3/css/geofeatures.css @@ -0,0 +1,22 @@ +/* Active is selected cluster = BLUE */ +.marker-cluster-active { + background-color: rgba(181, 226, 255, 1); + color: #fff; +} + +.marker-cluster-active div { + background-color: rgba(59, 136, 255, 0.6); + color: #fff; +} + +/* Inactive is search result cluster - not selected = RED */ +.marker-cluster-inactive { + background-color: rgba(226, 140, 140, 1); + color:#fff; +} + +.marker-cluster-inactive div { + background-color: rgba(145, 10, 10, 0.6); + color:#fff; +} + diff --git a/themes/bootstrap3/css/lib/channel-slider.css b/themes/bootstrap3/css/lib/channel-slider.css deleted file mode 100644 index 8b49574400df6395ded0086bda94225ef582f258..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/css/lib/channel-slider.css +++ /dev/null @@ -1,63 +0,0 @@ -.channel-slider { - position: relative; - max-width: 1000px; - height: 300px; - margin: 50px auto; - border: 1px solid #aaa; -} -.slider-bounds { - position: relative; - width: 100%; - height: 100%; - overflow: hidden; -} -.slider-screen { - position: absolute; - top: 0; - left: 0; - display: block; - width: 9999px; - height: 300px; -} -.slider-screen::after { - display: table; - clear: both; - content: ""; -} -.slider-screen .slide { - float: left; - display: table; - max-width: 150px; - margin: 5px; - padding: 5px; - user-select: none; -} -.slider-screen .slide .thumb { - max-width: 100%; - max-height: 160px; -} -.slider-screen .slide img { - display: block; - max-width: 100%; - max-height: 100%; - margin: auto; -} -.slider-menu { - position: absolute; - bottom: 0; - width: 100%; - padding: 4px; - border-top: 1px solid #aaa; - background-color: #fff; -} -.slider-menu .scroll-bar { - position: absolute; - top: -10px; - left: 0; - z-index: 10; - width: 50px; - height: 8px; - background: #aaa; - color: transparent; - border-radius: 1px; -} \ No newline at end of file diff --git a/themes/bootstrap3/css/vendor/ajax-loader.gif b/themes/bootstrap3/css/vendor/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..e0e6e9760bc04861cc4771e327f22ed7962e0858 Binary files /dev/null and b/themes/bootstrap3/css/vendor/ajax-loader.gif differ diff --git a/themes/bootstrap3/css/vendor/fonts/slick.eot b/themes/bootstrap3/css/vendor/fonts/slick.eot new file mode 100644 index 0000000000000000000000000000000000000000..2cbab9ca97723bc24c50315a0a9bd155db4e0aa5 Binary files /dev/null and b/themes/bootstrap3/css/vendor/fonts/slick.eot differ diff --git a/themes/bootstrap3/css/vendor/fonts/slick.svg b/themes/bootstrap3/css/vendor/fonts/slick.svg new file mode 100644 index 0000000000000000000000000000000000000000..b36a66a6c454fb901133b8bce089a5ff7f74f871 --- /dev/null +++ b/themes/bootstrap3/css/vendor/fonts/slick.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata>Generated by Fontastic.me</metadata> +<defs> +<font id="slick" horiz-adv-x="512"> +<font-face font-family="slick" units-per-em="512" ascent="480" descent="-32"/> +<missing-glyph horiz-adv-x="512" /> + +<glyph unicode="→" d="M241 113l130 130c4 4 6 8 6 13 0 5-2 9-6 13l-130 130c-3 3-7 5-12 5-5 0-10-2-13-5l-29-30c-4-3-6-7-6-12 0-5 2-10 6-13l87-88-87-88c-4-3-6-8-6-13 0-5 2-9 6-12l29-30c3-3 8-5 13-5 5 0 9 2 12 5z m234 143c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/> +<glyph unicode="←" d="M296 113l29 30c4 3 6 7 6 12 0 5-2 10-6 13l-87 88 87 88c4 3 6 8 6 13 0 5-2 9-6 12l-29 30c-3 3-8 5-13 5-5 0-9-2-12-5l-130-130c-4-4-6-8-6-13 0-5 2-9 6-13l130-130c3-3 7-5 12-5 5 0 10 2 13 5z m179 143c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/> +<glyph unicode="•" d="M475 256c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/> +<glyph unicode="a" d="M475 439l0-128c0-5-1-9-5-13-4-4-8-5-13-5l-128 0c-8 0-13 3-17 11-3 7-2 14 4 20l40 39c-28 26-62 39-100 39-20 0-39-4-57-11-18-8-33-18-46-32-14-13-24-28-32-46-7-18-11-37-11-57 0-20 4-39 11-57 8-18 18-33 32-46 13-14 28-24 46-32 18-7 37-11 57-11 23 0 44 5 64 15 20 9 38 23 51 42 2 1 4 3 7 3 3 0 5-1 7-3l39-39c2-2 3-3 3-6 0-2-1-4-2-6-21-25-46-45-76-59-29-14-60-20-93-20-30 0-58 5-85 17-27 12-51 27-70 47-20 19-35 43-47 70-12 27-17 55-17 85 0 30 5 58 17 85 12 27 27 51 47 70 19 20 43 35 70 47 27 12 55 17 85 17 28 0 55-5 81-15 26-11 50-26 70-45l37 37c6 6 12 7 20 4 8-4 11-9 11-17z"/> +</font></defs></svg> diff --git a/themes/bootstrap3/css/vendor/fonts/slick.ttf b/themes/bootstrap3/css/vendor/fonts/slick.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9d03461b653373f7cda3b4af104c6bca07f9892b Binary files /dev/null and b/themes/bootstrap3/css/vendor/fonts/slick.ttf differ diff --git a/themes/bootstrap3/css/vendor/fonts/slick.woff b/themes/bootstrap3/css/vendor/fonts/slick.woff new file mode 100644 index 0000000000000000000000000000000000000000..8ee99721bb81b59a5e1ceee1d3256b15f907d96b Binary files /dev/null and b/themes/bootstrap3/css/vendor/fonts/slick.woff differ diff --git a/themes/bootstrap3/css/vendor/leaflet/MarkerCluster.Default.css b/themes/bootstrap3/css/vendor/leaflet/MarkerCluster.Default.css new file mode 100644 index 0000000000000000000000000000000000000000..86ebfbc75f9e405deb1fbb7206931c2b8f89dff2 --- /dev/null +++ b/themes/bootstrap3/css/vendor/leaflet/MarkerCluster.Default.css @@ -0,0 +1,84 @@ +/* + * + * Copyright 2012 David Leaver + * + * 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. + * + */ +.marker-cluster-small { + background-color: rgba(181, 226, 140, 0.6); + } +.marker-cluster-small div { + background-color: rgba(110, 204, 57, 0.6); + } + +.marker-cluster-medium { + background-color: rgba(241, 211, 87, 0.6); + } +.marker-cluster-medium div { + background-color: rgba(240, 194, 12, 0.6); + } + +.marker-cluster-large { + background-color: rgba(253, 156, 115, 0.6); + } +.marker-cluster-large div { + background-color: rgba(241, 128, 23, 0.6); + } + + /* IE 6-8 fallback colors */ +.leaflet-oldie .marker-cluster-small { + background-color: rgb(181, 226, 140); + } +.leaflet-oldie .marker-cluster-small div { + background-color: rgb(110, 204, 57); + } + +.leaflet-oldie .marker-cluster-medium { + background-color: rgb(241, 211, 87); + } +.leaflet-oldie .marker-cluster-medium div { + background-color: rgb(240, 194, 12); + } + +.leaflet-oldie .marker-cluster-large { + background-color: rgb(253, 156, 115); + } +.leaflet-oldie .marker-cluster-large div { + background-color: rgb(241, 128, 23); +} + +.marker-cluster { + background-clip: padding-box; + border-radius: 20px; + } +.marker-cluster div { + width: 30px; + height: 30px; + margin-left: 5px; + margin-top: 5px; + + text-align: center; + border-radius: 15px; + font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; + } +.marker-cluster span { + line-height: 30px; + } diff --git a/themes/bootstrap3/css/vendor/leaflet/MarkerCluster.css b/themes/bootstrap3/css/vendor/leaflet/MarkerCluster.css new file mode 100644 index 0000000000000000000000000000000000000000..898bd016c682b624321b3d7cad2746f4170341f9 --- /dev/null +++ b/themes/bootstrap3/css/vendor/leaflet/MarkerCluster.css @@ -0,0 +1,38 @@ +/* + * + * Copyright 2012 David Leaver + * + * 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. + * + */ +.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow { + -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in; + -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in; + -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in; + transition: transform 0.3s ease-out, opacity 0.3s ease-in; +} + +.leaflet-cluster-spider-leg { + /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */ + -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in; + -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in; + -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in; + transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in; +} diff --git a/themes/bootstrap3/css/vendor/leaflet/images/README.txt b/themes/bootstrap3/css/vendor/leaflet/images/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd44f82ac96f1c33155c1f5ea1aeaa0119a5f339 --- /dev/null +++ b/themes/bootstrap3/css/vendor/leaflet/images/README.txt @@ -0,0 +1,8 @@ +README +----------- +Leaflet color markers (i.e. marker-icon-2x-blue.png, marker-icon-2x-red.png) +are sourced from the pointhi/leaflet-color-markers github repository. + +https://github.com/pointhi/leaflet-color-markers/tree/master/img + +All other images in this folder come with the standard leaflet installation package. diff --git a/themes/bootstrap3/css/vendor/leaflet/images/layers-2x.png b/themes/bootstrap3/css/vendor/leaflet/images/layers-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..200c333dca9652ac4cba004d609e5af4eee168c1 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/layers-2x.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/layers.png b/themes/bootstrap3/css/vendor/leaflet/images/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..1a72e5784b2b456eac5d7670738db80697af3377 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/layers.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-blue.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..e4abba3b511d14752426e8cbadae03c1e5fe15fb Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-blue.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-red.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-red.png new file mode 100644 index 0000000000000000000000000000000000000000..1c26e9fc20884780a361a49238287ea2f9d50b44 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-red.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c26e9fc20884780a361a49238287ea2f9d50b44 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-blue.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..950edf24677ded147df13b26f91baa2b0fa70513 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-blue.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-red.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-red.png new file mode 100644 index 0000000000000000000000000000000000000000..3e64e06d1db4eecf0d6e4446630fc4f9b97694b2 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-red.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-icon.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3e64e06d1db4eecf0d6e4446630fc4f9b97694b2 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-icon.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png b/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd2979532a19a15b824ce763c76e04a8dafadfb Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-blue.png b/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..ba45077fdf36e3ac1f5c2a351a20e42cfd7984f2 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-blue.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-red.png b/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-red.png new file mode 100644 index 0000000000000000000000000000000000000000..4769c74108195a4fb2729c15254fbea174ce3ad4 Binary files /dev/null and b/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-red.png differ diff --git a/themes/bootstrap3/css/vendor/leaflet/leaflet.css b/themes/bootstrap3/css/vendor/leaflet/leaflet.css new file mode 100644 index 0000000000000000000000000000000000000000..230e5bad14f3494ce152dc69634b24c232f76462 --- /dev/null +++ b/themes/bootstrap3/css/vendor/leaflet/leaflet.css @@ -0,0 +1,636 @@ +/* required styles */ + +.leaflet-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-container, +.leaflet-pane > svg, +.leaflet-pane > canvas, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; + } +.leaflet-container { + overflow: hidden; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container .leaflet-overlay-pane svg, +.leaflet-container .leaflet-marker-pane img, +.leaflet-container .leaflet-shadow-pane img, +.leaflet-container .leaflet-tile-pane img, +.leaflet-container img.leaflet-image-layer { + max-width: none !important; + max-height: none !important; + } + +.leaflet-container.leaflet-touch-zoom { + -ms-touch-action: pan-x pan-y; + touch-action: pan-x pan-y; + } +.leaflet-container.leaflet-touch-drag { + -ms-touch-action: pinch-zoom; + /* Fallback for FF which doesn't support pinch-zoom */ + touch-action: none; + touch-action: pinch-zoom; +} +.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { + -ms-touch-action: none; + touch-action: none; +} +.leaflet-container { + -webkit-tap-highlight-color: transparent; +} +.leaflet-container a { + -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); +} +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } +.leaflet-zoom-box { + width: 0; + height: 0; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 800; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-pane { z-index: 400; } + +.leaflet-tile-pane { z-index: 200; } +.leaflet-overlay-pane { z-index: 400; } +.leaflet-shadow-pane { z-index: 500; } +.leaflet-marker-pane { z-index: 600; } +.leaflet-tooltip-pane { z-index: 650; } +.leaflet-popup-pane { z-index: 700; } + +.leaflet-map-pane canvas { z-index: 100; } +.leaflet-map-pane svg { z-index: 200; } + +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile { + will-change: opacity; + } +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } +.leaflet-zoom-animated { + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + will-change: transform; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); + } +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; + } +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + } +.leaflet-crosshair, +.leaflet-crosshair .leaflet-interactive { + cursor: crosshair; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging .leaflet-grab, +.leaflet-dragging .leaflet-grab .leaflet-interactive, +.leaflet-dragging .leaflet-marker-draggable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + +/* marker & overlays interactivity */ +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-image-layer, +.leaflet-pane > svg path, +.leaflet-tile-container { + pointer-events: none; + } + +.leaflet-marker-icon.leaflet-interactive, +.leaflet-image-layer.leaflet-interactive, +.leaflet-pane > svg path.leaflet-interactive { + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline: 0; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-container a.leaflet-active { + outline: 2px solid orange; + } +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); + } + + +/* general typography */ +.leaflet-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.leaflet-bar a, +.leaflet-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-bar a:hover { + background-color: #f4f4f4; + } +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; + } +.leaflet-touch .leaflet-bar a:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px; + } +.leaflet-touch .leaflet-bar a:last-child { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + } + +/* zoom control */ + +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; + } + +.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { + font-size: 22px; + } + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + overflow-x: hidden; + padding-right: 5px; + } +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + +/* Default icon URLs */ +.leaflet-default-icon-path { + background-image: url(images/marker-icon.png); + } + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; + } +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + } +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover { + text-decoration: underline; + } +.leaflet-container .leaflet-control-attribution, +.leaflet-container .leaflet-control-scale { + font-size: 11px; + } +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; + margin-bottom: 20px; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + border-radius: 12px; + } +.leaflet-popup-content { + margin: 13px 19px; + line-height: 1.4; + } +.leaflet-popup-content p { + margin: 18px 0; + } +.leaflet-popup-tip-container { + width: 40px; + height: 20px; + position: absolute; + left: 50%; + margin-left: -20px; + overflow: hidden; + pointer-events: none; + } +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + color: #333; + box-shadow: 0 3px 14px rgba(0,0,0,0.4); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + padding: 4px 4px 0 0; + border: none; + text-align: center; + width: 18px; + height: 14px; + font: 16px/14px Tahoma, Verdana, sans-serif; + color: #c3c3c3; + text-decoration: none; + font-weight: bold; + background: transparent; + } +.leaflet-container a.leaflet-popup-close-button:hover { + color: #999; + } +.leaflet-popup-scrolled { + overflow: auto; + border-bottom: 1px solid #ddd; + border-top: 1px solid #ddd; + } + +.leaflet-oldie .leaflet-popup-content-wrapper { + zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + } +.leaflet-oldie .leaflet-popup-tip-container { + margin-top: -1px; + } + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; + } + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } + + +/* Tooltip */ +/* Base styles for the element that has a tooltip */ +.leaflet-tooltip { + position: absolute; + padding: 6px; + background-color: #fff; + border: 1px solid #fff; + border-radius: 3px; + color: #222; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + box-shadow: 0 1px 3px rgba(0,0,0,0.4); + } +.leaflet-tooltip.leaflet-clickable { + cursor: pointer; + pointer-events: auto; + } +.leaflet-tooltip-top:before, +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + position: absolute; + pointer-events: none; + border: 6px solid transparent; + background: transparent; + content: ""; + } + +/* Directions */ + +.leaflet-tooltip-bottom { + margin-top: 6px; +} +.leaflet-tooltip-top { + margin-top: -6px; +} +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-top:before { + left: 50%; + margin-left: -6px; + } +.leaflet-tooltip-top:before { + bottom: 0; + margin-bottom: -12px; + border-top-color: #fff; + } +.leaflet-tooltip-bottom:before { + top: 0; + margin-top: -12px; + margin-left: -6px; + border-bottom-color: #fff; + } +.leaflet-tooltip-left { + margin-left: -6px; +} +.leaflet-tooltip-right { + margin-left: 6px; +} +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + top: 50%; + margin-top: -6px; + } +.leaflet-tooltip-left:before { + right: 0; + margin-right: -12px; + border-left-color: #fff; + } +.leaflet-tooltip-right:before { + left: 0; + margin-left: -12px; + border-right-color: #fff; + } diff --git a/themes/bootstrap3/css/vendor/leaflet/leaflet.draw-src.css b/themes/bootstrap3/css/vendor/leaflet/leaflet.draw-src.css new file mode 100644 index 0000000000000000000000000000000000000000..d38631641eb5effa1d784bc50fe3d3b6341927ba --- /dev/null +++ b/themes/bootstrap3/css/vendor/leaflet/leaflet.draw-src.css @@ -0,0 +1,348 @@ +/* +Copyright 2012-2017 Jacob Toye and Leaflet + +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. +*/ +/* ================================================================== */ +/* Toolbars +/* ================================================================== */ + +.leaflet-draw-section { + position: relative; +} + +.leaflet-draw-toolbar { + margin-top: 12px; +} + +.leaflet-draw-toolbar-top { + margin-top: 0; +} + +.leaflet-draw-toolbar-notop a:first-child { + border-top-right-radius: 0; +} + +.leaflet-draw-toolbar-nobottom a:last-child { + border-bottom-right-radius: 0; +} + +.leaflet-draw-toolbar a { + background-image: url('images/spritesheet.png'); + background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg'); + background-repeat: no-repeat; + background-size: 300px 30px; + background-clip: padding-box; +} + +.leaflet-retina .leaflet-draw-toolbar a { + background-image: url('images/spritesheet-2x.png'); + background-image: linear-gradient(transparent, transparent), url('images/spritesheet.svg'); +} + +.leaflet-draw a { + display: block; + text-align: center; + text-decoration: none; +} + +.leaflet-draw a .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} + +/* ================================================================== */ +/* Toolbar actions menu +/* ================================================================== */ + +.leaflet-draw-actions { + display: none; + list-style: none; + margin: 0; + padding: 0; + position: absolute; + left: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */ + top: 0; + white-space: nowrap; +} + +.leaflet-touch .leaflet-draw-actions { + left: 32px; +} + +.leaflet-right .leaflet-draw-actions { + right: 26px; + left: auto; +} + +.leaflet-touch .leaflet-right .leaflet-draw-actions { + right: 32px; + left: auto; +} + +.leaflet-draw-actions li { + display: inline-block; +} + +.leaflet-draw-actions li:first-child a { + border-left: none; +} + +.leaflet-draw-actions li:last-child a { + -webkit-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.leaflet-right .leaflet-draw-actions li:last-child a { + -webkit-border-radius: 0; + border-radius: 0; +} + +.leaflet-right .leaflet-draw-actions li:first-child a { + -webkit-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.leaflet-draw-actions a { + background-color: #919187; + border-left: 1px solid #AAA; + color: #FFF; + font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif; + line-height: 28px; + text-decoration: none; + padding-left: 10px; + padding-right: 10px; + height: 28px; +} + +.leaflet-touch .leaflet-draw-actions a { + font-size: 12px; + line-height: 30px; + height: 30px; +} + +.leaflet-draw-actions-bottom { + margin-top: 0; +} + +.leaflet-draw-actions-top { + margin-top: 1px; +} + +.leaflet-draw-actions-top a, +.leaflet-draw-actions-bottom a { + height: 27px; + line-height: 27px; +} + +.leaflet-draw-actions a:hover { + background-color: #A0A098; +} + +.leaflet-draw-actions-top.leaflet-draw-actions-bottom a { + height: 26px; + line-height: 26px; +} + +/* ================================================================== */ +/* Draw toolbar +/* ================================================================== */ + +.leaflet-draw-toolbar .leaflet-draw-draw-polyline { + background-position: -2px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline { + background-position: 0 -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-draw-polygon { + background-position: -31px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon { + background-position: -29px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-draw-rectangle { + background-position: -62px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle { + background-position: -60px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-draw-circle { + background-position: -92px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle { + background-position: -90px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-draw-marker { + background-position: -122px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker { + background-position: -120px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker { + background-position: -273px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker { + background-position: -271px -1px; +} + + +/* ================================================================== */ +/* Edit toolbar +/* ================================================================== */ + +.leaflet-draw-toolbar .leaflet-draw-edit-edit { + background-position: -152px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit { + background-position: -150px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-edit-remove { + background-position: -182px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove { + background-position: -180px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled { + background-position: -212px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled { + background-position: -210px -1px; +} + +.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled { + background-position: -242px -2px; +} + +.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled { + background-position: -240px -2px; +} + +/* ================================================================== */ +/* Drawing styles +/* ================================================================== */ + +.leaflet-mouse-marker { + background-color: #fff; + cursor: crosshair; +} + +.leaflet-draw-tooltip { + background: rgb(54, 54, 54); + background: rgba(0, 0, 0, 0.5); + border: 1px solid transparent; + -webkit-border-radius: 4px; + border-radius: 4px; + color: #fff; + font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif; + margin-left: 20px; + margin-top: -21px; + padding: 4px 8px; + position: absolute; + visibility: hidden; + white-space: nowrap; + z-index: 6; +} + +.leaflet-draw-tooltip:before { + border-right: 6px solid black; + border-right-color: rgba(0, 0, 0, 0.5); + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + content: ""; + position: absolute; + top: 7px; + left: -7px; +} + +.leaflet-error-draw-tooltip { + background-color: #F2DEDE; + border: 1px solid #E6B6BD; + color: #B94A48; +} + +.leaflet-error-draw-tooltip:before { + border-right-color: #E6B6BD; +} + +.leaflet-draw-tooltip-single { + margin-top: -12px +} + +.leaflet-draw-tooltip-subtext { + color: #f8d5e4; +} + +.leaflet-draw-guide-dash { + font-size: 1%; + opacity: 0.6; + position: absolute; + width: 5px; + height: 5px; +} + +/* ================================================================== */ +/* Edit styles +/* ================================================================== */ + +.leaflet-edit-marker-selected { + background-color: rgba(254, 87, 161, 0.1); + border: 4px dashed rgba(254, 87, 161, 0.6); + -webkit-border-radius: 4px; + border-radius: 4px; + box-sizing: content-box; +} + +.leaflet-edit-move { + cursor: move; +} + +.leaflet-edit-resize { + cursor: pointer; +} + +/* ================================================================== */ +/* Old IE styles +/* ================================================================== */ + +.leaflet-oldie .leaflet-draw-toolbar { + border: 1px solid #999; +} diff --git a/themes/bootstrap3/css/vendor/leaflet/leaflet.draw.css b/themes/bootstrap3/css/vendor/leaflet/leaflet.draw.css new file mode 100644 index 0000000000000000000000000000000000000000..5e314823f30d5b7efa217dfe4f7c4a25719f268f --- /dev/null +++ b/themes/bootstrap3/css/vendor/leaflet/leaflet.draw.css @@ -0,0 +1,32 @@ +/* +Copyright 2012-2017 Jacob Toye and Leaflet + +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. +*/ +.leaflet-draw-section{position:relative}.leaflet-draw-toolbar{margin-top:12px}.leaflet-draw-toolbar-top{margin-top:0}.leaflet-draw-toolbar-notop a:first-child{border-top-right-radius:0}.leaflet-draw-toolbar-nobottom a:last-child{border-bottom-right-radius:0}.leaflet-draw-toolbar a{background-image:url('images/spritesheet.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg');background-repeat:no-repeat;background-size:300px 30px;background-clip:padding-box}.leaflet-retina .leaflet-draw-toolbar a{background-image:url('images/spritesheet-2x.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg')} +.leaflet-draw a{display:block;text-align:center;text-decoration:none}.leaflet-draw a .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.leaflet-draw-actions{display:none;list-style:none;margin:0;padding:0;position:absolute;left:26px;top:0;white-space:nowrap}.leaflet-touch .leaflet-draw-actions{left:32px}.leaflet-right .leaflet-draw-actions{right:26px;left:auto}.leaflet-touch .leaflet-right .leaflet-draw-actions{right:32px;left:auto}.leaflet-draw-actions li{display:inline-block} +.leaflet-draw-actions li:first-child a{border-left:0}.leaflet-draw-actions li:last-child a{-webkit-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.leaflet-right .leaflet-draw-actions li:last-child a{-webkit-border-radius:0;border-radius:0}.leaflet-right .leaflet-draw-actions li:first-child a{-webkit-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.leaflet-draw-actions a{background-color:#919187;border-left:1px solid #AAA;color:#FFF;font:11px/19px "Helvetica Neue",Arial,Helvetica,sans-serif;line-height:28px;text-decoration:none;padding-left:10px;padding-right:10px;height:28px} +.leaflet-touch .leaflet-draw-actions a{font-size:12px;line-height:30px;height:30px}.leaflet-draw-actions-bottom{margin-top:0}.leaflet-draw-actions-top{margin-top:1px}.leaflet-draw-actions-top a,.leaflet-draw-actions-bottom a{height:27px;line-height:27px}.leaflet-draw-actions a:hover{background-color:#a0a098}.leaflet-draw-actions-top.leaflet-draw-actions-bottom a{height:26px;line-height:26px}.leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:-2px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:0 -1px} +.leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-31px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-29px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-62px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-60px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-92px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-90px -1px} +.leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-122px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-120px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-273px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-271px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-152px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-150px -1px} +.leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-182px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-180px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-212px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-210px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-242px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-240px -2px} +.leaflet-mouse-marker{background-color:#fff;cursor:crosshair}.leaflet-draw-tooltip{background:#363636;background:rgba(0,0,0,0.5);border:1px solid transparent;-webkit-border-radius:4px;border-radius:4px;color:#fff;font:12px/18px "Helvetica Neue",Arial,Helvetica,sans-serif;margin-left:20px;margin-top:-21px;padding:4px 8px;position:absolute;visibility:hidden;white-space:nowrap;z-index:6}.leaflet-draw-tooltip:before{border-right:6px solid black;border-right-color:rgba(0,0,0,0.5);border-top:6px solid transparent;border-bottom:6px solid transparent;content:"";position:absolute;top:7px;left:-7px} +.leaflet-error-draw-tooltip{background-color:#f2dede;border:1px solid #e6b6bd;color:#b94a48}.leaflet-error-draw-tooltip:before{border-right-color:#e6b6bd}.leaflet-draw-tooltip-single{margin-top:-12px}.leaflet-draw-tooltip-subtext{color:#f8d5e4}.leaflet-draw-guide-dash{font-size:1%;opacity:.6;position:absolute;width:5px;height:5px}.leaflet-edit-marker-selected{background-color:rgba(254,87,161,0.1);border:4px dashed rgba(254,87,161,0.6);-webkit-border-radius:4px;border-radius:4px;box-sizing:content-box} +.leaflet-edit-move{cursor:move}.leaflet-edit-resize{cursor:pointer}.leaflet-oldie .leaflet-draw-toolbar{border:1px solid #999} diff --git a/themes/bootstrap3/css/vendor/ol/ol.css b/themes/bootstrap3/css/vendor/ol/ol.css deleted file mode 100644 index 383e6dc5856beb420505a5932531d8cfea443564..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/css/vendor/ol/ol.css +++ /dev/null @@ -1 +0,0 @@ -.ol-box{box-sizing:border-box;border-radius:2px;border:2px solid #00f}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width}.ol-overlay-container{will-change:left,right,top,bottom}.ol-unsupported{display:none}.ol-unselectable,.ol-viewport{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ol-selectable{-webkit-touch-callout:default;-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.ol-grabbing{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.ol-grab{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.ol-control{position:absolute;background-color:rgba(255,255,255,.4);border-radius:4px;padding:2px}.ol-control:hover{background-color:rgba(255,255,255,.6)}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}@media print{.ol-control{display:none}}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:rgba(0,60,136,.5);border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:focus,.ol-control button:hover{text-decoration:none;background-color:rgba(0,60,136,.7)}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em)}.ol-attribution ul{margin:0;padding:0 .5em;font-size:.7rem;line-height:1.375em;color:#000;text-shadow:0 0 2px #fff}.ol-attribution li{display:inline;list-style:none;line-height:inherit}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button,.ol-attribution ul{display:inline-block}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution.ol-logo-only ul{display:block}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0;height:1.1em;line-height:1em}.ol-attribution.ol-logo-only{background:0 0;bottom:.4em;height:1.1em;line-height:1em}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-logo-only button,.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:inline-block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}.ol-overviewmap .ol-overviewmap-box:hover{cursor:move} diff --git a/themes/bootstrap3/css/vendor/slick-theme.css b/themes/bootstrap3/css/vendor/slick-theme.css new file mode 100644 index 0000000000000000000000000000000000000000..1232fcab02e2d8aea7ad438373a95e7bf6086f87 --- /dev/null +++ b/themes/bootstrap3/css/vendor/slick-theme.css @@ -0,0 +1,204 @@ +@charset 'UTF-8'; +/* Slider */ +.slick-loading .slick-list +{ + background: #fff url('./ajax-loader.gif') center center no-repeat; +} + +/* Icons */ +@font-face +{ + font-family: 'slick'; + font-weight: normal; + font-style: normal; + + src: url('./fonts/slick.eot'); + src: url('./fonts/slick.eot?#iefix') format('embedded-opentype'), url('./fonts/slick.woff') format('woff'), url('./fonts/slick.ttf') format('truetype'), url('./fonts/slick.svg#slick') format('svg'); +} +/* Arrows */ +.slick-prev, +.slick-next +{ + font-size: 0; + line-height: 0; + + position: absolute; + top: 50%; + + display: block; + + width: 20px; + height: 20px; + padding: 0; + -webkit-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); + + cursor: pointer; + + color: transparent; + border: none; + outline: none; + background: transparent; +} +.slick-prev:hover, +.slick-prev:focus, +.slick-next:hover, +.slick-next:focus +{ + color: transparent; + outline: none; + background: transparent; +} +.slick-prev:hover:before, +.slick-prev:focus:before, +.slick-next:hover:before, +.slick-next:focus:before +{ + opacity: 1; +} +.slick-prev.slick-disabled:before, +.slick-next.slick-disabled:before +{ + opacity: .25; +} + +.slick-prev:before, +.slick-next:before +{ + font-family: 'slick'; + font-size: 20px; + line-height: 1; + + opacity: .75; + color: white; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.slick-prev +{ + left: -25px; +} +[dir='rtl'] .slick-prev +{ + right: -25px; + left: auto; +} +.slick-prev:before +{ + content: 'â†'; +} +[dir='rtl'] .slick-prev:before +{ + content: '→'; +} + +.slick-next +{ + right: -25px; +} +[dir='rtl'] .slick-next +{ + right: auto; + left: -25px; +} +.slick-next:before +{ + content: '→'; +} +[dir='rtl'] .slick-next:before +{ + content: 'â†'; +} + +/* Dots */ +.slick-dotted.slick-slider +{ + margin-bottom: 30px; +} + +.slick-dots +{ + position: absolute; + bottom: -25px; + + display: block; + + width: 100%; + padding: 0; + margin: 0; + + list-style: none; + + text-align: center; +} +.slick-dots li +{ + position: relative; + + display: inline-block; + + width: 20px; + height: 20px; + margin: 0 5px; + padding: 0; + + cursor: pointer; +} +.slick-dots li button +{ + font-size: 0; + line-height: 0; + + display: block; + + width: 20px; + height: 20px; + padding: 5px; + + cursor: pointer; + + color: transparent; + border: 0; + outline: none; + background: transparent; +} +.slick-dots li button:hover, +.slick-dots li button:focus +{ + outline: none; +} +.slick-dots li button:hover:before, +.slick-dots li button:focus:before +{ + opacity: 1; +} +.slick-dots li button:before +{ + font-family: 'slick'; + font-size: 6px; + line-height: 20px; + + position: absolute; + top: 0; + left: 0; + + width: 20px; + height: 20px; + + content: '•'; + text-align: center; + + opacity: .25; + color: black; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.slick-dots li.slick-active button:before +{ + opacity: .75; + color: black; +} diff --git a/themes/bootstrap3/css/vendor/slick.css b/themes/bootstrap3/css/vendor/slick.css new file mode 100644 index 0000000000000000000000000000000000000000..d8e4b119c33b89bda888147d1c2d121bccede0f0 --- /dev/null +++ b/themes/bootstrap3/css/vendor/slick.css @@ -0,0 +1,119 @@ +/* Slider */ +.slick-slider +{ + position: relative; + + display: block; + box-sizing: border-box; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + -webkit-touch-callout: none; + -khtml-user-select: none; + -ms-touch-action: pan-y; + touch-action: pan-y; + -webkit-tap-highlight-color: transparent; +} + +.slick-list +{ + position: relative; + + display: block; + overflow: hidden; + + margin: 0; + padding: 0; +} +.slick-list:focus +{ + outline: none; +} +.slick-list.dragging +{ + cursor: pointer; + cursor: hand; +} + +.slick-slider .slick-track, +.slick-slider .slick-list +{ + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} + +.slick-track +{ + position: relative; + top: 0; + left: 0; + + display: block; + margin-left: auto; + margin-right: auto; +} +.slick-track:before, +.slick-track:after +{ + display: table; + + content: ''; +} +.slick-track:after +{ + clear: both; +} +.slick-loading .slick-track +{ + visibility: hidden; +} + +.slick-slide +{ + display: none; + float: left; + + height: 100%; + min-height: 1px; +} +[dir='rtl'] .slick-slide +{ + float: right; +} +.slick-slide img +{ + display: block; +} +.slick-slide.slick-loading img +{ + display: none; +} +.slick-slide.dragging img +{ + pointer-events: none; +} +.slick-initialized .slick-slide +{ + display: block; +} +.slick-loading .slick-slide +{ + visibility: hidden; +} +.slick-vertical .slick-slide +{ + display: block; + + height: auto; + + border: 1px solid transparent; +} +.slick-arrow.slick-hidden { + display: none; +} \ No newline at end of file diff --git a/themes/bootstrap3/js/cart.js b/themes/bootstrap3/js/cart.js index bafd28296266eeff7f230395b82662d3089f8810..5f087025c8db8de2ae9078dbefbbf63a5513995e 100644 --- a/themes/bootstrap3/js/cart.js +++ b/themes/bootstrap3/js/cart.js @@ -155,14 +155,15 @@ VuFind.register('cart', function Cart() { $("#updateCart, #bottom_updateCart").unbind('click').click(function cartUpdate() { var elId = this.id; var selected = []; + var addToSelected = function processCartFormValues() { + if (-1 === selected.indexOf(this.value)) { + selected.push(this.value); + } + }; var selectedInForm = $form.find('input[name="ids[]"]:checked'); var selectedFormAttr = $('input[form="' + $form.attr('id') + '"][name="ids[]"]:checked'); - $(selectedInForm).each(function cartFormCheckboxValues() { - selected.push(this.value); - }); - $(selectedFormAttr).each(function cartAttrCheckboxValues() { - selected.push(this.value); - }); + $(selectedInForm).each(addToSelected); + $(selectedFormAttr).each(addToSelected); if (selected.length > 0) { var msg = ""; var orig = getFullItems(); @@ -204,7 +205,8 @@ VuFind.register('cart', function Cart() { var currentId = $this.data('cart-id'); var currentSource = $this.data('cart-source'); $this.find('.correct').removeClass('correct hidden'); - $this.find('.cart-add').click(function cartAddClick() { + $this.find('.cart-add').click(function cartAddClick(e) { + e.preventDefault(); if (addItem(currentId, currentSource)) { $this.find('.cart-add').addClass('hidden'); $this.find('.cart-remove').removeClass('hidden'); @@ -215,7 +217,8 @@ VuFind.register('cart', function Cart() { }, 5000); } }); - $this.find('.cart-remove').click(function cartRemoveClick() { + $this.find('.cart-remove').click(function cartRemoveClick(e) { + e.preventDefault(); removeItem(currentId, currentSource); $this.find('.cart-add').removeClass('hidden'); $this.find('.cart-remove').addClass('hidden'); diff --git a/themes/bootstrap3/js/channels.js b/themes/bootstrap3/js/channels.js index 734f2531c2645f8150181693531b3279972a1c28..c67f839d4b6fbd482b2ac1f47b8219347cdf26a2 100644 --- a/themes/bootstrap3/js/channels.js +++ b/themes/bootstrap3/js/channels.js @@ -1,47 +1,96 @@ -/*global ChannelSlider, getUrlRoot, htmlEncode, VuFind */ -/*exported channelAddLinkButtons */ - -function channelAddLinkButtons(elem) { - var links = JSON.parse(elem.dataset.linkJson); - var $cont = $('<div class="channel-links pull-left"></div>'); - for (var i = 0; i < links.length; i++) { - $cont.append( - $('<a/> ', { - 'href': links[i].url, - 'class': links[i].label + " btn btn-default", - 'html': '<i class="fa ' + links[i].icon + '"></i> ' + VuFind.translate(links[i].label) - }) +/*global getUrlRoot, VuFind */ +VuFind.register('channels', function Channels() { + function addLinkButtons(elem) { + var links = JSON.parse(elem.dataset.linkJson); + if (links.length === 0) { + return; + } + var $cont = $( + '<div class="dropdown">' + + ' <button class="btn btn-link" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">' + + ' <i class="fa fa-caret-square-o-down"></i>' + + ' </button>' + + '</div>' ); + var $list = $('<ul class="dropdown-menu"></ul>'); + for (var i = 0; i < links.length; i++) { + var li = $('<li/>'); + li.append( + $('<a/> ', { + 'href': links[i].url, + 'class': links[i].label, + 'html': '<i class="fa ' + links[i].icon + '"></i> ' + VuFind.translate(links[i].label) + }) + ); + $list.append(li); + } + $cont.append($list); + $(elem).siblings('.channel-title').append($cont); } - $('#' + elem.id + ' .slider-menu').append($cont); -} -function setupChannelSlider(i, op) { - if (ChannelSlider(op)) { - $(op).find('.thumb').each(function thumbnailBackgrounds(index, thumb) { - var img = $(thumb).find('img'); - $(thumb).css('background-image', 'url(' + img.attr('src') + ')'); - img.remove(); - }); - // truncate long titles and add hover - $(op).find('.channel-record').dotdotdot({ - callback: function dddcallback(istrunc, orig) { - if (istrunc) { - $(this).attr('title', $(orig).text()); + var currentPopover = false; + function isCurrentPopoverRecord(record) { + return record && currentPopover + && record.data('record-id') === currentPopover.data('record-id'); + } + function switchPopover(record) { + // Hide the old popover: + if (currentPopover) { + currentPopover.popover('hide'); + } + // Special case: if the new popover is the same as the old one, reset the + // current popover status so that the next click will open it again (toggle) + if (isCurrentPopoverRecord(record)) { + currentPopover = false; + } else { + // Default case: set the currentPopover to the new incoming value: + currentPopover = record; + } + // currentPopover has now been updated; show it if appropriate: + if (currentPopover) { + currentPopover.popover('show'); + } + } + function redrawPopover(record, html) { + // Only update the popover if the context hasn't changed since the + // AJAX call was triggered. + if (isCurrentPopoverRecord(record)) { + record.data('bs.popover').tip().find('.popover-content').html(html); + } + record.data('bs.popover').options.content = html; + } + function setupChannelSlider(i, op) { + $(op).find(".slide").removeClass("hidden"); + $(op).slick({ + slidesToShow: 6, + slidesToScroll: 6, + infinite: false, + rtl: $(document.body).hasClass("rtl"), + responsive: [ + { + breakpoint: 768, + settings: { + slidesToShow: 3, + slidesToScroll: 3 + } + }, + { + breakpoint: 480, + settings: { + slidesToShow: 1, + slidesToScroll: 1 + } } - } + ] + }); + $(op).on('swipe', function channelDrag() { + switchPopover(false); }); - $('.channel-record').unbind('click').click(function channelRecord(event) { + // truncate long titles and add hover + $(op).find('.channel-record').dotdotdot(); + $(op).find('.channel-record').unbind('click').click(function channelRecord(event) { var record = $(event.delegateTarget); - if (record.data('popover')) { - if (record.attr('aria-describedby')) { - record.popover('hide'); - } else { - $('[aria-describedby]').popover('hide'); - record.popover('show'); - } - } else { - record.data('popover', true); + if (!record.data("popover-loaded")) { record.popover({ content: VuFind.translate('loading') + '...', html: true, @@ -49,16 +98,13 @@ function setupChannelSlider(i, op) { trigger: 'focus', container: '#' + record.closest('.channel').attr('id') }); - $('[aria-describedby]').popover('hide'); - record.popover('show'); $.ajax({ url: VuFind.path + getUrlRoot(record.attr('href')) + '/AjaxTab', type: 'POST', data: {tab: 'description'} }) - .done(function channelPopoverDone(data) { - record.data('bs.popover').options.content = '<h2>' + htmlEncode(record.text()) + '</h2>' - + '<div class="btn-group btn-group-justified">' + .done(function channelPopoverDone(data) { + var newContent = '<div class="btn-group btn-group-justified">' + '<a href="' + VuFind.path + '/Channels/Record?' + 'id=' + encodeURIComponent(record.attr('data-record-id')) + '&source=' + encodeURIComponent(record.attr('data-record-source')) @@ -66,60 +112,74 @@ function setupChannelSlider(i, op) { + '<a href="' + record.attr('href') + '" class="btn btn-default">' + VuFind.translate('View Record') + '</a>' + '</div>' + data; - record.popover('show'); - }); + redrawPopover(record, newContent); + record.data("popover-loaded", true); + }); } + switchPopover(record); return false; }); // Channel add buttons - channelAddLinkButtons(op); + addLinkButtons(op); $('.channel-add-menu[data-group="' + op.dataset.group + '"].hidden') .clone() - .addClass('pull-right') .removeClass('hidden') - .appendTo($(op).find('.slider-menu')); + .prependTo($(op).parent(".channel-wrapper")); } -} -function bindChannelAddMenu(iteration, scope) { - $(scope).find('.channel-add-menu .dropdown-menu a').click(function selectAddedChannel(e) { + function selectAddedChannel(e) { $.ajax(e.target.href).done(function addChannelAjaxDone(data) { var list = $(e.target).closest('.dropdown-menu'); - var $testEl = $(data); - // Make sure the channel has content - if ($testEl.find('.channel-record').length === 0) { - $(e.target).closest('.channel').after( - '<div class="channel-title no-results">' - + '<h2>' + $testEl.find('h2').html() + '</h2>' - + VuFind.translate('nohit_heading') - + '</div>' - ); - } else { - $(e.target).closest('.channel').after(data); - $('.channel').each(setupChannelSlider); - $('.channel').each(bindChannelAddMenu); - } + var $testEls = $('<div>' + data + '</div>').find('.channel-wrapper'); + var $dest = $(e.target).closest('.channel-wrapper'); // Remove dropdown link $('[data-token="' + e.target.dataset.token + '"]').parent().remove(); + // Insert new channels + $testEls.each(function addRetrievedNonEmptyChannels(i, element) { + var $testEl = $(element); + // Make sure the channel has content + if ($testEl.find('.channel-record').length === 0) { + $dest.after( + '<div class="channel-wrapper">' + + '<div class="channel-title no-results">' + + '<h2>' + $testEl.find('h2').html() + '</h2>' + + VuFind.translate('nohit_heading') + + '</div></div>' + ); + } else { + $dest.after($testEl); + $testEl.find('.channel').each(setupChannelSlider); + $testEl.find('.channel').each(bindChannelAddMenu); + } - if (list.children().length === 0) { - $('.channel-add-menu[data-group="' + list.closest('.channel-add-menu').data('group') + '"]').remove(); - } + if (list.children().length === 0) { + $('.channel-add-menu[data-group="' + list.closest('.channel-add-menu').data('group') + '"]').remove(); + } + }); }); return false; - }); - $(scope).find('.channel-add-menu .add-btn').click(function addChannels(e) { - var links = $(e.target).closest('.channel-add-menu').find('.dropdown-menu a'); - for (var i = 0; i < links.length && i < 2; i++) { - links[i].click(); - } - }); -} + } + + function bindChannelAddMenu(iteration, channel) { + var scope = $(channel).parent(".channel-wrapper"); + $(scope).find('.channel-add-menu .dropdown-menu a').click(selectAddedChannel); + $(scope).find('.channel-add-menu .add-btn').click(function addChannels(e) { + var links = $(e.target).closest('.channel-add-menu').find('.dropdown-menu a'); + for (var i = 0; i < links.length && i < 2; i++) { + links[i].click(); + } + }); + } + + function init () { + $('.channel').each(setupChannelSlider); + $('.channel').each(bindChannelAddMenu); + $(document).on("hidden.bs.popover", function deselectPopover(e) { + if (isCurrentPopoverRecord($(e.target))) { + switchPopover(false); + } + }); + } -$(document).ready(function channelReady() { - $('.channel').each(setupChannelSlider); - $('.channel').each(bindChannelAddMenu); - $('.channel').on('dragStart', function channelDrag() { - $('[aria-describedby]').popover('hide'); - }); + return { init: init }; }); diff --git a/themes/bootstrap3/js/check_item_statuses.js b/themes/bootstrap3/js/check_item_statuses.js index b88aa231889056c71e3658ed08bcecace6ca2c78..6942ce5137ebd7b046457a1721147a9c5cc7a18f 100644 --- a/themes/bootstrap3/js/check_item_statuses.js +++ b/themes/bootstrap3/js/check_item_statuses.js @@ -15,7 +15,17 @@ function displayItemStatus(result, $item) { $item.removeClass('js-item-pending'); $item.find('.status').empty().append(result.availability_message); $item.find('.ajax-availability').removeClass('ajax-availability hidden'); - if (typeof(result.full_status) != 'undefined' + if (typeof(result.error) != 'undefined' + && result.error.length > 0 + ) { + // Only show error message if we also have a status indicator active: + if ($item.find('.status').length > 0) { + $item.find('.callnumAndLocation').empty().addClass('text-danger').append(result.error); + } else { + $item.find('.callnumAndLocation').addClass('hidden'); + } + $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); + } else if (typeof(result.full_status) != 'undefined' && result.full_status.length > 0 && $item.find('.callnumAndLocation').length > 0 ) { @@ -50,7 +60,7 @@ function displayItemStatus(result, $item) { locationListHTML += '</div>'; locationListHTML += '<div class="groupCallnumber">'; locationListHTML += (result.locationList[x].callnumbers) - ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; + ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; locationListHTML += '</div>'; } $item.find('.locationDetails').removeClass('hidden'); @@ -70,7 +80,8 @@ function itemStatusFail(response, textStatus) { return; } // display the error message on each of the ajax status place holder - $('.js-item-pending').addClass('text-danger').append(response.responseJSON.data); + $('.js-item-pending .callnumAndLocation').addClass('text-danger').empty().removeClass('hidden') + .append(typeof response.responseJSON.data === 'string' ? response.responseJSON.data : VuFind.translate('error_occurred')); } var itemStatusIds = []; @@ -92,17 +103,18 @@ function runItemAjaxForQueue() { url: VuFind.path + '/AJAX/JSON?method=getItemStatuses', data: { 'id': itemStatusIds } }) - .done(function checkItemStatusDone(response) { - for (var j = 0; j < response.data.length; j++) { - displayItemStatus(response.data[j], itemStatusEls[response.data[j].id]); - itemStatusIds.splice(itemStatusIds.indexOf(response.data[j].id), 1); - } - itemStatusRunning = false; - }) - .fail(function checkItemStatusFail(response, textStatus) { - itemStatusFail(response, textStatus); - itemStatusRunning = false; - }); + .done(function checkItemStatusDone(response) { + for (var j = 0; j < response.data.statuses.length; j++) { + var status = response.data.statuses[j]; + displayItemStatus(status, itemStatusEls[status.id]); + itemStatusIds.splice(itemStatusIds.indexOf(status.id), 1); + } + itemStatusRunning = false; + }) + .fail(function checkItemStatusFail(response, textStatus) { + itemStatusFail(response, textStatus); + itemStatusRunning = false; + }); } function itemQueueAjax(id, el) { @@ -114,6 +126,8 @@ function itemQueueAjax(id, el) { itemStatusEls[id] = el; itemStatusTimer = setTimeout(runItemAjaxForQueue, itemStatusDelay); el.addClass('js-item-pending').removeClass('hidden'); + el.find('.callnumAndLocation').removeClass('hidden'); + el.find('.callnumAndLocation .ajax-availability').removeClass('hidden'); el.find('.status').removeClass('hidden'); } @@ -126,10 +140,11 @@ function checkItemStatus(el) { itemQueueAjax(id + '', $item); } +var itemStatusObserver = null; function checkItemStatuses(_container) { - var container = _container instanceof Element - ? _container - : document.body; + var container = typeof _container === 'undefined' + ? document.body + : _container; var ajaxItems = $(container).find('.ajaxItem'); for (var i = 0; i < ajaxItems.length; i++) { @@ -141,7 +156,6 @@ function checkItemStatuses(_container) { itemStatusObserver.disconnect(); } } -var itemStatusObserver = null; $(document).ready(function checkItemStatusReady() { if (typeof Hunt === 'undefined') { checkItemStatuses(); diff --git a/themes/bootstrap3/js/check_save_statuses.js b/themes/bootstrap3/js/check_save_statuses.js index 7e41a81748edc1f9e34dc9e2bb07b7d9683aaa33..07ff265e6ed05921596e364075b7337c8011877c 100644 --- a/themes/bootstrap3/js/check_save_statuses.js +++ b/themes/bootstrap3/js/check_save_statuses.js @@ -54,24 +54,25 @@ function runSaveAjaxForQueue() { 'source': sources } }) - .done(function checkSaveStatusDone(response) { - for (var id in response.data) { - if (response.data.hasOwnProperty(id)) { - displaySaveStatus(response.data[id], saveStatusEls[id]); - } - // Remove populated ids from the queue - for (var j = 0; j < saveStatusObjs; j++) { - if (saveStatusObjs[j].id === id) { - saveStatusObjs.splice(j, 1); + .done(function checkSaveStatusDone(response) { + for (var id in response.data.statuses) { + if (Object.prototype.hasOwnProperty.call(response.data.statuses, id)) { + displaySaveStatus(response.data.statuses[id], saveStatusEls[id]); + + // Remove populated ids from the queue + for (var j = 0; j < saveStatusObjs; j++) { + if (saveStatusObjs[j].id === id) { + saveStatusObjs.splice(j, 1); + } + } } } - } - saveStatusRunning = false; - }) - .fail(function checkItemStatusFail(response, textStatus) { - saveStatusFail(response, textStatus); - saveStatusRunning = false; - }); + saveStatusRunning = false; + }) + .fail(function checkItemStatusFail(response, textStatus) { + saveStatusFail(response, textStatus); + saveStatusRunning = false; + }); } function saveQueueAjax(obj, el) { if (el.hasClass('js-save-pending')) { @@ -105,6 +106,7 @@ function checkSaveStatus(el) { }, $item); } +var saveStatusObserver = null; function checkSaveStatuses(_container) { if (!userIsLoggedIn) { return; @@ -134,7 +136,6 @@ function checkSaveStatusesCallback() { checkSaveStatuses(); } -var saveStatusObserver = null; $(document).ready(function checkSaveStatusFail() { if (typeof Hunt === 'undefined') { checkSaveStatuses(); diff --git a/themes/bootstrap3/js/common.js b/themes/bootstrap3/js/common.js index f939ca89802e219fc4c87503370521438226cca3..707461a01badbdab7d04ce5b915a95acf5460ed3 100644 --- a/themes/bootstrap3/js/common.js +++ b/themes/bootstrap3/js/common.js @@ -1,8 +1,8 @@ -/*global grecaptcha, isPhoneNumberValid */ +/*global Event, grecaptcha, isPhoneNumberValid */ /*exported VuFind, htmlEncode, deparam, moreFacets, lessFacets, getUrlRoot, phoneNumberFormHandler, recaptchaOnLoad, resetCaptcha, bulkFormHandler */ // IE 9< console polyfill -window.console = window.console || {log: function polyfillLog() {}}; +window.console = window.console || { log: function polyfillLog() {} }; var VuFind = (function VuFind() { var defaultSearchBackend = null; @@ -11,6 +11,22 @@ var VuFind = (function VuFind() { var _submodules = []; var _translations = {}; + // Emit a custom event + // Recommendation: prefix with vf- + var emit = function emit(name, detail) { + if (typeof detail === 'undefined') { + document.dispatchEvent(new Event(name)); + } else { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent(name, true, true, detail); // name, canBubble, cancelable, detail + document.dispatchEvent(event); + } + }; + // Listen shortcut to put everyone on the same element + var listen = function listen(name, func) { + document.addEventListener(name, func, false); + }; + var register = function register(name, module) { if (_submodules.indexOf(name) === -1) { _submodules.push(name); @@ -73,6 +89,8 @@ var VuFind = (function VuFind() { addTranslations: addTranslations, init: init, + emit: emit, + listen: listen, refreshPage: refreshPage, register: register, translate: translate @@ -246,10 +264,8 @@ function setupAutocomplete() { if (searchbox.length < 1) { return; } - var cacheObj = {}; // Search autocomplete searchbox.autocomplete({ - cacheObj: cacheObj, rtl: $(document.body).hasClass("rtl"), maxResults: 10, loadingString: VuFind.translate('loading') + '...', @@ -271,10 +287,10 @@ function setupAutocomplete() { }, dataType: 'json', success: function autocompleteJSON(json) { - if (json.data.length > 0) { + if (json.data.suggestions.length > 0) { var datums = []; - for (var j = 0; j < json.data.length; j++) { - datums.push(json.data[j]); + for (var j = 0; j < json.data.suggestions.length; j++) { + datums.push(json.data.suggestions[j]); } cb(datums); } else { @@ -286,15 +302,7 @@ function setupAutocomplete() { }); // Update autocomplete on type change $('#searchForm_type').change(function searchTypeChange() { - for (var i in cacheObj) { - if (cacheObj.hasOwnProperty(i)) { - for (var j in cacheObj[i]) { - if (cacheObj[i].hasOwnProperty(j)) { - delete cacheObj[i][j]; - } - } - } - } + searchbox.autocomplete().clearCache(); }); } diff --git a/themes/bootstrap3/js/embedded_record.js b/themes/bootstrap3/js/embedded_record.js index 596729e83ba87219cd209593e7b955a6857705e5..6d39891392f0de0f7f1439c80097e8a81dafea0a 100644 --- a/themes/bootstrap3/js/embedded_record.js +++ b/themes/bootstrap3/js/embedded_record.js @@ -136,44 +136,42 @@ VuFind.register('embedded', function embedded() { source: result.find('.hiddenSource')[0].value }), success: function getRecordDetailsSuccess(response) { - if (response.status === 'OK') { - // Insert tabs html - longNode.html(response.data); - // Hide loading - loadingNode.addClass('hidden'); - longNode.collapse('show'); - // Load first tab - if (tabid) { - ajaxLoadTab(tabid, true); - } else { - var $firstTab = $(longNode).find('.list-tab-toggle.active'); - if ($firstTab.length === 0) { - $firstTab = $(longNode).find('.list-tab-toggle:eq(0)'); - } - ajaxLoadTab($firstTab.attr('id'), true); + // Insert tabs html + longNode.html(response.data.html); + // Hide loading + loadingNode.addClass('hidden'); + longNode.collapse('show'); + // Load first tab + if (tabid) { + ajaxLoadTab(tabid, true); + } else { + var $firstTab = $(longNode).find('.list-tab-toggle.active'); + if ($firstTab.length === 0) { + $firstTab = $(longNode).find('.list-tab-toggle:eq(0)'); } - // Bind tab clicks - longNode.find('.list-tab-toggle').click(function embeddedTabLoad() { - if (!$(this).parent().hasClass('noajax')) { - addToStorage(divID, this.id); - } - return ajaxLoadTab(this.id); - }); - longNode.find('[id^=usercomment]').find('input[type=submit]').unbind('click').click( - function embeddedComments() { - return registerAjaxCommentRecord( - longNode.find('[id^=usercomment]').find('input[type=submit]').closest('form') - ); - } - ); - longNode.find('[data-background]').each(function setupEmbeddedBackgroundTabs(index, el) { - ajaxLoadTab(el.id, false); - }); - // Add events to record toolbar - VuFind.lightbox.bind(longNode); - if (typeof checkSaveStatuses == 'function') { - checkSaveStatuses(longNode); + ajaxLoadTab($firstTab.attr('id'), true); + } + // Bind tab clicks + longNode.find('.list-tab-toggle').click(function embeddedTabLoad() { + if (!$(this).parent().hasClass('noajax')) { + addToStorage(divID, this.id); + } + return ajaxLoadTab(this.id); + }); + longNode.find('[id^=usercomment]').find('input[type=submit]').unbind('click').click( + function embeddedComments() { + return registerAjaxCommentRecord( + longNode.find('[id^=usercomment]').find('input[type=submit]').closest('form') + ); } + ); + longNode.find('[data-background]').each(function setupEmbeddedBackgroundTabs(index, el) { + ajaxLoadTab(el.id, false); + }); + // Add events to record toolbar + VuFind.lightbox.bind(longNode); + if (typeof checkSaveStatuses == 'function') { + checkSaveStatuses(longNode); } } }); diff --git a/themes/bootstrap3/js/facets.js b/themes/bootstrap3/js/facets.js index b8c43dc8f1f8584502424b308d8faa78ae085ada..b3a48e0b007aba5dbe4b863b4b232f43c734b9f8 100644 --- a/themes/bootstrap3/js/facets.js +++ b/themes/bootstrap3/js/facets.js @@ -1,51 +1,71 @@ -/*global htmlEncode, VuFind */ -/*exported initFacetTree */ +/*global VuFind */ +/*exported collapseTopFacets, initFacetTree */ function buildFacetNodes(data, currentPath, allowExclude, excludeTitle, counts) { var json = []; $(data).each(function facetNodesEach() { - var html = ''; - if (!this.isApplied && counts) { - html = '<span class="badge" style="float: right">' + this.count.toString().replace(/\B(?=(\d{3})+\b)/g, VuFind.translate('number_thousands_separator')); - if (allowExclude) { - var excludeURL = currentPath + this.exclude; - excludeURL.replace("'", "\\'"); - // Just to be safe - html += ' <a href="' + excludeURL + '" onclick="document.location.href=\'' + excludeURL + '\'; return false;" title="' + htmlEncode(excludeTitle) + '"><i class="fa fa-times"></i></a>'; - } - html += '</span>'; - } - + var $html = $('<div/>').addClass('facet'); var url = currentPath + this.href; - // Just to be safe - url.replace("'", "\\'"); - html += '<span class="main' + (this.isApplied ? ' applied' : '') + '" title="' + htmlEncode(this.displayText) + '"' - + ' onclick="document.location.href=\'' + url + '\'; return false;">'; + var $item = $('<span/>') + .addClass('main text' + (this.isApplied ? ' applied' : '')) + .attr('role', 'menuitem') + .attr('title', this.displayText); + + var $i = $('<i/>').addClass('fa'); if (this.operator === 'OR') { if (this.isApplied) { - html += '<i class="fa fa-check-square-o" title="' + VuFind.translate('Selected') + '"></i>'; + $i.addClass('fa-check-square-o').attr('title', VuFind.translate('Selected')); } else { - html += '<i class="fa fa-square-o" aria-hidden="true"></i>'; + $i.addClass('fa-square-o').attr('aria-hidden', 'true'); } + $i.appendTo($item); + $item.append(' '); } else if (this.isApplied) { - html += '<i class="fa fa-check pull-right" title="' + VuFind.translate('Selected') + '"></i>'; + $i.addClass('fa-check pull-right').attr('title', VuFind.translate('Selected')); + $i.appendTo($item); + $item.append(' '); + } + + $item.append(this.displayText); + $item.appendTo($html); + + if (!this.isApplied && counts) { + $('<span/>') + .addClass('badge') + .text( + this.count.toString().replace(/\B(?=(\d{3})+\b)/g, VuFind.translate('number_thousands_separator')) + ) + .appendTo($html); + + if (allowExclude) { + var excludeUrl = currentPath + this.exclude; + var $a = $('<a/>') + .addClass('exclude') + .attr('href', excludeUrl) + .attr('title', excludeTitle); + $('<i/>').addClass('fa fa-times').appendTo($a); + $a.appendTo($html); + } } - html += ' ' + this.displayText; - html += '</span>'; + + $html = $('<div/>').append($html); var children = null; if (typeof this.children !== 'undefined' && this.children.length > 0) { children = buildFacetNodes(this.children, currentPath, allowExclude, excludeTitle, counts); } json.push({ - 'text': html, + 'text': $html.html(), 'children': children, 'applied': this.isApplied, 'state': { 'opened': this.hasAppliedChildren }, - 'li_attr': this.isApplied ? { 'class': 'active' } : {} + 'li_attr': this.isApplied ? { 'class': 'active' } : {}, + 'data': { + 'url': url.replace(/&/g, '&') + } }); }); @@ -60,6 +80,15 @@ function initFacetTree(treeNode, inSidebar) } treeNode.data('loaded', true); + // Enable keyboard navigation also when a screen reader is active + treeNode.bind('select_node.jstree', function selectNode(event, data) { + $(this).closest('.collapse').html('<div class="facet">' + VuFind.translate('loading') + '...</div>'); + window.location = data.node.data.url; + event.preventDefault(); + return false; + }); + + var source = treeNode.data('source'); var facet = treeNode.data('facet'); var operator = treeNode.data('operator'); var currentPath = treeNode.data('path'); @@ -76,29 +105,48 @@ function initFacetTree(treeNode, inSidebar) $.getJSON(VuFind.path + '/AJAX/JSON?' + query, { method: "getFacetData", + source: source, facetName: facet, facetSort: sort, facetOperator: operator }, function getFacetData(response/*, textStatus*/) { - if (response.status === "OK") { - var results = buildFacetNodes(response.data, currentPath, allowExclude, excludeTitle, inSidebar); - treeNode.find('.fa-spinner').parent().remove(); - if (inSidebar) { - treeNode.on('loaded.jstree open_node.jstree', function treeNodeOpen(/*e, data*/) { - treeNode.find('ul.jstree-container-ul > li.jstree-node').addClass('list-group-item'); + var results = buildFacetNodes(response.data.facets, currentPath, allowExclude, excludeTitle, inSidebar); + treeNode.find('.fa-spinner').parent().remove(); + if (inSidebar) { + treeNode.on('loaded.jstree open_node.jstree', function treeNodeOpen(/*e, data*/) { + treeNode.find('ul.jstree-container-ul > li.jstree-node').addClass('list-group-item'); + treeNode.find('a.exclude').click(function excludeLinkClick(e) { + $(this).closest('.collapse').html('<div class="facet">' + VuFind.translate('loading') + '...</div>'); + window.location = this.href; + e.preventDefault(); + return false; }); - } - treeNode.jstree({ - 'core': { - 'data': results - } }); } + treeNode.jstree({ + 'core': { + 'data': results + } + }); } ); } +function collapseTopFacets() { + $('.top-facets').each(function setupToCollapses() { + $(this).find('.collapse').removeClass('in'); + $(this).on('show.bs.collapse', function toggleTopFacet() { + $(this).find('.top-title .fa').removeClass('fa-caret-right'); + $(this).find('.top-title .fa').addClass('fa-caret-down'); + }); + $(this).on('hide.bs.collapse', function toggleTopFacet() { + $(this).find('.top-title .fa').removeClass('fa-caret-down'); + $(this).find('.top-title .fa').addClass('fa-caret-right'); + }); + }); +} + /* --- Lightbox Facets --- */ VuFind.register('lightbox_facets', function LightboxFacets() { function lightboxFacetSorting() { diff --git a/themes/bootstrap3/js/hierarchyTree.js b/themes/bootstrap3/js/hierarchyTree.js index 6518eb69f7f8c7c5692d5b89f7007e0e16083a29..e49222f232cc90db20a5a9ebbc834d1045401263 100644 --- a/themes/bootstrap3/js/hierarchyTree.js +++ b/themes/bootstrap3/js/hierarchyTree.js @@ -30,15 +30,15 @@ function getRecord(id) { url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id}), dataType: 'html' }) - .done(function getRecordDone(response) { - $('#tree-preview').html(html_entity_decode(response)); - // Remove the old path highlighting - $('#hierarchyTree a').removeClass("jstree-highlight"); - // Add Current path highlighting - var jsTreeNode = $(":input[value='" + id + "']").parent(); - jsTreeNode.children("a").addClass("jstree-highlight"); - jsTreeNode.parents("li").children("a").addClass("jstree-highlight"); - }); + .done(function getRecordDone(response) { + $('#tree-preview').html(html_entity_decode(response)); + // Remove the old path highlighting + $('#hierarchyTree a').removeClass("jstree-highlight"); + // Add Current path highlighting + var jsTreeNode = $(":input[value='" + id + "']").parent(); + jsTreeNode.children("a").addClass("jstree-highlight"); + jsTreeNode.parents("li").children("a").addClass("jstree-highlight"); + }); } function changeNoResultLabel(display) { @@ -78,26 +78,26 @@ function doTreeSearch() { type: $("#treeSearchType").val() }) + "&format=true" }) - .done(function searchTreeAjaxDone(data) { - if (data.results.length > 0) { - $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search'); - var jstree = $('#hierarchyTree').jstree(true); - jstree.close_all(); - for (var i = data.results.length; i--;) { - var id = htmlEncodeId(data.results[i]); - jstree._open_to(id); - } - for (var j = data.results.length; j--;) { - var tid = htmlEncodeId(data.results[j]); - $('#hierarchyTree').find('#' + tid).addClass('jstree-search'); + .done(function searchTreeAjaxDone(data) { + if (data.results.length > 0) { + $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search'); + var jstree = $('#hierarchyTree').jstree(true); + jstree.close_all(); + for (var i = data.results.length; i--;) { + var id = htmlEncodeId(data.results[i]); + jstree._open_to(id); + } + for (var j = data.results.length; j--;) { + var tid = htmlEncodeId(data.results[j]); + $('#hierarchyTree').find('#' + tid).addClass('jstree-search'); + } + changeNoResultLabel(false); + changeLimitReachedLabel(data.limitReached); + } else { + changeNoResultLabel(true); } - changeNoResultLabel(false); - changeLimitReachedLabel(data.limitReached); - } else { - changeNoResultLabel(true); - } - $('#treeSearchLoadingImg').addClass('hidden'); - }); + $('#treeSearchLoadingImg').addClass('hidden'); + }); } } @@ -150,10 +150,10 @@ function buildTreeWithXml(cb) { mode: 'Tree' } }) - .done(function getTreeDone(xml) { - var nodes = buildJSONNodes($(xml).find('root')); - cb.call(this, nodes); - }); + .done(function getTreeDone(xml) { + var nodes = buildJSONNodes($(xml).find('root')); + cb.call(this, nodes); + }); } $(document).ready(function hierarchyTreeReady() { diff --git a/themes/bootstrap3/js/hold.js b/themes/bootstrap3/js/hold.js index 1359a489187f4c302e26a63cdbd9ce8a52404e26..4879cc8752222ac13162af1d8c5335b87c69eb6a 100644 --- a/themes/bootstrap3/js/hold.js +++ b/themes/bootstrap3/js/hold.js @@ -20,24 +20,24 @@ function setUpHoldRequestForm(recordId) { cache: false, url: VuFind.path + '/AJAX/JSON' }) - .done(function holdPickupLocationsDone(response) { - var defaultValue = $('#pickUpLocation').data('default'); - $.each(response.data.locations, function holdPickupLocationEach() { - var option = $('<option></option>').attr('value', this.locationID).text(this.locationDisplay); - // Make sure to compare locationID and defaultValue as Strings since locationID may be an integer - if (String(this.locationID) === String(defaultValue) || (defaultValue === '' && this.isDefault && $emptyOption.length === 0)) { - option.attr('selected', 'selected'); - } - $('#pickUpLocation').append(option); - }); + .done(function holdPickupLocationsDone(response) { + var defaultValue = $('#pickUpLocation').data('default'); + $.each(response.data.locations, function holdPickupLocationEach() { + var option = $('<option></option>').attr('value', this.locationID).text(this.locationDisplay); + // Make sure to compare locationID and defaultValue as Strings since locationID may be an integer + if (String(this.locationID) === String(defaultValue) || (defaultValue === '' && this.isDefault && $emptyOption.length === 0)) { + option.attr('selected', 'selected'); + } + $('#pickUpLocation').append(option); + }); - $('#pickUpLocationLabel i').removeClass("fa fa-spinner icon-spin"); - $('#pickUpLocation').removeAttr('disabled'); - }) - .fail(function holdPickupLocationsFail(/*response*/) { - $('#pickUpLocationLabel i').removeClass("fa fa-spinner icon-spin"); - $('#pickUpLocation').removeAttr('disabled'); - }); + $('#pickUpLocationLabel i').removeClass("fa fa-spinner icon-spin"); + $('#pickUpLocation').removeAttr('disabled'); + }) + .fail(function holdPickupLocationsFail(/*response*/) { + $('#pickUpLocationLabel i').removeClass("fa fa-spinner icon-spin"); + $('#pickUpLocation').removeAttr('disabled'); + }); }); $('#requestGroupId').change(); } diff --git a/themes/bootstrap3/js/ill.js b/themes/bootstrap3/js/ill.js index bafc8f371f88152f5d74fbc991b223e72015ec27..f0f7087aa0d70f3e7c9f4587a899c17a9fa37db5 100644 --- a/themes/bootstrap3/js/ill.js +++ b/themes/bootstrap3/js/ill.js @@ -14,19 +14,19 @@ function setUpILLRequestForm(recordId) { cache: false, url: url }) - .done(function illPickupLocationsDone(response) { - $.each(response.data.locations, function illPickupLocationEach() { - var option = $("<option></option>").attr("value", this.id).text(this.name); - if (this.isDefault) { - option.attr("selected", "selected"); - } - $("#ILLRequestForm #pickupLibraryLocation").append(option); + .done(function illPickupLocationsDone(response) { + $.each(response.data.locations, function illPickupLocationEach() { + var option = $("<option></option>").attr("value", this.id).text(this.name); + if (this.isDefault) { + option.attr("selected", "selected"); + } + $("#ILLRequestForm #pickupLibraryLocation").append(option); + }); + $("#ILLRequestForm #pickupLibraryLocationLabel i").removeClass("fa fa-spinner icon-spin"); + }) + .fail(function illPickupLocationsFail(/*response*/) { + $("#ILLRequestForm #pickupLibraryLocationLabel i").removeClass("fa fa-spinner icon-spin"); }); - $("#ILLRequestForm #pickupLibraryLocationLabel i").removeClass("fa fa-spinner icon-spin"); - }) - .fail(function illPickupLocationsFail(/*response*/) { - $("#ILLRequestForm #pickupLibraryLocationLabel i").removeClass("fa fa-spinner icon-spin"); - }); }); $("#ILLRequestForm #pickupLibrary").change(); } diff --git a/themes/bootstrap3/js/lib/accessible-carousels.js b/themes/bootstrap3/js/lib/accessible-carousels.js new file mode 100644 index 0000000000000000000000000000000000000000..a8c9307f38e740db95fb219cc018f26d2c02b970 --- /dev/null +++ b/themes/bootstrap3/js/lib/accessible-carousels.js @@ -0,0 +1,388 @@ +// Carousel Extension +// =============================== + +/** Adapted: + * - from https://github.com/paypal/bootstrap-accessibility-plugin/blob/master/src/js/carousel.js + * - for Slick: http://kenwheeler.github.io/slick/ + * - for VuFind: https://github.com/vufind-org/vufind + * - by Chris Hallberg + */ + +$(".carousel").each(function(index) { + // This function positions a highlight box around the tabs in the tablist to use in focus styling + + function setTablistHighlightBox() { + var $tab, + offset, + height, + width, + highlightBox = {}; + + highlightBox.top = 0; + highlightBox.left = 32000; + highlightBox.height = 0; + highlightBox.width = 0; + + for (var i = 0; i < $tabs.length; i++) { + $tab = $tabs[i]; + offset = $($tab).offset(); + height = $($tab).height(); + width = $($tab).width(); + + // console.log(" Top: " + offset.top + " Left: " + offset.left + " Height: " + height + " Width: " + width) + + if (highlightBox.top < offset.top) { + highlightBox.top = Math.round(offset.top); + } + + if (highlightBox.height < height) { + highlightBox.height = Math.round(height); + } + + if (highlightBox.left > offset.left) { + highlightBox.left = Math.round(offset.left); + } + + var w = offset.left - highlightBox.left + Math.round(width); + + if (highlightBox.width < w) { + highlightBox.width = w; + } + } // end for + + // console.log("[HIGHLIGHT] Top: " + highlightBox.top + " Left: " + highlightBox.left + " Height: " + highlightBox.height + " Width: " + highlightBox.width) + + $tablistHighlight.style.top = highlightBox.top - 2 + "px"; + $tablistHighlight.style.left = highlightBox.left - 2 + "px"; + $tablistHighlight.style.height = highlightBox.height + 7 + "px"; + $tablistHighlight.style.width = highlightBox.width + 8 + "px"; + } // end function + + var $this = $(this), + $prev = $this.find('.slick-prev'), + $next = $this.find('.slick-next'), + $tablist = $this.find(".slick-dots"), + $tabs = $this.find(".slick-dots button"), + $tabpanels = $this.find(".item"), + $tabpanel, + $tablistHighlight, + $pauseCarousel, + $complementaryLandmark, + $tab, + $is_paused = false, + offset, + height, + width, + i, + id_title = "id_title", + id_desc = "id_desc"; + + $tablist.attr("role", "tablist"); + + $tabs.focus(function() { + $this.carousel("pause"); + $is_paused = true; + $pauseCarousel.innerHTML = "Play Carousel"; + $(this) + .parent() + .addClass("active"); + // $(this).addClass('focus') + setTablistHighlightBox(); + $($tablistHighlight).addClass("focus"); + $(this) + .parents(".carousel") + .addClass("contrast"); + }); + + $tabs.blur(function(event) { + $(this) + .parent() + .removeClass("active"); + // $(this).removeClass('focus') + $($tablistHighlight).removeClass("focus"); + $(this) + .parents(".carousel") + .removeClass("contrast"); + }); + + for (i = 0; i < $tabpanels.length; i++) { + $tabpanel = $tabpanels[i]; + $tabpanel.setAttribute("role", "tabpanel"); + $tabpanel.setAttribute("id", "tabpanel-" + index + "-" + i); + $tabpanel.setAttribute("aria-labelledby", "tab-" + index + "-" + i); + } + + if (typeof $this.attr("role") !== "string") { + $this.attr("role", "complementary"); + $this.attr("aria-labelledby", id_title); + $this.attr("aria-describedby", id_desc); + $this.prepend( + '<p id="' + + id_desc + + '" class="sr-only">A carousel is a rotating set of images, rotation stops on keyboard focus on carousel tab controls or hovering the mouse pointer over images. Use the tabs or the previous and next buttons to change the displayed slide.</p>' + ); + $this.prepend( + '<h2 id="' + + id_title + + '" class="sr-only">Carousel content with ' + + $tabpanels.length + + " slides.</h2>" + ); + } + + for (i = 0; i < $tabs.length; i++) { + $tab = $tabs[i]; + + $tab.setAttribute("role", "tab"); + $tab.setAttribute("id", "tab-" + index + "-" + i); + $tab.setAttribute("aria-controls", "tabpanel-" + index + "-" + i); + + var tpId = "#tabpanel-" + index + "-" + i; + var caption = $this + .find(tpId) + .find("h1") + .text(); + + if (typeof caption !== "string" || caption.length === 0) + caption = $this.find(tpId).text(); + if (typeof caption !== "string" || caption.length === 0) + caption = $this + .find(tpId) + .find("h3") + .text(); + if (typeof caption !== "string" || caption.length === 0) + caption = $this + .find(tpId) + .find("h4") + .text(); + if (typeof caption !== "string" || caption.length === 0) + caption = $this + .find(tpId) + .find("h5") + .text(); + if (typeof caption !== "string" || caption.length === 0) + caption = $this + .find(tpId) + .find("h6") + .text(); + if (typeof caption !== "string" || caption.length === 0) + caption = "no title"; + + // console.log("CAPTION: " + caption ) + + var tabName = document.createElement("span"); + tabName.setAttribute("class", "sr-only"); + tabName.innerHTML = "Slide " + (i + 1); + if (caption) tabName.innerHTML += ": " + caption; + $tab.appendChild(tabName); + } + + // create div for focus styling of tablist + $tablistHighlight = document.createElement("div"); + $tablistHighlight.className = "carousel-tablist-highlight"; + document.body.appendChild($tablistHighlight); + + // create button for screen reader users to stop rotation of carousel + + // create button for screen reader users to pause carousel for virtual mode review + $complementaryLandmark = document.createElement("aside"); + $complementaryLandmark.setAttribute("class", "carousel-aside-pause"); + $complementaryLandmark.setAttribute( + "aria-label", + "carousel pause/play control" + ); + $this.prepend($complementaryLandmark); + + $pauseCarousel = document.createElement("button"); + $pauseCarousel.className = "carousel-pause-button"; + $pauseCarousel.innerHTML = "Pause Carousel"; + $pauseCarousel.setAttribute( + "title", + "Pause/Play carousel button can be used by screen reader users to stop carousel animations" + ); + $($complementaryLandmark).append($pauseCarousel); + + $($pauseCarousel).click(function() { + if ($is_paused) { + $pauseCarousel.innerHTML = "Pause Carousel"; + $this.carousel("cycle"); + $is_paused = false; + } else { + $pauseCarousel.innerHTML = "Play Carousel"; + $this.carousel("pause"); + $is_paused = true; + } + }); + $($pauseCarousel).focus(function() { + $(this).addClass("focus"); + }); + + $($pauseCarousel).blur(function() { + $(this).removeClass("focus"); + }); + + setTablistHighlightBox(); + + $(window).resize(function() { + setTablistHighlightBox(); + }); + + // Add space bar behavior to prev and next buttons for SR compatibility + $prev.attr("aria-label", "Previous Slide"); + $prev.keydown(function(e) { + var k = e.which || e.keyCode; + if (/(13|32)/.test(k)) { + e.preventDefault(); + e.stopPropagation(); + $prev.trigger("click"); + } + }); + + $prev.focus(function() { + $(this) + .parents(".carousel") + .addClass("contrast"); + }); + + $prev.blur(function() { + $(this) + .parents(".carousel") + .removeClass("contrast"); + }); + + $next.attr("aria-label", "Next Slide"); + $next.keydown(function(e) { + var k = e.which || e.keyCode; + if (/(13|32)/.test(k)) { + e.preventDefault(); + e.stopPropagation(); + $next.trigger("click"); + } + }); + + $next.focus(function() { + $(this) + .parents(".carousel") + .addClass("contrast"); + }); + + $next.blur(function() { + $(this) + .parents(".carousel") + .removeClass("contrast"); + }); + + $(".carousel-inner a").focus(function() { + $(this) + .parents(".carousel") + .addClass("contrast"); + }); + + $(".carousel-inner a").blur(function() { + $(this) + .parents(".carousel") + .removeClass("contrast"); + }); + + $tabs.each(function() { + var item = $(this); + if (item.hasClass("active")) { + item.attr({ "aria-selected": "true", tabindex: "0" }); + } else { + item.attr({ "aria-selected": "false", tabindex: "-1" }); + } + }); +}); + +var slideCarousel = $.fn.carousel.Constructor.prototype.slide; +$.fn.carousel.Constructor.prototype.slide = function(type, next) { + var $element = this.$element, + $active = $element.find("[role=tabpanel].active"), + $next = next || $active[type](), + $tab, + $tab_count = $element.find("[role=tabpanel]").size(), + $prev_side = $element.find('[data-slide="prev"]'), + $next_side = $element.find('[data-slide="next"]'), + $index = 0, + $prev_index = $tab_count - 1, + $next_index = 1, + $id; + + if ($next && $next.attr("id")) { + $id = $next.attr("id"); + $index = $id.lastIndexOf("-"); + if ($index >= 0) $index = parseInt($id.substring($index + 1), 10); + + $prev_index = $index - 1; + if ($prev_index < 1) $prev_index = $tab_count - 1; + + $next_index = $index + 1; + if ($next_index >= $tab_count) $next_index = 0; + } + + $prev_side.attr( + "aria-label", + "Show slide " + ($prev_index + 1) + " of " + $tab_count + ); + $next_side.attr( + "aria-label", + "Show slide " + ($next_index + 1) + " of " + $tab_count + ); + + slideCarousel.apply(this, arguments); + + $active.one("bsTransitionEnd", function() { + var $tab; + + $tab = $element.find('li[aria-controls="' + $active.attr("id") + '"]'); + if ($tab) $tab.attr({ "aria-selected": false, tabIndex: "-1" }); + + $tab = $element.find('li[aria-controls="' + $next.attr("id") + '"]'); + if ($tab) $tab.attr({ "aria-selected": true, tabIndex: "0" }); + }); +}; + +var $this; +$.fn.carousel.Constructor.prototype.keydown = function(e) { + $this = $this || $(this); + if (this instanceof Node) $this = $(this); + + function selectTab(index) { + if (index >= $tabs.length) return; + if (index < 0) return; + + $carousel.carousel(index); + setTimeout(function() { + $tabs[index].focus(); + // $this.prev().focus() + }, 150); + } + + var $carousel = $(e.target).closest(".carousel"), + $tabs = $carousel.find("[role=tab]"), + k = e.which || e.keyCode, + index; + + if (!/(37|38|39|40)/.test(k)) return; + + index = $tabs.index($tabs.filter(".active")); + if (k == 37 || k == 38) { + // Up + index--; + selectTab(index); + } + + if (k == 39 || k == 40) { + // Down + index++; + selectTab(index); + } + + e.preventDefault(); + e.stopPropagation(); +}; +$(document).on( + "keydown.carousel.data-api", + "li[role=tab]", + $.fn.carousel.Constructor.prototype.keydown +); diff --git a/themes/bootstrap3/js/lib/autocomplete.js b/themes/bootstrap3/js/lib/autocomplete.js index e981011e4086149f83030987d93b152a529b03ba..cb6e6ee04e1b005cffefe4490c2185877bdc3c0d 100644 --- a/themes/bootstrap3/js/lib/autocomplete.js +++ b/themes/bootstrap3/js/lib/autocomplete.js @@ -1,14 +1,14 @@ -/* https://github.com/vufind-org/autocomplete.js 1.0b */ -(function autocomplete( $ ) { +/* https://github.com/vufind-org/autocomplete.js 1.0b2 */ +(function autocomplete($) { var element = false, + cache = {}, + optionStorage = {}, xhr = false; function Factory(_input, settings) { - var cache = (typeof(settings) === "object" && typeof(settings.cacheObj) === "object") - ? settings.cacheObj : {}; - return (function acClosure() { - var input = $(this), - options; + return function acClosure() { + var input = $(this); + var options = optionStorage[input.data('ac-id')]; var _align = function _align() { var position = input.offset(); @@ -23,59 +23,65 @@ } }; - var show = function show() { + var _show = function _show() { element.removeClass(options.hidingClass); - } + }; var hide = function hide() { element.addClass(options.hidingClass); - } + }; var _populate = function _populate(item, eventType) { + input.trigger('ac:select', [item, eventType]); + var ret; if (options.callback) { - if (options.callback(item, input, eventType) === true && typeof item.href !== 'undefined') { + ret = options.callback(item, input, eventType); + if (ret === true && typeof item.href !== 'undefined') { return window.location.assign(item.href); } + if (ret === false) { + return false; + } } else if (typeof item.href !== 'undefined') { return window.location.assign(item.href); } - input.val(item.value); + input.val(typeof ret === 'undefined' ? item.value : ret); // Reset element.find('.ac-item.selected').removeClass('selected'); - $(this).data('selected', -1); + $(this).data('ac-selected', -1); setTimeout(function acPopulateDelay() { input.focus(); hide(); }, 10); - } + }; var _listToHTML = function _listToHTML(list, regex) { var shell = $('<div/>'); for (var i = 0; i < list.length; i++) { if (typeof list[i] === 'string') { - list[i] = {value: list[i]}; + list[i] = { value: list[i] }; } var content = list[i].label || list[i].value; if (options.highlight) { content = content.replace(regex, '<b>$1</b>'); } - var item = typeof list[i].href === 'undefined' - ? $('<div/>') - : $('<a/>').attr('href', list[i].href); + var item = typeof list[i].href === 'undefined' ? $('<div/>') : $('<a/>').attr('href', list[i].href); // list - item.data(list[i]) - .addClass('ac-item') - .html(content); + item + .data(list[i]) + .addClass('ac-item') + .html(content); if (typeof list[i].description !== 'undefined') { - item.append($('<small/>').html( - options.highlight - ? list[i].description.replace(regex, '<b>$1</b>') - : list[i].description - )); + item.append( + $('<small/>').html( + options.highlight ? list[i].description.replace(regex, '<b>$1</b>') : list[i].description + ) + ); } shell.append(item); } + input.trigger('ac:render', [shell[0]]); return shell; - } + }; var _createList = function _createList(data) { // highlighting setup // escape term for regex - https://github.com/sindresorhus/escape-string-regexp/blob/master/index.js @@ -91,10 +97,12 @@ shell.append($('<hr/>', { class: 'ac-section-divider' })); } if (typeof data.groups[i].label !== 'undefined') { - shell.append($('<header>', { - class: 'ac-section-header', - html: data.groups[i].label - })); + shell.append( + $('<header>', { + class: 'ac-section-header', + html: data.groups[i].label + }) + ); } if (typeof data.groups[i].label !== 'undefined' && data.groups[i].items.length > 0) { shell.append(_listToHTML(data.groups[i].items, regex)); @@ -104,29 +112,31 @@ } } element.html(shell); - input.data('length', shell.find('.ac-item').length); + input.data('ac-length', shell.find('.ac-item').length); element.find('.ac-item').mousedown(function acItemClick() { - _populate($(this).data(), {mouse: true}); + _populate($(this).data(), { mouse: true }); }); _align(); - } + }; var _handleResults = function _handleResults(term, _data) { // Limit results - var data = typeof _data.groups === 'undefined' - ? _data.slice(0, Math.min(options.maxResults, _data.length)) - : _data; - var cid = input.data('cacheId'); + var data = + typeof _data.groups === 'undefined' + ? _data.slice(0, Math.min(options.maxResults, _data.length)) + : _data; + var cid = input.data('ac-id'); cache[cid][term] = data; if (data.length === 0 || (typeof data.groups !== 'undefined' && data.groups.length === 0)) { hide(); } else { _createList(data); } - } - var _defaultStaticSort = function _defaultStaticSort(a, b) { // .bind(lcterm) + }; + var _defaultStaticSort = function _defaultStaticSort(a, b) { + // .bind(lcterm) return a.match.indexOf(this) - b.match.indexOf(this); - } + }; var _staticGroups = function _staticGroups(lcterm) { var matches = []; for (var i = 0; i < options.static.groups.length; i++) { @@ -160,24 +170,26 @@ } } return matches; - } + }; var search = function search() { - if (xhr) { xhr.abort(); } + if (xhr) { + xhr.abort(); + } if (input.val().length >= options.minLength) { element.html('<i class="ac-item loading">' + options.loadingString + '</i>'); - show(); + _show(); _align(); - input.data('selected', -1); + input.data('ac-selected', -1); var term = input.val(); // Check cache (only for handler-based setups) - var cid = input.data('cacheId'); - if (options.cache && typeof cache[cid][term] !== "undefined") { + var cid = input.data('ac-id'); + if (options.cache && typeof cache[cid][term] !== 'undefined') { if (cache[cid][term].length === 0) { hide(); } else { _createList(cache[cid][term]); } - // Check for static list + // Check for static list } else if (typeof options.static !== 'undefined') { var lcterm = term.toLowerCase(); var matches; @@ -194,7 +206,7 @@ } } _handleResults(term, matches); - // Call handler + // Call handler } else { options.handler(input, function achandlerCallback(data) { _handleResults(term, data); @@ -203,16 +215,26 @@ } else { hide(); } - } + }; function preprocessStatic(_item) { - var item = typeof _item === 'string' - ? { value: _item } - : _item; + var item = typeof _item === 'string' ? { value: _item } : _item; item.match = (item.label || item.value).toLowerCase(); return item; } - var _setup = function _setup() { + var _setup = function _setup(settings) { + var cid = Math.floor(Math.random() * 1000); + input.data('ac-id', cid); + input.data('ac-selected', -1); + input.data('ac-length', 0); + + options = $.extend({}, $.fn.autocomplete.defaults, settings); + optionStorage[input.data('ac-id')] = options; + + if (options.cache) { + cache[cid] = {}; + } + element = $('.autocomplete-results'); if (element.length === 0) { element = $('<div/>') @@ -222,15 +244,6 @@ $(document.body).append(element); } - input.data('selected', -1); - input.data('length', 0); - - if (options.cache) { - var cid = Math.floor(Math.random() * 1000); - input.data('cacheId', cid); - cache[cid] = {}; - } - input.blur(function acinputBlur(e) { if (e.target.acitem) { setTimeout(hide, 10); @@ -244,6 +257,9 @@ input.focus(function acinputFocus() { search(); }); + input.on('paste', function acinputPaste() { + requestAnimationFrame(search); + }); input.keyup(function acinputKeyup(event) { // Ignore navigation keys // - Ignore control functions @@ -255,26 +271,26 @@ return; } switch (event.which) { - case 9: // tab - case 13: // enter - case 16: // shift - case 20: // caps lock - case 27: // esc - case 33: // page up - case 34: // page down - case 35: // end - case 36: // home - case 37: // arrows - case 38: - case 39: - case 40: - case 45: // insert - case 144: // num lock - case 145: // scroll lock - case 19: // pause/break - return; - default: - search(); + case 9: // tab + case 13: // enter + case 16: // shift + case 20: // caps lock + case 27: // esc + case 33: // page up + case 34: // page down + case 35: // end + case 36: // home + case 37: // arrows + case 38: + case 39: + case 40: + case 45: // insert + case 144: // num lock + case 145: // scroll lock + case 19: // pause/break + return; + default: + search(); } }); input.keydown(function acinputKeydown(event) { @@ -282,69 +298,63 @@ if (event.ctrlKey || event.which === 17) { return; } - var position = $(this).data('selected'); + var position = $(this).data('ac-selected'); switch (event.which) { // arrow keys through items - case 38: // up key - event.preventDefault(); - element.find('.ac-item.selected').removeClass('selected'); - if (position > -1) { - if (position-- > 0) { + case 38: // up key + event.preventDefault(); + element.find('.ac-item.selected').removeClass('selected'); + if (position > -1) { + if (position-- > 0) { + element.find('.ac-item:eq(' + position + ')').addClass('selected'); + } + $(this).data('ac-selected', position); + } + break; + case 40: // down key + event.preventDefault(); + if (element.hasClass(options.hidingClass)) { + search(); + } else if (position < input.data('ac-length') - 1) { + position++; + element.find('.ac-item.selected').removeClass('selected'); element.find('.ac-item:eq(' + position + ')').addClass('selected'); + $(this).data('ac-selected', position); } - $(this).data('selected', position); - } - break; - case 40: // down key - event.preventDefault(); - if (element.hasClass(options.hidingClass)) { - search(); - } else if (position < input.data('length') - 1) { - position++; - element.find('.ac-item.selected').removeClass('selected'); - element.find('.ac-item:eq(' + position + ')').addClass('selected'); - $(this).data('selected', position); - } - break; + break; // enter to nav or populate - case 9: - case 13: - var selected = element.find('.ac-item.selected'); - if (selected.length > 0) { - event.preventDefault(); - if (event.which === 13 && selected.attr('href')) { - return window.location.assign(selected.attr('href')); - } else { - _populate(selected.data(), $(this), {key: true}); + case 9: + case 13: + var selected = element.find('.ac-item.selected'); + if (selected.length > 0) { + event.preventDefault(); + if (event.which === 13 && selected.attr('href') && options.callback === 'undefined') { + return window.location.assign(selected.attr('href')); + } else { + _populate(selected.data(), $(this), { key: true }); + } } - } - break; + break; // hide on escape - case 27: - hide(); - $(this).data('selected', -1); - break; + case 27: + hide(); + $(this).data('ac-selected', -1); + break; } }); - window.addEventListener("resize", hide, false); - } + window.addEventListener('resize', hide, false); + }; - if (typeof settings === "string") { - if (settings === "show") { - show(); - _align(); - } else if (settings === "hide") { - hide(); - } else if (options.cache && settings === "clear cache") { - var cid = parseInt(input.data('cacheId'), 10); - cache[cid] = {}; + // Setup + if (!input.data('ac-id')) { + if (typeof settings === 'undefined') { + console.error('Autocomplete not initialized, please pass setup parameters.'); + } + if (typeof settings.handler === 'undefined' && typeof settings.static === 'undefined') { + console.error('Neither handler function nor static result list provided for autocomplete'); + return null; } - return input; - } else if (typeof settings.handler === 'undefined' && typeof settings.static === 'undefined') { - console.error('Neither handler function nor static result list provided for autocomplete'); - return input; - } else { if (typeof settings.static !== 'undefined') { // Preprocess strings into items if (typeof settings.static.groups !== 'undefined') { @@ -359,18 +369,29 @@ settings.static = settings.static.map(preprocessStatic); } } - options = $.extend( {}, $.fn.autocomplete.defaults, settings ); - _setup(); + _setup(settings); } - return input; - }.bind(_input))(); + return { + show: function show() { + _show(); + _align(); + }, + hide: hide, + search: search, + clearCache: function clearCache() { + cache[input.data('ac-id')] = {}; + } + }; + }.bind(_input)(); } $.fn.autocomplete = function acJQuery(settings) { - return this.each(function acJQueryEach() { - return Factory(this, settings); + var ac; + this.each(function acJQueryEach() { + ac = Factory(this, settings); }); + return ac; }; $.fn.autocomplete.defaults = { @@ -385,12 +406,14 @@ var timer = false; $.fn.autocomplete.ajax = function acAjax(ops) { - if (timer) { clearTimeout(timer); } - if (xhr) { xhr.abort(); } - timer = setTimeout( - function acajaxDelay() { xhr = $.ajax(ops); }, - 200 - ); + if (timer) { + clearTimeout(timer); + } + if (xhr) { + xhr.abort(); + } + timer = setTimeout(function acajaxDelay() { + xhr = $.ajax(ops); + }, 200); }; - -}( jQuery )); +})(jQuery); diff --git a/themes/bootstrap3/js/lib/channel-slider.js b/themes/bootstrap3/js/lib/channel-slider.js deleted file mode 100644 index 9d6f59b047606f263303ff0cfe50dea062d3b46c..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/js/lib/channel-slider.js +++ /dev/null @@ -1,178 +0,0 @@ -function ChannelSlider(el) { - return (function Slider() { - // Elements - var _container; // Entire element - var _slider; // Moving, too-wide element - var _menu; - var _leftbtn; - var _rightbtn; - var _scrollbar; - // Data - var _slidePositions; - var _xpos = 0; - var _maxpos = false; - var _targetx = 0; - var _current = 0; - - var _setupWrappers = function _setupWrappers() { - var bounds = $('<div class="slider-bounds"></div>'); - _slider = $('<div class="slider-screen"></div>'); - _container.children().appendTo(_slider); - bounds.append(_slider); - _container.append(bounds); - }; - var _addMenu = function _addMenu() { - _menu = $('<nav class="slider-menu"></nav>'); - var group = $('<div class="btn-group pull-left"></div>'); - _leftbtn = $('<button class="btn btn-default" disabled><i class="fa fa-arrow-left"></i></button>').click(pageLeft); - _rightbtn = $('<button class="btn btn-default"><i class="fa fa-arrow-right"></i></button>').click(pageRight); - _scrollbar = $('<div class="scroll-bar"></div>'); - group.append(_leftbtn); - group.append(_rightbtn); - _menu.append(group); - _menu.append(_scrollbar); - _container.append(_menu); - }; - - var _adjustWidth = function _adjustWidth() { - // Make the slider ridiculously wide - _slider.css('width', 1e6); - // Grab slide positions - var slides = _slider.find('.slide'); - var farLeft = slides[0].getBoundingClientRect().left; - _slidePositions = slides.map(function slidePositionsMap(i, op) { - var box = op.getBoundingClientRect(); - if (i === 0) { - return { - left: 0, - right: box.width - }; - } - return { - left: box.left - farLeft, - right: box.right - farLeft - } - }); - // Reign it in - _slider.css('width', _slidePositions[_slidePositions.length - 1].right + 100); - _scrollbar.css('width', _container.width() / _slidePositions.length); - _maxpos = _slidePositions[_slidePositions.length - 1].right - _container.width() + 10; - }; - var _move = function _move(newpos) { - _targetx = Math.max(0, Math.min(newpos, _maxpos)); - _animate(); - // If we're running into the end, we need a new current - if (_targetx === _maxpos) { - _leftbtn.removeAttr('disabled'); - _rightbtn.attr('disabled', 1); - for (var i = 0; i < _slidePositions.length; i++) { - if (_slidePositions[i].left >= _maxpos) { - _current = i + 1; - break; - } - } - } else if (_targetx === 0) { - _leftbtn.attr('disabled', 1); - _rightbtn.removeAttr('disabled'); - } else { - _leftbtn.removeAttr('disabled'); - _rightbtn.removeAttr('disabled'); - } - }; - var _animate = function _animate() { - _xpos += (_targetx - _xpos) / 10; - if (Math.abs(_xpos - _targetx) < 1) { - _xpos = _targetx; - } else { - requestAnimationFrame(_animate); - } - _slider.css('left', 0 - Math.round(_xpos)); - var barMax = _container.width() - _scrollbar.width(); - _scrollbar.css('left', Math.max(0, Math.min(barMax, (_xpos / _maxpos) * barMax))); - }; - var _moveToClosest = function _moveToClosest(threshold) { - for (var i = 0; i < _slidePositions.length; i++) { - if (_slidePositions[i].right >= threshold) { - _current = i; - _move(_slidePositions[i].left); - break; - } - } - }; - var pageLeft = function pageLeft() { - _moveToClosest(_slidePositions[_current].right - _container.width()); - }; - var pageRight = function pageRight() { - _moveToClosest(_xpos + _container.width()); - }; - - /* --- Setup --- */ - if (typeof el.dataset.slider !== 'undefined') { - return false; - } - _container = $(el); - _container.attr('data-slider', true); - _setupWrappers(); - _addMenu(); - // Image loading listeners - _slider.find('img').on('load', _adjustWidth); - // Adjust width - _adjustWidth(); - - var _touchX = null; - var _draggingScrollbar = false; - var _sliderDragStart = function _sliderDragStart(e) { - _touchX = e.clientX || e.originalEvent.touches[0].clientX; - _draggingScrollbar = false; - }; - var _scrollbarDragStart = function _scrollbarDragStart(e) { - _touchX = e.clientX || e.originalEvent.touches[0].clientX; - _draggingScrollbar = true; - }; - var _sliderDragMove = function _sliderDrag(e) { - if (_touchX === null) { - return; - } - var x = e.clientX || e.originalEvent.touches[0].clientX; - if (_draggingScrollbar) { - _targetx = _maxpos * (x - _container.offset().left) / _container.width(); - _animate(); - } else { - var diffX = _touchX - x; - if (Math.abs(diffX) > 100) { - if (diffX < 0) { - pageLeft(); - } else { - pageRight(); - } - _touchX = null; - } - } - }; - var _dragEnd = function _dragEnd() { - _touchX = null; - // Move to true closest - if (_draggingScrollbar) { - var mindist = Math.abs(_slidePositions[0].left - _xpos); - var closest = 0; - for (var i = 1; i < _slidePositions.length; i++) { - var d = Math.abs(_slidePositions[i].left - _xpos); - if (d < mindist) { - mindist = d; - closest = i; - } - } - _move(_slidePositions[closest].left); - } - }; - _slider.on('mousedown', _sliderDragStart); - _scrollbar.on('mousedown', _scrollbarDragStart); - _slider.on('touchstart', _sliderDragStart); - _slider.on('touchmove', _sliderDragMove); - _container.on('mousemove', _sliderDragMove); - $(document).on('mouseup', _dragEnd); - $(document).on('touchend', _dragEnd); - - return true; - })(); -} diff --git a/themes/bootstrap3/js/lightbox.js b/themes/bootstrap3/js/lightbox.js index 51393a46e4a0eda8aedd1eb6ecbe0e8bc2ace01f..065af6315564af831e93629f32952dd7fd253834 100644 --- a/themes/bootstrap3/js/lightbox.js +++ b/themes/bootstrap3/js/lightbox.js @@ -27,7 +27,6 @@ VuFind.register('lightbox', function Lightbox() { } function _emit(msg, _details) { var details = _details || {}; - // Fallback to document.createEvent() if creating a new CustomEvent fails (e.g. IE 11) var event; try { event = new CustomEvent(msg, { @@ -36,6 +35,7 @@ VuFind.register('lightbox', function Lightbox() { cancelable: true }); } catch (e) { + // Fallback to document.createEvent() if creating a new CustomEvent fails (e.g. IE 11) event = document.createEvent('CustomEvent'); event.initCustomEvent(msg, true, true, details); } @@ -55,6 +55,9 @@ VuFind.register('lightbox', function Lightbox() { _modalBody.find('h2:first-of-type') .after('<div class="flash-message alert alert-' + type + '">' + message + '</div>'); } + function close() { + _modal.modal('hide'); + } /** * Update content @@ -63,8 +66,9 @@ VuFind.register('lightbox', function Lightbox() { * * data-lightbox-ignore = do not submit this form in lightbox */ - var _constrainLink; // function declarations to avoid style warnings - var _formSubmit; // about circular references + // function declarations to avoid style warnings about circular references + var _constrainLink; + var _formSubmit; function render(content) { if (!content.match) { return; @@ -79,7 +83,7 @@ VuFind.register('lightbox', function Lightbox() { var href = alerts.find('.download').attr('href'); if (typeof href !== 'undefined') { location.href = href; - _modal.modal('hide'); + close(); } else { showAlert(msgs, 'success'); } @@ -136,27 +140,31 @@ VuFind.register('lightbox', function Lightbox() { _xhr = $.ajax(obj); _xhr.always(function lbAjaxAlways() { _xhr = false; }) .done(function lbAjaxDone(content, status, jq_xhr) { - if (jq_xhr.status === 205) { - VuFind.refreshPage(); - return; - } - var testDiv = $('<div/>').html(content); - var errorMsgs = testDiv.find('.flash-message.alert-danger:not([data-lightbox-ignore])'); - // Place Hold error isolation - if (obj.url.match(/\/Record\/.*(Hold|Request)\?/)) { - if (errorMsgs.length && testDiv.find('.record').length) { - var msgs = errorMsgs.toArray().map(function getAlertHtml(el) { - return el.innerHTML; - }).join('<br/>'); - showAlert(msgs, 'danger'); - return false; + var errorMsgs = []; + if (jq_xhr.status !== 205) { + var testDiv = $('<div/>').html(content); + errorMsgs = testDiv.find('.flash-message.alert-danger:not([data-lightbox-ignore])'); + // Place Hold error isolation + if (obj.url.match(/\/Record\/.*(Hold|Request)\?/)) { + if (errorMsgs.length && testDiv.find('.record').length) { + var msgs = errorMsgs.toArray().map(function getAlertHtml(el) { + return el.innerHTML; + }).join('<br/>'); + showAlert(msgs, 'danger'); + return false; + } } } - if ( // Close the lightbox after deliberate login - obj.method && ( // is a form - obj.url.match(/catalogLogin/) // catalog login for holds - || obj.url.match(/MyResearch\/(?!Bulk|Delete|Recover)/) // or that matches login/create account - ) && errorMsgs.length === 0 // skip failed logins + // Close the lightbox after deliberate login provided that: + // - is a form + // - catalog login for holds + // - or that matches login/create account + // - not a failed login + if ( + obj.method && ( + obj.url.match(/catalogLogin/) + || obj.url.match(/MyResearch\/(?!Bulk|Delete|Recover)/) + ) && errorMsgs.length === 0 ) { var eventResult = _emit('VuFind.lightbox.login', { @@ -173,6 +181,10 @@ VuFind.register('lightbox', function Lightbox() { } _currentUrl = _originalUrl; // Now that we're logged in, where were we? } + if (jq_xhr.status === 205) { + VuFind.refreshPage(); + return; + } render(content); }) .fail(function lbAjaxFail(deferred, errorType, msg) { @@ -219,7 +231,9 @@ VuFind.register('lightbox', function Lightbox() { */ _constrainLink = function constrainLink(event) { if (typeof $(this).data('lightboxIgnore') != 'undefined' - || typeof this.attributes.href === 'undefined' || this.attributes.href.value.charAt(0) === '#' + || typeof this.attributes.href === 'undefined' + || this.attributes.href.value.charAt(0) === '#' + || this.href.match(/^[a-zA-Z]+:[^/]/) // ignore resource identifiers (mailto:, tel:, etc.) ) { return true; } @@ -270,6 +284,10 @@ VuFind.register('lightbox', function Lightbox() { _clickedButton = null; var buttonData = { name: 'submit', value: 1 }; if (submit.length > 0) { + if (typeof submit.data('lightbox-close') !== 'undefined') { + close(); + return false; + } if (typeof submit.data('lightbox-ignore') !== 'undefined') { return true; } @@ -348,6 +366,7 @@ VuFind.register('lightbox', function Lightbox() { if (VuFind.lightbox.refreshOnClose) { VuFind.refreshPage(); } + this.setAttribute('aria-hidden', true); _emit('VuFind.lightbox.closing'); }); _modal.on('hidden.bs.modal', function lightboxHidden() { @@ -357,7 +376,7 @@ VuFind.register('lightbox', function Lightbox() { VuFind.modal = function modalShortcut(cmd) { if (cmd === 'show') { - _modal.modal($.extend({ show: true }, _modalParams)); + _modal.modal($.extend({ show: true }, _modalParams)).attr('aria-hidden', false); } else { _modal.modal(cmd); } @@ -374,6 +393,7 @@ VuFind.register('lightbox', function Lightbox() { ajax: ajax, alert: showAlert, bind: bind, + close: close, flashMessage: flashMessage, reload: reload, render: render, diff --git a/themes/bootstrap3/js/map_selection.js b/themes/bootstrap3/js/map_selection.js deleted file mode 100644 index 7641bd1fc7bd2b8e799f4558f199f3065223a4a7..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/js/map_selection.js +++ /dev/null @@ -1,276 +0,0 @@ -/*global ol */ -/*exported loadMapSelection */ -//Coordinate order: Storage and Query: WENS ; Display: WSEN - -function loadMapSelection(geoField, boundingBox, baseURL, homeURL, searchParams, showSelection, resultsCoords, popupTitle) { - var init = true; - var pTitle = popupTitle + '<button class="close">×</button>'; - var srcProj = 'EPSG:4326'; - var dstProj = 'EPSG:900913'; - var osm = new ol.layer.Tile({source: new ol.source.OSM()}); - var searchboxSource = new ol.source.Vector(); - var searchboxStyle = new ol.style.Style({ - fill: new ol.style.Fill({ - color: [255, 0, 0, 0.1] - }), - stroke: new ol.style.Stroke({ - color: [255, 0, 0, 1], - width: 2 - }) - }); - var searchboxLayer = new ol.layer.Vector({ - source: searchboxSource, - style: searchboxStyle - }); - var draw, map; - var count = resultsCoords.length; - var searchResults = new Array(count); - var searchIds = new Array(count); - for (var i = 0; i < count; ++i) { - var coordinates = ol.proj.transform( - [resultsCoords[i][1], resultsCoords[i][2]], srcProj, dstProj - ); - searchResults[i] = new ol.Feature({ - geometry: new ol.geom.Point(coordinates), - id: resultsCoords[i][0], - name: resultsCoords[i][3] - }); - searchIds[i] = resultsCoords[i][0]; - } - var resultSource = new ol.source.Vector({ - features: searchResults - }); - var clusterSource = new ol.source.Cluster({ - distance: 60, - source: resultSource - }); - var styleCache = {}; - var clusterLayer = new ol.layer.Vector({ - id: 'clusterLayer', - source: clusterSource, - style: function addClusterStyle(feature) { - var size = feature.get('features').length; - var pointRadius = 8 + (size.toString().length * 2); - var style = styleCache[size]; - if (!style) { - style = [ - new ol.style.Style({ - image: new ol.style.Circle({ - radius: pointRadius, - stroke: new ol.style.Stroke({ - color: '#ff0000' - }), - fill: new ol.style.Fill({ - color: '#ffb3b3' - }) - }), - text: new ol.style.Text({ - text: size.toString(), - font: 'bold 12px arial,sans-serif', - fill: new ol.style.Fill({ - color: 'black' - }) - }) - }) - ]; - styleCache[size] = style; - } - map.removeInteraction(draw); - return style; - } - }); - - $('#geo_search').show(); - init = function drawMap() { - map = new ol.Map({ - interactions: ol.interaction.defaults({ - shiftDragZoom: false - }), - target: 'geo_search_map', - projection: dstProj, - layers: [osm, searchboxLayer, clusterLayer], - view: new ol.View({ - center: [0, 0], - zoom: 1 - }) - }); - - if (showSelection === true) { - searchboxSource.clear(); - // Adjust bounding box (WSEN) display for queries crossing the dateline - if (boundingBox[0] > boundingBox[2]) { - boundingBox[2] = boundingBox[2] + 360; - } - var newBbox = new ol.geom.Polygon([[ - ol.proj.transform([boundingBox[0], boundingBox[3]], srcProj, dstProj), - ol.proj.transform([boundingBox[0], boundingBox[1]], srcProj, dstProj), - ol.proj.transform([boundingBox[2], boundingBox[1]], srcProj, dstProj), - ol.proj.transform([boundingBox[2], boundingBox[3]], srcProj, dstProj), - ol.proj.transform([boundingBox[0], boundingBox[3]], srcProj, dstProj) - ]]); - var featureBbox = new ol.Feature({ - name: "bbox", - geometry: newBbox - }); - searchboxSource.addFeature(featureBbox); - map.getView().fit(searchboxSource.getExtent(), map.getSize()); - } - - //Get popup elements from webpage - var element = document.getElementById('popup'); - - // Add popup element to map - var popup = new ol.Overlay({ - element: element, - stopEvent: true - }); - map.addOverlay(popup); - - // Display popup on click - map.on('click', function displayPopup(evt) { - var popupfeature = map.forEachFeatureAtPixel(evt.pixel, - function showFeature(feature) { - return {'feature': feature, 'layer': clusterLayer}; - }); - if (popupfeature) { - var cFeatures = popupfeature.feature.get('features'); - var fType = typeof cFeatures; - var fLayerId = popupfeature.layer.get('id'); - if ((fLayerId === 'clusterLayer') && (fType === 'object') && (cFeatures.length < 5)) { - var coordinate = map.getCoordinateFromPixel(evt.pixel); - var pcontent = ''; - for (var j = 0; j < cFeatures.length; j++) { - var cFeatureName = cFeatures[j].get('name'); - var cFeatureId = cFeatures[j].get('id'); - var cFeatureContent = '<article class="geoItem">' + - cFeatureName.link(homeURL + 'Record/' + cFeatureId) + '</article>'; - pcontent += cFeatureContent; - } - popup.setPosition(coordinate); - $(element).popover({ - 'placement': 'auto', - 'container': 'body', - 'animation': false, - 'html': true, - 'title': pTitle - }).on('shown.bs.popover', function closePopup(e) { - // 'aria-describedby' is the id of the current popover - var current_popover = '#' + $(e.target).attr('aria-describedby'); - var $cur_pop = $(current_popover); - $cur_pop.find('.close').click(function closeCurPop(){ - $(element).popover('hide'); - }); - }); - $(element).data('bs.popover').options.content = pcontent; - $(element).popover('show'); - } - } else { - $(element).popover('destroy'); - } - }); - - // change mouse cursor when over marker - map.on('pointermove', function changeMouseCursor(evt) { - if (evt.dragging) { - $(element).popover('destroy'); - return; - } - var pixel = map.getEventPixel(evt.originalEvent); - var hit = map.hasFeatureAtPixel(pixel); - if (hit) { - var fl = map.forEachFeatureAtPixel(pixel, function getFeature(feature) { - return {'feature': feature, 'layer': clusterLayer}; - }); - var cFeatures = fl.feature.get('features'); - var fType = typeof cFeatures; - var fLayerId = fl.layer.get('id'); - if ((fLayerId === 'clusterLayer') && (fType === 'object') && (cFeatures.length < 5)) { - map.getTargetElement().style.cursor = 'pointer'; - } else { - map.getTargetElement().style.cursor = 'default'; - } - } - }); - // close popup if zoom in / out occurs - map.getView().on('change:resolution', function closePopupsOnZoom() { - $(element).popover('destroy'); - }); - }; - function addInteraction() { - draw = new ol.interaction.Draw ({ - source: searchboxSource, - type: 'Box', - geometryFunction: function rectangleFunction(coords, geometryParam) { - var geometry = geometryParam ? geometryParam : new ol.geom.Polygon(null); - var start = coords[0]; - var end = coords[1]; - geometry.setCoordinates([ - [start, [start[0], end[1]], end, [end[0], start[1]], start] - ]); - return geometry; - }, - freehand: true - }); - - draw.on('drawend', function drawSearchBox(evt) { - var geometry = evt.feature.getGeometry(); - var geoCoordinates = geometry.getCoordinates(); - var westnorth = ol.proj.transform(geoCoordinates[0][0], dstProj, srcProj); - var eastsouth = ol.proj.transform(geoCoordinates[0][2], dstProj, srcProj); - // Check to make sure the coordinates are in the correct order - var west = westnorth[0]; - var east = eastsouth[0]; - var north = westnorth[1]; - var south = eastsouth[1]; - if (west > east){ - west = eastsouth[0]; - east = westnorth[0]; - } - if (south > north) { - north = eastsouth[1]; - south = westnorth[1]; - } - // Make corrections for queries that cross the dateline - if (west > 180) { - if (west > 360) { - west = west - (360 * Math.floor(west / -360)); - if (west > 180) { - west = west - 360; - } - } else { - west = west - 360; - } - } - if (west < -180) { - if (west < -360) { - west = west + (360 * Math.floor(west / -360)); - if (west < -180) { - west = west + 360; - } - } else { - west = west + 360; - } - } - if (east > 180) { - // Fix overlapping longitudinal query parameters - if (east > 360) { - east = east - (360 * Math.floor(east / 360)); - if (east > 180) { - east = east - 360; - } - } else { - east = east - 360; - } - } - var rawFilter = geoField + ':Intersects(ENVELOPE(' + west + ', ' + east + ', ' + north + ', ' + south + '))'; - location.href = baseURL + searchParams + "&filter[]=" + rawFilter; - }, this); - map.addInteraction(draw); - } - init(); - document.getElementById("draw_box").onclick = function clearAndDrawMap() { - map.removeInteraction(draw); - addInteraction(); - }; - init = false; -} diff --git a/themes/bootstrap3/js/map_selection_leaflet.js b/themes/bootstrap3/js/map_selection_leaflet.js new file mode 100644 index 0000000000000000000000000000000000000000..7db126cf7870e27861b534f4ee0b4accdad41156 --- /dev/null +++ b/themes/bootstrap3/js/map_selection_leaflet.js @@ -0,0 +1,337 @@ +/*global L, VuFind */ +/*exported loadMapSelection */ +//Coordinate order: Storage and Query: WENS ; Display: WSEN + +function loadMapSelection(geoField, boundingBox, baseURL, homeURL, searchParams, showSelection, resultsCoords, basemap) { + // Initialize variables + var searchboxLayer = L.featureGroup(); + var drawnItemsLayer = L.featureGroup(); + var mcgIDs = []; + var clickedIDs = []; + var clickedBounds = []; + var drawnItems; + var basemapLayer = new L.TileLayer(basemap[0], {attribution: basemap[1]}); + var mapSearch; + // Define styles for icons and clusters + var searchIcon = L.Icon.extend({ + options: { + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41] + } + }); + + // Red will be used for search results display + var redIcon = new searchIcon({ + iconUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-red.png', + shadowUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png' + }); + + var redRectIcon = new searchIcon({ + iconUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-red.png', + shadowUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png' + }); + + // Blue will be used when a user selects a geofeature + var blueIcon = new searchIcon({ + iconUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-blue.png', + shadowUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png' + }); + var blueRectIcon = new searchIcon({ + iconUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/rectangle-icon-2x-blue.png', + shadowUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png' + }); + + // Initialize marker clusters with icon colors + var markerClusters = new L.MarkerClusterGroup({ + iconCreateFunction: function icf(cluster) { + var childCount = cluster.getChildCount(); + var markers = cluster.getAllChildMarkers(); + var cstatus = ""; + for (var i = 0; i < markers.length; i++) { + cstatus = markers[i].options.recStatus; + i = i + markers[i].options.total; + } + var c = ' marker-cluster-'; + if (cstatus === 'active') { + c += 'active'; + } else { + c += 'inactive'; + } + return new L.DivIcon({ html: '<div><span><b>' + childCount + '</b></span></div>', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) }); + } + }); + + // Handle user interaction with markers and rectangles + //----------------------------------------------------// + function onClick() { + mapSearch.eachLayer(function msl(layer) { + if (layer.options.id === "mRect") { + mapSearch.removeLayer(layer); + } + }); + + // Reset previously selected features to inactive color - RED + if (clickedIDs.length > 0) { + markerClusters.eachLayer(function mcl(layer){ + if (layer.options.recID === clickedIDs[0]) { + layer.options.recStatus = 'inactive'; + layer._popup.setContent(layer.options.recPopup); + if (layer.options.recType === "rectangle") { + layer.setIcon(redRectIcon); + } else { + layer.setIcon(redIcon); + } + } + }); + clickedIDs = []; + clickedBounds = []; + } + + //Handle current feature selection + //Change color of all features with thisID to BLUE + var thisID = this.options.recID; + clickedIDs.push(thisID); + var j = 0; + markerClusters.eachLayer(function mc(layer){ + if (layer.options.recID === thisID) { + j = j++; + layer.options.recStatus = 'active'; + if (layer.options.recType === "rectangle") { + layer.setIcon(blueRectIcon); + } else { + layer.setIcon(blueIcon); + } + clickedBounds.push([layer.getLatLng().lat, layer.getLatLng().lng]); + if (layer.options.recType === "rectangle") { + // create rectangle from options and show + var mRect_sw = L.latLng([layer.options.recS, layer.options.recW]); + var mRect_ne = L.latLng([layer.options.recN, layer.options.recE]); + var mRect = L.rectangle([[mRect_sw, mRect_ne]], { + color: '#3388ff', + fillOcpacity: 0.1, + weight: 2, + id: 'mRect' + }); + mRect.bindPopup(L.popup().setContent(layer.options.rmPopup)); + var mrBounds = mRect.getBounds(); + clickedBounds.push([ + [mrBounds.getSouthWest().lat, mrBounds.getSouthWest().lng], + [mrBounds.getNorthEast().lat, mrBounds.getNorthEast().lng] + ]); + mapSearch.addLayer(mRect); + } + } + }); + markerClusters.refreshClusters(); + + // Check if there are multiple markers at this location + // If so, update popup to show title for all rectangles by + // combining popup content. + thisID = this.options.recID; + var thisLat = this.getLatLng().lat; + var thisLng = this.getLatLng().lng; + var updatePopup = [this._popup.getContent()]; + markerClusters.eachLayer(function mc(layer){ + var mLat = layer.getLatLng().lat; + var mLng = layer.getLatLng().lng; + var mPopup = layer._popup.getContent(); + if ((mLat === thisLat && mLng === thisLng) && updatePopup.indexOf(mPopup) < 0) { + updatePopup.push(mPopup); + } + }); + this._popup.setContent(updatePopup.join(" ")); + } + + // Searchbox + //-------------------------------------// + // Retrieve searchbox coordinates + var sb_west = boundingBox[0]; + var sb_south = boundingBox[1]; + var sb_east = boundingBox[2]; + var sb_north = boundingBox[3]; + + // Adjust searchbox to a 0-360 grid if it crosses the dateline + if (sb_west > sb_east) { + // Move west left of east + if (sb_east >= 0) { + sb_west = sb_west - 360; + } else { + // Move east right of west + sb_east = sb_east + 360; + } + } + var sb_sw = L.latLng([sb_south, sb_west]); + var sb_ne = L.latLng([sb_north, sb_east]); + + // Create searchBox feature + var searchboxFeature = L.rectangle ([[sb_sw, sb_ne]], { + color: 'red', + fillColor: 'red', + fillOcpacity: 0.4, + weight: 2 + }); + + var sb_bounds = searchboxFeature.getBounds(); + var sb_center = sb_bounds.getCenter(); + searchboxFeature.addTo(searchboxLayer); + + // Search results + //-------------------------------------// + // Create a new vector type for rectangle features + // with getLatLng and setLatLng methods. + L.RectangleClusterable = L.Rectangle.extend({ + _originalInitialize: L.Rectangle.prototype.initialize, + initialize: function lrc(bounds, options) { + this._originalInitialize(bounds, options); + this._latlng = this.getBounds().getCenter(); + }, + getLatLng: function e() { + return this._latlng; + }, + setLatLng: function e() {} + }); + + // Process result coordinates + for (var i = 0; i < resultsCoords.length; ++i ) + { + var rcType; + var rcFeature; + var rcStatus = "inactive"; + var recID = resultsCoords[i][0]; + var recTitle = resultsCoords[i][1]; + var popupContent = '<article class="geoItem"><a href="' + homeURL + 'Record/' + recID + '">' + recTitle + '</a></article>'; + var popup = L.popup().setContent(popupContent); + var rc_west = resultsCoords[i][2]; + var rc_east = resultsCoords[i][3]; + var rc_north = resultsCoords[i][4]; + var rc_south = resultsCoords[i][5]; + sb_west = searchboxFeature.getBounds().getWest(); + sb_east = searchboxFeature.getBounds().getEast(); + + if (sb_west >= -180 && sb_east <= 180) { + // do nothing + } else { + //move coordinates if they are outside of searchbox bounds + if (rc_west < sb_west ) { + rc_west = rc_west + 360; + rc_east = rc_east + 360; + } + if (rc_east > sb_east) { + rc_west = rc_west - 360; + rc_east = rc_east - 360; + } + } + + if (rc_west === rc_east && rc_north === rc_south) { + rcType = "point"; + rcFeature = L.marker([rc_south, rc_west], { + recID: recID, + recType: rcType, + recStatus: rcStatus, + recPopup: popupContent, + icon: redIcon + }); + } else { + rcType = "rectangle"; + rcFeature = new L.RectangleClusterable([ + [rc_south, rc_west], + [rc_north, rc_east] + ], {recID: recID, recType: rcType, recStatus: rcStatus, recPopup: popupContent, color: '#910a0a' }); + } + rcFeature.bindPopup(popup); + rcFeature.on('click', onClick); + + // Only add feature to markerClusters if it is within or intersects searchbox + if (rcType === "rectangle") { + if (searchboxFeature.getBounds().intersects(rcFeature.getBounds())) { + // add center point to layer + var rectCtr = rcFeature.getBounds().getCenter(); + var rmPopupContent = '<article class="geoItem"><a href="' + homeURL + + 'Record/' + recID + '">' + recTitle + '</a><br><em>' + + VuFind.translate('rectangle_center_message') + '</em></article>'; + var rmPopup = L.popup().setContent(rmPopupContent); + var rectMarker = L.marker(rectCtr, { + recID: recID, + recType: "rectangle", + recStatus: rcStatus, + recPopup: rmPopupContent, + rmPopup: popupContent, + recN: rc_north, + recS: rc_south, + recE: rc_east, + recW: rc_west, + icon: redRectIcon + }); + rectMarker.bindPopup(rmPopup); + rectMarker.on('click', onClick); + markerClusters.addLayer(rectMarker); + mcgIDs.push(recID); + } + } else if (searchboxFeature.getBounds().contains(rcFeature.getLatLng())){ + markerClusters.addLayer(rcFeature); + mcgIDs.push(recID); + } + } + + // Turn on map selection pane + $('#geo_search').show(); + + // Create map + mapSearch = new L.Map ("geo_search_map", { + layers: [basemapLayer, searchboxLayer, markerClusters, drawnItemsLayer], + center: sb_center + }); + mapSearch.fitBounds(sb_bounds); + + // Add search functionality + drawnItems = new L.Draw.Rectangle(mapSearch); + + mapSearch.on('draw:created', function ms(e) { + var layer = e.layer; + drawnItemsLayer.addLayer(layer); + + // Get search box coordinates SW, NW, NE, SE + // note the wrap() function creates 180 to -180 compliant longitude values. + var di_ne = layer.getBounds().getNorthEast().wrap(); + var di_sw = layer.getBounds().getSouthWest().wrap(); + var di_north = di_ne.lat; + var di_east = di_ne.lng; + var di_south = di_sw.lat; + var di_west = di_sw.lng; + + //Create search query + var rawFilter = geoField + ':Intersects(ENVELOPE(' + di_west + ', ' + di_east + ', ' + di_north + ', ' + di_south + '))'; + location.href = baseURL + searchParams + "&filter[]=" + rawFilter; + }, this); + + document.getElementById("draw_box").onclick = function drawSearchBox() { + drawnItemsLayer.clearLayers(); + new L.Draw.Rectangle(mapSearch, drawnItems.options.rectangle).enable(); + }; + + // If user clicks on map anywhere turn all features to inactive color - RED + // and reset clicked arrays + mapSearch.on('click', function ms() { + mapSearch.eachLayer(function msl(layer) { + if (layer.options.id === "mRect") { + mapSearch.removeLayer(layer); + } + }); + + if (clickedIDs.length > 0) { + markerClusters.eachLayer(function mc(layer){ + layer.options.recStatus = 'inactive'; + if (layer.options.recType === "rectangle") { + layer.setIcon(redRectIcon); + } else { + layer.setIcon(redIcon); + } + }); + clickedIDs = []; + clickedBounds = []; + markerClusters.refreshClusters(); + } + }); +} diff --git a/themes/bootstrap3/js/map_tab_leaflet.js b/themes/bootstrap3/js/map_tab_leaflet.js new file mode 100644 index 0000000000000000000000000000000000000000..9d7087466b1c99a0b026250e4a49f41d1ad1d03f --- /dev/null +++ b/themes/bootstrap3/js/map_tab_leaflet.js @@ -0,0 +1,112 @@ +/*global L, VuFind */ +/*exported loadMapTab */ +//Coordinate order: Storage and Query: WENS ; Display: WSEN + +function loadMapTab(mapData, mapGraticule, basemap) { + var init = true; + var basemapLayer = new L.TileLayer(basemap[0], {attribution: basemap[1]}); + var geoFeatureGroup = L.featureGroup(); + // Define styles for icons + var displayIcon = L.Icon.extend({ + options: { + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41] + } + }); + var redIcon = new displayIcon({ + iconUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-icon-2x-red.png', + shadowUrl: VuFind.path + '/themes/bootstrap3/css/vendor/leaflet/images/marker-shadow.png' + }); + + $('#map-canvas').show(); + init = function drawMap() { + var featureCount = mapData.length; + var label, label_name, label_coords, split_coords; + var i = 0; + // Construct geofeatures with labels + for (i; i < featureCount; i++) { + // Construct the label names + label_name = mapData[i][4]; + //Construct the coordinate labels + label_coords = mapData[i][5].split(/[ ]+/).join(', '); + if (label_coords.split(",").length - 1 === 3) { + split_coords = label_coords.match("(.*,.*),(.*,.*)"); + label_coords = split_coords[1] + "<br>" + split_coords[2]; + } + // Construct the entire label string + var labelParts = []; + if (label_name) { + labelParts[labelParts.length] = '<strong>' + label_name + '</strong>'; + } + if (label_coords) { + labelParts[labelParts.length] = VuFind.translate('Coordinates') + ":<br>" + + label_coords; + } + label = labelParts.length > 0 + ? labelParts.join('<br>') : VuFind.translate('no_description'); + // Get coordinate data + var west = mapData[i][0]; + var south = mapData[i][1]; + var east = mapData[i][2]; + var north = mapData[i][3]; + + // Create features + var geoFeature; + if (west === east && north === south) { + geoFeature = L.marker([south, west], {icon: redIcon}); + } else { // It's a polygon feature // + // Adjust rectangle display if it crosses the dateline + if (west > east) { + // Move west left of east + if (east >= 0) { + west = west - 360; + } else { + // Move east right of west + east = east + 360; + } + } + geoFeature = L.rectangle([[south, west], [north, east]], { + color: '#910a0a', + fillColor: '#f03', + fillOcpacity: 0.3, + weight: 2 + }); + } + geoFeature.bindPopup(label); + geoFeature.addTo(geoFeatureGroup); + } + //Get center of geoFeatures group + var geoBounds = geoFeatureGroup.getBounds(); + var geoCenter = geoBounds.getCenter(); + + // Draw map and add layers + var mapTab = new L.Map("map-canvas", { + layers: [basemapLayer, geoFeatureGroup], + center: geoCenter + }); + + mapTab.fitBounds(geoBounds); + + // Reset zoom level if we have only a few features and high zoom level + if (mapTab.getZoom() > 8 && featureCount < 3) { + mapTab.setZoom(2); + } + + // Create the graticule component + if (mapGraticule) { + L.latlngGraticule({ + showLabel: true, + zoomInterval: [ + {start: 2, end: 3, interval: 30}, + {start: 4, end: 4, interval: 10}, + {start: 5, end: 7, interval: 5}, + {start: 8, end: 10, interval: 1} + ] + }).addTo(mapTab); + } + }; + init(); + init = false; +} diff --git a/themes/bootstrap3/js/map_tab_ol.js b/themes/bootstrap3/js/map_tab_ol.js deleted file mode 100644 index 9c4ecf2d5180d46650922efb6684f07f3628d283..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/js/map_tab_ol.js +++ /dev/null @@ -1,210 +0,0 @@ -/*global ol */ -/*exported loadMapTab */ -//Coordinate order: Storage and Query: WENS ; Display: WSEN -function loadMapTab(mapData, popupTitle, mapGraticule) { - var init = true; - var pTitle = popupTitle + '<button class="close">×</button>'; - var srcProj = 'EPSG:4326'; - var dstProj = 'EPSG:900913'; - var osm = new ol.layer.Tile({source: new ol.source.OSM()}); - var vectorSource = new ol.source.Vector(); - var map; - var iconStyle = new ol.style.Style({ - image: new ol.style.Circle({ - radius: 5, - fill: new ol.style.Fill({ - color: 'red' - }) - }) - }); - var polyStyle = new ol.style.Style({ - fill: new ol.style.Fill({ - color: [200, 0, 0, 0.1] - }), - stroke: new ol.style.Stroke({ - color: 'red', - width: 2 - }) - }); - - // close map popups if not on the Map tab - $('.record-tabs .map').on('hide.bs.tab', function closePopups() { - $('#popup').popover('destroy'); - }); - - $('#map-canvas').show(); - init = function drawMap() { - var featureCount = mapData.length; - var label, label_on; - var label_name; - var label_coords, label_coord, label_coord1, label_coord2; - var i = 0; - for (i; i < featureCount; i++) { - // Construct the label names - label_name = mapData[i][5]; - //Construct the coordinate labels - label_coords = mapData[i][6]; - if (label_coords) { - label_coord1 = mapData[i][6].substring(0, 16); - label_coord2 = mapData[i][6].substring(16); - if (label_coord2) { - label_coord = label_coord1 + '<br/>' + label_coord2; - } else { - label_coord = label_coord1; - } - } - // Construct the entire label string - if (label_coord && label_name) { - label = label_coord + '<br/>' + label_name; - label_on = true; - } else if (label_name){ - label = label_name; - label_on = true; - } else if (label_coord) { - label = label_coord; - label_on = true; - } else { - label = 'No information available'; - label_on = false; - } - // Determine if entry is point or polygon - Does W=E & N=S? // - if (mapData[i][4] === 2) { - //It's a point feature // - var lonlat = ol.proj.transform([mapData[i][0], mapData[i][1]], srcProj, dstProj); - var iconFeature = new ol.Feature({ - geometry: new ol.geom.Point(lonlat), - name: label - }); - iconFeature.setStyle(iconStyle); - vectorSource.addFeature(iconFeature); - } else if (mapData[i][4] === 4) { // It's a polygon feature // - var west = mapData[i][0]; - var east = mapData[i][2]; - var north = mapData[i][3]; - var south = mapData[i][1]; - // handle dateline crossing - if (west > east) { - var wpt = 180 - west; - var ept = 180 + east; - east = west + wpt + ept; - } - // move S90 off the poles so ol will map it - if (south === -90) { - south = south + 0.5; - } - var point1 = ol.proj.transform([west, north], srcProj, dstProj); - var point2 = ol.proj.transform([west, south], srcProj, dstProj); - var point3 = ol.proj.transform([east, south], srcProj, dstProj); - var point4 = ol.proj.transform([east, north], srcProj, dstProj); - - var polyFeature = new ol.Feature({ - geometry: new ol.geom.Polygon([ - [point1, point2, point3, point4, point1] - ]), - name: label - }); - polyFeature.setStyle(polyStyle); - vectorSource.addFeature(polyFeature); - } - } - var vectorLayer = new ol.layer.Vector({ - source: vectorSource, - renderBuffer: 500 - }); - map = new ol.Map({ - renderer: 'canvas', - projection: dstProj, - layers: [osm, vectorLayer], - target: 'map-canvas', - view: new ol.View({ - center: [0, 0], - zoom: 1 - }) - }); - - // Adjust zoom extent and center of map - if (featureCount === 1 && mapData[0][4] === 2) { - map.getView().setZoom(4); - var centerCoord = ol.proj.transform([mapData[0][0], mapData[0][1]], srcProj, dstProj); - map.getView().setCenter(centerCoord); - } else { - var extent = vectorLayer.getSource().getExtent(); - map.getView().fit(extent, map.getSize()); - } - - // Create the graticule component - if (mapGraticule) { - var graticule = new ol.Graticule({ - // the style to use for the lines, optional. - strokeStyle: new ol.style.Stroke({ - color: 'rgba(255,120,0,0.9)', - width: 2, - lineDash: [0.5, 4] - }), - showLabels: true - }); - graticule.setMap(map); - } - // Turn on popup tool tips if labels or coordinates are enabled. - if (label_on === true) { - var element = document.getElementById('popup'); - var popup = new ol.Overlay({ - element: element, - stopEvent: true - }); - map.addOverlay(popup); - - // Display popup on click - map.on('click', function displayPopup(evt) { - var popupfeature = map.forEachFeatureAtPixel(evt.pixel, - function showFeature(feature) { - return feature; - }); - if (popupfeature) { - var coordinate = evt.coordinate; - popup.setPosition(coordinate); - $(element).popover({ - 'placement': 'auto', - 'container': 'body', - 'animation': false, - 'html': true, - 'title': pTitle - }).on('shown.bs.popover', function closePopup(e) { - // 'aria-describedby' is the id of the current popover - var current_popover = '#' + $(e.target).attr('aria-describedby'); - var $cur_pop = $(current_popover); - $cur_pop.find('.close').click(function closeCurPop(){ - $(element).popover('destroy'); - }); - }); - $(element).data('bs.popover').options.content = popupfeature.get('name'); - $(element).popover('show'); - } else { - $(element).popover('destroy'); - } - }); - - // change mouse cursor when over marker - map.on('pointermove', function changeMouseCursor(e) { - if (e.dragging) { - $(element).popover('destroy'); - return; - } - var pixel = map.getEventPixel(e.originalEvent); - var hit = map.hasFeatureAtPixel(pixel); - var target = map.getTarget(); - if (hit === true) { - document.getElementById(target).style.cursor = "pointer"; - } else { - document.getElementById(target).style.cursor = "default"; - } - }); - // close popup if zoom in / out occurs - map.getView().on('change:resolution', function closePopupsOnZoom() { - $(element).popover('destroy'); - }); - } - }; - init(); - init = false; -} diff --git a/themes/bootstrap3/js/openurl.js b/themes/bootstrap3/js/openurl.js index 8056d7df8c3f8ad43a2852e332f169ccfb2c4c86..6d26147839cc2ff9e10cd24be4e18e7c5d62d970 100644 --- a/themes/bootstrap3/js/openurl.js +++ b/themes/bootstrap3/js/openurl.js @@ -11,14 +11,14 @@ VuFind.register('openurl', function OpenUrl() { dataType: 'json', url: url }) - .done(function getResolverLinksDone(response) { - $target.removeClass('ajax_availability').empty().append(response.data); - }) - .fail(function getResolverLinksFail(response, textStatus) { - $target.removeClass('ajax_availability').addClass('text-danger').empty(); - if (textStatus === 'abort' || typeof response.responseJSON == 'undefined') { return; } - $target.append(response.responseJSON.data); - }); + .done(function getResolverLinksDone(response) { + $target.removeClass('ajax_availability').empty().append(response.data.html); + }) + .fail(function getResolverLinksFail(response, textStatus) { + $target.removeClass('ajax_availability').addClass('text-danger').empty(); + if (textStatus === 'abort' || typeof response.responseJSON == 'undefined') { return; } + $target.append(response.responseJSON.data); + }); } function embedOpenUrlLinks(el) { @@ -44,7 +44,7 @@ VuFind.register('openurl', function OpenUrl() { // combined results fetched with AJAX are loaded. function init(_container) { var container = _container || $('body'); - // assign action to the openUrlWindow link class + // assign action to the openUrlWindow link class container.find('a.openUrlWindow').unbind('click').click(function openUrlWindowClick() { var params = extractClassParams(this); var settings = params.window_settings; diff --git a/themes/bootstrap3/js/preview.js b/themes/bootstrap3/js/preview.js index 8d71e1bf15c61e5a6fe8af49ddba8a8f49e9448d..677118850de673eaac37e9275da2211914d30368 100644 --- a/themes/bootstrap3/js/preview.js +++ b/themes/bootstrap3/js/preview.js @@ -22,9 +22,9 @@ function getOLOptions() { function getHTPreviews(keys) { var skeys = keys.replace(/(ISBN|LCCN|OCLC)/gi, '$1:').toLowerCase(); var bibkeys = skeys.split(/\s+/); - // fetch 20 books at time if there are more than 20 - // since hathitrust only allows 20 at a time - // as per https://vufind.org/jira/browse/VUFIND-317 + // fetch 20 books at time if there are more than 20 + // since hathitrust only allows 20 at a time + // as per https://vufind.org/jira/browse/VUFIND-317 var batch = []; for (var i = 0; i < bibkeys.length; i++) { batch.push(bibkeys[i]); @@ -40,12 +40,12 @@ function getHTPreviews(keys) { function applyPreviewUrl($link, url) { // Update the preview button: $link.attr('href', url).removeClass('hidden') - .attr('rel', 'noopener'); // Performance improvement + .attr('rel', 'noopener'); // Performance improvement // Update associated record thumbnail, if any: $link.parents('.result,.record') - .find('.recordcover[data-linkpreview="true"]').parents('a').attr('href', url) - .attr('rel', 'noopener'); + .find('.recordcover[data-linkpreview="true"]').parents('a').attr('href', url) + .attr('rel', 'noopener'); } function processBookInfo(booksInfo, previewClass, viewOptions) { @@ -66,7 +66,7 @@ function processGBSBookInfo(booksInfo) { processBookInfo(booksInfo, 'previewGBS', viewOptions.link); } if (viewOptions.tab && viewOptions.tab.length > 0) { - // check for "embeddable: true" in bookinfo + // check for "embeddable: true" in bookinfo for (var bibkey in booksInfo) { if (booksInfo[bibkey]) { if (viewOptions.tab.indexOf(booksInfo[bibkey].preview) >= 0 @@ -112,9 +112,9 @@ function setIndexOf() { throw new TypeError(); } var t = Object(this); - /*jslint bitwise: false*/ + /*jslint bitwise: false*/ var len = t.length >>> 0; - /*jslint bitwise: true*/ + /*jslint bitwise: true*/ if (len === 0) { return -1; } @@ -153,24 +153,24 @@ function getBookPreviews() { var bibkeys = skeys.split(/\s+/); var script; - // fetch Google preview if enabled + // fetch Google preview if enabled if ($('[class*="googlePreviewSpan"]').length > 0) { - // checks if query string might break URI limit - if not, run as normal - if (bibkeys.length <= 150){ + // checks if query string might break URI limit - if not, run as normal + if (bibkeys.length <= 150) { script = 'https://encrypted.google.com/books?jscmd=viewapi&bibkeys=' - + bibkeys.join(',') + '&callback=processGBSBookInfo'; + + bibkeys.join(',') + '&callback=processGBSBookInfo'; $.getScript(script); } else { - // if so, break request into chunks of 100 + // if so, break request into chunks of 100 var keyString = ''; - // loop through array + // loop through array for (var i = 0; i < bibkeys.length; i++){ keyString += bibkeys[i] + ','; - // send request when there are 100 requests ready or when there are no - // more elements to be sent + // send request when there are 100 requests ready or when there are no + // more elements to be sent if ((i > 0 && i % 100 === 0) || i === bibkeys.length - 1) { script = 'https://encrypted.google.com/books?jscmd=viewapi&bibkeys=' - + keyString + '&callback=processGBSBookInfo'; + + keyString + '&callback=processGBSBookInfo'; $.getScript(script); keyString = ''; } @@ -178,14 +178,14 @@ function getBookPreviews() { } } - // fetch OpenLibrary preview if enabled + // fetch OpenLibrary preview if enabled if ($('[class*="olPreviewSpan"]').length > 0) { script = '//openlibrary.org/api/books?bibkeys=' - + bibkeys.join(',') + '&callback=processOLBookInfo'; + + bibkeys.join(',') + '&callback=processOLBookInfo'; $.getScript(script); } - // fetch HathiTrust preview if enabled + // fetch HathiTrust preview if enabled if ($('[class*="hathiPreviewSpan"]').length > 0) { getHTPreviews(skeys); } diff --git a/themes/bootstrap3/js/pubdate_vis.js b/themes/bootstrap3/js/pubdate_vis.js index 85857a1b9181a75d18d739b669a521ac60f7aa38..cfe8de321b9fb200cc5334ba7a847cbea24b0af0 100644 --- a/themes/bootstrap3/js/pubdate_vis.js +++ b/themes/bootstrap3/js/pubdate_vis.js @@ -18,15 +18,19 @@ function PadDigits(number, totalDigits) { function loadVis(facetFields, searchParams, baseURL, zooming) { // Get colors from CSS var cssColorSettings = { - 'background-color': '#fff', // background of box - 'fill': '#eee', // box fill color - 'stroke': '#265680', // box outline color - 'outline-color': '#c38835' // selection color + // background of box + 'background-color': '#fff', + // box fill color + 'fill': '#eee', + // box outline color + 'stroke': '#265680', + // selection color + 'outline-color': '#c38835' }; var $dateVisColorSettings = $('#dateVisColorSettings'); for (var rule in cssColorSettings) { if ($dateVisColorSettings.css(rule)) { - var match = $dateVisColorSettings.css(rule).match(/rgb[a]?\([^\)]+\)|#[a-fA-F0-9]+/); + var match = $dateVisColorSettings.css(rule).match(/rgb[a]?\([^)]+\)|#[a-fA-F0-9]+/); if (null != match) { cssColorSettings[rule] = match[0]; } @@ -51,9 +55,11 @@ function loadVis(facetFields, searchParams, baseURL, zooming) { }; // AJAX call + var graphMargin = 5; + var url = baseURL + '/AJAX/json?method=getVisData&facetFields=' + encodeURIComponent(facetFields) + '&' + searchParams; $.getJSON(url, function getVisDataJSON(data) { - $.each(data.data, function getVisDataEach(key, val) { + $.each(data.data.facets, function getVisDataEach(key, val) { //check if there is data to display, if there isn't hide the box if (val.data === undefined || val.data.length === 0) { return; @@ -73,24 +79,24 @@ function loadVis(facetFields, searchParams, baseURL, zooming) { //check if the min and max value have been set otherwise set them to the ends of the graph if (val.min === 0) { - val.min = val.data[0][0] - 5; + val.min = val.data[0][0] - graphMargin; } if (val.max === 0) { - val.max = parseInt(val.data[val.data.length - 1][0], 10) + 5; + val.max = parseInt(val.data[val.data.length - 1][0], 10) + graphMargin; } if (zooming && hasFilter) { //check the first and last elements of the data array against min and max value (+padding) //if the element exists leave it, otherwise create a new marker with a minus one value - if (val.data[val.data.length - 1][0] !== parseInt(val.max, 10) + 5) { - val.data.push([parseInt(val.max, 10) + 5, -1]); + if (val.data[val.data.length - 1][0] !== parseInt(val.max, 10) + graphMargin) { + val.data.push([parseInt(val.max, 10) + graphMargin, -1]); } - if (val.data[0][0] !== val.min - 5) { - val.data.push([val.min - 5, -1]); + if (val.data[0][0] !== val.min - graphMargin) { + val.data.push([val.min - graphMargin, -1]); } //check for values outside the selected range and remove them by setting them to null for (var i = 0; i < val.data.length; i++) { - if (val.data[i][0] < val.min - 5 || val.data[i][0] > parseInt(val.max, 10) + 5) { + if (val.data[i][0] < val.min - graphMargin || val.data[i][0] > parseInt(val.max, 10) + graphMargin) { //remove this val.data.splice(i, 1); i--; @@ -100,9 +106,9 @@ function loadVis(facetFields, searchParams, baseURL, zooming) { } else { //no zooming means that we need to specifically set the margins //do the last one first to avoid getting the new last element - val.data.push([parseInt(val.data[val.data.length - 1][0], 10) + 5, -1]); + val.data.push([parseInt(val.data[val.data.length - 1][0], 10) + graphMargin, -1]); //now get the first element - val.data.push([val.data[0][0] - 5, -1]); + val.data.push([val.data[0][0] - graphMargin, -1]); } diff --git a/themes/bootstrap3/js/record.js b/themes/bootstrap3/js/record.js index 10abb8eaefb53788f451dfb339e65e08fc26ca8b..5caa8621cdbd7c994207c5a6f537f8ceeb6db049 100644 --- a/themes/bootstrap3/js/record.js +++ b/themes/bootstrap3/js/record.js @@ -5,7 +5,7 @@ * Functions and event handlers specific to record pages. */ function checkRequestIsValid(element, requestType) { - var recordId = element.href.match(/\/Record\/([^\/]+)\//)[1]; + var recordId = element.href.match(/\/Record\/([^/]+)\//)[1]; var vars = deparam(element.href); vars.id = recordId; @@ -20,18 +20,18 @@ function checkRequestIsValid(element, requestType) { cache: false, url: url }) - .done(function checkValidDone(response) { - if (response.data.status) { - $(element).removeClass('disabled') - .attr('title', response.data.msg) - .html('<i class="fa fa-flag" aria-hidden="true"></i> ' + response.data.msg); - } else { + .done(function checkValidDone(response) { + if (response.data.status) { + $(element).removeClass('disabled') + .attr('title', response.data.msg) + .html('<i class="fa fa-flag" aria-hidden="true"></i> ' + response.data.msg); + } else { + $(element).remove(); + } + }) + .fail(function checkValidFail(/*response*/) { $(element).remove(); - } - }) - .fail(function checkValidFail(/*response*/) { - $(element).remove(); - }); + }); } function setUpCheckRequest() { @@ -52,9 +52,9 @@ function deleteRecordComment(element, recordId, recordSource, commentId) { dataType: 'json', url: url }) - .done(function deleteCommentDone(/*response*/) { - $($(element).closest('.comment')[0]).remove(); - }); + .done(function deleteCommentDone(/*response*/) { + $($(element).closest('.comment')[0]).remove(); + }); } function refreshCommentList($target, recordId, recordSource) { @@ -67,19 +67,19 @@ function refreshCommentList($target, recordId, recordSource) { dataType: 'json', url: url }) - .done(function refreshCommentListDone(response) { - // Update HTML - var $commentList = $target.find('.comment-list'); - $commentList.empty(); - $commentList.append(response.data); - $commentList.find('.delete').unbind('click').click(function commentRefreshDeleteClick() { - var commentId = $(this).attr('id').substr('recordComment'.length); - deleteRecordComment(this, recordId, recordSource, commentId); - return false; + .done(function refreshCommentListDone(response) { + // Update HTML + var $commentList = $target.find('.comment-list'); + $commentList.empty(); + $commentList.append(response.data.html); + $commentList.find('.delete').unbind('click').click(function commentRefreshDeleteClick() { + var commentId = $(this).attr('id').substr('recordComment'.length); + deleteRecordComment(this, recordId, recordSource, commentId); + return false; + }); + $target.find('.comment-form input[type="submit"]').button('reset'); + resetCaptcha($target); }); - $target.find('.comment-form input[type="submit"]').button('reset'); - resetCaptcha($target); - }); } function registerAjaxCommentRecord() { @@ -106,21 +106,21 @@ function registerAjaxCommentRecord() { data: data, dataType: 'json' }) - .done(function addCommentDone(/*response, textStatus*/) { - var $form = $(form); - var $tab = $form.closest('.list-tab-content'); - if (!$tab.length) { - $tab = $form.closest('.tab-pane'); - } - refreshCommentList($tab, id, recordSource); - $form.find('textarea[name="comment"]').val(''); - $form.find('input[type="submit"]').button('loading'); - resetCaptcha($form); - }) - .fail(function addCommentFail(response, textStatus) { - if (textStatus === 'abort' || typeof response.responseJSON === 'undefined') { return; } - VuFind.lightbox.alert(response.responseJSON.data, 'danger'); - }); + .done(function addCommentDone(/*response, textStatus*/) { + var $form = $(form); + var $tab = $form.closest('.list-tab-content'); + if (!$tab.length) { + $tab = $form.closest('.tab-pane'); + } + refreshCommentList($tab, id, recordSource); + $form.find('textarea[name="comment"]').val(''); + $form.find('input[type="submit"]').button('loading'); + resetCaptcha($form); + }) + .fail(function addCommentFail(response, textStatus) { + if (textStatus === 'abort' || typeof response.responseJSON === 'undefined') { return; } + VuFind.lightbox.alert(response.responseJSON.data, 'danger'); + }); return false; }); // Delete links @@ -138,11 +138,6 @@ function registerTabEvents() { registerAjaxCommentRecord(); // Render recaptcha recaptchaOnLoad(); - // Delete links - $('.delete').click(function commentTabDeleteClick() { - deleteRecordComment(this, $('.hiddenId').val(), $('.hiddenSource').val(), this.id.substr(13)); - return false; - }); setUpCheckRequest(); @@ -165,23 +160,23 @@ function ajaxLoadTab($newTab, tabid, setHash) { type: 'POST', data: {tab: tabid} }) - .always(function ajaxLoadTabDone(data) { - if (typeof data === 'object') { - $newTab.html(data.responseText ? data.responseText : VuFind.translate('error_occurred')); - } else { - $newTab.html(data); - } - registerTabEvents(); - if (typeof syn_get_widget === "function") { - syn_get_widget(); - } - if (typeof setHash == 'undefined' || setHash) { - window.location.hash = tabid; - } else { - removeHashFromLocation(); - } - setupJumpMenus($newTab); - }); + .always(function ajaxLoadTabDone(data) { + if (typeof data === 'object') { + $newTab.html(data.responseText ? data.responseText : VuFind.translate('error_occurred')); + } else { + $newTab.html(data); + } + registerTabEvents(); + if (typeof syn_get_widget === "function") { + syn_get_widget(); + } + if (typeof setHash == 'undefined' || setHash) { + window.location.hash = tabid; + } else { + removeHashFromLocation(); + } + setupJumpMenus($newTab); + }); return false; } @@ -198,18 +193,18 @@ function refreshTagList(_target, _loggedin) { source: recordSource }); $.ajax({ - dataType: 'html', + dataType: 'json', url: url }) - .done(function getRecordTagsDone(response) { - $tagList.empty(); - $tagList.replaceWith(response); - if (loggedin) { - $tagList.addClass('loggedin'); - } else { - $tagList.removeClass('loggedin'); - } - }); + .done(function getRecordTagsDone(response) { + $tagList.empty(); + $tagList.replaceWith(response.data.html); + if (loggedin) { + $tagList.addClass('loggedin'); + } else { + $tagList.removeClass('loggedin'); + } + }); } } function refreshTagListCallback() { @@ -232,9 +227,9 @@ function ajaxTagUpdate(_link, tag, _remove) { remove: remove } }) - .always(function tagRecordAlways() { - refreshTagList($target, false); - }); + .always(function tagRecordAlways() { + refreshTagList($target, false); + }); } function getNewRecordTab(tabid) { @@ -251,7 +246,7 @@ function backgroundLoadTab(tabid) { } function applyRecordTabHash() { - var activeTab = $('.record-tabs li.active a').attr('class'); + var activeTab = $('.record-tabs li.active').attr('data-tab'); var $initiallyActiveTab = $('.record-tabs li.initiallyActive a'); var newTab = typeof window.location.hash !== 'undefined' ? window.location.hash.toLowerCase() : ''; @@ -260,7 +255,7 @@ function applyRecordTabHash() { if (newTab.length <= 1 || newTab === '#tabnav') { $initiallyActiveTab.click(); } else if (newTab.length > 1 && '#' + activeTab !== newTab) { - $('.' + newTab.substr(1)).click(); + $('.' + newTab.substr(1) + ' a').click(); } } @@ -273,7 +268,7 @@ function recordDocReady() { if ($li.hasClass('active')) { return true; } - var tabid = this.className; + var tabid = $li.attr('data-tab'); var $top = $(this).closest('.record-tabs'); // if we're flagged to skip AJAX for this tab, we need special behavior: if ($li.hasClass('noajax')) { diff --git a/themes/bootstrap3/js/relais.js b/themes/bootstrap3/js/relais.js new file mode 100644 index 0000000000000000000000000000000000000000..0524c1750f912fe43bbab69ba39deb825f8c5a99 --- /dev/null +++ b/themes/bootstrap3/js/relais.js @@ -0,0 +1,105 @@ +/*global VuFind*/ +VuFind.register('relais', function Relais() { + function hideAvailabilityCheckMessages(failLink) { + $("span[class='relaisLink']").each(function linkFormatter() { + var $current = $(this); + var text = VuFind.translate('relais_search'); + $current.html('<a class="relaisRecordButton" target="new" href="' + failLink + '">' + text + '</a>'); + }); + } + + function checkAvailability(addLink, oclc, failLink) { + // Don't waste time checking availability if there are no links! + if (!$('.relaisLink').length) { + return false; + } + + var url = VuFind.path + '/AJAX/JSON?' + $.param({ + method: 'relaisAvailability', + oclcNumber: oclc + }); + $.ajax({ + dataType: 'json', + url: url, + success: function checkAvailabilitySuccessCallback(response) { + if (response.data.result === "ok") { + $("span[class='relaisLink']").each(function linkFormatter() { + var $current = $(this); + var text = VuFind.translate('relais_request'); + $current.html('<a class="relaisRecordButton" class="modal-link">' + text + '</a>'); + $current.find('.relaisRecordButton').click(function addRecordButtonOnClick() { VuFind.lightbox.ajax({url: addLink + '?' + $.param({oclc: oclc, failLink: failLink})}); }); + }); + } else { + hideAvailabilityCheckMessages(failLink); + } + }, + error: function checkAvailabilityError() { hideAvailabilityCheckMessages(failLink); } + }); + } + + function cancelRequestOnClick() { + $('#modal').modal('hide'); // hide the modal + $('#modal-dynamic-content').empty(); // empties dynamic content + $('.modal-backdrop').remove(); // removes all modal-backdrops + } + + function errorCallback(failLink) { + $('#requestButton').html("<input class='btn btn-primary' data-dismiss='modal' id='cancelRelaisRequest' type='submit' value='" + VuFind.translate('close') + "'>"); + $('#requestMessage').html(VuFind.translate('relais_error_html', {'%%url%%': failLink})); + $('#cancelRelaisRequest').unbind('click').click(cancelRequestOnClick); + } + + function makeRequest(url, failLink) { + $('#requestButton').html( + '<i class="fa fa-spinner fa-spin"></i> ' + VuFind.translate('relais_requesting') + ); + $.ajax({ + dataType: 'json', + url: url, + success: function makeRequestSuccessCallback(response) { + var obj = jQuery.parseJSON(response.data.result); + $('#requestButton').html("<input class='btn btn-primary' data-dismiss='modal' id='cancelRelaisRequest' type='submit' value='" + VuFind.translate('close') + "'>"); + $('#requestMessage').html("<b>" + VuFind.translate('relais_success_label') + "</b> " + VuFind.translate('relais_success_message', {'%%id%%': obj.RequestNumber})); + $('#cancelRelaisRequest').unbind('click').click(cancelRequestOnClick); + }, + error: function makeRequestErrorWrapper() { errorCallback(failLink); } + }); + } + + function addItem(oclc, failLink) { + var url = VuFind.path + '/AJAX/JSON?' + $.param({ + method: 'relaisInfo', + oclcNumber: oclc + }); + $.ajax({ + dataType: 'json', + url: url, + success: function infoSuccessCallback(response) { + var obj = jQuery.parseJSON(response.data.result); + if (obj && obj.Available) { + $('#requestMessage').html(VuFind.translate('relais_available')); + $('#requestButton').html( + "<input class='btn btn-primary' id='makeRelaisRequest' type='submit' value='" + VuFind.translate('confirm_dialog_yes') + "'>" + + " <input class='btn btn-primary' data-dismiss='modal' id='cancelRelaisRequest' type='submit' value='" + VuFind.translate('confirm_dialog_no') + "'>" + ); + $('#makeRelaisRequest').unbind('click').click(function makeRequestOnClick() { + var orderUrl = VuFind.path + '/AJAX/JSON?' + $.param({ + method: 'relaisOrder', + oclcNumber: oclc + }); + makeRequest(orderUrl, failLink); + }); + $('#cancelRelaisRequest').unbind('click').click(cancelRequestOnClick); + } else { + errorCallback(failLink); + } + }, + error: function addItemErrorWrapper() { errorCallback(failLink); } + }); + } + + return { + checkAvailability: checkAvailability, + addItem: addItem + }; +}); diff --git a/themes/bootstrap3/js/vendor/base64.js b/themes/bootstrap3/js/vendor/base64.js deleted file mode 100644 index eef795f051c09e4e258115e22ce614c9dae9e832..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/js/vendor/base64.js +++ /dev/null @@ -1,149 +0,0 @@ -/** -* -* Base64 encode / decode -* http://www.webtoolkit.info/ -* -**/ - -var Base64 = { - - // private property - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - - // public method for encoding - encode : function (input) { - var output = ""; - var chr1, chr2, chr3, enc1, enc2, enc3, enc4; - var i = 0; - - input = Base64._utf8_encode(input); - - while (i < input.length) { - - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - output = output + - this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + - this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); - - } - - return output; - }, - - // public method for decoding - decode : function (input) { - var output = ""; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0; - - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - - while (i < input.length) { - - enc1 = this._keyStr.indexOf(input.charAt(i++)); - enc2 = this._keyStr.indexOf(input.charAt(i++)); - enc3 = this._keyStr.indexOf(input.charAt(i++)); - enc4 = this._keyStr.indexOf(input.charAt(i++)); - - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - - output = output + String.fromCharCode(chr1); - - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } - - } - - output = Base64._utf8_decode(output); - - return output; - - }, - - // private method for UTF-8 encoding - _utf8_encode : function (string) { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } - - } - - return utftext; - }, - - // private method for UTF-8 decoding - _utf8_decode : function (utftext) { - var string = ""; - var i = 0; - var c = c1 = c2 = 0; - - while ( i < utftext.length ) { - - c = utftext.charCodeAt(i); - - if (c < 128) { - string += String.fromCharCode(c); - i++; - } - else if((c > 191) && (c < 224)) { - c2 = utftext.charCodeAt(i+1); - string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); - i += 2; - } - else { - c2 = utftext.charCodeAt(i+1); - c3 = utftext.charCodeAt(i+2); - string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); - i += 3; - } - - } - - return string; - } - -} - -function btoa(op) { - return Base64.encode(op); -} -function atob(op) { - return Base64.decode(op); -} \ No newline at end of file diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet-src.js b/themes/bootstrap3/js/vendor/leaflet/leaflet-src.js new file mode 100644 index 0000000000000000000000000000000000000000..0206a73143e5f2c308f0a1b0a403a719e834423f --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet-src.js @@ -0,0 +1,13802 @@ +/* @preserve + * Leaflet 1.3.1+Detached: ba6f97fff8647e724e4dfe66d2ed7da11f908989.ba6f97f, a JS library for interactive maps. http://leafletjs.com + * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.L = {}))); +}(this, (function (exports) { 'use strict'; + +var version = "1.3.1+HEAD.ba6f97f"; + +/* + * @namespace Util + * + * Various utility functions, used by Leaflet internally. + */ + +var freeze = Object.freeze; +Object.freeze = function (obj) { return obj; }; + +// @function extend(dest: Object, src?: Object): Object +// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. +function extend(dest) { + var i, j, len, src; + + for (j = 1, len = arguments.length; j < len; j++) { + src = arguments[j]; + for (i in src) { + dest[i] = src[i]; + } + } + return dest; +} + +// @function create(proto: Object, properties?: Object): Object +// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) +var create = Object.create || (function () { + function F() {} + return function (proto) { + F.prototype = proto; + return new F(); + }; +})(); + +// @function bind(fn: Function, …): Function +// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). +// Has a `L.bind()` shortcut. +function bind(fn, obj) { + var slice = Array.prototype.slice; + + if (fn.bind) { + return fn.bind.apply(fn, slice.call(arguments, 1)); + } + + var args = slice.call(arguments, 2); + + return function () { + return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); + }; +} + +// @property lastId: Number +// Last unique ID used by [`stamp()`](#util-stamp) +var lastId = 0; + +// @function stamp(obj: Object): Number +// Returns the unique ID of an object, assigning it one if it doesn't have it. +function stamp(obj) { + /*eslint-disable */ + obj._leaflet_id = obj._leaflet_id || ++lastId; + return obj._leaflet_id; + /* eslint-enable */ +} + +// @function throttle(fn: Function, time: Number, context: Object): Function +// Returns a function which executes function `fn` with the given scope `context` +// (so that the `this` keyword refers to `context` inside `fn`'s code). The function +// `fn` will be called no more than one time per given amount of `time`. The arguments +// received by the bound function will be any arguments passed when binding the +// function, followed by any arguments passed when invoking the bound function. +// Has an `L.throttle` shortcut. +function throttle(fn, time, context) { + var lock, args, wrapperFn, later; + + later = function () { + // reset lock and call if queued + lock = false; + if (args) { + wrapperFn.apply(context, args); + args = false; + } + }; + + wrapperFn = function () { + if (lock) { + // called too soon, queue to call later + args = arguments; + + } else { + // call and lock until later + fn.apply(context, arguments); + setTimeout(later, time); + lock = true; + } + }; + + return wrapperFn; +} + +// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number +// Returns the number `num` modulo `range` in such a way so it lies within +// `range[0]` and `range[1]`. The returned value will be always smaller than +// `range[1]` unless `includeMax` is set to `true`. +function wrapNum(x, range, includeMax) { + var max = range[1], + min = range[0], + d = max - min; + return x === max && includeMax ? x : ((x - min) % d + d) % d + min; +} + +// @function falseFn(): Function +// Returns a function which always returns `false`. +function falseFn() { return false; } + +// @function formatNum(num: Number, digits?: Number): Number +// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default. +function formatNum(num, digits) { + var pow = Math.pow(10, (digits === undefined ? 6 : digits)); + return Math.round(num * pow) / pow; +} + +// @function trim(str: String): String +// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) +function trim(str) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); +} + +// @function splitWords(str: String): String[] +// Trims and splits the string on whitespace and returns the array of parts. +function splitWords(str) { + return trim(str).split(/\s+/); +} + +// @function setOptions(obj: Object, options: Object): Object +// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. +function setOptions(obj, options) { + if (!obj.hasOwnProperty('options')) { + obj.options = obj.options ? create(obj.options) : {}; + } + for (var i in options) { + obj.options[i] = options[i]; + } + return obj.options; +} + +// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String +// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` +// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will +// be appended at the end. If `uppercase` is `true`, the parameter names will +// be uppercased (e.g. `'?A=foo&B=bar'`) +function getParamString(obj, existingUrl, uppercase) { + var params = []; + for (var i in obj) { + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); + } + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); +} + +var templateRe = /\{ *([\w_-]+) *\}/g; + +// @function template(str: String, data: Object): String +// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` +// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string +// `('Hello foo, bar')`. You can also specify functions instead of strings for +// data values — they will be evaluated passing `data` as an argument. +function template(str, data) { + return str.replace(templateRe, function (str, key) { + var value = data[key]; + + if (value === undefined) { + throw new Error('No value provided for variable ' + str); + + } else if (typeof value === 'function') { + value = value(data); + } + return value; + }); +} + +// @function isArray(obj): Boolean +// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) +var isArray = Array.isArray || function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); +}; + +// @function indexOf(array: Array, el: Object): Number +// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) +function indexOf(array, el) { + for (var i = 0; i < array.length; i++) { + if (array[i] === el) { return i; } + } + return -1; +} + +// @property emptyImageUrl: String +// Data URI string containing a base64-encoded empty GIF image. +// Used as a hack to free memory from unused images on WebKit-powered +// mobile devices (by setting image `src` to this string). +var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; + +// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + +function getPrefixed(name) { + return window['webkit' + name] || window['moz' + name] || window['ms' + name]; +} + +var lastTime = 0; + +// fallback for IE 7-8 +function timeoutDefer(fn) { + var time = +new Date(), + timeToCall = Math.max(0, 16 - (time - lastTime)); + + lastTime = time + timeToCall; + return window.setTimeout(fn, timeToCall); +} + +var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; +var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; + +// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number +// Schedules `fn` to be executed when the browser repaints. `fn` is bound to +// `context` if given. When `immediate` is set, `fn` is called immediately if +// the browser doesn't have native support for +// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), +// otherwise it's delayed. Returns a request ID that can be used to cancel the request. +function requestAnimFrame(fn, context, immediate) { + if (immediate && requestFn === timeoutDefer) { + fn.call(context); + } else { + return requestFn.call(window, bind(fn, context)); + } +} + +// @function cancelAnimFrame(id: Number): undefined +// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). +function cancelAnimFrame(id) { + if (id) { + cancelFn.call(window, id); + } +} + + +var Util = (Object.freeze || Object)({ + freeze: freeze, + extend: extend, + create: create, + bind: bind, + lastId: lastId, + stamp: stamp, + throttle: throttle, + wrapNum: wrapNum, + falseFn: falseFn, + formatNum: formatNum, + trim: trim, + splitWords: splitWords, + setOptions: setOptions, + getParamString: getParamString, + template: template, + isArray: isArray, + indexOf: indexOf, + emptyImageUrl: emptyImageUrl, + requestFn: requestFn, + cancelFn: cancelFn, + requestAnimFrame: requestAnimFrame, + cancelAnimFrame: cancelAnimFrame +}); + +// @class Class +// @aka L.Class + +// @section +// @uninheritable + +// Thanks to John Resig and Dean Edwards for inspiration! + +function Class() {} + +Class.extend = function (props) { + + // @function extend(props: Object): Function + // [Extends the current class](#class-inheritance) given the properties to be included. + // Returns a Javascript function that is a class constructor (to be called with `new`). + var NewClass = function () { + + // call the constructor + if (this.initialize) { + this.initialize.apply(this, arguments); + } + + // call all constructor hooks + this.callInitHooks(); + }; + + var parentProto = NewClass.__super__ = this.prototype; + + var proto = create(parentProto); + proto.constructor = NewClass; + + NewClass.prototype = proto; + + // inherit parent's statics + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { + NewClass[i] = this[i]; + } + } + + // mix static properties into the class + if (props.statics) { + extend(NewClass, props.statics); + delete props.statics; + } + + // mix includes into the prototype + if (props.includes) { + checkDeprecatedMixinEvents(props.includes); + extend.apply(null, [proto].concat(props.includes)); + delete props.includes; + } + + // merge options + if (proto.options) { + props.options = extend(create(proto.options), props.options); + } + + // mix given properties into the prototype + extend(proto, props); + + proto._initHooks = []; + + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parentProto.callInitHooks) { + parentProto.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + + return NewClass; +}; + + +// @function include(properties: Object): this +// [Includes a mixin](#class-includes) into the current class. +Class.include = function (props) { + extend(this.prototype, props); + return this; +}; + +// @function mergeOptions(options: Object): this +// [Merges `options`](#class-options) into the defaults of the class. +Class.mergeOptions = function (options) { + extend(this.prototype.options, options); + return this; +}; + +// @function addInitHook(fn: Function): this +// Adds a [constructor hook](#class-constructor-hooks) to the class. +Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); + return this; +}; + +function checkDeprecatedMixinEvents(includes) { + if (typeof L === 'undefined' || !L || !L.Mixin) { return; } + + includes = isArray(includes) ? includes : [includes]; + + for (var i = 0; i < includes.length; i++) { + if (includes[i] === L.Mixin.Events) { + console.warn('Deprecated include of L.Mixin.Events: ' + + 'this property will be removed in future releases, ' + + 'please inherit from L.Evented instead.', new Error().stack); + } + } +} + +/* + * @class Evented + * @aka L.Evented + * @inherits Class + * + * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event). + * + * @example + * + * ```js + * map.on('click', function(e) { + * alert(e.latlng); + * } ); + * ``` + * + * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: + * + * ```js + * function onClick(e) { ... } + * + * map.on('click', onClick); + * map.off('click', onClick); + * ``` + */ + +var Events = { + /* @method on(type: String, fn: Function, context?: Object): this + * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). + * + * @alternative + * @method on(eventMap: Object): this + * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + */ + on: function (types, fn, context) { + + // types can be a map of types/handlers + if (typeof types === 'object') { + for (var type in types) { + // we don't process space-separated events here for performance; + // it's a hot path since Layer uses the on(obj) syntax + this._on(type, types[type], fn); + } + + } else { + // types can be a string of space-separated words + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._on(types[i], fn, context); + } + } + + return this; + }, + + /* @method off(type: String, fn?: Function, context?: Object): this + * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener. + * + * @alternative + * @method off(eventMap: Object): this + * Removes a set of type/listener pairs. + * + * @alternative + * @method off: this + * Removes all listeners to all events on the object. + */ + off: function (types, fn, context) { + + if (!types) { + // clear all listeners if called without arguments + delete this._events; + + } else if (typeof types === 'object') { + for (var type in types) { + this._off(type, types[type], fn); + } + + } else { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + this._off(types[i], fn, context); + } + } + + return this; + }, + + // attach listener (without syntactic sugar now) + _on: function (type, fn, context) { + this._events = this._events || {}; + + /* get/init listeners for type */ + var typeListeners = this._events[type]; + if (!typeListeners) { + typeListeners = []; + this._events[type] = typeListeners; + } + + if (context === this) { + // Less memory footprint. + context = undefined; + } + var newListener = {fn: fn, ctx: context}, + listeners = typeListeners; + + // check if fn already there + for (var i = 0, len = listeners.length; i < len; i++) { + if (listeners[i].fn === fn && listeners[i].ctx === context) { + return; + } + } + + listeners.push(newListener); + }, + + _off: function (type, fn, context) { + var listeners, + i, + len; + + if (!this._events) { return; } + + listeners = this._events[type]; + + if (!listeners) { + return; + } + + if (!fn) { + // Set all removed listeners to noop so they are not called if remove happens in fire + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].fn = falseFn; + } + // clear all listeners for a type if function isn't specified + delete this._events[type]; + return; + } + + if (context === this) { + context = undefined; + } + + if (listeners) { + + // find fn and remove it + for (i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + if (l.ctx !== context) { continue; } + if (l.fn === fn) { + + // set the removed listener to noop so that's not called if remove happens in fire + l.fn = falseFn; + + if (this._firingCount) { + /* copy array in case events are being fired */ + this._events[type] = listeners = listeners.slice(); + } + listeners.splice(i, 1); + + return; + } + } + } + }, + + // @method fire(type: String, data?: Object, propagate?: Boolean): this + // Fires an event of the specified type. You can optionally provide an data + // object — the first argument of the listener function will contain its + // properties. The event can optionally be propagated to event parents. + fire: function (type, data, propagate) { + if (!this.listens(type, propagate)) { return this; } + + var event = extend({}, data, { + type: type, + target: this, + sourceTarget: data && data.sourceTarget || this + }); + + if (this._events) { + var listeners = this._events[type]; + + if (listeners) { + this._firingCount = (this._firingCount + 1) || 1; + for (var i = 0, len = listeners.length; i < len; i++) { + var l = listeners[i]; + l.fn.call(l.ctx || this, event); + } + + this._firingCount--; + } + } + + if (propagate) { + // propagate the event to parents (set with addEventParent) + this._propagateEvent(event); + } + + return this; + }, + + // @method listens(type: String): Boolean + // Returns `true` if a particular event type has any listeners attached to it. + listens: function (type, propagate) { + var listeners = this._events && this._events[type]; + if (listeners && listeners.length) { return true; } + + if (propagate) { + // also check parents for listeners if event propagates + for (var id in this._eventParents) { + if (this._eventParents[id].listens(type, propagate)) { return true; } + } + } + return false; + }, + + // @method once(…): this + // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. + once: function (types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + this.once(type, types[type], fn); + } + return this; + } + + var handler = bind(function () { + this + .off(types, fn, context) + .off(types, handler, context); + }, this); + + // add a listener that's executed once and removed after that + return this + .on(types, fn, context) + .on(types, handler, context); + }, + + // @method addEventParent(obj: Evented): this + // Adds an event parent - an `Evented` that will receive propagated events + addEventParent: function (obj) { + this._eventParents = this._eventParents || {}; + this._eventParents[stamp(obj)] = obj; + return this; + }, + + // @method removeEventParent(obj: Evented): this + // Removes an event parent, so it will stop receiving propagated events + removeEventParent: function (obj) { + if (this._eventParents) { + delete this._eventParents[stamp(obj)]; + } + return this; + }, + + _propagateEvent: function (e) { + for (var id in this._eventParents) { + this._eventParents[id].fire(e.type, extend({ + layer: e.target, + propagatedFrom: e.target + }, e), true); + } + } +}; + +// aliases; we should ditch those eventually + +// @method addEventListener(…): this +// Alias to [`on(…)`](#evented-on) +Events.addEventListener = Events.on; + +// @method removeEventListener(…): this +// Alias to [`off(…)`](#evented-off) + +// @method clearAllEventListeners(…): this +// Alias to [`off()`](#evented-off) +Events.removeEventListener = Events.clearAllEventListeners = Events.off; + +// @method addOneTimeEventListener(…): this +// Alias to [`once(…)`](#evented-once) +Events.addOneTimeEventListener = Events.once; + +// @method fireEvent(…): this +// Alias to [`fire(…)`](#evented-fire) +Events.fireEvent = Events.fire; + +// @method hasEventListeners(…): Boolean +// Alias to [`listens(…)`](#evented-listens) +Events.hasEventListeners = Events.listens; + +var Evented = Class.extend(Events); + +/* + * @class Point + * @aka L.Point + * + * Represents a point with `x` and `y` coordinates in pixels. + * + * @example + * + * ```js + * var point = L.point(200, 300); + * ``` + * + * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent: + * + * ```js + * map.panBy([200, 300]); + * map.panBy(L.point(200, 300)); + * ``` + * + * Note that `Point` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function Point(x, y, round) { + // @property x: Number; The `x` coordinate of the point + this.x = (round ? Math.round(x) : x); + // @property y: Number; The `y` coordinate of the point + this.y = (round ? Math.round(y) : y); +} + +var trunc = Math.trunc || function (v) { + return v > 0 ? Math.floor(v) : Math.ceil(v); +}; + +Point.prototype = { + + // @method clone(): Point + // Returns a copy of the current point. + clone: function () { + return new Point(this.x, this.y); + }, + + // @method add(otherPoint: Point): Point + // Returns the result of addition of the current and the given points. + add: function (point) { + // non-destructive, returns a new point + return this.clone()._add(toPoint(point)); + }, + + _add: function (point) { + // destructive, used directly for performance in situations where it's safe to modify existing point + this.x += point.x; + this.y += point.y; + return this; + }, + + // @method subtract(otherPoint: Point): Point + // Returns the result of subtraction of the given point from the current. + subtract: function (point) { + return this.clone()._subtract(toPoint(point)); + }, + + _subtract: function (point) { + this.x -= point.x; + this.y -= point.y; + return this; + }, + + // @method divideBy(num: Number): Point + // Returns the result of division of the current point by the given number. + divideBy: function (num) { + return this.clone()._divideBy(num); + }, + + _divideBy: function (num) { + this.x /= num; + this.y /= num; + return this; + }, + + // @method multiplyBy(num: Number): Point + // Returns the result of multiplication of the current point by the given number. + multiplyBy: function (num) { + return this.clone()._multiplyBy(num); + }, + + _multiplyBy: function (num) { + this.x *= num; + this.y *= num; + return this; + }, + + // @method scaleBy(scale: Point): Point + // Multiply each coordinate of the current point by each coordinate of + // `scale`. In linear algebra terms, multiply the point by the + // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) + // defined by `scale`. + scaleBy: function (point) { + return new Point(this.x * point.x, this.y * point.y); + }, + + // @method unscaleBy(scale: Point): Point + // Inverse of `scaleBy`. Divide each coordinate of the current point by + // each coordinate of `scale`. + unscaleBy: function (point) { + return new Point(this.x / point.x, this.y / point.y); + }, + + // @method round(): Point + // Returns a copy of the current point with rounded coordinates. + round: function () { + return this.clone()._round(); + }, + + _round: function () { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }, + + // @method floor(): Point + // Returns a copy of the current point with floored coordinates (rounded down). + floor: function () { + return this.clone()._floor(); + }, + + _floor: function () { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }, + + // @method ceil(): Point + // Returns a copy of the current point with ceiled coordinates (rounded up). + ceil: function () { + return this.clone()._ceil(); + }, + + _ceil: function () { + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + return this; + }, + + // @method trunc(): Point + // Returns a copy of the current point with truncated coordinates (rounded towards zero). + trunc: function () { + return this.clone()._trunc(); + }, + + _trunc: function () { + this.x = trunc(this.x); + this.y = trunc(this.y); + return this; + }, + + // @method distanceTo(otherPoint: Point): Number + // Returns the cartesian distance between the current and the given points. + distanceTo: function (point) { + point = toPoint(point); + + var x = point.x - this.x, + y = point.y - this.y; + + return Math.sqrt(x * x + y * y); + }, + + // @method equals(otherPoint: Point): Boolean + // Returns `true` if the given point has the same coordinates. + equals: function (point) { + point = toPoint(point); + + return point.x === this.x && + point.y === this.y; + }, + + // @method contains(otherPoint: Point): Boolean + // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). + contains: function (point) { + point = toPoint(point); + + return Math.abs(point.x) <= Math.abs(this.x) && + Math.abs(point.y) <= Math.abs(this.y); + }, + + // @method toString(): String + // Returns a string representation of the point for debugging purposes. + toString: function () { + return 'Point(' + + formatNum(this.x) + ', ' + + formatNum(this.y) + ')'; + } +}; + +// @factory L.point(x: Number, y: Number, round?: Boolean) +// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. + +// @alternative +// @factory L.point(coords: Number[]) +// Expects an array of the form `[x, y]` instead. + +// @alternative +// @factory L.point(coords: Object) +// Expects a plain object of the form `{x: Number, y: Number}` instead. +function toPoint(x, y, round) { + if (x instanceof Point) { + return x; + } + if (isArray(x)) { + return new Point(x[0], x[1]); + } + if (x === undefined || x === null) { + return x; + } + if (typeof x === 'object' && 'x' in x && 'y' in x) { + return new Point(x.x, x.y); + } + return new Point(x, y, round); +} + +/* + * @class Bounds + * @aka L.Bounds + * + * Represents a rectangular area in pixel coordinates. + * + * @example + * + * ```js + * var p1 = L.point(10, 10), + * p2 = L.point(40, 60), + * bounds = L.bounds(p1, p2); + * ``` + * + * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * otherBounds.intersects([[10, 10], [40, 60]]); + * ``` + * + * Note that `Bounds` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function Bounds(a, b) { + if (!a) { return; } + + var points = b ? [a, b] : a; + + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } +} + +Bounds.prototype = { + // @method extend(point: Point): this + // Extends the bounds to contain the given point. + extend: function (point) { // (Point) + point = toPoint(point); + + // @property min: Point + // The top left corner of the rectangle. + // @property max: Point + // The bottom right corner of the rectangle. + if (!this.min && !this.max) { + this.min = point.clone(); + this.max = point.clone(); + } else { + this.min.x = Math.min(point.x, this.min.x); + this.max.x = Math.max(point.x, this.max.x); + this.min.y = Math.min(point.y, this.min.y); + this.max.y = Math.max(point.y, this.max.y); + } + return this; + }, + + // @method getCenter(round?: Boolean): Point + // Returns the center point of the bounds. + getCenter: function (round) { + return new Point( + (this.min.x + this.max.x) / 2, + (this.min.y + this.max.y) / 2, round); + }, + + // @method getBottomLeft(): Point + // Returns the bottom-left point of the bounds. + getBottomLeft: function () { + return new Point(this.min.x, this.max.y); + }, + + // @method getTopRight(): Point + // Returns the top-right point of the bounds. + getTopRight: function () { // -> Point + return new Point(this.max.x, this.min.y); + }, + + // @method getTopLeft(): Point + // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). + getTopLeft: function () { + return this.min; // left, top + }, + + // @method getBottomRight(): Point + // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). + getBottomRight: function () { + return this.max; // right, bottom + }, + + // @method getSize(): Point + // Returns the size of the given bounds + getSize: function () { + return this.max.subtract(this.min); + }, + + // @method contains(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle contains the given one. + // @alternative + // @method contains(point: Point): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { + var min, max; + + if (typeof obj[0] === 'number' || obj instanceof Point) { + obj = toPoint(obj); + } else { + obj = toBounds(obj); + } + + if (obj instanceof Bounds) { + min = obj.min; + max = obj.max; + } else { + min = max = obj; + } + + return (min.x >= this.min.x) && + (max.x <= this.max.x) && + (min.y >= this.min.y) && + (max.y <= this.max.y); + }, + + // @method intersects(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds + // intersect if they have at least one point in common. + intersects: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xIntersects = (max2.x >= min.x) && (min2.x <= max.x), + yIntersects = (max2.y >= min.y) && (min2.y <= max.y); + + return xIntersects && yIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds + // overlap if their intersection is an area. + overlaps: function (bounds) { // (Bounds) -> Boolean + bounds = toBounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xOverlaps = (max2.x > min.x) && (min2.x < max.x), + yOverlaps = (max2.y > min.y) && (min2.y < max.y); + + return xOverlaps && yOverlaps; + }, + + isValid: function () { + return !!(this.min && this.max); + } +}; + + +// @factory L.bounds(corner1: Point, corner2: Point) +// Creates a Bounds object from two corners coordinate pairs. +// @alternative +// @factory L.bounds(points: Point[]) +// Creates a Bounds object from the given array of points. +function toBounds(a, b) { + if (!a || a instanceof Bounds) { + return a; + } + return new Bounds(a, b); +} + +/* + * @class LatLngBounds + * @aka L.LatLngBounds + * + * Represents a rectangular geographical area on a map. + * + * @example + * + * ```js + * var corner1 = L.latLng(40.712, -74.227), + * corner2 = L.latLng(40.774, -74.125), + * bounds = L.latLngBounds(corner1, corner2); + * ``` + * + * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * map.fitBounds([ + * [40.712, -74.227], + * [40.774, -74.125] + * ]); + * ``` + * + * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. + * + * Note that `LatLngBounds` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) + if (!corner1) { return; } + + var latlngs = corner2 ? [corner1, corner2] : corner1; + + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +} + +LatLngBounds.prototype = { + + // @method extend(latlng: LatLng): this + // Extend the bounds to contain the given point + + // @alternative + // @method extend(otherBounds: LatLngBounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLng) { + sw2 = obj; + ne2 = obj; + + } else if (obj instanceof LatLngBounds) { + sw2 = obj._southWest; + ne2 = obj._northEast; + + if (!sw2 || !ne2) { return this; } + + } else { + return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; + } + + if (!sw && !ne) { + this._southWest = new LatLng(sw2.lat, sw2.lng); + this._northEast = new LatLng(ne2.lat, ne2.lng); + } else { + sw.lat = Math.min(sw2.lat, sw.lat); + sw.lng = Math.min(sw2.lng, sw.lng); + ne.lat = Math.max(ne2.lat, ne.lat); + ne.lng = Math.max(ne2.lng, ne.lng); + } + + return this; + }, + + // @method pad(bufferRatio: Number): LatLngBounds + // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. + // For example, a ratio of 0.5 extends the bounds by 50% in each direction. + // Negative values will retract the bounds. + pad: function (bufferRatio) { + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; + + return new LatLngBounds( + new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); + }, + + // @method getCenter(): LatLng + // Returns the center point of the bounds. + getCenter: function () { + return new LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); + }, + + // @method getSouthWest(): LatLng + // Returns the south-west point of the bounds. + getSouthWest: function () { + return this._southWest; + }, + + // @method getNorthEast(): LatLng + // Returns the north-east point of the bounds. + getNorthEast: function () { + return this._northEast; + }, + + // @method getNorthWest(): LatLng + // Returns the north-west point of the bounds. + getNorthWest: function () { + return new LatLng(this.getNorth(), this.getWest()); + }, + + // @method getSouthEast(): LatLng + // Returns the south-east point of the bounds. + getSouthEast: function () { + return new LatLng(this.getSouth(), this.getEast()); + }, + + // @method getWest(): Number + // Returns the west longitude of the bounds + getWest: function () { + return this._southWest.lng; + }, + + // @method getSouth(): Number + // Returns the south latitude of the bounds + getSouth: function () { + return this._southWest.lat; + }, + + // @method getEast(): Number + // Returns the east longitude of the bounds + getEast: function () { + return this._northEast.lng; + }, + + // @method getNorth(): Number + // Returns the north latitude of the bounds + getNorth: function () { + return this._northEast.lat; + }, + + // @method contains(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle contains the given one. + + // @alternative + // @method contains (latlng: LatLng): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { + obj = toLatLng(obj); + } else { + obj = toLatLngBounds(obj); + } + + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); + } else { + sw2 = ne2 = obj; + } + + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + }, + + // @method intersects(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. + intersects: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + + return latIntersects && lngIntersects; + }, + + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. + overlaps: function (bounds) { + bounds = toLatLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), + lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); + + return latOverlaps && lngOverlaps; + }, + + // @method toBBoxString(): String + // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); + }, + + // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean + // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (bounds, maxMargin) { + if (!bounds) { return false; } + + bounds = toLatLngBounds(bounds); + + return this._southWest.equals(bounds.getSouthWest(), maxMargin) && + this._northEast.equals(bounds.getNorthEast(), maxMargin); + }, + + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. + isValid: function () { + return !!(this._southWest && this._northEast); + } +}; + +// TODO International date line? + +// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) +// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. + +// @alternative +// @factory L.latLngBounds(latlngs: LatLng[]) +// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). +function toLatLngBounds(a, b) { + if (a instanceof LatLngBounds) { + return a; + } + return new LatLngBounds(a, b); +} + +/* @class LatLng + * @aka L.LatLng + * + * Represents a geographical point with a certain latitude and longitude. + * + * @example + * + * ``` + * var latlng = L.latLng(50.5, 30.5); + * ``` + * + * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent: + * + * ``` + * map.panTo([50, 30]); + * map.panTo({lon: 30, lat: 50}); + * map.panTo({lat: 50, lng: 30}); + * map.panTo(L.latLng(50, 30)); + * ``` + * + * Note that `LatLng` does not inherit from Leafet's `Class` object, + * which means new classes can't inherit from it, and new methods + * can't be added to it with the `include` function. + */ + +function LatLng(lat, lng, alt) { + if (isNaN(lat) || isNaN(lng)) { + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); + } + + // @property lat: Number + // Latitude in degrees + this.lat = +lat; + + // @property lng: Number + // Longitude in degrees + this.lng = +lng; + + // @property alt: Number + // Altitude in meters (optional) + if (alt !== undefined) { + this.alt = +alt; + } +} + +LatLng.prototype = { + // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean + // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number. + equals: function (obj, maxMargin) { + if (!obj) { return false; } + + obj = toLatLng(obj); + + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + + return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); + }, + + // @method toString(): String + // Returns a string representation of the point (for debugging purposes). + toString: function (precision) { + return 'LatLng(' + + formatNum(this.lat, precision) + ', ' + + formatNum(this.lng, precision) + ')'; + }, + + // @method distanceTo(otherLatLng: LatLng): Number + // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines). + distanceTo: function (other) { + return Earth.distance(this, toLatLng(other)); + }, + + // @method wrap(): LatLng + // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. + wrap: function () { + return Earth.wrapLatLng(this); + }, + + // @method toBounds(sizeInMeters: Number): LatLngBounds + // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. + toBounds: function (sizeInMeters) { + var latAccuracy = 180 * sizeInMeters / 40075017, + lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); + + return toLatLngBounds( + [this.lat - latAccuracy, this.lng - lngAccuracy], + [this.lat + latAccuracy, this.lng + lngAccuracy]); + }, + + clone: function () { + return new LatLng(this.lat, this.lng, this.alt); + } +}; + + + +// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng +// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). + +// @alternative +// @factory L.latLng(coords: Array): LatLng +// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. + +// @alternative +// @factory L.latLng(coords: Object): LatLng +// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. + +function toLatLng(a, b, c) { + if (a instanceof LatLng) { + return a; + } + if (isArray(a) && typeof a[0] !== 'object') { + if (a.length === 3) { + return new LatLng(a[0], a[1], a[2]); + } + if (a.length === 2) { + return new LatLng(a[0], a[1]); + } + return null; + } + if (a === undefined || a === null) { + return a; + } + if (typeof a === 'object' && 'lat' in a) { + return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); + } + if (b === undefined) { + return null; + } + return new LatLng(a, b, c); +} + +/* + * @namespace CRS + * @crs L.CRS.Base + * Object that defines coordinate reference systems for projecting + * geographical points into pixel (screen) coordinates and back (and to + * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See + * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). + * + * Leaflet defines the most usual CRSs by default. If you want to use a + * CRS not defined by default, take a look at the + * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. + * + * Note that the CRS instances do not inherit from Leafet's `Class` object, + * and can't be instantiated. Also, new classes can't inherit from them, + * and methods can't be added to them with the `include` function. + */ + +var CRS = { + // @method latLngToPoint(latlng: LatLng, zoom: Number): Point + // Projects geographical coordinates into pixel coordinates for a given zoom. + latLngToPoint: function (latlng, zoom) { + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + + return this.transformation._transform(projectedPoint, scale); + }, + + // @method pointToLatLng(point: Point, zoom: Number): LatLng + // The inverse of `latLngToPoint`. Projects pixel coordinates on a given + // zoom into geographical coordinates. + pointToLatLng: function (point, zoom) { + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + + return this.projection.unproject(untransformedPoint); + }, + + // @method project(latlng: LatLng): Point + // Projects geographical coordinates into coordinates in units accepted for + // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). + project: function (latlng) { + return this.projection.project(latlng); + }, + + // @method unproject(point: Point): LatLng + // Given a projected coordinate returns the corresponding LatLng. + // The inverse of `project`. + unproject: function (point) { + return this.projection.unproject(point); + }, + + // @method scale(zoom: Number): Number + // Returns the scale used when transforming projected coordinates into + // pixel coordinates for a particular zoom. For example, it returns + // `256 * 2^zoom` for Mercator-based CRS. + scale: function (zoom) { + return 256 * Math.pow(2, zoom); + }, + + // @method zoom(scale: Number): Number + // Inverse of `scale()`, returns the zoom level corresponding to a scale + // factor of `scale`. + zoom: function (scale) { + return Math.log(scale / 256) / Math.LN2; + }, + + // @method getProjectedBounds(zoom: Number): Bounds + // Returns the projection's bounds scaled and transformed for the provided `zoom`. + getProjectedBounds: function (zoom) { + if (this.infinite) { return null; } + + var b = this.projection.bounds, + s = this.scale(zoom), + min = this.transformation.transform(b.min, s), + max = this.transformation.transform(b.max, s); + + return new Bounds(min, max); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates. + + // @property code: String + // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) + // + // @property wrapLng: Number[] + // An array of two numbers defining whether the longitude (horizontal) coordinate + // axis wraps around a given range and how. Defaults to `[-180, 180]` in most + // geographical CRSs. If `undefined`, the longitude axis does not wrap around. + // + // @property wrapLat: Number[] + // Like `wrapLng`, but for the latitude (vertical) axis. + + // wrapLng: [min, max], + // wrapLat: [min, max], + + // @property infinite: Boolean + // If true, the coordinate space will be unbounded (infinite in both axes) + infinite: false, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where lat and lng has been wrapped according to the + // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. + wrapLatLng: function (latlng) { + var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, + lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, + alt = latlng.alt; + + return new LatLng(lat, lng, alt); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring + // that its center is within the CRS's bounds. + // Only accepts actual `L.LatLngBounds` instances, not arrays. + wrapLatLngBounds: function (bounds) { + var center = bounds.getCenter(), + newCenter = this.wrapLatLng(center), + latShift = center.lat - newCenter.lat, + lngShift = center.lng - newCenter.lng; + + if (latShift === 0 && lngShift === 0) { + return bounds; + } + + var sw = bounds.getSouthWest(), + ne = bounds.getNorthEast(), + newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), + newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); + + return new LatLngBounds(newSw, newNe); + } +}; + +/* + * @namespace CRS + * @crs L.CRS.Earth + * + * Serves as the base for CRS that are global such that they cover the earth. + * Can only be used as the base for other CRS and cannot be used directly, + * since it does not have a `code`, `projection` or `transformation`. `distance()` returns + * meters. + */ + +var Earth = extend({}, CRS, { + wrapLng: [-180, 180], + + // Mean Earth Radius, as recommended for use by + // the International Union of Geodesy and Geophysics, + // see http://rosettacode.org/wiki/Haversine_formula + R: 6371000, + + // distance between two geographical points using spherical law of cosines approximation + distance: function (latlng1, latlng2) { + var rad = Math.PI / 180, + lat1 = latlng1.lat * rad, + lat2 = latlng2.lat * rad, + sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), + sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), + a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, + c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return this.R * c; + } +}); + +/* + * @namespace Projection + * @projection L.Projection.SphericalMercator + * + * Spherical Mercator projection — the most common projection for online maps, + * used by almost all free and commercial tile providers. Assumes that Earth is + * a sphere. Used by the `EPSG:3857` CRS. + */ + +var SphericalMercator = { + + R: 6378137, + MAX_LATITUDE: 85.0511287798, + + project: function (latlng) { + var d = Math.PI / 180, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + sin = Math.sin(lat * d); + + return new Point( + this.R * latlng.lng * d, + this.R * Math.log((1 + sin) / (1 - sin)) / 2); + }, + + unproject: function (point) { + var d = 180 / Math.PI; + + return new LatLng( + (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, + point.x * d / this.R); + }, + + bounds: (function () { + var d = 6378137 * Math.PI; + return new Bounds([-d, -d], [d, d]); + })() +}; + +/* + * @class Transformation + * @aka L.Transformation + * + * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` + * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing + * the reverse. Used by Leaflet in its projections code. + * + * @example + * + * ```js + * var transformation = L.transformation(2, 5, -1, 10), + * p = L.point(1, 2), + * p2 = transformation.transform(p), // L.point(7, 8) + * p3 = transformation.untransform(p2); // L.point(1, 2) + * ``` + */ + + +// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) +// Creates a `Transformation` object with the given coefficients. +function Transformation(a, b, c, d) { + if (isArray(a)) { + // use array properties + this._a = a[0]; + this._b = a[1]; + this._c = a[2]; + this._d = a[3]; + return; + } + this._a = a; + this._b = b; + this._c = c; + this._d = d; +} + +Transformation.prototype = { + // @method transform(point: Point, scale?: Number): Point + // Returns a transformed point, optionally multiplied by the given scale. + // Only accepts actual `L.Point` instances, not arrays. + transform: function (point, scale) { // (Point, Number) -> Point + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (point, scale) { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + // @method untransform(point: Point, scale?: Number): Point + // Returns the reverse transformation of the given point, optionally divided + // by the given scale. Only accepts actual `L.Point` instances, not arrays. + untransform: function (point, scale) { + scale = scale || 1; + return new Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } +}; + +// factory L.transformation(a: Number, b: Number, c: Number, d: Number) + +// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) +// Instantiates a Transformation object with the given coefficients. + +// @alternative +// @factory L.transformation(coefficients: Array): Transformation +// Expects an coefficients array of the form +// `[a: Number, b: Number, c: Number, d: Number]`. + +function toTransformation(a, b, c, d) { + return new Transformation(a, b, c, d); +} + +/* + * @namespace CRS + * @crs L.CRS.EPSG3857 + * + * The most common CRS for online maps, used by almost all free and commercial + * tile providers. Uses Spherical Mercator projection. Set in by default in + * Map's `crs` option. + */ + +var EPSG3857 = extend({}, Earth, { + code: 'EPSG:3857', + projection: SphericalMercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * SphericalMercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +var EPSG900913 = extend({}, EPSG3857, { + code: 'EPSG:900913' +}); + +// @namespace SVG; @section +// There are several static functions which can be called without instantiating L.SVG: + +// @function create(name: String): SVGElement +// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), +// corresponding to the class name passed. For example, using 'line' will return +// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). +function svgCreate(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); +} + +// @function pointsToPath(rings: Point[], closed: Boolean): String +// Generates a SVG path string for multiple rings, with each ring turning +// into "M..L..L.." instructions +function pointsToPath(rings, closed) { + var str = '', + i, j, len, len2, points, p; + + for (i = 0, len = rings.length; i < len; i++) { + points = rings[i]; + + for (j = 0, len2 = points.length; j < len2; j++) { + p = points[j]; + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + + // closes the ring for polygons; "x" is VML syntax + str += closed ? (svg ? 'z' : 'x') : ''; + } + + // SVG complains about empty path strings + return str || 'M0 0'; +} + +/* + * @namespace Browser + * @aka L.Browser + * + * A namespace with static properties for browser/feature detection used by Leaflet internally. + * + * @example + * + * ```js + * if (L.Browser.ielt9) { + * alert('Upgrade your browser, dude!'); + * } + * ``` + */ + +var style$1 = document.documentElement.style; + +// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). +var ie = 'ActiveXObject' in window; + +// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. +var ielt9 = ie && !document.addEventListener; + +// @property edge: Boolean; `true` for the Edge web browser. +var edge = 'msLaunchUri' in navigator && !('documentMode' in document); + +// @property webkit: Boolean; +// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). +var webkit = userAgentContains('webkit'); + +// @property android: Boolean +// `true` for any browser running on an Android platform. +var android = userAgentContains('android'); + +// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. +var android23 = userAgentContains('android 2') || userAgentContains('android 3'); + +/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ +var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit +// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome) +var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); + +// @property opera: Boolean; `true` for the Opera browser +var opera = !!window.opera; + +// @property chrome: Boolean; `true` for the Chrome browser. +var chrome = userAgentContains('chrome'); + +// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. +var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; + +// @property safari: Boolean; `true` for the Safari browser. +var safari = !chrome && userAgentContains('safari'); + +var phantom = userAgentContains('phantom'); + +// @property opera12: Boolean +// `true` for the Opera browser supporting CSS transforms (version 12 or later). +var opera12 = 'OTransition' in style$1; + +// @property win: Boolean; `true` when the browser is running in a Windows platform +var win = navigator.platform.indexOf('Win') === 0; + +// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. +var ie3d = ie && ('transition' in style$1); + +// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. +var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; + +// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. +var gecko3d = 'MozPerspective' in style$1; + +// @property any3d: Boolean +// `true` for all browsers supporting CSS transforms. +var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; + +// @property mobile: Boolean; `true` for all browsers running in a mobile device. +var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); + +// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. +var mobileWebkit = mobile && webkit; + +// @property mobileWebkit3d: Boolean +// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. +var mobileWebkit3d = mobile && webkit3d; + +// @property msPointer: Boolean +// `true` for browsers implementing the Microsoft touch events model (notably IE10). +var msPointer = !window.PointerEvent && window.MSPointerEvent; + +// @property pointer: Boolean +// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). +var pointer = !!(window.PointerEvent || msPointer); + +// @property touch: Boolean +// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). +// This does not necessarily mean that the browser is running in a computer with +// a touchscreen, it only means that the browser is capable of understanding +// touch events. +var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || + (window.DocumentTouch && document instanceof window.DocumentTouch)); + +// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. +var mobileOpera = mobile && opera; + +// @property mobileGecko: Boolean +// `true` for gecko-based browsers running in a mobile device. +var mobileGecko = mobile && gecko; + +// @property retina: Boolean +// `true` for browsers on a high-resolution "retina" screen. +var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; + + +// @property canvas: Boolean +// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). +var canvas = (function () { + return !!document.createElement('canvas').getContext; +}()); + +// @property svg: Boolean +// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). +var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); + +// @property vml: Boolean +// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). +var vml = !svg && (function () { + try { + var div = document.createElement('div'); + div.innerHTML = '<v:shape adj="1"/>'; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + + } catch (e) { + return false; + } +}()); + + +function userAgentContains(str) { + return navigator.userAgent.toLowerCase().indexOf(str) >= 0; +} + + +var Browser = (Object.freeze || Object)({ + ie: ie, + ielt9: ielt9, + edge: edge, + webkit: webkit, + android: android, + android23: android23, + androidStock: androidStock, + opera: opera, + chrome: chrome, + gecko: gecko, + safari: safari, + phantom: phantom, + opera12: opera12, + win: win, + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + any3d: any3d, + mobile: mobile, + mobileWebkit: mobileWebkit, + mobileWebkit3d: mobileWebkit3d, + msPointer: msPointer, + pointer: pointer, + touch: touch, + mobileOpera: mobileOpera, + mobileGecko: mobileGecko, + retina: retina, + canvas: canvas, + svg: svg, + vml: vml +}); + +/* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + + +var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; +var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; +var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; +var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; +var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; + +var _pointers = {}; +var _pointerDocListener = false; + +// DomEvent.DoubleTap needs to know about this +var _pointersCount = 0; + +// Provides a touch events wrapper for (ms)pointer events. +// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 + +function addPointerListener(obj, type, handler, id) { + if (type === 'touchstart') { + _addPointerStart(obj, handler, id); + + } else if (type === 'touchmove') { + _addPointerMove(obj, handler, id); + + } else if (type === 'touchend') { + _addPointerEnd(obj, handler, id); + } + + return this; +} + +function removePointerListener(obj, type, id) { + var handler = obj['_leaflet_' + type + id]; + + if (type === 'touchstart') { + obj.removeEventListener(POINTER_DOWN, handler, false); + + } else if (type === 'touchmove') { + obj.removeEventListener(POINTER_MOVE, handler, false); + + } else if (type === 'touchend') { + obj.removeEventListener(POINTER_UP, handler, false); + obj.removeEventListener(POINTER_CANCEL, handler, false); + } + + return this; +} + +function _addPointerStart(obj, handler, id) { + var onDown = bind(function (e) { + if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + // In IE11, some touch events needs to fire for form controls, or + // the controls will stop working. We keep a whitelist of tag names that + // need these events. For other target tags, we prevent default on the event. + if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { + preventDefault(e); + } else { + return; + } + } + + _handlePointer(e, handler); + }); + + obj['_leaflet_touchstart' + id] = onDown; + obj.addEventListener(POINTER_DOWN, onDown, false); + + // need to keep track of what pointers and how many are active to provide e.touches emulation + if (!_pointerDocListener) { + // we listen documentElement as any drags that end by moving the touch off the screen get fired there + document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); + document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); + document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); + document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); + + _pointerDocListener = true; + } +} + +function _globalPointerDown(e) { + _pointers[e.pointerId] = e; + _pointersCount++; +} + +function _globalPointerMove(e) { + if (_pointers[e.pointerId]) { + _pointers[e.pointerId] = e; + } +} + +function _globalPointerUp(e) { + delete _pointers[e.pointerId]; + _pointersCount--; +} + +function _handlePointer(e, handler) { + e.touches = []; + for (var i in _pointers) { + e.touches.push(_pointers[i]); + } + e.changedTouches = [e]; + + handler(e); +} + +function _addPointerMove(obj, handler, id) { + var onMove = function (e) { + // don't fire touch moves when mouse isn't down + if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } + + _handlePointer(e, handler); + }; + + obj['_leaflet_touchmove' + id] = onMove; + obj.addEventListener(POINTER_MOVE, onMove, false); +} + +function _addPointerEnd(obj, handler, id) { + var onUp = function (e) { + _handlePointer(e, handler); + }; + + obj['_leaflet_touchend' + id] = onUp; + obj.addEventListener(POINTER_UP, onUp, false); + obj.addEventListener(POINTER_CANCEL, onUp, false); +} + +/* + * Extends the event handling code with double tap support for mobile browsers. + */ + +var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; +var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; +var _pre = '_leaflet_'; + +// inspired by Zepto touch code by Thomas Fuchs +function addDoubleTapListener(obj, handler, id) { + var last, touch$$1, + doubleTap = false, + delay = 250; + + function onTouchStart(e) { + var count; + + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + count = _pointersCount; + } else { + count = e.touches.length; + } + + if (count > 1) { return; } + + var now = Date.now(), + delta = now - (last || now); + + touch$$1 = e.touches ? e.touches[0] : e; + doubleTap = (delta > 0 && delta <= delay); + last = now; + } + + function onTouchEnd(e) { + if (doubleTap && !touch$$1.cancelBubble) { + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + // work around .type being readonly with MSPointer* events + var newTouch = {}, + prop, i; + + for (i in touch$$1) { + prop = touch$$1[i]; + newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop; + } + touch$$1 = newTouch; + } + touch$$1.type = 'dblclick'; + handler(touch$$1); + last = null; + } + } + + obj[_pre + _touchstart + id] = onTouchStart; + obj[_pre + _touchend + id] = onTouchEnd; + obj[_pre + 'dblclick' + id] = handler; + + obj.addEventListener(_touchstart, onTouchStart, false); + obj.addEventListener(_touchend, onTouchEnd, false); + + // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), + // the browser doesn't fire touchend/pointerup events but does fire + // native dblclicks. See #4127. + // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. + obj.addEventListener('dblclick', handler, false); + + return this; +} + +function removeDoubleTapListener(obj, id) { + var touchstart = obj[_pre + _touchstart + id], + touchend = obj[_pre + _touchend + id], + dblclick = obj[_pre + 'dblclick' + id]; + + obj.removeEventListener(_touchstart, touchstart, false); + obj.removeEventListener(_touchend, touchend, false); + if (!edge) { + obj.removeEventListener('dblclick', dblclick, false); + } + + return this; +} + +/* + * @namespace DomEvent + * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. + */ + +// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. + +// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Adds a listener function (`fn`) to a particular DOM event type of the +// element `el`. You can optionally specify the context of the listener +// (object the `this` keyword will point to). You can also pass several +// space-separated types (e.g. `'click dblclick'`). + +// @alternative +// @function on(el: HTMLElement, eventMap: Object, context?: Object): this +// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` +function on(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + addOne(obj, type, types[type], fn); + } + } else { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + addOne(obj, types[i], fn, context); + } + } + + return this; +} + +var eventsKey = '_leaflet_events'; + +// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Removes a previously added listener function. +// Note that if you passed a custom context to on, you must pass the same +// context to `off` in order to remove the listener. + +// @alternative +// @function off(el: HTMLElement, eventMap: Object, context?: Object): this +// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` +function off(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + removeOne(obj, type, types[type], fn); + } + } else if (types) { + types = splitWords(types); + + for (var i = 0, len = types.length; i < len; i++) { + removeOne(obj, types[i], fn, context); + } + } else { + for (var j in obj[eventsKey]) { + removeOne(obj, j, obj[eventsKey][j]); + } + delete obj[eventsKey]; + } + + return this; +} + +function addOne(obj, type, fn, context) { + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); + + if (obj[eventsKey] && obj[eventsKey][id]) { return this; } + + var handler = function (e) { + return fn.call(context || obj, e || window.event); + }; + + var originalHandler = handler; + + if (pointer && type.indexOf('touch') === 0) { + // Needs DomEvent.Pointer.js + addPointerListener(obj, type, handler, id); + + } else if (touch && (type === 'dblclick') && addDoubleTapListener && + !(pointer && chrome)) { + // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener + // See #5180 + addDoubleTapListener(obj, handler, id); + + } else if ('addEventListener' in obj) { + + if (type === 'mousewheel') { + obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + handler = function (e) { + e = e || window.event; + if (isExternalTarget(obj, e)) { + originalHandler(e); + } + }; + obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); + + } else { + if (type === 'click' && android) { + handler = function (e) { + filterClick(e, originalHandler); + }; + } + obj.addEventListener(type, handler, false); + } + + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + + obj[eventsKey] = obj[eventsKey] || {}; + obj[eventsKey][id] = handler; +} + +function removeOne(obj, type, fn, context) { + + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), + handler = obj[eventsKey] && obj[eventsKey][id]; + + if (!handler) { return this; } + + if (pointer && type.indexOf('touch') === 0) { + removePointerListener(obj, type, id); + + } else if (touch && (type === 'dblclick') && removeDoubleTapListener && + !(pointer && chrome)) { + removeDoubleTapListener(obj, id); + + } else if ('removeEventListener' in obj) { + + if (type === 'mousewheel') { + obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else { + obj.removeEventListener( + type === 'mouseenter' ? 'mouseover' : + type === 'mouseleave' ? 'mouseout' : type, handler, false); + } + + } else if ('detachEvent' in obj) { + obj.detachEvent('on' + type, handler); + } + + obj[eventsKey][id] = null; +} + +// @function stopPropagation(ev: DOMEvent): this +// Stop the given event from propagation to parent elements. Used inside the listener functions: +// ```js +// L.DomEvent.on(div, 'click', function (ev) { +// L.DomEvent.stopPropagation(ev); +// }); +// ``` +function stopPropagation(e) { + + if (e.stopPropagation) { + e.stopPropagation(); + } else if (e.originalEvent) { // In case of Leaflet event. + e.originalEvent._stopped = true; + } else { + e.cancelBubble = true; + } + skipped(e); + + return this; +} + +// @function disableScrollPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). +function disableScrollPropagation(el) { + addOne(el, 'mousewheel', stopPropagation); + return this; +} + +// @function disableClickPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, +// `'mousedown'` and `'touchstart'` events (plus browser variants). +function disableClickPropagation(el) { + on(el, 'mousedown touchstart dblclick', stopPropagation); + addOne(el, 'click', fakeStop); + return this; +} + +// @function preventDefault(ev: DOMEvent): this +// Prevents the default action of the DOM Event `ev` from happening (such as +// following a link in the href of the a element, or doing a POST request +// with page reload when a `<form>` is submitted). +// Use it inside listener functions. +function preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; +} + +// @function stop(ev: DOMEvent): this +// Does `stopPropagation` and `preventDefault` at the same time. +function stop(e) { + preventDefault(e); + stopPropagation(e); + return this; +} + +// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point +// Gets normalized mouse position from a DOM event relative to the +// `container` or to the whole page if not specified. +function getMousePosition(e, container) { + if (!container) { + return new Point(e.clientX, e.clientY); + } + + var rect = container.getBoundingClientRect(); + + var scaleX = rect.width / container.offsetWidth || 1; + var scaleY = rect.height / container.offsetHeight || 1; + return new Point( + e.clientX / scaleX - rect.left - container.clientLeft, + e.clientY / scaleY - rect.top - container.clientTop); +} + +// Chrome on Win scrolls double the pixels as in other platforms (see #4538), +// and Firefox scrolls device pixels, not CSS pixels +var wheelPxFactor = + (win && chrome) ? 2 * window.devicePixelRatio : + gecko ? window.devicePixelRatio : 1; + +// @function getWheelDelta(ev: DOMEvent): Number +// Gets normalized wheel delta from a mousewheel DOM event, in vertical +// pixels scrolled (negative if scrolling down). +// Events from pointing devices without precise scrolling are mapped to +// a best guess of 60 pixels. +function getWheelDelta(e) { + return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta + (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels + (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines + (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages + (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events + e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels + (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines + e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages + 0; +} + +var skipEvents = {}; + +function fakeStop(e) { + // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) + skipEvents[e.type] = true; +} + +function skipped(e) { + var events = skipEvents[e.type]; + // reset when checking, as it's only used in map container and propagates outside of the map + skipEvents[e.type] = false; + return events; +} + +// check if element really left/entered the event target (for mouseenter/mouseleave) +function isExternalTarget(el, e) { + + var related = e.relatedTarget; + + if (!related) { return true; } + + try { + while (related && (related !== el)) { + related = related.parentNode; + } + } catch (err) { + return false; + } + return (related !== el); +} + +var lastClick; + +// this is a horrible workaround for a bug in Android where a single touch triggers two click events +function filterClick(e, handler) { + var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), + elapsed = lastClick && (timeStamp - lastClick); + + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events + + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + stop(e); + return; + } + lastClick = timeStamp; + + handler(e); +} + + + + +var DomEvent = (Object.freeze || Object)({ + on: on, + off: off, + stopPropagation: stopPropagation, + disableScrollPropagation: disableScrollPropagation, + disableClickPropagation: disableClickPropagation, + preventDefault: preventDefault, + stop: stop, + getMousePosition: getMousePosition, + getWheelDelta: getWheelDelta, + fakeStop: fakeStop, + skipped: skipped, + isExternalTarget: isExternalTarget, + addListener: on, + removeListener: off +}); + +/* + * @namespace DomUtil + * + * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) + * tree, used by Leaflet internally. + * + * Most functions expecting or returning a `HTMLElement` also work for + * SVG elements. The only difference is that classes refer to CSS classes + * in HTML and SVG classes in SVG. + */ + + +// @property TRANSFORM: String +// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit). +var TRANSFORM = testProp( + ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); + +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser + +// @property TRANSITION: String +// Vendor-prefixed transition style name. +var TRANSITION = testProp( + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); + +// @property TRANSITION_END: String +// Vendor-prefixed transitionend event name. +var TRANSITION_END = + TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend'; + + +// @function get(id: String|HTMLElement): HTMLElement +// Returns an element given its DOM id, or returns the element itself +// if it was passed directly. +function get(id) { + return typeof id === 'string' ? document.getElementById(id) : id; +} + +// @function getStyle(el: HTMLElement, styleAttrib: String): String +// Returns the value for a certain style attribute on an element, +// including computed values or values set through CSS. +function getStyle(el, style) { + var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); + + if ((!value || value === 'auto') && document.defaultView) { + var css = document.defaultView.getComputedStyle(el, null); + value = css ? css[style] : null; + } + return value === 'auto' ? null : value; +} + +// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement +// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. +function create$1(tagName, className, container) { + var el = document.createElement(tagName); + el.className = className || ''; + + if (container) { + container.appendChild(el); + } + return el; +} + +// @function remove(el: HTMLElement) +// Removes `el` from its parent element +function remove(el) { + var parent = el.parentNode; + if (parent) { + parent.removeChild(el); + } +} + +// @function empty(el: HTMLElement) +// Removes all of `el`'s children elements from `el` +function empty(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} + +// @function toFront(el: HTMLElement) +// Makes `el` the last child of its parent, so it renders in front of the other children. +function toFront(el) { + var parent = el.parentNode; + if (parent.lastChild !== el) { + parent.appendChild(el); + } +} + +// @function toBack(el: HTMLElement) +// Makes `el` the first child of its parent, so it renders behind the other children. +function toBack(el) { + var parent = el.parentNode; + if (parent.firstChild !== el) { + parent.insertBefore(el, parent.firstChild); + } +} + +// @function hasClass(el: HTMLElement, name: String): Boolean +// Returns `true` if the element's class attribute contains `name`. +function hasClass(el, name) { + if (el.classList !== undefined) { + return el.classList.contains(name); + } + var className = getClass(el); + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); +} + +// @function addClass(el: HTMLElement, name: String) +// Adds `name` to the element's class attribute. +function addClass(el, name) { + if (el.classList !== undefined) { + var classes = splitWords(name); + for (var i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!hasClass(el, name)) { + var className = getClass(el); + setClass(el, (className ? className + ' ' : '') + name); + } +} + +// @function removeClass(el: HTMLElement, name: String) +// Removes `name` from the element's class attribute. +function removeClass(el, name) { + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' '))); + } +} + +// @function setClass(el: HTMLElement, name: String) +// Sets the element's class. +function setClass(el, name) { + if (el.className.baseVal === undefined) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } +} + +// @function getClass(el: HTMLElement): String +// Returns the element's class. +function getClass(el) { + return el.className.baseVal === undefined ? el.className : el.className.baseVal; +} + +// @function setOpacity(el: HTMLElement, opacity: Number) +// Set the opacity of an element (including old IE support). +// `opacity` must be a number from `0` to `1`. +function setOpacity(el, value) { + if ('opacity' in el.style) { + el.style.opacity = value; + } else if ('filter' in el.style) { + _setOpacityIE(el, value); + } +} + +function _setOpacityIE(el, value) { + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; + + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { + filter = el.filters.item(filterName); + } catch (e) { + // don't set opacity to 1 if we haven't already set an opacity, + // it isn't needed and breaks transparent pngs. + if (value === 1) { return; } + } + + value = Math.round(value * 100); + + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + } +} + +// @function testProp(props: String[]): String|false +// Goes through the array of style names and returns the first name +// that is a valid style name for an element. If no such name is found, +// it returns false. Useful for vendor-prefixed styles like `transform`. +function testProp(props) { + var style = document.documentElement.style; + + for (var i = 0; i < props.length; i++) { + if (props[i] in style) { + return props[i]; + } + } + return false; +} + +// @function setTransform(el: HTMLElement, offset: Point, scale?: Number) +// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels +// and optionally scaled by `scale`. Does not have an effect if the +// browser doesn't support 3D CSS transforms. +function setTransform(el, offset, scale) { + var pos = offset || new Point(0, 0); + + el.style[TRANSFORM] = + (ie3d ? + 'translate(' + pos.x + 'px,' + pos.y + 'px)' : + 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + + (scale ? ' scale(' + scale + ')' : ''); +} + +// @function setPosition(el: HTMLElement, position: Point) +// Sets the position of `el` to coordinates specified by `position`, +// using CSS translate or top/left positioning depending on the browser +// (used by Leaflet internally to position its layers). +function setPosition(el, point) { + + /*eslint-disable */ + el._leaflet_pos = point; + /* eslint-enable */ + + if (any3d) { + setTransform(el, point); + } else { + el.style.left = point.x + 'px'; + el.style.top = point.y + 'px'; + } +} + +// @function getPosition(el: HTMLElement): Point +// Returns the coordinates of an element previously positioned with setPosition. +function getPosition(el) { + // this method is only used for elements previously positioned using setPosition, + // so it's safe to cache the position for performance + + return el._leaflet_pos || new Point(0, 0); +} + +// @function disableTextSelection() +// Prevents the user from generating `selectstart` DOM events, usually generated +// when the user drags the mouse through a page with text. Used internally +// by Leaflet to override the behaviour of any click-and-drag interaction on +// the map. Affects drag interactions on the whole document. + +// @function enableTextSelection() +// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). +var disableTextSelection; +var enableTextSelection; +var _userSelect; +if ('onselectstart' in document) { + disableTextSelection = function () { + on(window, 'selectstart', preventDefault); + }; + enableTextSelection = function () { + off(window, 'selectstart', preventDefault); + }; +} else { + var userSelectProperty = testProp( + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); + + disableTextSelection = function () { + if (userSelectProperty) { + var style = document.documentElement.style; + _userSelect = style[userSelectProperty]; + style[userSelectProperty] = 'none'; + } + }; + enableTextSelection = function () { + if (userSelectProperty) { + document.documentElement.style[userSelectProperty] = _userSelect; + _userSelect = undefined; + } + }; +} + +// @function disableImageDrag() +// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but +// for `dragstart` DOM events, usually generated when the user drags an image. +function disableImageDrag() { + on(window, 'dragstart', preventDefault); +} + +// @function enableImageDrag() +// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). +function enableImageDrag() { + off(window, 'dragstart', preventDefault); +} + +var _outlineElement; +var _outlineStyle; +// @function preventOutline(el: HTMLElement) +// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) +// of the element `el` invisible. Used internally by Leaflet to prevent +// focusable elements from displaying an outline when the user performs a +// drag interaction on them. +function preventOutline(element) { + while (element.tabIndex === -1) { + element = element.parentNode; + } + if (!element.style) { return; } + restoreOutline(); + _outlineElement = element; + _outlineStyle = element.style.outline; + element.style.outline = 'none'; + on(window, 'keydown', restoreOutline); +} + +// @function restoreOutline() +// Cancels the effects of a previous [`L.DomUtil.preventOutline`](). +function restoreOutline() { + if (!_outlineElement) { return; } + _outlineElement.style.outline = _outlineStyle; + _outlineElement = undefined; + _outlineStyle = undefined; + off(window, 'keydown', restoreOutline); +} + + +var DomUtil = (Object.freeze || Object)({ + TRANSFORM: TRANSFORM, + TRANSITION: TRANSITION, + TRANSITION_END: TRANSITION_END, + get: get, + getStyle: getStyle, + create: create$1, + remove: remove, + empty: empty, + toFront: toFront, + toBack: toBack, + hasClass: hasClass, + addClass: addClass, + removeClass: removeClass, + setClass: setClass, + getClass: getClass, + setOpacity: setOpacity, + testProp: testProp, + setTransform: setTransform, + setPosition: setPosition, + getPosition: getPosition, + disableTextSelection: disableTextSelection, + enableTextSelection: enableTextSelection, + disableImageDrag: disableImageDrag, + enableImageDrag: enableImageDrag, + preventOutline: preventOutline, + restoreOutline: restoreOutline +}); + +/* + * @class PosAnimation + * @aka L.PosAnimation + * @inherits Evented + * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. + * + * @example + * ```js + * var fx = new L.PosAnimation(); + * fx.run(el, [300, 500], 0.5); + * ``` + * + * @constructor L.PosAnimation() + * Creates a `PosAnimation` object. + * + */ + +var PosAnimation = Evented.extend({ + + // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) + // Run an animation of a given element to a new position, optionally setting + // duration in seconds (`0.25` by default) and easing linearity factor (3rd + // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), + // `0.5` by default). + run: function (el, newPos, duration, easeLinearity) { + this.stop(); + + this._el = el; + this._inProgress = true; + this._duration = duration || 0.25; + this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); + + this._startPos = getPosition(el); + this._offset = newPos.subtract(this._startPos); + this._startTime = +new Date(); + + // @event start: Event + // Fired when the animation starts + this.fire('start'); + + this._animate(); + }, + + // @method stop() + // Stops the animation (if currently running). + stop: function () { + if (!this._inProgress) { return; } + + this._step(true); + this._complete(); + }, + + _animate: function () { + // animation loop + this._animId = requestAnimFrame(this._animate, this); + this._step(); + }, + + _step: function (round) { + var elapsed = (+new Date()) - this._startTime, + duration = this._duration * 1000; + + if (elapsed < duration) { + this._runFrame(this._easeOut(elapsed / duration), round); + } else { + this._runFrame(1); + this._complete(); + } + }, + + _runFrame: function (progress, round) { + var pos = this._startPos.add(this._offset.multiplyBy(progress)); + if (round) { + pos._round(); + } + setPosition(this._el, pos); + + // @event step: Event + // Fired continuously during the animation. + this.fire('step'); + }, + + _complete: function () { + cancelAnimFrame(this._animId); + + this._inProgress = false; + // @event end: Event + // Fired when the animation ends. + this.fire('end'); + }, + + _easeOut: function (t) { + return 1 - Math.pow(1 - t, this._easeOutPower); + } +}); + +/* + * @class Map + * @aka L.Map + * @inherits Evented + * + * The central class of the API — it is used to create a map on a page and manipulate it. + * + * @example + * + * ```js + * // initialize the map on the "map" div with a given center and zoom + * var map = L.map('map', { + * center: [51.505, -0.09], + * zoom: 13 + * }); + * ``` + * + */ + +var Map = Evented.extend({ + + options: { + // @section Map State Options + // @option crs: CRS = L.CRS.EPSG3857 + // The [Coordinate Reference System](#crs) to use. Don't change this if you're not + // sure what it means. + crs: EPSG3857, + + // @option center: LatLng = undefined + // Initial geographic center of the map + center: undefined, + + // @option zoom: Number = undefined + // Initial map zoom level + zoom: undefined, + + // @option minZoom: Number = * + // Minimum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the lowest of their `minZoom` options will be used instead. + minZoom: undefined, + + // @option maxZoom: Number = * + // Maximum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the highest of their `maxZoom` options will be used instead. + maxZoom: undefined, + + // @option layers: Layer[] = [] + // Array of layers that will be added to the map initially + layers: [], + + // @option maxBounds: LatLngBounds = null + // When this option is set, the map restricts the view to the given + // geographical bounds, bouncing the user back if the user tries to pan + // outside the view. To set the restriction dynamically, use + // [`setMaxBounds`](#map-setmaxbounds) method. + maxBounds: undefined, + + // @option renderer: Renderer = * + // The default method for drawing vector layers on the map. `L.SVG` + // or `L.Canvas` by default depending on browser support. + renderer: undefined, + + + // @section Animation Options + // @option zoomAnimation: Boolean = true + // Whether the map zoom animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + zoomAnimation: true, + + // @option zoomAnimationThreshold: Number = 4 + // Won't animate zoom if the zoom difference exceeds this value. + zoomAnimationThreshold: 4, + + // @option fadeAnimation: Boolean = true + // Whether the tile fade animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + fadeAnimation: true, + + // @option markerZoomAnimation: Boolean = true + // Whether markers animate their zoom with the zoom animation, if disabled + // they will disappear for the length of the animation. By default it's + // enabled in all browsers that support CSS3 Transitions except Android. + markerZoomAnimation: true, + + // @option transform3DLimit: Number = 2^23 + // Defines the maximum size of a CSS translation transform. The default + // value should not be changed unless a web browser positions layers in + // the wrong place after doing a large `panBy`. + transform3DLimit: 8388608, // Precision limit of a 32-bit float + + // @section Interaction Options + // @option zoomSnap: Number = 1 + // Forces the map's zoom level to always be a multiple of this, particularly + // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. + // By default, the zoom level snaps to the nearest integer; lower values + // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` + // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. + zoomSnap: 1, + + // @option zoomDelta: Number = 1 + // Controls how much the map's zoom level will change after a + // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` + // or `-` on the keyboard, or using the [zoom controls](#control-zoom). + // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. + zoomDelta: 1, + + // @option trackResize: Boolean = true + // Whether the map automatically handles browser window resize to update itself. + trackResize: true + }, + + initialize: function (id, options) { // (HTMLElement or String, Object) + options = setOptions(this, options); + + this._initContainer(id); + this._initLayout(); + + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = bind(this._onResize, this); + + this._initEvents(); + + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); + } + + if (options.zoom !== undefined) { + this._zoom = this._limitZoom(options.zoom); + } + + if (options.center && options.zoom !== undefined) { + this.setView(toLatLng(options.center), options.zoom, {reset: true}); + } + + this._handlers = []; + this._layers = {}; + this._zoomBoundLayers = {}; + this._sizeChanged = true; + + this.callInitHooks(); + + // don't animate on browsers without hardware-accelerated transitions or old Android/Opera + this._zoomAnimated = TRANSITION && any3d && !mobileOpera && + this.options.zoomAnimation; + + // zoom transitions run with the same duration for all layers, so if one of transitionend events + // happens after starting zoom animation (propagating to the map pane), we know that it ended globally + if (this._zoomAnimated) { + this._createAnimProxy(); + on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); + } + + this._addLayers(this.options.layers); + }, + + + // @section Methods for modifying map state + + // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) with the given + // animation options. + setView: function (center, zoom, options) { + + zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); + center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); + options = options || {}; + + this._stop(); + + if (this._loaded && !options.reset && options !== true) { + + if (options.animate !== undefined) { + options.zoom = extend({animate: options.animate}, options.zoom); + options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); + } + + // try animating pan or zoom + var moved = (this._zoom !== zoom) ? + this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : + this._tryAnimatedPan(center, options.pan); + + if (moved) { + // prevent resize handler call, the view will refresh after animation anyway + clearTimeout(this._sizeTimer); + return this; + } + } + + // animation didn't start, just reset the map view + this._resetView(center, zoom); + + return this; + }, + + // @method setZoom(zoom: Number, options?: Zoom/pan options): this + // Sets the zoom of the map. + setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = zoom; + return this; + } + return this.setView(this.getCenter(), zoom, {zoom: options}); + }, + + // @method zoomIn(delta?: Number, options?: Zoom options): this + // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomIn: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom + delta, options); + }, + + // @method zoomOut(delta?: Number, options?: Zoom options): this + // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomOut: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom - delta, options); + }, + + // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified geographical point on the map + // stationary (e.g. used internally for scroll zoom and double-click zoom). + // @alternative + // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. + setZoomAround: function (latlng, zoom, options) { + var scale = this.getZoomScale(zoom), + viewHalf = this.getSize().divideBy(2), + containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), + + centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); + + return this.setView(newCenter, zoom, {zoom: options}); + }, + + _getBoundsCenterZoom: function (bounds, options) { + + options = options || {}; + bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); + + var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), + + zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); + + zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; + + if (zoom === Infinity) { + return { + center: bounds.getCenter(), + zoom: zoom + }; + } + + var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), + + swPoint = this.project(bounds.getSouthWest(), zoom), + nePoint = this.project(bounds.getNorthEast(), zoom), + center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); + + return { + center: center, + zoom: zoom + }; + }, + + // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets a map view that contains the given geographical bounds with the + // maximum zoom level possible. + fitBounds: function (bounds, options) { + + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + throw new Error('Bounds are not valid.'); + } + + var target = this._getBoundsCenterZoom(bounds, options); + return this.setView(target.center, target.zoom, options); + }, + + // @method fitWorld(options?: fitBounds options): this + // Sets a map view that mostly contains the whole world with the maximum + // zoom level possible. + fitWorld: function (options) { + return this.fitBounds([[-90, -180], [90, 180]], options); + }, + + // @method panTo(latlng: LatLng, options?: Pan options): this + // Pans the map to a given center. + panTo: function (center, options) { // (LatLng) + return this.setView(center, this._zoom, {pan: options}); + }, + + // @method panBy(offset: Point, options?: Pan options): this + // Pans the map by a given number of pixels (animated). + panBy: function (offset, options) { + offset = toPoint(offset).round(); + options = options || {}; + + if (!offset.x && !offset.y) { + return this.fire('moveend'); + } + // If we pan too far, Chrome gets issues with tiles + // and makes them disappear or appear in the wrong place (slightly offset) #2602 + if (options.animate !== true && !this.getSize().contains(offset)) { + this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); + return this; + } + + if (!this._panAnim) { + this._panAnim = new PosAnimation(); + + this._panAnim.on({ + 'step': this._onPanTransitionStep, + 'end': this._onPanTransitionEnd + }, this); + } + + // don't fire movestart if animating inertia + if (!options.noMoveStart) { + this.fire('movestart'); + } + + // animate pan unless animate: false specified + if (options.animate !== false) { + addClass(this._mapPane, 'leaflet-pan-anim'); + + var newPos = this._getMapPanePos().subtract(offset).round(); + this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); + } else { + this._rawPanBy(offset); + this.fire('move').fire('moveend'); + } + + return this; + }, + + // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) performing a smooth + // pan-zoom animation. + flyTo: function (targetCenter, targetZoom, options) { + + options = options || {}; + if (options.animate === false || !any3d) { + return this.setView(targetCenter, targetZoom, options); + } + + this._stop(); + + var from = this.project(this.getCenter()), + to = this.project(targetCenter), + size = this.getSize(), + startZoom = this._zoom; + + targetCenter = toLatLng(targetCenter); + targetZoom = targetZoom === undefined ? startZoom : targetZoom; + + var w0 = Math.max(size.x, size.y), + w1 = w0 * this.getZoomScale(startZoom, targetZoom), + u1 = (to.distanceTo(from)) || 1, + rho = 1.42, + rho2 = rho * rho; + + function r(i) { + var s1 = i ? -1 : 1, + s2 = i ? w1 : w0, + t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, + b1 = 2 * s2 * rho2 * u1, + b = t1 / b1, + sq = Math.sqrt(b * b + 1) - b; + + // workaround for floating point precision bug when sq = 0, log = -Infinite, + // thus triggering an infinite loop in flyTo + var log = sq < 0.000000001 ? -18 : Math.log(sq); + + return log; + } + + function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } + function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } + function tanh(n) { return sinh(n) / cosh(n); } + + var r0 = r(0); + + function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } + function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } + + function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } + + var start = Date.now(), + S = (r(1) - r0) / rho, + duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; + + function frame() { + var t = (Date.now() - start) / duration, + s = easeOut(t) * S; + + if (t <= 1) { + this._flyToFrame = requestAnimFrame(frame, this); + + this._move( + this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), + this.getScaleZoom(w0 / w(s), startZoom), + {flyTo: true}); + + } else { + this + ._move(targetCenter, targetZoom) + ._moveEnd(true); + } + } + + this._moveStart(true, options.noMoveStart); + + frame.call(this); + return this; + }, + + // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), + // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). + flyToBounds: function (bounds, options) { + var target = this._getBoundsCenterZoom(bounds, options); + return this.flyTo(target.center, target.zoom, options); + }, + + // @method setMaxBounds(bounds: Bounds): this + // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). + setMaxBounds: function (bounds) { + bounds = toLatLngBounds(bounds); + + if (!bounds.isValid()) { + this.options.maxBounds = null; + return this.off('moveend', this._panInsideMaxBounds); + } else if (this.options.maxBounds) { + this.off('moveend', this._panInsideMaxBounds); + } + + this.options.maxBounds = bounds; + + if (this._loaded) { + this._panInsideMaxBounds(); + } + + return this.on('moveend', this._panInsideMaxBounds); + }, + + // @method setMinZoom(zoom: Number): this + // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). + setMinZoom: function (zoom) { + var oldZoom = this.options.minZoom; + this.options.minZoom = zoom; + + if (this._loaded && oldZoom !== zoom) { + this.fire('zoomlevelschange'); + + if (this.getZoom() < this.options.minZoom) { + return this.setZoom(zoom); + } + } + + return this; + }, + + // @method setMaxZoom(zoom: Number): this + // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). + setMaxZoom: function (zoom) { + var oldZoom = this.options.maxZoom; + this.options.maxZoom = zoom; + + if (this._loaded && oldZoom !== zoom) { + this.fire('zoomlevelschange'); + + if (this.getZoom() > this.options.maxZoom) { + return this.setZoom(zoom); + } + } + + return this; + }, + + // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this + // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. + panInsideBounds: function (bounds, options) { + this._enforcingBounds = true; + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); + + if (!center.equals(newCenter)) { + this.panTo(newCenter, options); + } + + this._enforcingBounds = false; + return this; + }, + + // @method invalidateSize(options: Zoom/pan options): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. If `options.pan` is `false`, panning will not occur. + // If `options.debounceMoveend` is `true`, it will delay `moveend` event so + // that it doesn't happen often even if the method is called many + // times in a row. + + // @alternative + // @method invalidateSize(animate: Boolean): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. + invalidateSize: function (options) { + if (!this._loaded) { return this; } + + options = extend({ + animate: false, + pan: true + }, options === true ? {animate: true} : options); + + var oldSize = this.getSize(); + this._sizeChanged = true; + this._lastCenter = null; + + var newSize = this.getSize(), + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); + + if (!offset.x && !offset.y) { return this; } + + if (options.animate && options.pan) { + this.panBy(offset); + + } else { + if (options.pan) { + this._rawPanBy(offset); + } + + this.fire('move'); + + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } + } + + // @section Map state change events + // @event resize: ResizeEvent + // Fired when the map is resized. + return this.fire('resize', { + oldSize: oldSize, + newSize: newSize + }); + }, + + // @section Methods for modifying map state + // @method stop(): this + // Stops the currently running `panTo` or `flyTo` animation, if any. + stop: function () { + this.setZoom(this._limitZoom(this._zoom)); + if (!this.options.zoomSnap) { + this.fire('viewreset'); + } + return this._stop(); + }, + + // @section Geolocation methods + // @method locate(options?: Locate options): this + // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) + // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, + // and optionally sets the map view to the user's location with respect to + // detection accuracy (or to the world view if geolocation failed). + // Note that, if your page doesn't use HTTPS, this method will fail in + // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) + // See `Locate options` for more details. + locate: function (options) { + + options = this._locateOptions = extend({ + timeout: 10000, + watch: false + // setView: false + // maxZoom: <Number> + // maximumAge: 0 + // enableHighAccuracy: false + }, options); + + if (!('geolocation' in navigator)) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + var onResponse = bind(this._handleGeolocationResponse, this), + onError = bind(this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + return this; + }, + + // @method stopLocate(): this + // Stops watching location previously initiated by `map.locate({watch: true})` + // and aborts resetting the map view if map.locate was called with + // `{setView: true}`. + stopLocate: function () { + if (navigator.geolocation && navigator.geolocation.clearWatch) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + if (this._locateOptions) { + this._locateOptions.setView = false; + } + return this; + }, + + _handleGeolocationError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); + + if (this._locateOptions.setView && !this._loaded) { + this.fitWorld(); + } + + // @section Location events + // @event locationerror: ErrorEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) failed. + this.fire('locationerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); + }, + + _handleGeolocationResponse: function (pos) { + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new LatLng(lat, lng), + bounds = latlng.toBounds(pos.coords.accuracy), + options = this._locateOptions; + + if (options.setView) { + var zoom = this.getBoundsZoom(bounds); + this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); + } + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + // @event locationfound: LocationEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) + // went successfully. + this.fire('locationfound', data); + }, + + // TODO Appropriate docs section? + // @section Other Methods + // @method addHandler(name: String, HandlerClass: Function): this + // Adds a new `Handler` to the map, given its name and constructor function. + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return this; } + + var handler = this[name] = new HandlerClass(this); + + this._handlers.push(handler); + + if (this.options[name]) { + handler.enable(); + } + + return this; + }, + + // @method remove(): this + // Destroys the map and clears all related event listeners. + remove: function () { + + this._initEvents(true); + + if (this._containerId !== this._container._leaflet_id) { + throw new Error('Map container is being reused by another instance'); + } + + try { + // throws error in IE6-8 + delete this._container._leaflet_id; + delete this._containerId; + } catch (e) { + /*eslint-disable */ + this._container._leaflet_id = undefined; + /* eslint-enable */ + this._containerId = undefined; + } + + if (this._locationWatchId !== undefined) { + this.stopLocate(); + } + + this._stop(); + + remove(this._mapPane); + + if (this._clearControlPos) { + this._clearControlPos(); + } + + this._clearHandlers(); + + if (this._loaded) { + // @section Map state change events + // @event unload: Event + // Fired when the map is destroyed with [remove](#map-remove) method. + this.fire('unload'); + } + + var i; + for (i in this._layers) { + this._layers[i].remove(); + } + for (i in this._panes) { + remove(this._panes[i]); + } + + this._layers = []; + this._panes = []; + delete this._mapPane; + delete this._renderer; + + return this; + }, + + // @section Other Methods + // @method createPane(name: String, container?: HTMLElement): HTMLElement + // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, + // then returns it. The pane is created as a child of `container`, or + // as a child of the main map pane if not set. + createPane: function (name, container) { + var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), + pane = create$1('div', className, container || this._mapPane); + + if (name) { + this._panes[name] = pane; + } + return pane; + }, + + // @section Methods for Getting Map State + + // @method getCenter(): LatLng + // Returns the geographical center of the map view + getCenter: function () { + this._checkIfLoaded(); + + if (this._lastCenter && !this._moved()) { + return this._lastCenter; + } + return this.layerPointToLatLng(this._getCenterLayerPoint()); + }, + + // @method getZoom(): Number + // Returns the current zoom level of the map view + getZoom: function () { + return this._zoom; + }, + + // @method getBounds(): LatLngBounds + // Returns the geographical bounds visible in the current map view + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); + + return new LatLngBounds(sw, ne); + }, + + // @method getMinZoom(): Number + // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. + getMinZoom: function () { + return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; + }, + + // @method getMaxZoom(): Number + // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). + getMaxZoom: function () { + return this.options.maxZoom === undefined ? + (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : + this.options.maxZoom; + }, + + // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number + // Returns the maximum zoom level on which the given bounds fit to the map + // view in its entirety. If `inside` (optional) is set to `true`, the method + // instead returns the minimum zoom level on which the map view fits into + // the given bounds in its entirety. + getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number + bounds = toLatLngBounds(bounds); + padding = toPoint(padding || [0, 0]); + + var zoom = this.getZoom() || 0, + min = this.getMinZoom(), + max = this.getMaxZoom(), + nw = bounds.getNorthWest(), + se = bounds.getSouthEast(), + size = this.getSize().subtract(padding), + boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), + snap = any3d ? this.options.zoomSnap : 1, + scalex = size.x / boundsSize.x, + scaley = size.y / boundsSize.y, + scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); + + zoom = this.getScaleZoom(scale, zoom); + + if (snap) { + zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level + zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; + } + + return Math.max(min, Math.min(max, zoom)); + }, + + // @method getSize(): Point + // Returns the current size of the map container (in pixels). + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new Point( + this._container.clientWidth || 0, + this._container.clientHeight || 0); + + this._sizeChanged = false; + } + return this._size.clone(); + }, + + // @method getPixelBounds(): Bounds + // Returns the bounds of the current map view in projected pixel + // coordinates (sometimes useful in layer and overlay implementations). + getPixelBounds: function (center, zoom) { + var topLeftPoint = this._getTopLeftPoint(center, zoom); + return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + }, + + // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to + // the map pane? "left point of the map layer" can be confusing, specially + // since there can be negative offsets. + // @method getPixelOrigin(): Point + // Returns the projected pixel coordinates of the top left point of + // the map layer (useful in custom layer and overlay implementations). + getPixelOrigin: function () { + this._checkIfLoaded(); + return this._pixelOrigin; + }, + + // @method getPixelWorldBounds(zoom?: Number): Bounds + // Returns the world's bounds in pixel coordinates for zoom level `zoom`. + // If `zoom` is omitted, the map's current zoom level is used. + getPixelWorldBounds: function (zoom) { + return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); + }, + + // @section Other Methods + + // @method getPane(pane: String|HTMLElement): HTMLElement + // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). + getPane: function (pane) { + return typeof pane === 'string' ? this._panes[pane] : pane; + }, + + // @method getPanes(): Object + // Returns a plain object containing the names of all [panes](#map-pane) as keys and + // the panes as values. + getPanes: function () { + return this._panes; + }, + + // @method getContainer: HTMLElement + // Returns the HTML element that contains the map. + getContainer: function () { + return this._container; + }, + + + // @section Conversion Methods + + // @method getZoomScale(toZoom: Number, fromZoom: Number): Number + // Returns the scale factor to be applied to a map transition from zoom level + // `fromZoom` to `toZoom`. Used internally to help with zoom animations. + getZoomScale: function (toZoom, fromZoom) { + // TODO replace with universal implementation after refactoring projections + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + return crs.scale(toZoom) / crs.scale(fromZoom); + }, + + // @method getScaleZoom(scale: Number, fromZoom: Number): Number + // Returns the zoom level that the map would end up at, if it is at `fromZoom` + // level and everything is scaled by a factor of `scale`. Inverse of + // [`getZoomScale`](#map-getZoomScale). + getScaleZoom: function (scale, fromZoom) { + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + var zoom = crs.zoom(scale * crs.scale(fromZoom)); + return isNaN(zoom) ? Infinity : zoom; + }, + + // @method project(latlng: LatLng, zoom: Number): Point + // Projects a geographical coordinate `LatLng` according to the projection + // of the map's CRS, then scales it according to `zoom` and the CRS's + // `Transformation`. The result is pixel coordinate relative to + // the CRS origin. + project: function (latlng, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); + }, + + // @method unproject(point: Point, zoom: Number): LatLng + // Inverse of [`project`](#map-project). + unproject: function (point, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(toPoint(point), zoom); + }, + + // @method layerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding geographical coordinate (for the current zoom level). + layerPointToLatLng: function (point) { + var projectedPoint = toPoint(point).add(this.getPixelOrigin()); + return this.unproject(projectedPoint); + }, + + // @method latLngToLayerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the [origin pixel](#map-getpixelorigin). + latLngToLayerPoint: function (latlng) { + var projectedPoint = this.project(toLatLng(latlng))._round(); + return projectedPoint._subtract(this.getPixelOrigin()); + }, + + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the + // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the + // CRS's bounds. + // By default this means longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees. + wrapLatLng: function (latlng) { + return this.options.crs.wrapLatLng(toLatLng(latlng)); + }, + + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring that + // its center is within the CRS's bounds. + // By default this means the center longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees, and the majority of the bounds + // overlaps the CRS's bounds. + wrapLatLngBounds: function (latlng) { + return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); + }, + + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates according to + // the map's CRS. By default this measures distance in meters. + distance: function (latlng1, latlng2) { + return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); + }, + + // @method containerPointToLayerPoint(point: Point): Point + // Given a pixel coordinate relative to the map container, returns the corresponding + // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). + containerPointToLayerPoint: function (point) { // (Point) + return toPoint(point).subtract(this._getMapPanePos()); + }, + + // @method layerPointToContainerPoint(point: Point): Point + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding pixel coordinate relative to the map container. + layerPointToContainerPoint: function (point) { // (Point) + return toPoint(point).add(this._getMapPanePos()); + }, + + // @method containerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the map container, returns + // the corresponding geographical coordinate (for the current zoom level). + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(toPoint(point)); + return this.layerPointToLatLng(layerPoint); + }, + + // @method latLngToContainerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the map container. + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); + }, + + // @method mouseEventToContainerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to the + // map container where the event took place. + mouseEventToContainerPoint: function (e) { + return getMousePosition(e, this._container); + }, + + // @method mouseEventToLayerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to + // the [origin pixel](#map-getpixelorigin) where the event took place. + mouseEventToLayerPoint: function (e) { + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, + + // @method mouseEventToLatLng(ev: MouseEvent): LatLng + // Given a MouseEvent object, returns geographical coordinate where the + // event took place. + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, + + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = get(id); + + if (!container) { + throw new Error('Map container not found.'); + } else if (container._leaflet_id) { + throw new Error('Map container is already initialized.'); + } + + on(container, 'scroll', this._onScroll, this); + this._containerId = stamp(container); + }, + + _initLayout: function () { + var container = this._container; + + this._fadeAnimated = this.options.fadeAnimation && any3d; + + addClass(container, 'leaflet-container' + + (touch ? ' leaflet-touch' : '') + + (retina ? ' leaflet-retina' : '') + + (ielt9 ? ' leaflet-oldie' : '') + + (safari ? ' leaflet-safari' : '') + + (this._fadeAnimated ? ' leaflet-fade-anim' : '')); + + var position = getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { + container.style.position = 'relative'; + } + + this._initPanes(); + + if (this._initControlPos) { + this._initControlPos(); + } + }, + + _initPanes: function () { + var panes = this._panes = {}; + this._paneRenderers = {}; + + // @section + // + // Panes are DOM elements used to control the ordering of layers on the map. You + // can access panes with [`map.getPane`](#map-getpane) or + // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the + // [`map.createPane`](#map-createpane) method. + // + // Every map has the following default panes that differ only in zIndex. + // + // @pane mapPane: HTMLElement = 'auto' + // Pane that contains all other map panes + + this._mapPane = this.createPane('mapPane', this._container); + setPosition(this._mapPane, new Point(0, 0)); + + // @pane tilePane: HTMLElement = 200 + // Pane for `GridLayer`s and `TileLayer`s + this.createPane('tilePane'); + // @pane overlayPane: HTMLElement = 400 + // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s + this.createPane('shadowPane'); + // @pane shadowPane: HTMLElement = 500 + // Pane for overlay shadows (e.g. `Marker` shadows) + this.createPane('overlayPane'); + // @pane markerPane: HTMLElement = 600 + // Pane for `Icon`s of `Marker`s + this.createPane('markerPane'); + // @pane tooltipPane: HTMLElement = 650 + // Pane for `Tooltip`s. + this.createPane('tooltipPane'); + // @pane popupPane: HTMLElement = 700 + // Pane for `Popup`s. + this.createPane('popupPane'); + + if (!this.options.markerZoomAnimation) { + addClass(panes.markerPane, 'leaflet-zoom-hide'); + addClass(panes.shadowPane, 'leaflet-zoom-hide'); + } + }, + + + // private methods that modify map state + + // @section Map state change events + _resetView: function (center, zoom) { + setPosition(this._mapPane, new Point(0, 0)); + + var loading = !this._loaded; + this._loaded = true; + zoom = this._limitZoom(zoom); + + this.fire('viewprereset'); + + var zoomChanged = this._zoom !== zoom; + this + ._moveStart(zoomChanged, false) + ._move(center, zoom) + ._moveEnd(zoomChanged); + + // @event viewreset: Event + // Fired when the map needs to redraw its content (this usually happens + // on map zoom or load). Very useful for creating custom overlays. + this.fire('viewreset'); + + // @event load: Event + // Fired when the map is initialized (when its center and zoom are set + // for the first time). + if (loading) { + this.fire('load'); + } + }, + + _moveStart: function (zoomChanged, noMoveStart) { + // @event zoomstart: Event + // Fired when the map zoom is about to change (e.g. before zoom animation). + // @event movestart: Event + // Fired when the view of the map starts changing (e.g. user starts dragging the map). + if (zoomChanged) { + this.fire('zoomstart'); + } + if (!noMoveStart) { + this.fire('movestart'); + } + return this; + }, + + _move: function (center, zoom, data) { + if (zoom === undefined) { + zoom = this._zoom; + } + var zoomChanged = this._zoom !== zoom; + + this._zoom = zoom; + this._lastCenter = center; + this._pixelOrigin = this._getNewPixelOrigin(center); + + // @event zoom: Event + // Fired repeatedly during any change in zoom level, including zoom + // and fly animations. + if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 + this.fire('zoom', data); + } + + // @event move: Event + // Fired repeatedly during any movement of the map, including pan and + // fly animations. + return this.fire('move', data); + }, + + _moveEnd: function (zoomChanged) { + // @event zoomend: Event + // Fired when the map has changed, after any animations. + if (zoomChanged) { + this.fire('zoomend'); + } + + // @event moveend: Event + // Fired when the center of the map stops changing (e.g. user stopped + // dragging the map). + return this.fire('moveend'); + }, + + _stop: function () { + cancelAnimFrame(this._flyToFrame); + if (this._panAnim) { + this._panAnim.stop(); + } + return this; + }, + + _rawPanBy: function (offset) { + setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, + + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, + + _panInsideMaxBounds: function () { + if (!this._enforcingBounds) { + this.panInsideBounds(this.options.maxBounds); + } + }, + + _checkIfLoaded: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + }, + + // DOM event handling + + // @section Interaction events + _initEvents: function (remove$$1) { + this._targets = {}; + this._targets[stamp(this._container)] = this; + + var onOff = remove$$1 ? off : on; + + // @event click: MouseEvent + // Fired when the user clicks (or taps) the map. + // @event dblclick: MouseEvent + // Fired when the user double-clicks (or double-taps) the map. + // @event mousedown: MouseEvent + // Fired when the user pushes the mouse button on the map. + // @event mouseup: MouseEvent + // Fired when the user releases the mouse button on the map. + // @event mouseover: MouseEvent + // Fired when the mouse enters the map. + // @event mouseout: MouseEvent + // Fired when the mouse leaves the map. + // @event mousemove: MouseEvent + // Fired while the mouse moves over the map. + // @event contextmenu: MouseEvent + // Fired when the user pushes the right mouse button on the map, prevents + // default browser context menu from showing if there are listeners on + // this event. Also fired on mobile when the user holds a single touch + // for a second (also called long press). + // @event keypress: KeyboardEvent + // Fired when the user presses a key from the keyboard while the map is focused. + onOff(this._container, 'click dblclick mousedown mouseup ' + + 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); + + if (this.options.trackResize) { + onOff(window, 'resize', this._onResize, this); + } + + if (any3d && this.options.transform3DLimit) { + (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); + } + }, + + _onResize: function () { + cancelAnimFrame(this._resizeRequest); + this._resizeRequest = requestAnimFrame( + function () { this.invalidateSize({debounceMoveend: true}); }, this); + }, + + _onScroll: function () { + this._container.scrollTop = 0; + this._container.scrollLeft = 0; + }, + + _onMoveEnd: function () { + var pos = this._getMapPanePos(); + if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have + // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ + this._resetView(this.getCenter(), this.getZoom()); + } + }, + + _findEventTargets: function (e, type) { + var targets = [], + target, + isHover = type === 'mouseout' || type === 'mouseover', + src = e.target || e.srcElement, + dragging = false; + + while (src) { + target = this._targets[stamp(src)]; + if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { + // Prevent firing click after you just dragged an object. + dragging = true; + break; + } + if (target && target.listens(type, true)) { + if (isHover && !isExternalTarget(src, e)) { break; } + targets.push(target); + if (isHover) { break; } + } + if (src === this._container) { break; } + src = src.parentNode; + } + if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { + targets = [this]; + } + return targets; + }, + + _handleDOMEvent: function (e) { + if (!this._loaded || skipped(e)) { return; } + + var type = e.type; + + if (type === 'mousedown' || type === 'keypress') { + // prevents outline when clicking on keyboard-focusable element + preventOutline(e.target || e.srcElement); + } + + this._fireDOMEvent(e, type); + }, + + _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], + + _fireDOMEvent: function (e, type, targets) { + + if (e.type === 'click') { + // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). + // @event preclick: MouseEvent + // Fired before mouse click on the map (sometimes useful when you + // want something to happen on click before any existing click + // handlers start running). + var synth = extend({}, e); + synth.type = 'preclick'; + this._fireDOMEvent(synth, synth.type, targets); + } + + if (e._stopped) { return; } + + // Find the layer the event is propagating from and its parents. + targets = (targets || []).concat(this._findEventTargets(e, type)); + + if (!targets.length) { return; } + + var target = targets[0]; + if (type === 'contextmenu' && target.listens(type, true)) { + preventDefault(e); + } + + var data = { + originalEvent: e + }; + + if (e.type !== 'keypress') { + var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); + data.containerPoint = isMarker ? + this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); + data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); + data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); + } + + for (var i = 0; i < targets.length; i++) { + targets[i].fire(type, data, true); + if (data.originalEvent._stopped || + (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } + } + }, + + _draggableMoved: function (obj) { + obj = obj.dragging && obj.dragging.enabled() ? obj : this; + return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); + }, + + _clearHandlers: function () { + for (var i = 0, len = this._handlers.length; i < len; i++) { + this._handlers[i].disable(); + } + }, + + // @section Other Methods + + // @method whenReady(fn: Function, context?: Object): this + // Runs the given function `fn` when the map gets initialized with + // a view (center and zoom) and at least one layer, or immediately + // if it's already initialized, optionally passing a function context. + whenReady: function (callback, context) { + if (this._loaded) { + callback.call(context || this, {target: this}); + } else { + this.on('load', callback, context); + } + return this; + }, + + + // private methods for getting map state + + _getMapPanePos: function () { + return getPosition(this._mapPane) || new Point(0, 0); + }, + + _moved: function () { + var pos = this._getMapPanePos(); + return pos && !pos.equals([0, 0]); + }, + + _getTopLeftPoint: function (center, zoom) { + var pixelOrigin = center && zoom !== undefined ? + this._getNewPixelOrigin(center, zoom) : + this.getPixelOrigin(); + return pixelOrigin.subtract(this._getMapPanePos()); + }, + + _getNewPixelOrigin: function (center, zoom) { + var viewHalf = this.getSize()._divideBy(2); + return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); + }, + + _latLngToNewLayerPoint: function (latlng, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return this.project(latlng, zoom)._subtract(topLeft); + }, + + _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return toBounds([ + this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) + ]); + }, + + // layer point of the current center + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + }, + + // offset of the specified place to the current center in pixels + _getCenterOffset: function (latlng) { + return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); + }, + + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { + + if (!bounds) { return center; } + + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); + + // If offset is less than a pixel, ignore. + // This prevents unstable projections from getting into + // an infinite loop of tiny offsets. + if (offset.round().equals([0, 0])) { + return center; + } + + return this.unproject(centerPoint.add(offset), zoom); + }, + + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } + + var viewBounds = this.getPixelBounds(), + newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var projectedMaxBounds = toBounds( + this.project(maxBounds.getNorthEast(), zoom), + this.project(maxBounds.getSouthWest(), zoom) + ), + minOffset = projectedMaxBounds.min.subtract(pxBounds.min), + maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), + + dx = this._rebound(minOffset.x, -maxOffset.x), + dy = this._rebound(minOffset.y, -maxOffset.y); + + return new Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(), + snap = any3d ? this.options.zoomSnap : 1; + if (snap) { + zoom = Math.round(zoom / snap) * snap; + } + return Math.max(min, Math.min(max, zoom)); + }, + + _onPanTransitionStep: function () { + this.fire('move'); + }, + + _onPanTransitionEnd: function () { + removeClass(this._mapPane, 'leaflet-pan-anim'); + this.fire('moveend'); + }, + + _tryAnimatedPan: function (center, options) { + // difference between the new and current centers in pixels + var offset = this._getCenterOffset(center)._trunc(); + + // don't animate too far unless animate: true specified in options + if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } + + this.panBy(offset, options); + + return true; + }, + + _createAnimProxy: function () { + + var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); + this._panes.mapPane.appendChild(proxy); + + this.on('zoomanim', function (e) { + var prop = TRANSFORM, + transform = this._proxy.style[prop]; + + setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + + // workaround for case when transform is the same and so transitionend event is not fired + if (transform === this._proxy.style[prop] && this._animatingZoom) { + this._onZoomTransitionEnd(); + } + }, this); + + this.on('load moveend', function () { + var c = this.getCenter(), + z = this.getZoom(); + setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); + }, this); + + this._on('unload', this._destroyAnimProxy, this); + }, + + _destroyAnimProxy: function () { + remove(this._proxy); + delete this._proxy; + }, + + _catchTransitionEnd: function (e) { + if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { + this._onZoomTransitionEnd(); + } + }, + + _nothingToAnimate: function () { + return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + }, + + _tryAnimatedZoom: function (center, zoom, options) { + + if (this._animatingZoom) { return true; } + + options = options || {}; + + // don't animate if disabled, not supported or zoom difference is too large + if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || + Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + + // offset is the pixel coords of the zoom origin relative to the current center + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); + + // don't animate if the zoom origin isn't within one screen from the current center, unless forced + if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + + requestAnimFrame(function () { + this + ._moveStart(true, false) + ._animateZoom(center, zoom, true); + }, this); + + return true; + }, + + _animateZoom: function (center, zoom, startAnim, noUpdate) { + if (!this._mapPane) { return; } + + if (startAnim) { + this._animatingZoom = true; + + // remember what center/zoom to set after animation + this._animateToCenter = center; + this._animateToZoom = zoom; + + addClass(this._mapPane, 'leaflet-zoom-anim'); + } + + // @event zoomanim: ZoomAnimEvent + // Fired on every frame of a zoom animation + this.fire('zoomanim', { + center: center, + zoom: zoom, + noUpdate: noUpdate + }); + + // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 + setTimeout(bind(this._onZoomTransitionEnd, this), 250); + }, + + _onZoomTransitionEnd: function () { + if (!this._animatingZoom) { return; } + + if (this._mapPane) { + removeClass(this._mapPane, 'leaflet-zoom-anim'); + } + + this._animatingZoom = false; + + this._move(this._animateToCenter, this._animateToZoom); + + // This anim frame should prevent an obscure iOS webkit tile loading race condition. + requestAnimFrame(function () { + this._moveEnd(true); + }, this); + } +}); + +// @section + +// @factory L.map(id: String, options?: Map options) +// Instantiates a map object given the DOM ID of a `<div>` element +// and optionally an object literal with `Map options`. +// +// @alternative +// @factory L.map(el: HTMLElement, options?: Map options) +// Instantiates a map object given an instance of a `<div>` HTML element +// and optionally an object literal with `Map options`. +function createMap(id, options) { + return new Map(id, options); +} + +/* + * @class Control + * @aka L.Control + * @inherits Class + * + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ + +var Control = Class.extend({ + // @section + // @aka Control options + options: { + // @option position: String = 'topright' + // The position of the control (one of the map corners). Possible values are `'topleft'`, + // `'topright'`, `'bottomleft'` or `'bottomright'` + position: 'topright' + }, + + initialize: function (options) { + setOptions(this, options); + }, + + /* @section + * Classes extending L.Control will inherit the following methods: + * + * @method getPosition: string + * Returns the position of the control. + */ + getPosition: function () { + return this.options.position; + }, + + // @method setPosition(position: string): this + // Sets the position of the control. + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + // @method getContainer: HTMLElement + // Returns the HTMLElement that contains the control. + getContainer: function () { + return this._container; + }, + + // @method addTo(map: Map): this + // Adds the control to the given map. + addTo: function (map) { + this.remove(); + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + return this; + }, + + // @method remove: this + // Removes the control from the map it is currently active on. + remove: function () { + if (!this._map) { + return this; + } + + remove(this._container); + + if (this.onRemove) { + this.onRemove(this._map); + } + + this._map = null; + + return this; + }, + + _refocusOnMap: function (e) { + // if map exists and event is not a keyboard event + if (this._map && e && e.screenX > 0 && e.screenY > 0) { + this._map.getContainer().focus(); + } + } +}); + +var control = function (options) { + return new Control(options); +}; + +/* @section Extension methods + * @uninheritable + * + * Every control should extend from `L.Control` and (re-)implement the following methods. + * + * @method onAdd(map: Map): HTMLElement + * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). + * + * @method onRemove(map: Map) + * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). + */ + +/* @namespace Map + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addControl(control: Control): this + // Adds the given control to the map + addControl: function (control) { + control.addTo(this); + return this; + }, + + // @method removeControl(control: Control): this + // Removes the given control from the map + removeControl: function (control) { + control.remove(); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + create$1('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = create$1('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + _clearControlPos: function () { + for (var i in this._controlCorners) { + remove(this._controlCorners[i]); + } + remove(this._controlContainer); + delete this._controlCorners; + delete this._controlContainer; + } +}); + +/* + * @class Control.Layers + * @aka L.Control.Layers + * @inherits Control + * + * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`. + * + * @example + * + * ```js + * var baseLayers = { + * "Mapbox": mapbox, + * "OpenStreetMap": osm + * }; + * + * var overlays = { + * "Marker": marker, + * "Roads": roadsLayer + * }; + * + * L.control.layers(baseLayers, overlays).addTo(map); + * ``` + * + * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: + * + * ```js + * { + * "<someName1>": layer1, + * "<someName2>": layer2 + * } + * ``` + * + * The layer names can contain HTML, which allows you to add additional styling to the items: + * + * ```js + * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer} + * ``` + */ + +var Layers = Control.extend({ + // @section + // @aka Control.Layers options + options: { + // @option collapsed: Boolean = true + // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. + collapsed: true, + position: 'topright', + + // @option autoZIndex: Boolean = true + // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. + autoZIndex: true, + + // @option hideSingleBase: Boolean = false + // If `true`, the base layers in the control will be hidden when there is only one. + hideSingleBase: false, + + // @option sortLayers: Boolean = false + // Whether to sort the layers. When `false`, layers will keep the order + // in which they were added to the control. + sortLayers: false, + + // @option sortFunction: Function = * + // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) + // that will be used for sorting the layers, when `sortLayers` is `true`. + // The function receives both the `L.Layer` instances and their names, as in + // `sortFunction(layerA, layerB, nameA, nameB)`. + // By default, it sorts layers alphabetically by their name. + sortFunction: function (layerA, layerB, nameA, nameB) { + return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); + } + }, + + initialize: function (baseLayers, overlays, options) { + setOptions(this, options); + + this._layerControlInputs = []; + this._layers = []; + this._lastZIndex = 0; + this._handlingClick = false; + + for (var i in baseLayers) { + this._addLayer(baseLayers[i], i); + } + + for (i in overlays) { + this._addLayer(overlays[i], i, true); + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + this._map = map; + map.on('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.on('add remove', this._onLayerChange, this); + } + + return this._container; + }, + + addTo: function (map) { + Control.prototype.addTo.call(this, map); + // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. + return this._expandIfNotCollapsed(); + }, + + onRemove: function () { + this._map.off('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.off('add remove', this._onLayerChange, this); + } + }, + + // @method addBaseLayer(layer: Layer, name: String): this + // Adds a base layer (radio button entry) with the given name to the control. + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + return (this._map) ? this._update() : this; + }, + + // @method addOverlay(layer: Layer, name: String): this + // Adds an overlay (checkbox entry) with the given name to the control. + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + return (this._map) ? this._update() : this; + }, + + // @method removeLayer(layer: Layer): this + // Remove the given layer from the control. + removeLayer: function (layer) { + layer.off('add remove', this._onLayerChange, this); + + var obj = this._getLayer(stamp(layer)); + if (obj) { + this._layers.splice(this._layers.indexOf(obj), 1); + } + return (this._map) ? this._update() : this; + }, + + // @method expand(): this + // Expand the control container if collapsed. + expand: function () { + addClass(this._container, 'leaflet-control-layers-expanded'); + this._form.style.height = null; + var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); + if (acceptableHeight < this._form.clientHeight) { + addClass(this._form, 'leaflet-control-layers-scrollbar'); + this._form.style.height = acceptableHeight + 'px'; + } else { + removeClass(this._form, 'leaflet-control-layers-scrollbar'); + } + this._checkDisabledLayers(); + return this; + }, + + // @method collapse(): this + // Collapse the control container if expanded. + collapse: function () { + removeClass(this._container, 'leaflet-control-layers-expanded'); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = create$1('div', className), + collapsed = this.options.collapsed; + + // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); + + disableClickPropagation(container); + disableScrollPropagation(container); + + var form = this._form = create$1('form', className + '-list'); + + if (collapsed) { + this._map.on('click', this.collapse, this); + + if (!android) { + on(container, { + mouseenter: this.expand, + mouseleave: this.collapse + }, this); + } + } + + var link = this._layersLink = create$1('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (touch) { + on(link, 'click', stop); + on(link, 'click', this.expand, this); + } else { + on(link, 'focus', this.expand, this); + } + + if (!collapsed) { + this.expand(); + } + + this._baseLayersList = create$1('div', className + '-base', form); + this._separator = create$1('div', className + '-separator', form); + this._overlaysList = create$1('div', className + '-overlays', form); + + container.appendChild(form); + }, + + _getLayer: function (id) { + for (var i = 0; i < this._layers.length; i++) { + + if (this._layers[i] && stamp(this._layers[i].layer) === id) { + return this._layers[i]; + } + } + }, + + _addLayer: function (layer, name, overlay) { + if (this._map) { + layer.on('add remove', this._onLayerChange, this); + } + + this._layers.push({ + layer: layer, + name: name, + overlay: overlay + }); + + if (this.options.sortLayers) { + this._layers.sort(bind(function (a, b) { + return this.options.sortFunction(a.layer, b.layer, a.name, b.name); + }, this)); + } + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + + this._expandIfNotCollapsed(); + }, + + _update: function () { + if (!this._container) { return this; } + + empty(this._baseLayersList); + empty(this._overlaysList); + + this._layerControlInputs = []; + var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; + + for (i = 0; i < this._layers.length; i++) { + obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + baseLayersCount += !obj.overlay ? 1 : 0; + } + + // Hide base layers section if there's only one layer. + if (this.options.hideSingleBase) { + baseLayersPresent = baseLayersPresent && baseLayersCount > 1; + this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; + } + + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; + + return this; + }, + + _onLayerChange: function (e) { + if (!this._handlingClick) { + this._update(); + } + + var obj = this._getLayer(stamp(e.target)); + + // @namespace Map + // @section Layer events + // @event baselayerchange: LayersControlEvent + // Fired when the base layer is changed through the [layer control](#control-layers). + // @event overlayadd: LayersControlEvent + // Fired when an overlay is selected through the [layer control](#control-layers). + // @event overlayremove: LayersControlEvent + // Fired when an overlay is deselected through the [layer control](#control-layers). + // @namespace Control.Layers + var type = obj.overlay ? + (e.type === 'add' ? 'overlayadd' : 'overlayremove') : + (e.type === 'add' ? 'baselayerchange' : null); + + if (type) { + this._map.fire(type, obj); + } + }, + + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) + _createRadioElement: function (name, checked) { + + var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + + name + '"' + (checked ? ' checked="checked"' : '') + '/>'; + + var radioFragment = document.createElement('div'); + radioFragment.innerHTML = radioHtml; + + return radioFragment.firstChild; + }, + + _addItem: function (obj) { + var label = document.createElement('label'), + checked = this._map.hasLayer(obj.layer), + input; + + if (obj.overlay) { + input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'leaflet-control-layers-selector'; + input.defaultChecked = checked; + } else { + input = this._createRadioElement('leaflet-base-layers', checked); + } + + this._layerControlInputs.push(input); + input.layerId = stamp(obj.layer); + + on(input, 'click', this._onInputClick, this); + + var name = document.createElement('span'); + name.innerHTML = ' ' + obj.name; + + // Helps from preventing layer control flicker when checkboxes are disabled + // https://github.com/Leaflet/Leaflet/issues/2771 + var holder = document.createElement('div'); + + label.appendChild(holder); + holder.appendChild(input); + holder.appendChild(name); + + var container = obj.overlay ? this._overlaysList : this._baseLayersList; + container.appendChild(label); + + this._checkDisabledLayers(); + return label; + }, + + _onInputClick: function () { + var inputs = this._layerControlInputs, + input, layer; + var addedLayers = [], + removedLayers = []; + + this._handlingClick = true; + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + + if (input.checked) { + addedLayers.push(layer); + } else if (!input.checked) { + removedLayers.push(layer); + } + } + + // Bugfix issue 2318: Should remove all old layers before readding new ones + for (i = 0; i < removedLayers.length; i++) { + if (this._map.hasLayer(removedLayers[i])) { + this._map.removeLayer(removedLayers[i]); + } + } + for (i = 0; i < addedLayers.length; i++) { + if (!this._map.hasLayer(addedLayers[i])) { + this._map.addLayer(addedLayers[i]); + } + } + + this._handlingClick = false; + + this._refocusOnMap(); + }, + + _checkDisabledLayers: function () { + var inputs = this._layerControlInputs, + input, + layer, + zoom = this._map.getZoom(); + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || + (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); + + } + }, + + _expandIfNotCollapsed: function () { + if (this._map && !this.options.collapsed) { + this.expand(); + } + return this; + }, + + _expand: function () { + // Backward compatibility, remove me in 1.1. + return this.expand(); + }, + + _collapse: function () { + // Backward compatibility, remove me in 1.1. + return this.collapse(); + } + +}); + + +// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) +// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. +var layers = function (baseLayers, overlays, options) { + return new Layers(baseLayers, overlays, options); +}; + +/* + * @class Control.Zoom + * @aka L.Control.Zoom + * @inherits Control + * + * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. + */ + +var Zoom = Control.extend({ + // @section + // @aka Control.Zoom options + options: { + position: 'topleft', + + // @option zoomInText: String = '+' + // The text set on the 'zoom in' button. + zoomInText: '+', + + // @option zoomInTitle: String = 'Zoom in' + // The title set on the 'zoom in' button. + zoomInTitle: 'Zoom in', + + // @option zoomOutText: String = '−' + // The text set on the 'zoom out' button. + zoomOutText: '−', + + // @option zoomOutTitle: String = 'Zoom out' + // The title set on the 'zoom out' button. + zoomOutTitle: 'Zoom out' + }, + + onAdd: function (map) { + var zoomName = 'leaflet-control-zoom', + container = create$1('div', zoomName + ' leaflet-bar'), + options = this.options; + + this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, + zoomName + '-in', container, this._zoomIn); + this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut); + + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); + + return container; + }, + + onRemove: function (map) { + map.off('zoomend zoomlevelschange', this._updateDisabled, this); + }, + + disable: function () { + this._disabled = true; + this._updateDisabled(); + return this; + }, + + enable: function () { + this._disabled = false; + this._updateDisabled(); + return this; + }, + + _zoomIn: function (e) { + if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { + this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _zoomOut: function (e) { + if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { + this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _createButton: function (html, title, className, container, fn) { + var link = create$1('a', className, container); + link.innerHTML = html; + link.href = '#'; + link.title = title; + + /* + * Will force screen readers like VoiceOver to read this as "Zoom in - button" + */ + link.setAttribute('role', 'button'); + link.setAttribute('aria-label', title); + + disableClickPropagation(link); + on(link, 'click', stop); + on(link, 'click', fn, this); + on(link, 'click', this._refocusOnMap, this); + + return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-disabled'; + + removeClass(this._zoomInButton, className); + removeClass(this._zoomOutButton, className); + + if (this._disabled || map._zoom === map.getMinZoom()) { + addClass(this._zoomOutButton, className); + } + if (this._disabled || map._zoom === map.getMaxZoom()) { + addClass(this._zoomInButton, className); + } + } +}); + +// @namespace Map +// @section Control options +// @option zoomControl: Boolean = true +// Whether a [zoom control](#control-zoom) is added to the map by default. +Map.mergeOptions({ + zoomControl: true +}); + +Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new Zoom(); + this.addControl(this.zoomControl); + } +}); + +// @namespace Control.Zoom +// @factory L.control.zoom(options: Control.Zoom options) +// Creates a zoom control +var zoom = function (options) { + return new Zoom(options); +}; + +/* + * @class Control.Scale + * @aka L.Control.Scale + * @inherits Control + * + * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. + * + * @example + * + * ```js + * L.control.scale().addTo(map); + * ``` + */ + +var Scale = Control.extend({ + // @section + // @aka Control.Scale options + options: { + position: 'bottomleft', + + // @option maxWidth: Number = 100 + // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). + maxWidth: 100, + + // @option metric: Boolean = True + // Whether to show the metric scale line (m/km). + metric: true, + + // @option imperial: Boolean = True + // Whether to show the imperial scale line (mi/ft). + imperial: true + + // @option updateWhenIdle: Boolean = false + // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). + }, + + onAdd: function (map) { + var className = 'leaflet-control-scale', + container = create$1('div', className), + options = this.options; + + this._addScales(options, className + '-line', container); + + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + map.whenReady(this._update, this); + + return container; + }, + + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + }, + + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = create$1('div', className, container); + } + if (options.imperial) { + this._iScale = create$1('div', className, container); + } + }, + + _update: function () { + var map = this._map, + y = map.getSize().y / 2; + + var maxMeters = map.distance( + map.containerPointToLatLng([0, y]), + map.containerPointToLatLng([this.options.maxWidth, y])); + + this._updateScales(maxMeters); + }, + + _updateScales: function (maxMeters) { + if (this.options.metric && maxMeters) { + this._updateMetric(maxMeters); + } + if (this.options.imperial && maxMeters) { + this._updateImperial(maxMeters); + } + }, + + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters), + label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; + + this._updateScale(this._mScale, label, meters / maxMeters); + }, + + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + maxMiles, miles, feet; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); + + } else { + feet = this._getRoundNum(maxFeet); + this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); + } + }, + + _updateScale: function (scale, text, ratio) { + scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; + scale.innerHTML = text; + }, + + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; + + d = d >= 10 ? 10 : + d >= 5 ? 5 : + d >= 3 ? 3 : + d >= 2 ? 2 : 1; + + return pow10 * d; + } +}); + + +// @factory L.control.scale(options?: Control.Scale options) +// Creates an scale control with the given options. +var scale = function (options) { + return new Scale(options); +}; + +/* + * @class Control.Attribution + * @aka L.Control.Attribution + * @inherits Control + * + * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. + */ + +var Attribution = Control.extend({ + // @section + // @aka Control.Attribution options + options: { + position: 'bottomright', + + // @option prefix: String = 'Leaflet' + // The HTML text shown before the attributions. Pass `false` to disable. + prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>' + }, + + initialize: function (options) { + setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + map.attributionControl = this; + this._container = create$1('div', 'leaflet-control-attribution'); + disableClickPropagation(this._container); + + // TODO ugly, refactor + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + + this._update(); + + return this._container; + }, + + // @method setPrefix(prefix: String): this + // Sets the text before the attributions. + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + // @method addAttribution(text: String): this + // Adds an attribution text (e.g. `'Vector data © Mapbox'`). + addAttribution: function (text) { + if (!text) { return this; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + // @method removeAttribution(text: String): this + // Removes an attribution text. + removeAttribution: function (text) { + if (!text) { return this; } + + if (this._attributions[text]) { + this._attributions[text]--; + this._update(); + } + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' | '); + } +}); + +// @namespace Map +// @section Control options +// @option attributionControl: Boolean = true +// Whether a [attribution control](#control-attribution) is added to the map by default. +Map.mergeOptions({ + attributionControl: true +}); + +Map.addInitHook(function () { + if (this.options.attributionControl) { + new Attribution().addTo(this); + } +}); + +// @namespace Control.Attribution +// @factory L.control.attribution(options: Control.Attribution options) +// Creates an attribution control. +var attribution = function (options) { + return new Attribution(options); +}; + +Control.Layers = Layers; +Control.Zoom = Zoom; +Control.Scale = Scale; +Control.Attribution = Attribution; + +control.layers = layers; +control.zoom = zoom; +control.scale = scale; +control.attribution = attribution; + +/* + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ + +// @class Handler +// @aka L.Handler +// Abstract class for map interaction handlers + +var Handler = Class.extend({ + initialize: function (map) { + this._map = map; + }, + + // @method enable(): this + // Enables the handler + enable: function () { + if (this._enabled) { return this; } + + this._enabled = true; + this.addHooks(); + return this; + }, + + // @method disable(): this + // Disables the handler + disable: function () { + if (!this._enabled) { return this; } + + this._enabled = false; + this.removeHooks(); + return this; + }, + + // @method enabled(): Boolean + // Returns `true` if the handler is enabled + enabled: function () { + return !!this._enabled; + } + + // @section Extension methods + // Classes inheriting from `Handler` must implement the two following methods: + // @method addHooks() + // Called when the handler is enabled, should add event hooks. + // @method removeHooks() + // Called when the handler is disabled, should remove the event hooks added previously. +}); + +// @section There is static function which can be called without instantiating L.Handler: +// @function addTo(map: Map, name: String): this +// Adds a new Handler to the given map with the given name. +Handler.addTo = function (map, name) { + map.addHandler(name, this); + return this; +}; + +var Mixin = {Events: Events}; + +/* + * @class Draggable + * @aka L.Draggable + * @inherits Evented + * + * A class for making DOM elements draggable (including touch support). + * Used internally for map and marker dragging. Only works for elements + * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). + * + * @example + * ```js + * var draggable = new L.Draggable(elementToDrag); + * draggable.enable(); + * ``` + */ + +var START = touch ? 'touchstart mousedown' : 'mousedown'; +var END = { + mousedown: 'mouseup', + touchstart: 'touchend', + pointerdown: 'touchend', + MSPointerDown: 'touchend' +}; +var MOVE = { + mousedown: 'mousemove', + touchstart: 'touchmove', + pointerdown: 'touchmove', + MSPointerDown: 'touchmove' +}; + + +var Draggable = Evented.extend({ + + options: { + // @section + // @aka Draggable options + // @option clickTolerance: Number = 3 + // The max number of pixels a user can shift the mouse pointer during a click + // for it to be considered a valid click (as opposed to a mouse drag). + clickTolerance: 3 + }, + + // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) + // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). + initialize: function (element, dragStartTarget, preventOutline$$1, options) { + setOptions(this, options); + + this._element = element; + this._dragStartTarget = dragStartTarget || element; + this._preventOutline = preventOutline$$1; + }, + + // @method enable() + // Enables the dragging ability + enable: function () { + if (this._enabled) { return; } + + on(this._dragStartTarget, START, this._onDown, this); + + this._enabled = true; + }, + + // @method disable() + // Disables the dragging ability + disable: function () { + if (!this._enabled) { return; } + + // If we're currently dragging this draggable, + // disabling it counts as first ending the drag. + if (Draggable._dragging === this) { + this.finishDrag(); + } + + off(this._dragStartTarget, START, this._onDown, this); + + this._enabled = false; + this._moved = false; + }, + + _onDown: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + + this._moved = false; + + if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } + + if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } + Draggable._dragging = this; // Prevent dragging multiple objects at once. + + if (this._preventOutline) { + preventOutline(this._element); + } + + disableImageDrag(); + disableTextSelection(); + + if (this._moving) { return; } + + // @event down: Event + // Fired when a drag is about to start. + this.fire('down'); + + var first = e.touches ? e.touches[0] : e; + + this._startPoint = new Point(first.clientX, first.clientY); + + on(document, MOVE[e.type], this._onMove, this); + on(document, END[e.type], this._onUp, this); + }, + + _onMove: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + + if (e.touches && e.touches.length > 1) { + this._moved = true; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + newPoint = new Point(first.clientX, first.clientY), + offset = newPoint.subtract(this._startPoint); + + if (!offset.x && !offset.y) { return; } + if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } + + preventDefault(e); + + if (!this._moved) { + // @event dragstart: Event + // Fired when a drag starts + this.fire('dragstart'); + + this._moved = true; + this._startPos = getPosition(this._element).subtract(offset); + + addClass(document.body, 'leaflet-dragging'); + + this._lastTarget = e.target || e.srcElement; + // IE and Edge do not give the <use> element, so fetch it + // if necessary + if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { + this._lastTarget = this._lastTarget.correspondingUseElement; + } + addClass(this._lastTarget, 'leaflet-drag-target'); + } + + this._newPos = this._startPos.add(offset); + this._moving = true; + + cancelAnimFrame(this._animRequest); + this._lastEvent = e; + this._animRequest = requestAnimFrame(this._updatePosition, this, true); + }, + + _updatePosition: function () { + var e = {originalEvent: this._lastEvent}; + + // @event predrag: Event + // Fired continuously during dragging *before* each corresponding + // update of the element's position. + this.fire('predrag', e); + setPosition(this._element, this._newPos); + + // @event drag: Event + // Fired continuously during dragging. + this.fire('drag', e); + }, + + _onUp: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + this.finishDrag(); + }, + + finishDrag: function () { + removeClass(document.body, 'leaflet-dragging'); + + if (this._lastTarget) { + removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } + + for (var i in MOVE) { + off(document, MOVE[i], this._onMove, this); + off(document, END[i], this._onUp, this); + } + + enableImageDrag(); + enableTextSelection(); + + if (this._moved && this._moving) { + // ensure drag is not fired after dragend + cancelAnimFrame(this._animRequest); + + // @event dragend: DragEndEvent + // Fired when the drag ends. + this.fire('dragend', { + distance: this._newPos.distanceTo(this._startPos) + }); + } + + this._moving = false; + Draggable._dragging = false; + } + +}); + +/* + * @namespace LineUtil + * + * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast. + */ + +// Simplify polyline with vertex reduction and Douglas-Peucker simplification. +// Improves rendering performance dramatically by lessening the number of points to draw. + +// @function simplify(points: Point[], tolerance: Number): Point[] +// Dramatically reduces the number of points in a polyline while retaining +// its shape and returns a new array of simplified points, using the +// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). +// Used for a huge performance boost when processing/displaying Leaflet polylines for +// each zoom level and also reducing visual noise. tolerance affects the amount of +// simplification (lesser value means higher quality but slower and with more points). +// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). +function simplify(points, tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } + + var sqTolerance = tolerance * tolerance; + + // stage 1: vertex reduction + points = _reducePoints(points, sqTolerance); + + // stage 2: Douglas-Peucker simplification + points = _simplifyDP(points, sqTolerance); + + return points; +} + +// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number +// Returns the distance between point `p` and segment `p1` to `p2`. +function pointToSegmentDistance(p, p1, p2) { + return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); +} + +// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number +// Returns the closest point from a point `p` on a segment `p1` to `p2`. +function closestPointOnSegment(p, p1, p2) { + return _sqClosestPointOnSegment(p, p1, p2); +} + +// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm +function _simplifyDP(points, sqTolerance) { + + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); + + markers[0] = markers[len - 1] = 1; + + _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + + var i, + newPoints = []; + + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); + } + } + + return newPoints; +} + +function _simplifyDPStep(points, markers, sqTolerance, first, last) { + + var maxSqDist = 0, + index, i, sqDist; + + for (i = first + 1; i <= last - 1; i++) { + sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + _simplifyDPStep(points, markers, sqTolerance, first, index); + _simplifyDPStep(points, markers, sqTolerance, index, last); + } +} + +// reduce points that are too close to each other to a single point +function _reducePoints(points, sqTolerance) { + var reducedPoints = [points[0]]; + + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (_sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; + } + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; +} + +var _lastCode; + +// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean +// Clips the segment a to b by rectangular bounds with the +// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) +// (modifying the segment points directly!). Used by Leaflet to only show polyline +// points that are on the screen or near, increasing performance. +function clipSegment(a, b, bounds, useLastCode, round) { + var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), + codeB = _getBitCode(b, bounds), + + codeOut, p, newCode; + + // save 2nd code to avoid calculating it on the next segment + _lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; + } + + // if a,b is outside the clip window (trivial reject) + if (codeA & codeB) { + return false; + } + + // other cases + codeOut = codeA || codeB; + p = _getEdgeIntersection(a, b, codeOut, bounds, round); + newCode = _getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } +} + +function _getEdgeIntersection(a, b, code, bounds, round) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max, + x, y; + + if (code & 8) { // top + x = a.x + dx * (max.y - a.y) / dy; + y = max.y; + + } else if (code & 4) { // bottom + x = a.x + dx * (min.y - a.y) / dy; + y = min.y; + + } else if (code & 2) { // right + x = max.x; + y = a.y + dy * (max.x - a.x) / dx; + + } else if (code & 1) { // left + x = min.x; + y = a.y + dy * (min.x - a.x) / dx; + } + + return new Point(x, y, round); +} + +function _getBitCode(p, bounds) { + var code = 0; + + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } + + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } + + return code; +} + +// square distance (to avoid unnecessary Math.sqrt calls) +function _sqDist(p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; +} + +// return closest point on segment or distance to that point +function _sqClosestPointOnSegment(p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return sqDist ? dx * dx + dy * dy : new Point(x, y); +} + + +// @function isFlat(latlngs: LatLng[]): Boolean +// Returns true if `latlngs` is a flat array, false is nested. +function isFlat(latlngs) { + return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); +} + +function _flat(latlngs) { + console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.'); + return isFlat(latlngs); +} + + +var LineUtil = (Object.freeze || Object)({ + simplify: simplify, + pointToSegmentDistance: pointToSegmentDistance, + closestPointOnSegment: closestPointOnSegment, + clipSegment: clipSegment, + _getEdgeIntersection: _getEdgeIntersection, + _getBitCode: _getBitCode, + _sqClosestPointOnSegment: _sqClosestPointOnSegment, + isFlat: isFlat, + _flat: _flat +}); + +/* + * @namespace PolyUtil + * Various utility functions for polygon geometries. + */ + +/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] + * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). + * Used by Leaflet to only show polygon points that are on the screen or near, increasing + * performance. Note that polygon points needs different algorithm for clipping + * than polyline, so there's a separate method for it. + */ +function clipPolygon(points, bounds, round) { + var clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = _getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + } + points = clippedPoints; + } + + return points; +} + + +var PolyUtil = (Object.freeze || Object)({ + clipPolygon: clipPolygon +}); + +/* + * @namespace Projection + * @section + * Leaflet comes with a set of already defined Projections out of the box: + * + * @projection L.Projection.LonLat + * + * Equirectangular, or Plate Carree projection — the most simple projection, + * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as + * latitude. Also suitable for flat worlds, e.g. game maps. Used by the + * `EPSG:4326` and `Simple` CRS. + */ + +var LonLat = { + project: function (latlng) { + return new Point(latlng.lng, latlng.lat); + }, + + unproject: function (point) { + return new LatLng(point.y, point.x); + }, + + bounds: new Bounds([-180, -90], [180, 90]) +}; + +/* + * @namespace Projection + * @projection L.Projection.Mercator + * + * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. + */ + +var Mercator = { + R: 6378137, + R_MINOR: 6356752.314245179, + + bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), + + project: function (latlng) { + var d = Math.PI / 180, + r = this.R, + y = latlng.lat * d, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + con = e * Math.sin(y); + + var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); + y = -r * Math.log(Math.max(ts, 1E-10)); + + return new Point(latlng.lng * d * r, y); + }, + + unproject: function (point) { + var d = 180 / Math.PI, + r = this.R, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + ts = Math.exp(-point.y / r), + phi = Math.PI / 2 - 2 * Math.atan(ts); + + for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { + con = e * Math.sin(phi); + con = Math.pow((1 - con) / (1 + con), e / 2); + dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; + phi += dphi; + } + + return new LatLng(phi * d, point.x * d / r); + } +}; + +/* + * @class Projection + + * An object with methods for projecting geographical coordinates of the world onto + * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). + + * @property bounds: Bounds + * The bounds (specified in CRS units) where the projection is valid + + * @method project(latlng: LatLng): Point + * Projects geographical coordinates into a 2D point. + * Only accepts actual `L.LatLng` instances, not arrays. + + * @method unproject(point: Point): LatLng + * The inverse of `project`. Projects a 2D point into a geographical location. + * Only accepts actual `L.Point` instances, not arrays. + + * Note that the projection instances do not inherit from Leafet's `Class` object, + * and can't be instantiated. Also, new classes can't inherit from them, + * and methods can't be added to them with the `include` function. + + */ + + + + +var index = (Object.freeze || Object)({ + LonLat: LonLat, + Mercator: Mercator, + SphericalMercator: SphericalMercator +}); + +/* + * @namespace CRS + * @crs L.CRS.EPSG3395 + * + * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. + */ +var EPSG3395 = extend({}, Earth, { + code: 'EPSG:3395', + projection: Mercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * Mercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +/* + * @namespace CRS + * @crs L.CRS.EPSG4326 + * + * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. + * + * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), + * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` + * with this CRS, ensure that there are two 256x256 pixel tiles covering the + * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), + * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. + */ + +var EPSG4326 = extend({}, Earth, { + code: 'EPSG:4326', + projection: LonLat, + transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) +}); + +/* + * @namespace CRS + * @crs L.CRS.Simple + * + * A simple CRS that maps longitude and latitude into `x` and `y` directly. + * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` + * axis should still be inverted (going from bottom to top). `distance()` returns + * simple euclidean distance. + */ + +var Simple = extend({}, CRS, { + projection: LonLat, + transformation: toTransformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + }, + + zoom: function (scale) { + return Math.log(scale) / Math.LN2; + }, + + distance: function (latlng1, latlng2) { + var dx = latlng2.lng - latlng1.lng, + dy = latlng2.lat - latlng1.lat; + + return Math.sqrt(dx * dx + dy * dy); + }, + + infinite: true +}); + +CRS.Earth = Earth; +CRS.EPSG3395 = EPSG3395; +CRS.EPSG3857 = EPSG3857; +CRS.EPSG900913 = EPSG900913; +CRS.EPSG4326 = EPSG4326; +CRS.Simple = Simple; + +/* + * @class Layer + * @inherits Evented + * @aka L.Layer + * @aka ILayer + * + * A set of methods from the Layer base class that all Leaflet layers use. + * Inherits all methods, options and events from `L.Evented`. + * + * @example + * + * ```js + * var layer = L.Marker(latlng).addTo(map); + * layer.addTo(map); + * layer.remove(); + * ``` + * + * @event add: Event + * Fired after the layer is added to a map + * + * @event remove: Event + * Fired after the layer is removed from a map + */ + + +var Layer = Evented.extend({ + + // Classes extending `L.Layer` will inherit the following options: + options: { + // @option pane: String = 'overlayPane' + // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. + pane: 'overlayPane', + + // @option attribution: String = null + // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox". + attribution: null, + + bubblingMouseEvents: true + }, + + /* @section + * Classes extending `L.Layer` will inherit the following methods: + * + * @method addTo(map: Map|LayerGroup): this + * Adds the layer to the given map or layer group. + */ + addTo: function (map) { + map.addLayer(this); + return this; + }, + + // @method remove: this + // Removes the layer from the map it is currently active on. + remove: function () { + return this.removeFrom(this._map || this._mapToAdd); + }, + + // @method removeFrom(map: Map): this + // Removes the layer from the given map + removeFrom: function (obj) { + if (obj) { + obj.removeLayer(this); + } + return this; + }, + + // @method getPane(name? : String): HTMLElement + // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. + getPane: function (name) { + return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); + }, + + addInteractiveTarget: function (targetEl) { + this._map._targets[stamp(targetEl)] = this; + return this; + }, + + removeInteractiveTarget: function (targetEl) { + delete this._map._targets[stamp(targetEl)]; + return this; + }, + + // @method getAttribution: String + // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). + getAttribution: function () { + return this.options.attribution; + }, + + _layerAdd: function (e) { + var map = e.target; + + // check in case layer gets added and then removed before the map is ready + if (!map.hasLayer(this)) { return; } + + this._map = map; + this._zoomAnimated = map._zoomAnimated; + + if (this.getEvents) { + var events = this.getEvents(); + map.on(events, this); + this.once('remove', function () { + map.off(events, this); + }, this); + } + + this.onAdd(map); + + if (this.getAttribution && map.attributionControl) { + map.attributionControl.addAttribution(this.getAttribution()); + } + + this.fire('add'); + map.fire('layeradd', {layer: this}); + } +}); + +/* @section Extension methods + * @uninheritable + * + * Every layer should extend from `L.Layer` and (re-)implement the following methods. + * + * @method onAdd(map: Map): this + * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). + * + * @method onRemove(map: Map): this + * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). + * + * @method getEvents(): Object + * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. + * + * @method getAttribution(): String + * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. + * + * @method beforeAdd(map: Map): this + * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. + */ + + +/* @namespace Map + * @section Layer events + * + * @event layeradd: LayerEvent + * Fired when a new layer is added to the map. + * + * @event layerremove: LayerEvent + * Fired when some layer is removed from the map + * + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addLayer(layer: Layer): this + // Adds the given layer to the map + addLayer: function (layer) { + if (!layer._layerAdd) { + throw new Error('The provided object is not a Layer.'); + } + + var id = stamp(layer); + if (this._layers[id]) { return this; } + this._layers[id] = layer; + + layer._mapToAdd = this; + + if (layer.beforeAdd) { + layer.beforeAdd(this); + } + + this.whenReady(layer._layerAdd, layer); + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the map. + removeLayer: function (layer) { + var id = stamp(layer); + + if (!this._layers[id]) { return this; } + + if (this._loaded) { + layer.onRemove(this); + } + + if (layer.getAttribution && this.attributionControl) { + this.attributionControl.removeAttribution(layer.getAttribution()); + } + + delete this._layers[id]; + + if (this._loaded) { + this.fire('layerremove', {layer: layer}); + layer.fire('remove'); + } + + layer._map = layer._mapToAdd = null; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the map + hasLayer: function (layer) { + return !!layer && (stamp(layer) in this._layers); + }, + + /* @method eachLayer(fn: Function, context?: Object): this + * Iterates over the layers of the map, optionally specifying context of the iterator function. + * ``` + * map.eachLayer(function(layer){ + * layer.bindPopup('Hello'); + * }); + * ``` + */ + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + _addLayers: function (layers) { + layers = layers ? (isArray(layers) ? layers : [layers]) : []; + + for (var i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + }, + + _addZoomLimit: function (layer) { + if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { + this._zoomBoundLayers[stamp(layer)] = layer; + this._updateZoomLevels(); + } + }, + + _removeZoomLimit: function (layer) { + var id = stamp(layer); + + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } + }, + + _updateZoomLevels: function () { + var minZoom = Infinity, + maxZoom = -Infinity, + oldZoomSpan = this._getZoomSpan(); + + for (var i in this._zoomBoundLayers) { + var options = this._zoomBoundLayers[i].options; + + minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); + maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); + } + + this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; + this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; + + // @section Map state change events + // @event zoomlevelschange: Event + // Fired when the number of zoomlevels on the map is changed due + // to adding or removing a layer. + if (oldZoomSpan !== this._getZoomSpan()) { + this.fire('zoomlevelschange'); + } + + if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { + this.setZoom(this._layersMaxZoom); + } + if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { + this.setZoom(this._layersMinZoom); + } + } +}); + +/* + * @class LayerGroup + * @aka L.LayerGroup + * @inherits Layer + * + * Used to group several layers and handle them as one. If you add it to the map, + * any layers added or removed from the group will be added/removed on the map as + * well. Extends `Layer`. + * + * @example + * + * ```js + * L.layerGroup([marker1, marker2]) + * .addLayer(polyline) + * .addTo(map); + * ``` + */ + +var LayerGroup = Layer.extend({ + + initialize: function (layers, options) { + setOptions(this, options); + + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + // @method addLayer(layer: Layer): this + // Adds the given layer to the group. + addLayer: function (layer) { + var id = this.getLayerId(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the group. + // @alternative + // @method removeLayer(id: Number): this + // Removes the layer with the given internal ID from the group. + removeLayer: function (layer) { + var id = layer in this._layers ? layer : this.getLayerId(layer); + + if (this._map && this._layers[id]) { + this._map.removeLayer(this._layers[id]); + } + + delete this._layers[id]; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the group. + // @alternative + // @method hasLayer(id: Number): Boolean + // Returns `true` if the given internal ID is currently added to the group. + hasLayer: function (layer) { + return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); + }, + + // @method clearLayers(): this + // Removes all the layers from the group. + clearLayers: function () { + return this.eachLayer(this.removeLayer, this); + }, + + // @method invoke(methodName: String, …): this + // Calls `methodName` on every layer contained in this group, passing any + // additional parameters. Has no effect if the layers contained do not + // implement `methodName`. + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + + return this; + }, + + onAdd: function (map) { + this.eachLayer(map.addLayer, map); + }, + + onRemove: function (map) { + this.eachLayer(map.removeLayer, map); + }, + + // @method eachLayer(fn: Function, context?: Object): this + // Iterates over the layers of the group, optionally specifying context of the iterator function. + // ```js + // group.eachLayer(function (layer) { + // layer.bindPopup('Hello'); + // }); + // ``` + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + // @method getLayer(id: Number): Layer + // Returns the layer with the given internal ID. + getLayer: function (id) { + return this._layers[id]; + }, + + // @method getLayers(): Layer[] + // Returns an array of all the layers added to the group. + getLayers: function () { + var layers = []; + this.eachLayer(layers.push, layers); + return layers; + }, + + // @method setZIndex(zIndex: Number): this + // Calls `setZIndex` on every layer contained in this group, passing the z-index. + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); + }, + + // @method getLayerId(layer: Layer): Number + // Returns the internal ID for a layer + getLayerId: function (layer) { + return stamp(layer); + } +}); + + +// @factory L.layerGroup(layers?: Layer[], options?: Object) +// Create a layer group, optionally given an initial set of layers and an `options` object. +var layerGroup = function (layers, options) { + return new LayerGroup(layers, options); +}; + +/* + * @class FeatureGroup + * @aka L.FeatureGroup + * @inherits LayerGroup + * + * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: + * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) + * * Events are propagated to the `FeatureGroup`, so if the group has an event + * handler, it will handle events from any of the layers. This includes mouse events + * and custom events. + * * Has `layeradd` and `layerremove` events + * + * @example + * + * ```js + * L.featureGroup([marker1, marker2, polyline]) + * .bindPopup('Hello world!') + * .on('click', function() { alert('Clicked on a member of the group!'); }) + * .addTo(map); + * ``` + */ + +var FeatureGroup = LayerGroup.extend({ + + addLayer: function (layer) { + if (this.hasLayer(layer)) { + return this; + } + + layer.addEventParent(this); + + LayerGroup.prototype.addLayer.call(this, layer); + + // @event layeradd: LayerEvent + // Fired when a layer is added to this `FeatureGroup` + return this.fire('layeradd', {layer: layer}); + }, + + removeLayer: function (layer) { + if (!this.hasLayer(layer)) { + return this; + } + if (layer in this._layers) { + layer = this._layers[layer]; + } + + layer.removeEventParent(this); + + LayerGroup.prototype.removeLayer.call(this, layer); + + // @event layerremove: LayerEvent + // Fired when a layer is removed from this `FeatureGroup` + return this.fire('layerremove', {layer: layer}); + }, + + // @method setStyle(style: Path options): this + // Sets the given path options to each layer of the group that has a `setStyle` method. + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + // @method bringToFront(): this + // Brings the layer group to the top of all other layers + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + // @method bringToBack(): this + // Brings the layer group to the back of all other layers + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + // @method getBounds(): LatLngBounds + // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). + getBounds: function () { + var bounds = new LatLngBounds(); + + for (var id in this._layers) { + var layer = this._layers[id]; + bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); + } + return bounds; + } +}); + +// @factory L.featureGroup(layers: Layer[]) +// Create a feature group, optionally given an initial set of layers. +var featureGroup = function (layers) { + return new FeatureGroup(layers); +}; + +/* + * @class Icon + * @aka L.Icon + * + * Represents an icon to provide when creating a marker. + * + * @example + * + * ```js + * var myIcon = L.icon({ + * iconUrl: 'my-icon.png', + * iconRetinaUrl: 'my-icon@2x.png', + * iconSize: [38, 95], + * iconAnchor: [22, 94], + * popupAnchor: [-3, -76], + * shadowUrl: 'my-icon-shadow.png', + * shadowRetinaUrl: 'my-icon-shadow@2x.png', + * shadowSize: [68, 95], + * shadowAnchor: [22, 94] + * }); + * + * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); + * ``` + * + * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. + * + */ + +var Icon = Class.extend({ + + /* @section + * @aka Icon options + * + * @option iconUrl: String = null + * **(required)** The URL to the icon image (absolute or relative to your script path). + * + * @option iconRetinaUrl: String = null + * The URL to a retina sized version of the icon image (absolute or relative to your + * script path). Used for Retina screen devices. + * + * @option iconSize: Point = null + * Size of the icon image in pixels. + * + * @option iconAnchor: Point = null + * The coordinates of the "tip" of the icon (relative to its top left corner). The icon + * will be aligned so that this point is at the marker's geographical location. Centered + * by default if size is specified, also can be set in CSS with negative margins. + * + * @option popupAnchor: Point = [0, 0] + * The coordinates of the point from which popups will "open", relative to the icon anchor. + * + * @option tooltipAnchor: Point = [0, 0] + * The coordinates of the point from which tooltips will "open", relative to the icon anchor. + * + * @option shadowUrl: String = null + * The URL to the icon shadow image. If not specified, no shadow image will be created. + * + * @option shadowRetinaUrl: String = null + * + * @option shadowSize: Point = null + * Size of the shadow image in pixels. + * + * @option shadowAnchor: Point = null + * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same + * as iconAnchor if not specified). + * + * @option className: String = '' + * A custom class name to assign to both icon and shadow images. Empty by default. + */ + + options: { + popupAnchor: [0, 0], + tooltipAnchor: [0, 0], + }, + + initialize: function (options) { + setOptions(this, options); + }, + + // @method createIcon(oldIcon?: HTMLElement): HTMLElement + // Called internally when the icon has to be shown, returns a `<img>` HTML element + // styled according to the options. + createIcon: function (oldIcon) { + return this._createIcon('icon', oldIcon); + }, + + // @method createShadow(oldIcon?: HTMLElement): HTMLElement + // As `createIcon`, but for the shadow beneath it. + createShadow: function (oldIcon) { + return this._createIcon('shadow', oldIcon); + }, + + _createIcon: function (name, oldIcon) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error('iconUrl not set in Icon options (see the docs).'); + } + return null; + } + + var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); + this._setIconStyles(img, name); + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options; + var sizeOption = options[name + 'Size']; + + if (typeof sizeOption === 'number') { + sizeOption = [sizeOption, sizeOption]; + } + + var size = toPoint(sizeOption), + anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || + size && size.divideBy(2, true)); + + img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.src = src; + return el; + }, + + _getIconUrl: function (name) { + return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; + } +}); + + +// @factory L.icon(options: Icon options) +// Creates an icon instance with the given options. +function icon(options) { + return new Icon(options); +} + +/* + * @miniclass Icon.Default (Icon) + * @aka L.Icon.Default + * @section + * + * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when + * no icon is specified. Points to the blue marker image distributed with Leaflet + * releases. + * + * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` + * (which is a set of `Icon options`). + * + * If you want to _completely_ replace the default icon, override the + * `L.Marker.prototype.options.icon` with your own icon instead. + */ + +var IconDefault = Icon.extend({ + + options: { + iconUrl: 'marker-icon.png', + iconRetinaUrl: 'marker-icon-2x.png', + shadowUrl: 'marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + shadowSize: [41, 41] + }, + + _getIconUrl: function (name) { + if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only + IconDefault.imagePath = this._detectIconPath(); + } + + // @option imagePath: String + // `Icon.Default` will try to auto-detect the location of the + // blue icon images. If you are placing these images in a non-standard + // way, set this option to point to the right path. + return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); + }, + + _detectIconPath: function () { + var el = create$1('div', 'leaflet-default-icon-path', document.body); + var path = getStyle(el, 'background-image') || + getStyle(el, 'backgroundImage'); // IE8 + + document.body.removeChild(el); + + if (path === null || path.indexOf('url') !== 0) { + path = ''; + } else { + path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, ''); + } + + return path; + } +}); + +/* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ + + +/* @namespace Marker + * @section Interaction handlers + * + * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: + * + * ```js + * marker.dragging.disable(); + * ``` + * + * @property dragging: Handler + * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). + */ + +var MarkerDrag = Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, + + addHooks: function () { + var icon = this._marker._icon; + + if (!this._draggable) { + this._draggable = new Draggable(icon, icon, true); + } + + this._draggable.on({ + dragstart: this._onDragStart, + predrag: this._onPreDrag, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).enable(); + + addClass(icon, 'leaflet-marker-draggable'); + }, + + removeHooks: function () { + this._draggable.off({ + dragstart: this._onDragStart, + predrag: this._onPreDrag, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).disable(); + + if (this._marker._icon) { + removeClass(this._marker._icon, 'leaflet-marker-draggable'); + } + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _adjustPan: function (e) { + var marker = this._marker, + map = marker._map, + speed = this._marker.options.autoPanSpeed, + padding = this._marker.options.autoPanPadding, + iconPos = L.DomUtil.getPosition(marker._icon), + bounds = map.getPixelBounds(), + origin = map.getPixelOrigin(); + + var panBounds = toBounds( + bounds.min._subtract(origin).add(padding), + bounds.max._subtract(origin).subtract(padding) + ); + + if (!panBounds.contains(iconPos)) { + // Compute incremental movement + var movement = toPoint( + (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - + (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), + + (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - + (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) + ).multiplyBy(speed); + + map.panBy(movement, {animate: false}); + + this._draggable._newPos._add(movement); + this._draggable._startPos._add(movement); + + L.DomUtil.setPosition(marker._icon, this._draggable._newPos); + this._onDrag(e); + + this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); + } + }, + + _onDragStart: function () { + // @section Dragging events + // @event dragstart: Event + // Fired when the user starts dragging the marker. + + // @event movestart: Event + // Fired when the marker starts moving (because of dragging). + + this._oldLatLng = this._marker.getLatLng(); + this._marker + .closePopup() + .fire('movestart') + .fire('dragstart'); + }, + + _onPreDrag: function (e) { + if (this._marker.options.autoPan) { + cancelAnimFrame(this._panRequest); + this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); + } + }, + + _onDrag: function (e) { + var marker = this._marker, + shadow = marker._shadow, + iconPos = getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); + + // update shadow position + if (shadow) { + setPosition(shadow, iconPos); + } + + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; + + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('move', e) + .fire('drag', e); + }, + + _onDragEnd: function (e) { + // @event dragend: DragEndEvent + // Fired when the user stops dragging the marker. + + cancelAnimFrame(this._panRequest); + + // @event moveend: Event + // Fired when the marker stops moving (because of dragging). + delete this._oldLatLng; + this._marker + .fire('moveend') + .fire('dragend', e); + } +}); + +/* + * @class Marker + * @inherits Interactive layer + * @aka L.Marker + * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. + * + * @example + * + * ```js + * L.marker([50.5, 30.5]).addTo(map); + * ``` + */ + +var Marker = Layer.extend({ + + // @section + // @aka Marker options + options: { + // @option icon: Icon = * + // Icon instance to use for rendering the marker. + // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. + // If not specified, a common instance of `L.Icon.Default` is used. + icon: new IconDefault(), + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option draggable: Boolean = false + // Whether the marker is draggable with mouse/touch or not. + draggable: false, + + // @option autoPan: Boolean = false + // Set it to `true` if you want the map to do panning animation when marker hits the edges. + autoPan: false, + + // @option autoPanPadding: Point = Point(50, 50) + // Equivalent of setting both top left and bottom right autopan padding to the same value. + autoPanPadding: [50, 50], + + // @option autoPanSpeed: Number = 10 + // Number of pixels the map should move by. + autoPanSpeed: 10, + + // @option keyboard: Boolean = true + // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. + keyboard: true, + + // @option title: String = '' + // Text for the browser tooltip that appear on marker hover (no tooltip by default). + title: '', + + // @option alt: String = '' + // Text for the `alt` attribute of the icon image (useful for accessibility). + alt: '', + + // @option zIndexOffset: Number = 0 + // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). + zIndexOffset: 0, + + // @option opacity: Number = 1.0 + // The opacity of the marker. + opacity: 1, + + // @option riseOnHover: Boolean = false + // If `true`, the marker will get on top of others when you hover the mouse over it. + riseOnHover: false, + + // @option riseOffset: Number = 250 + // The z-index offset used for the `riseOnHover` feature. + riseOffset: 250, + + // @option pane: String = 'markerPane' + // `Map pane` where the markers icon will be added. + pane: 'markerPane', + + // @option bubblingMouseEvents: Boolean = false + // When `true`, a mouse event on this marker will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: false + }, + + /* @section + * + * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: + */ + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + }, + + onAdd: function (map) { + this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; + + if (this._zoomAnimated) { + map.on('zoomanim', this._animateZoom, this); + } + + this._initIcon(); + this.update(); + }, + + onRemove: function (map) { + if (this.dragging && this.dragging.enabled()) { + this.options.draggable = true; + this.dragging.removeHooks(); + } + delete this.dragging; + + if (this._zoomAnimated) { + map.off('zoomanim', this._animateZoom, this); + } + + this._removeIcon(); + this._removeShadow(); + }, + + getEvents: function () { + return { + zoom: this.update, + viewreset: this.update + }; + }, + + // @method getLatLng: LatLng + // Returns the current geographical position of the marker. + getLatLng: function () { + return this._latlng; + }, + + // @method setLatLng(latlng: LatLng): this + // Changes the marker position to the given point. + setLatLng: function (latlng) { + var oldLatLng = this._latlng; + this._latlng = toLatLng(latlng); + this.update(); + + // @event move: Event + // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); + }, + + // @method setZIndexOffset(offset: Number): this + // Changes the [zIndex offset](#marker-zindexoffset) of the marker. + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + return this.update(); + }, + + // @method setIcon(icon: Icon): this + // Changes the marker icon. + setIcon: function (icon) { + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + + if (this._popup) { + this.bindPopup(this._popup, this._popup.options); + } + + return this; + }, + + getElement: function () { + return this._icon; + }, + + update: function () { + + if (this._icon && this._map) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } + + return this; + }, + + _initIcon: function () { + var options = this.options, + classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); + + var icon = options.icon.createIcon(this._icon), + addIcon = false; + + // if we're not reusing the icon, remove the old one and init new one + if (icon !== this._icon) { + if (this._icon) { + this._removeIcon(); + } + addIcon = true; + + if (options.title) { + icon.title = options.title; + } + + if (icon.tagName === 'IMG') { + icon.alt = options.alt || ''; + } + } + + addClass(icon, classToAdd); + + if (options.keyboard) { + icon.tabIndex = '0'; + } + + this._icon = icon; + + if (options.riseOnHover) { + this.on({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + var newShadow = options.icon.createShadow(this._shadow), + addShadow = false; + + if (newShadow !== this._shadow) { + this._removeShadow(); + addShadow = true; + } + + if (newShadow) { + addClass(newShadow, classToAdd); + newShadow.alt = ''; + } + this._shadow = newShadow; + + + if (options.opacity < 1) { + this._updateOpacity(); + } + + + if (addIcon) { + this.getPane().appendChild(this._icon); + } + this._initInteraction(); + if (newShadow && addShadow) { + this.getPane('shadowPane').appendChild(this._shadow); + } + }, + + _removeIcon: function () { + if (this.options.riseOnHover) { + this.off({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + remove(this._icon); + this.removeInteractiveTarget(this._icon); + + this._icon = null; + }, + + _removeShadow: function () { + if (this._shadow) { + remove(this._shadow); + } + this._shadow = null; + }, + + _setPos: function (pos) { + setPosition(this._icon, pos); + + if (this._shadow) { + setPosition(this._shadow, pos); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + _updateZIndex: function (offset) { + this._icon.style.zIndex = this._zIndex + offset; + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); + + this._setPos(pos); + }, + + _initInteraction: function () { + + if (!this.options.interactive) { return; } + + addClass(this._icon, 'leaflet-interactive'); + + this.addInteractiveTarget(this._icon); + + if (MarkerDrag) { + var draggable = this.options.draggable; + if (this.dragging) { + draggable = this.dragging.enabled(); + this.dragging.disable(); + } + + this.dragging = new MarkerDrag(this); + + if (draggable) { + this.dragging.enable(); + } + } + }, + + // @method setOpacity(opacity: Number): this + // Changes the opacity of the marker. + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + _updateOpacity: function () { + var opacity = this.options.opacity; + + setOpacity(this._icon, opacity); + + if (this._shadow) { + setOpacity(this._shadow, opacity); + } + }, + + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + + _resetZIndex: function () { + this._updateZIndex(0); + }, + + _getPopupAnchor: function () { + return this.options.icon.options.popupAnchor; + }, + + _getTooltipAnchor: function () { + return this.options.icon.options.tooltipAnchor; + } +}); + + +// factory L.marker(latlng: LatLng, options? : Marker options) + +// @factory L.marker(latlng: LatLng, options? : Marker options) +// Instantiates a Marker object given a geographical point and optionally an options object. +function marker(latlng, options) { + return new Marker(latlng, options); +} + +/* + * @class Path + * @aka L.Path + * @inherits Interactive layer + * + * An abstract class that contains options and constants shared between vector + * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. + */ + +var Path = Layer.extend({ + + // @section + // @aka Path options + options: { + // @option stroke: Boolean = true + // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. + stroke: true, + + // @option color: String = '#3388ff' + // Stroke color + color: '#3388ff', + + // @option weight: Number = 3 + // Stroke width in pixels + weight: 3, + + // @option opacity: Number = 1.0 + // Stroke opacity + opacity: 1, + + // @option lineCap: String= 'round' + // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. + lineCap: 'round', + + // @option lineJoin: String = 'round' + // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. + lineJoin: 'round', + + // @option dashArray: String = null + // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashArray: null, + + // @option dashOffset: String = null + // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashOffset: null, + + // @option fill: Boolean = depends + // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. + fill: false, + + // @option fillColor: String = * + // Fill color. Defaults to the value of the [`color`](#path-color) option + fillColor: null, + + // @option fillOpacity: Number = 0.2 + // Fill opacity. + fillOpacity: 0.2, + + // @option fillRule: String = 'evenodd' + // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. + fillRule: 'evenodd', + + // className: '', + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option bubblingMouseEvents: Boolean = true + // When `true`, a mouse event on this path will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: true + }, + + beforeAdd: function (map) { + // Renderer is set here because we need to call renderer.getEvents + // before this.getEvents. + this._renderer = map.getRenderer(this); + }, + + onAdd: function () { + this._renderer._initPath(this); + this._reset(); + this._renderer._addPath(this); + }, + + onRemove: function () { + this._renderer._removePath(this); + }, + + // @method redraw(): this + // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. + redraw: function () { + if (this._map) { + this._renderer._updatePath(this); + } + return this; + }, + + // @method setStyle(style: Path options): this + // Changes the appearance of a Path based on the options in the `Path options` object. + setStyle: function (style) { + setOptions(this, style); + if (this._renderer) { + this._renderer._updateStyle(this); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all path layers. + bringToFront: function () { + if (this._renderer) { + this._renderer._bringToFront(this); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all path layers. + bringToBack: function () { + if (this._renderer) { + this._renderer._bringToBack(this); + } + return this; + }, + + getElement: function () { + return this._path; + }, + + _reset: function () { + // defined in child classes + this._project(); + this._update(); + }, + + _clickTolerance: function () { + // used when doing hit detection for Canvas layers + return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance; + } +}); + +/* + * @class CircleMarker + * @aka L.CircleMarker + * @inherits Path + * + * A circle of a fixed size with radius specified in pixels. Extends `Path`. + */ + +var CircleMarker = Path.extend({ + + // @section + // @aka CircleMarker options + options: { + fill: true, + + // @option radius: Number = 10 + // Radius of the circle marker, in pixels + radius: 10 + }, + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + this._radius = this.options.radius; + }, + + // @method setLatLng(latLng: LatLng): this + // Sets the position of a circle marker to a new location. + setLatLng: function (latlng) { + this._latlng = toLatLng(latlng); + this.redraw(); + return this.fire('move', {latlng: this._latlng}); + }, + + // @method getLatLng(): LatLng + // Returns the current geographical position of the circle marker + getLatLng: function () { + return this._latlng; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle marker. Units are in pixels. + setRadius: function (radius) { + this.options.radius = this._radius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of the circle + getRadius: function () { + return this._radius; + }, + + setStyle : function (options) { + var radius = options && options.radius || this._radius; + Path.prototype.setStyle.call(this, options); + this.setRadius(radius); + return this; + }, + + _project: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + this._updateBounds(); + }, + + _updateBounds: function () { + var r = this._radius, + r2 = this._radiusY || r, + w = this._clickTolerance(), + p = [r + w, r2 + w]; + this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); + }, + + _update: function () { + if (this._map) { + this._updatePath(); + } + }, + + _updatePath: function () { + this._renderer._updateCircle(this); + }, + + _empty: function () { + return this._radius && !this._renderer._bounds.intersects(this._pxBounds); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); + } +}); + + +// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) +// Instantiates a circle marker object given a geographical point, and an optional options object. +function circleMarker(latlng, options) { + return new CircleMarker(latlng, options); +} + +/* + * @class Circle + * @aka L.Circle + * @inherits CircleMarker + * + * A class for drawing circle overlays on a map. Extends `CircleMarker`. + * + * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). + * + * @example + * + * ```js + * L.circle([50.5, 30.5], {radius: 200}).addTo(map); + * ``` + */ + +var Circle = CircleMarker.extend({ + + initialize: function (latlng, options, legacyOptions) { + if (typeof options === 'number') { + // Backwards compatibility with 0.7.x factory (latlng, radius, options?) + options = extend({}, legacyOptions, {radius: options}); + } + setOptions(this, options); + this._latlng = toLatLng(latlng); + + if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } + + // @section + // @aka Circle options + // @option radius: Number; Radius of the circle, in meters. + this._mRadius = this.options.radius; + }, + + // @method setRadius(radius: Number): this + // Sets the radius of a circle. Units are in meters. + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, + + // @method getRadius(): Number + // Returns the current radius of a circle. Units are in meters. + getRadius: function () { + return this._mRadius; + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + var half = [this._radius, this._radiusY || this._radius]; + + return new LatLngBounds( + this._map.layerPointToLatLng(this._point.subtract(half)), + this._map.layerPointToLatLng(this._point.add(half))); + }, + + setStyle: Path.prototype.setStyle, + + _project: function () { + + var lng = this._latlng.lng, + lat = this._latlng.lat, + map = this._map, + crs = map.options.crs; + + if (crs.distance === Earth.distance) { + var d = Math.PI / 180, + latR = (this._mRadius / Earth.R) / d, + top = map.project([lat + latR, lng]), + bottom = map.project([lat - latR, lng]), + p = top.add(bottom).divideBy(2), + lat2 = map.unproject(p).lat, + lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / + (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; + + if (isNaN(lngR) || lngR === 0) { + lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 + } + + this._point = p.subtract(map.getPixelOrigin()); + this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x; + this._radiusY = p.y - top.y; + + } else { + var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); + + this._point = map.latLngToLayerPoint(this._latlng); + this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; + } + + this._updateBounds(); + } +}); + +// @factory L.circle(latlng: LatLng, options?: Circle options) +// Instantiates a circle object given a geographical point, and an options object +// which contains the circle radius. +// @alternative +// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) +// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. +// Do not use in new applications or plugins. +function circle(latlng, options, legacyOptions) { + return new Circle(latlng, options, legacyOptions); +} + +/* + * @class Polyline + * @aka L.Polyline + * @inherits Path + * + * A class for drawing polyline overlays on a map. Extends `Path`. + * + * @example + * + * ```js + * // create a red polyline from an array of LatLng points + * var latlngs = [ + * [45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2] + * ]; + * + * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polyline + * map.fitBounds(polyline.getBounds()); + * ``` + * + * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: + * + * ```js + * // create a red polyline from an array of arrays of LatLng points + * var latlngs = [ + * [[45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2]], + * [[40.78, -73.91], + * [41.83, -87.62], + * [32.76, -96.72]] + * ]; + * ``` + */ + + +var Polyline = Path.extend({ + + // @section + // @aka Polyline options + options: { + // @option smoothFactor: Number = 1.0 + // How much to simplify the polyline on each zoom level. More means + // better performance and smoother look, and less means more accurate representation. + smoothFactor: 1.0, + + // @option noClip: Boolean = false + // Disable polyline clipping. + noClip: false + }, + + initialize: function (latlngs, options) { + setOptions(this, options); + this._setLatLngs(latlngs); + }, + + // @method getLatLngs(): LatLng[] + // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. + getLatLngs: function () { + return this._latlngs; + }, + + // @method setLatLngs(latlngs: LatLng[]): this + // Replaces all the points in the polyline with the given array of geographical points. + setLatLngs: function (latlngs) { + this._setLatLngs(latlngs); + return this.redraw(); + }, + + // @method isEmpty(): Boolean + // Returns `true` if the Polyline has no LatLngs. + isEmpty: function () { + return !this._latlngs.length; + }, + + // @method closestLayerPoint: Point + // Returns the point closest to `p` on the Polyline. + closestLayerPoint: function (p) { + var minDistance = Infinity, + minPoint = null, + closest = _sqClosestPointOnSegment, + p1, p2; + + for (var j = 0, jLen = this._parts.length; j < jLen; j++) { + var points = this._parts[j]; + + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; + + var sqDist = closest(p, p1, p2, true); + + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = closest(p, p1, p2); + } + } + } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; + }, + + // @method getCenter(): LatLng + // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + + var i, halfDist, segDist, dist, p1, p2, ratio, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } + + // polyline centroid algorithm; only uses the first ring if there are multiple + + for (i = 0, halfDist = 0; i < len - 1; i++) { + halfDist += points[i].distanceTo(points[i + 1]) / 2; + } + + // The line is so small in the current view that all points are on the same pixel. + if (halfDist === 0) { + return this._map.layerPointToLatLng(points[0]); + } + + for (i = 0, dist = 0; i < len - 1; i++) { + p1 = points[i]; + p2 = points[i + 1]; + segDist = p1.distanceTo(p2); + dist += segDist; + + if (dist > halfDist) { + ratio = (dist - halfDist) / segDist; + return this._map.layerPointToLatLng([ + p2.x - ratio * (p2.x - p1.x), + p2.y - ratio * (p2.y - p1.y) + ]); + } + } + }, + + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + return this._bounds; + }, + + // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this + // Adds a given point to the polyline. By default, adds to the first ring of + // the polyline in case of a multi-polyline, but can be overridden by passing + // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). + addLatLng: function (latlng, latlngs) { + latlngs = latlngs || this._defaultShape(); + latlng = toLatLng(latlng); + latlngs.push(latlng); + this._bounds.extend(latlng); + return this.redraw(); + }, + + _setLatLngs: function (latlngs) { + this._bounds = new LatLngBounds(); + this._latlngs = this._convertLatLngs(latlngs); + }, + + _defaultShape: function () { + return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + + // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way + _convertLatLngs: function (latlngs) { + var result = [], + flat = isFlat(latlngs); + + for (var i = 0, len = latlngs.length; i < len; i++) { + if (flat) { + result[i] = toLatLng(latlngs[i]); + this._bounds.extend(result[i]); + } else { + result[i] = this._convertLatLngs(latlngs[i]); + } + } + + return result; + }, + + _project: function () { + var pxBounds = new Bounds(); + this._rings = []; + this._projectLatlngs(this._latlngs, this._rings, pxBounds); + + var w = this._clickTolerance(), + p = new Point(w, w); + + if (this._bounds.isValid() && pxBounds.isValid()) { + pxBounds.min._subtract(p); + pxBounds.max._add(p); + this._pxBounds = pxBounds; + } + }, + + // recursively turns latlngs into a set of rings with projected coordinates + _projectLatlngs: function (latlngs, result, projectedBounds) { + var flat = latlngs[0] instanceof LatLng, + len = latlngs.length, + i, ring; + + if (flat) { + ring = []; + for (i = 0; i < len; i++) { + ring[i] = this._map.latLngToLayerPoint(latlngs[i]); + projectedBounds.extend(ring[i]); + } + result.push(ring); + } else { + for (i = 0; i < len; i++) { + this._projectLatlngs(latlngs[i], result, projectedBounds); + } + } + }, + + // clip polyline by renderer bounds so that we have less to render for performance + _clipPoints: function () { + var bounds = this._renderer._bounds; + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + var parts = this._parts, + i, j, k, len, len2, segment, points; + + for (i = 0, k = 0, len = this._rings.length; i < len; i++) { + points = this._rings[i]; + + for (j = 0, len2 = points.length; j < len2 - 1; j++) { + segment = clipSegment(points[j], points[j + 1], bounds, j, true); + + if (!segment) { continue; } + + parts[k] = parts[k] || []; + parts[k].push(segment[0]); + + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { + parts[k].push(segment[1]); + k++; + } + } + } + }, + + // simplify each clipped part of the polyline for performance + _simplifyPoints: function () { + var parts = this._parts, + tolerance = this.options.smoothFactor; + + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = simplify(parts[i], tolerance); + } + }, + + _update: function () { + if (!this._map) { return; } + + this._clipPoints(); + this._simplifyPoints(); + this._updatePath(); + }, + + _updatePath: function () { + this._renderer._updatePoly(this); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p, closed) { + var i, j, k, len, len2, part, + w = this._clickTolerance(); + + if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } + + // hit detection for polylines + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { continue; } + + if (pointToSegmentDistance(p, part[k], part[j]) <= w) { + return true; + } + } + } + return false; + } +}); + +// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) +// Instantiates a polyline object given an array of geographical points and +// optionally an options object. You can create a `Polyline` object with +// multiple separate lines (`MultiPolyline`) by passing an array of arrays +// of geographic points. +function polyline(latlngs, options) { + return new Polyline(latlngs, options); +} + +// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1. +Polyline._flat = _flat; + +/* + * @class Polygon + * @aka L.Polygon + * @inherits Polyline + * + * A class for drawing polygon overlays on a map. Extends `Polyline`. + * + * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points. + * + * + * @example + * + * ```js + * // create a red polygon from an array of LatLng points + * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; + * + * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polygon + * map.fitBounds(polygon.getBounds()); + * ``` + * + * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: + * + * ```js + * var latlngs = [ + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ]; + * ``` + * + * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. + * + * ```js + * var latlngs = [ + * [ // first polygon + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ], + * [ // second polygon + * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] + * ] + * ]; + * ``` + */ + +var Polygon = Polyline.extend({ + + options: { + fill: true + }, + + isEmpty: function () { + return !this._latlngs.length || !this._latlngs[0].length; + }, + + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } + + var i, j, p1, p2, f, area, x, y, center, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } + + // polygon centroid algorithm; only uses the first ring if there are multiple + + area = x = y = 0; + + for (i = 0, j = len - 1; i < len; j = i++) { + p1 = points[i]; + p2 = points[j]; + + f = p1.y * p2.x - p2.y * p1.x; + x += (p1.x + p2.x) * f; + y += (p1.y + p2.y) * f; + area += f * 3; + } + + if (area === 0) { + // Polygon is so small that all points are on same pixel. + center = points[0]; + } else { + center = [x / area, y / area]; + } + return this._map.layerPointToLatLng(center); + }, + + _convertLatLngs: function (latlngs) { + var result = Polyline.prototype._convertLatLngs.call(this, latlngs), + len = result.length; + + // remove last point if it equals first one + if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { + result.pop(); + } + return result; + }, + + _setLatLngs: function (latlngs) { + Polyline.prototype._setLatLngs.call(this, latlngs); + if (isFlat(this._latlngs)) { + this._latlngs = [this._latlngs]; + } + }, + + _defaultShape: function () { + return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; + }, + + _clipPoints: function () { + // polygons need a different clipping algorithm so we redefine that + + var bounds = this._renderer._bounds, + w = this.options.weight, + p = new Point(w, w); + + // increase clip padding by stroke width to avoid stroke on clip edges + bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); + + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } + + if (this.options.noClip) { + this._parts = this._rings; + return; + } + + for (var i = 0, len = this._rings.length, clipped; i < len; i++) { + clipped = clipPolygon(this._rings[i], bounds, true); + if (clipped.length) { + this._parts.push(clipped); + } + } + }, + + _updatePath: function () { + this._renderer._updatePoly(this, true); + }, + + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + var inside = false, + part, p1, p2, i, j, k, len, len2; + + if (!this._pxBounds.contains(p)) { return false; } + + // ray casting algorithm for detecting if point is in polygon + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; + + if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } + } + } + + // also check if it's on polygon stroke + return inside || Polyline.prototype._containsPoint.call(this, p, true); + } + +}); + + +// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) +function polygon(latlngs, options) { + return new Polygon(latlngs, options); +} + +/* + * @class GeoJSON + * @aka L.GeoJSON + * @inherits FeatureGroup + * + * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse + * GeoJSON data and display it on the map. Extends `FeatureGroup`. + * + * @example + * + * ```js + * L.geoJSON(data, { + * style: function (feature) { + * return {color: feature.properties.color}; + * } + * }).bindPopup(function (layer) { + * return layer.feature.properties.description; + * }).addTo(map); + * ``` + */ + +var GeoJSON = FeatureGroup.extend({ + + /* @section + * @aka GeoJSON options + * + * @option pointToLayer: Function = * + * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally + * called when data is added, passing the GeoJSON point feature and its `LatLng`. + * The default is to spawn a default `Marker`: + * ```js + * function(geoJsonPoint, latlng) { + * return L.marker(latlng); + * } + * ``` + * + * @option style: Function = * + * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, + * called internally when data is added. + * The default value is to not override any defaults: + * ```js + * function (geoJsonFeature) { + * return {} + * } + * ``` + * + * @option onEachFeature: Function = * + * A `Function` that will be called once for each created `Feature`, after it has + * been created and styled. Useful for attaching events and popups to features. + * The default is to do nothing with the newly created layers: + * ```js + * function (feature, layer) {} + * ``` + * + * @option filter: Function = * + * A `Function` that will be used to decide whether to include a feature or not. + * The default is to include all features: + * ```js + * function (geoJsonFeature) { + * return true; + * } + * ``` + * Note: dynamically changing the `filter` option will have effect only on newly + * added data. It will _not_ re-evaluate already included features. + * + * @option coordsToLatLng: Function = * + * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. + * The default is the `coordsToLatLng` static method. + */ + + initialize: function (geojson, options) { + setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + // @method addData( <GeoJSON> data ): this + // Adds a GeoJSON object to the layer. + addData: function (geojson) { + var features = isArray(geojson) ? geojson : geojson.features, + i, len, feature; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + // only add this if geometry or geometries are set and not null + feature = features[i]; + if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { + this.addData(feature); + } + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return this; } + + var layer = geometryToLayer(geojson, options); + if (!layer) { + return this; + } + layer.feature = asFeature(geojson); + + layer.defaultOptions = layer.options; + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + // @method resetStyle( <Path> layer ): this + // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. + resetStyle: function (layer) { + // reset any custom styles + layer.options = extend({}, layer.defaultOptions); + this._setLayerStyle(layer, this.options.style); + return this; + }, + + // @method setStyle( <Function> style ): this + // Changes styles of GeoJSON vector layers with the given style function. + setStyle: function (style) { + return this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (typeof style === 'function') { + style = style(layer.feature); + } + if (layer.setStyle) { + layer.setStyle(style); + } + } +}); + +// @section +// There are several static functions which can be called without instantiating L.GeoJSON: + +// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer +// Creates a `Layer` from a given GeoJSON feature. Can use a custom +// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) +// functions if provided as options. +function geometryToLayer(geojson, options) { + + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry ? geometry.coordinates : null, + layers = [], + pointToLayer = options && options.pointToLayer, + _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, + latlng, latlngs, i, len; + + if (!coords && !geometry) { + return null; + } + + switch (geometry.type) { + case 'Point': + latlng = _coordsToLatLng(coords); + return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = _coordsToLatLng(coords[i]); + layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng)); + } + return new FeatureGroup(layers); + + case 'LineString': + case 'MultiLineString': + latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); + return new Polyline(latlngs, options); + + case 'Polygon': + case 'MultiPolygon': + latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); + return new Polygon(latlngs, options); + + case 'GeometryCollection': + for (i = 0, len = geometry.geometries.length; i < len; i++) { + var layer = geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, options); + + if (layer) { + layers.push(layer); + } + } + return new FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } +} + +// @function coordsToLatLng(coords: Array): LatLng +// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) +// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. +function coordsToLatLng(coords) { + return new LatLng(coords[1], coords[0], coords[2]); +} + +// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array +// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. +// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). +// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. +function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { + var latlngs = []; + + for (var i = 0, len = coords.length, latlng; i < len; i++) { + latlng = levelsDeep ? + coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : + (_coordsToLatLng || coordsToLatLng)(coords[i]); + + latlngs.push(latlng); + } + + return latlngs; +} + +// @function latLngToCoords(latlng: LatLng, precision?: Number): Array +// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) +function latLngToCoords(latlng, precision) { + precision = typeof precision === 'number' ? precision : 6; + return latlng.alt !== undefined ? + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; +} + +// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array +// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) +// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. +function latLngsToCoords(latlngs, levelsDeep, closed, precision) { + var coords = []; + + for (var i = 0, len = latlngs.length; i < len; i++) { + coords.push(levelsDeep ? + latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : + latLngToCoords(latlngs[i], precision)); + } + + if (!levelsDeep && closed) { + coords.push(coords[0]); + } + + return coords; +} + +function getFeature(layer, newGeometry) { + return layer.feature ? + extend({}, layer.feature, {geometry: newGeometry}) : + asFeature(newGeometry); +} + +// @function asFeature(geojson: Object): Object +// Normalize GeoJSON geometries/features into GeoJSON features. +function asFeature(geojson) { + if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { + return geojson; + } + + return { + type: 'Feature', + properties: {}, + geometry: geojson + }; +} + +var PointToGeoJSON = { + toGeoJSON: function (precision) { + return getFeature(this, { + type: 'Point', + coordinates: latLngToCoords(this.getLatLng(), precision) + }); + } +}; + +// @namespace Marker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). +Marker.include(PointToGeoJSON); + +// @namespace CircleMarker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). +Circle.include(PointToGeoJSON); +CircleMarker.include(PointToGeoJSON); + + +// @namespace Polyline +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). +Polyline.include({ + toGeoJSON: function (precision) { + var multi = !isFlat(this._latlngs); + + var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'LineString', + coordinates: coords + }); + } +}); + +// @namespace Polygon +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). +Polygon.include({ + toGeoJSON: function (precision) { + var holes = !isFlat(this._latlngs), + multi = holes && !isFlat(this._latlngs[0]); + + var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); + + if (!holes) { + coords = [coords]; + } + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'Polygon', + coordinates: coords + }); + } +}); + + +// @namespace LayerGroup +LayerGroup.include({ + toMultiPoint: function (precision) { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON(precision).geometry.coordinates); + }); + + return getFeature(this, { + type: 'MultiPoint', + coordinates: coords + }); + }, + + // @method toGeoJSON(): Object + // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). + toGeoJSON: function (precision) { + + var type = this.feature && this.feature.geometry && this.feature.geometry.type; + + if (type === 'MultiPoint') { + return this.toMultiPoint(precision); + } + + var isGeometryCollection = type === 'GeometryCollection', + jsons = []; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + var json = layer.toGeoJSON(precision); + if (isGeometryCollection) { + jsons.push(json.geometry); + } else { + var feature = asFeature(json); + // Squash nested feature collections + if (feature.type === 'FeatureCollection') { + jsons.push.apply(jsons, feature.features); + } else { + jsons.push(feature); + } + } + } + }); + + if (isGeometryCollection) { + return getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } +}); + +// @namespace GeoJSON +// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) +// Creates a GeoJSON layer. Optionally accepts an object in +// [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map +// (you can alternatively add it later with `addData` method) and an `options` object. +function geoJSON(geojson, options) { + return new GeoJSON(geojson, options); +} + +// Backward compatibility. +var geoJson = geoJSON; + +/* + * @class ImageOverlay + * @aka L.ImageOverlay + * @inherits Interactive layer + * + * Used to load and display a single image over specific bounds of the map. Extends `Layer`. + * + * @example + * + * ```js + * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', + * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; + * L.imageOverlay(imageUrl, imageBounds).addTo(map); + * ``` + */ + +var ImageOverlay = Layer.extend({ + + // @section + // @aka ImageOverlay options + options: { + // @option opacity: Number = 1.0 + // The opacity of the image overlay. + opacity: 1, + + // @option alt: String = '' + // Text for the `alt` attribute of the image (useful for accessibility). + alt: '', + + // @option interactive: Boolean = false + // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. + interactive: false, + + // @option crossOrigin: Boolean = false + // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data. + crossOrigin: false, + + // @option errorOverlayUrl: String = '' + // URL to the overlay image to show in place of the overlay that failed to load. + errorOverlayUrl: '', + + // @option zIndex: Number = 1 + // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer. + zIndex: 1, + + // @option className: String = '' + // A custom class name to assign to the image. Empty by default. + className: '', + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = toLatLngBounds(bounds); + + setOptions(this, options); + }, + + onAdd: function () { + if (!this._image) { + this._initImage(); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + + if (this.options.interactive) { + addClass(this._image, 'leaflet-interactive'); + this.addInteractiveTarget(this._image); + } + + this.getPane().appendChild(this._image); + this._reset(); + }, + + onRemove: function () { + remove(this._image); + if (this.options.interactive) { + this.removeInteractiveTarget(this._image); + } + }, + + // @method setOpacity(opacity: Number): this + // Sets the opacity of the overlay. + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._image) { + this._updateOpacity(); + } + return this; + }, + + setStyle: function (styleOpts) { + if (styleOpts.opacity) { + this.setOpacity(styleOpts.opacity); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all overlays. + bringToFront: function () { + if (this._map) { + toFront(this._image); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all overlays. + bringToBack: function () { + if (this._map) { + toBack(this._image); + } + return this; + }, + + // @method setUrl(url: String): this + // Changes the URL of the image. + setUrl: function (url) { + this._url = url; + + if (this._image) { + this._image.src = url; + } + return this; + }, + + // @method setBounds(bounds: LatLngBounds): this + // Update the bounds that this ImageOverlay covers + setBounds: function (bounds) { + this._bounds = toLatLngBounds(bounds); + + if (this._map) { + this._reset(); + } + return this; + }, + + getEvents: function () { + var events = { + zoom: this._reset, + viewreset: this._reset + }; + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + + return events; + }, + + // @method: setZIndex(value: Number) : this + // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. + setZIndex: function (value) { + this.options.zIndex = value; + this._updateZIndex(); + return this; + }, + + // @method getBounds(): LatLngBounds + // Get the bounds that this ImageOverlay covers + getBounds: function () { + return this._bounds; + }, + + // @method getElement(): HTMLElement + // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) + // used by this overlay. + getElement: function () { + return this._image; + }, + + _initImage: function () { + var wasElementSupplied = this._url.tagName === 'IMG'; + var img = this._image = wasElementSupplied ? this._url : create$1('img'); + + addClass(img, 'leaflet-image-layer'); + if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); } + if (this.options.className) { addClass(img, this.options.className); } + + img.onselectstart = falseFn; + img.onmousemove = falseFn; + + // @event load: Event + // Fired when the ImageOverlay layer has loaded its image + img.onload = bind(this.fire, this, 'load'); + img.onerror = bind(this._overlayOnError, this, 'error'); + + if (this.options.crossOrigin) { + img.crossOrigin = ''; + } + + if (this.options.zIndex) { + this._updateZIndex(); + } + + if (wasElementSupplied) { + this._url = img.src; + return; + } + + img.src = this._url; + img.alt = this.options.alt; + }, + + _animateZoom: function (e) { + var scale = this._map.getZoomScale(e.zoom), + offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; + + setTransform(this._image, offset, scale); + }, + + _reset: function () { + var image = this._image, + bounds = new Bounds( + this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + this._map.latLngToLayerPoint(this._bounds.getSouthEast())), + size = bounds.getSize(); + + setPosition(image, bounds.min); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _updateOpacity: function () { + setOpacity(this._image, this.options.opacity); + }, + + _updateZIndex: function () { + if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { + this._image.style.zIndex = this.options.zIndex; + } + }, + + _overlayOnError: function () { + // @event error: Event + // Fired when the ImageOverlay layer has loaded its image + this.fire('error'); + + var errorUrl = this.options.errorOverlayUrl; + if (errorUrl && this._url !== errorUrl) { + this._url = errorUrl; + this._image.src = errorUrl; + } + } +}); + +// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) +// Instantiates an image overlay object given the URL of the image and the +// geographical bounds it is tied to. +var imageOverlay = function (url, bounds, options) { + return new ImageOverlay(url, bounds, options); +}; + +/* + * @class VideoOverlay + * @aka L.VideoOverlay + * @inherits ImageOverlay + * + * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. + * + * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video) + * HTML5 element. + * + * @example + * + * ```js + * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm', + * videoBounds = [[ 32, -130], [ 13, -100]]; + * L.VideoOverlay(videoUrl, videoBounds ).addTo(map); + * ``` + */ + +var VideoOverlay = ImageOverlay.extend({ + + // @section + // @aka VideoOverlay options + options: { + // @option autoplay: Boolean = true + // Whether the video starts playing automatically when loaded. + autoplay: true, + + // @option loop: Boolean = true + // Whether the video will loop back to the beginning when played. + loop: true + }, + + _initImage: function () { + var wasElementSupplied = this._url.tagName === 'VIDEO'; + var vid = this._image = wasElementSupplied ? this._url : create$1('video'); + + addClass(vid, 'leaflet-image-layer'); + if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); } + + vid.onselectstart = falseFn; + vid.onmousemove = falseFn; + + // @event load: Event + // Fired when the video has finished loading the first frame + vid.onloadeddata = bind(this.fire, this, 'load'); + + if (wasElementSupplied) { + var sourceElements = vid.getElementsByTagName('source'); + var sources = []; + for (var j = 0; j < sourceElements.length; j++) { + sources.push(sourceElements[j].src); + } + + this._url = (sourceElements.length > 0) ? sources : [vid.src]; + return; + } + + if (!isArray(this._url)) { this._url = [this._url]; } + + vid.autoplay = !!this.options.autoplay; + vid.loop = !!this.options.loop; + for (var i = 0; i < this._url.length; i++) { + var source = create$1('source'); + source.src = this._url[i]; + vid.appendChild(source); + } + } + + // @method getElement(): HTMLVideoElement + // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement) + // used by this overlay. +}); + + +// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options) +// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the +// geographical bounds it is tied to. + +function videoOverlay(video, bounds, options) { + return new VideoOverlay(video, bounds, options); +} + +/* + * @class DivOverlay + * @inherits Layer + * @aka L.DivOverlay + * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins. + */ + +// @namespace DivOverlay +var DivOverlay = Layer.extend({ + + // @section + // @aka DivOverlay options + options: { + // @option offset: Point = Point(0, 7) + // The offset of the popup position. Useful to control the anchor + // of the popup when opening it on some overlays. + offset: [0, 7], + + // @option className: String = '' + // A custom CSS class name to assign to the popup. + className: '', + + // @option pane: String = 'popupPane' + // `Map pane` where the popup will be added. + pane: 'popupPane' + }, + + initialize: function (options, source) { + setOptions(this, options); + + this._source = source; + }, + + onAdd: function (map) { + this._zoomAnimated = map._zoomAnimated; + + if (!this._container) { + this._initLayout(); + } + + if (map._fadeAnimated) { + setOpacity(this._container, 0); + } + + clearTimeout(this._removeTimeout); + this.getPane().appendChild(this._container); + this.update(); + + if (map._fadeAnimated) { + setOpacity(this._container, 1); + } + + this.bringToFront(); + }, + + onRemove: function (map) { + if (map._fadeAnimated) { + setOpacity(this._container, 0); + this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200); + } else { + remove(this._container); + } + }, + + // @namespace Popup + // @method getLatLng: LatLng + // Returns the geographical point of popup. + getLatLng: function () { + return this._latlng; + }, + + // @method setLatLng(latlng: LatLng): this + // Sets the geographical point where the popup will open. + setLatLng: function (latlng) { + this._latlng = toLatLng(latlng); + if (this._map) { + this._updatePosition(); + this._adjustPan(); + } + return this; + }, + + // @method getContent: String|HTMLElement + // Returns the content of the popup. + getContent: function () { + return this._content; + }, + + // @method setContent(htmlContent: String|HTMLElement|Function): this + // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup. + setContent: function (content) { + this._content = content; + this.update(); + return this; + }, + + // @method getElement: String|HTMLElement + // Alias for [getContent()](#popup-getcontent) + getElement: function () { + return this._container; + }, + + // @method update: null + // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded. + update: function () { + if (!this._map) { return; } + + this._container.style.visibility = 'hidden'; + + this._updateContent(); + this._updateLayout(); + this._updatePosition(); + + this._container.style.visibility = ''; + + this._adjustPan(); + }, + + getEvents: function () { + var events = { + zoom: this._updatePosition, + viewreset: this._updatePosition + }; + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + return events; + }, + + // @method isOpen: Boolean + // Returns `true` when the popup is visible on the map. + isOpen: function () { + return !!this._map && this._map.hasLayer(this); + }, + + // @method bringToFront: this + // Brings this popup in front of other popups (in the same map pane). + bringToFront: function () { + if (this._map) { + toFront(this._container); + } + return this; + }, + + // @method bringToBack: this + // Brings this popup to the back of other popups (in the same map pane). + bringToBack: function () { + if (this._map) { + toBack(this._container); + } + return this; + }, + + _updateContent: function () { + if (!this._content) { return; } + + var node = this._contentNode; + var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content; + + if (typeof content === 'string') { + node.innerHTML = content; + } else { + while (node.hasChildNodes()) { + node.removeChild(node.firstChild); + } + node.appendChild(content); + } + this.fire('contentupdate'); + }, + + _updatePosition: function () { + if (!this._map) { return; } + + var pos = this._map.latLngToLayerPoint(this._latlng), + offset = toPoint(this.options.offset), + anchor = this._getAnchor(); + + if (this._zoomAnimated) { + setPosition(this._container, pos.add(anchor)); + } else { + offset = offset.add(pos).add(anchor); + } + + var bottom = this._containerBottom = -offset.y, + left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x; + + // bottom position the popup in case the height of the popup changes (images loading etc) + this._container.style.bottom = bottom + 'px'; + this._container.style.left = left + 'px'; + }, + + _getAnchor: function () { + return [0, 0]; + } + +}); + +/* + * @class Popup + * @inherits DivOverlay + * @aka L.Popup + * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to + * open popups while making sure that only one popup is open at one time + * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want. + * + * @example + * + * If you want to just bind a popup to marker click and then open it, it's really easy: + * + * ```js + * marker.bindPopup(popupContent).openPopup(); + * ``` + * Path overlays like polylines also have a `bindPopup` method. + * Here's a more complicated way to open a popup on a map: + * + * ```js + * var popup = L.popup() + * .setLatLng(latlng) + * .setContent('<p>Hello world!<br />This is a nice popup.</p>') + * .openOn(map); + * ``` + */ + + +// @namespace Popup +var Popup = DivOverlay.extend({ + + // @section + // @aka Popup options + options: { + // @option maxWidth: Number = 300 + // Max width of the popup, in pixels. + maxWidth: 300, + + // @option minWidth: Number = 50 + // Min width of the popup, in pixels. + minWidth: 50, + + // @option maxHeight: Number = null + // If set, creates a scrollable container of the given height + // inside a popup if its content exceeds it. + maxHeight: null, + + // @option autoPan: Boolean = true + // Set it to `false` if you don't want the map to do panning animation + // to fit the opened popup. + autoPan: true, + + // @option autoPanPaddingTopLeft: Point = null + // The margin between the popup and the top left corner of the map + // view after autopanning was performed. + autoPanPaddingTopLeft: null, + + // @option autoPanPaddingBottomRight: Point = null + // The margin between the popup and the bottom right corner of the map + // view after autopanning was performed. + autoPanPaddingBottomRight: null, + + // @option autoPanPadding: Point = Point(5, 5) + // Equivalent of setting both top left and bottom right autopan padding to the same value. + autoPanPadding: [5, 5], + + // @option keepInView: Boolean = false + // Set it to `true` if you want to prevent users from panning the popup + // off of the screen while it is open. + keepInView: false, + + // @option closeButton: Boolean = true + // Controls the presence of a close button in the popup. + closeButton: true, + + // @option autoClose: Boolean = true + // Set it to `false` if you want to override the default behavior of + // the popup closing when another popup is opened. + autoClose: true, + + // @option closeOnEscapeKey: Boolean = true + // Set it to `false` if you want to override the default behavior of + // the ESC key for closing of the popup. + closeOnEscapeKey: true, + + // @option closeOnClick: Boolean = * + // Set it if you want to override the default behavior of the popup closing when user clicks + // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option. + + // @option className: String = '' + // A custom CSS class name to assign to the popup. + className: '' + }, + + // @namespace Popup + // @method openOn(map: Map): this + // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`. + openOn: function (map) { + map.openPopup(this); + return this; + }, + + onAdd: function (map) { + DivOverlay.prototype.onAdd.call(this, map); + + // @namespace Map + // @section Popup events + // @event popupopen: PopupEvent + // Fired when a popup is opened in the map + map.fire('popupopen', {popup: this}); + + if (this._source) { + // @namespace Layer + // @section Popup events + // @event popupopen: PopupEvent + // Fired when a popup bound to this layer is opened + this._source.fire('popupopen', {popup: this}, true); + // For non-path layers, we toggle the popup when clicking + // again the layer, so prevent the map to reopen it. + if (!(this._source instanceof Path)) { + this._source.on('preclick', stopPropagation); + } + } + }, + + onRemove: function (map) { + DivOverlay.prototype.onRemove.call(this, map); + + // @namespace Map + // @section Popup events + // @event popupclose: PopupEvent + // Fired when a popup in the map is closed + map.fire('popupclose', {popup: this}); + + if (this._source) { + // @namespace Layer + // @section Popup events + // @event popupclose: PopupEvent + // Fired when a popup bound to this layer is closed + this._source.fire('popupclose', {popup: this}, true); + if (!(this._source instanceof Path)) { + this._source.off('preclick', stopPropagation); + } + } + }, + + getEvents: function () { + var events = DivOverlay.prototype.getEvents.call(this); + + if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) { + events.preclick = this._close; + } + + if (this.options.keepInView) { + events.moveend = this._adjustPan; + } + + return events; + }, + + _close: function () { + if (this._map) { + this._map.closePopup(this); + } + }, + + _initLayout: function () { + var prefix = 'leaflet-popup', + container = this._container = create$1('div', + prefix + ' ' + (this.options.className || '') + + ' leaflet-zoom-animated'); + + var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container); + this._contentNode = create$1('div', prefix + '-content', wrapper); + + disableClickPropagation(wrapper); + disableScrollPropagation(this._contentNode); + on(wrapper, 'contextmenu', stopPropagation); + + this._tipContainer = create$1('div', prefix + '-tip-container', container); + this._tip = create$1('div', prefix + '-tip', this._tipContainer); + + if (this.options.closeButton) { + var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container); + closeButton.href = '#close'; + closeButton.innerHTML = '×'; + + on(closeButton, 'click', this._onCloseButtonClick, this); + } + }, + + _updateLayout: function () { + var container = this._contentNode, + style = container.style; + + style.width = ''; + style.whiteSpace = 'nowrap'; + + var width = container.offsetWidth; + width = Math.min(width, this.options.maxWidth); + width = Math.max(width, this.options.minWidth); + + style.width = (width + 1) + 'px'; + style.whiteSpace = ''; + + style.height = ''; + + var height = container.offsetHeight, + maxHeight = this.options.maxHeight, + scrolledClass = 'leaflet-popup-scrolled'; + + if (maxHeight && height > maxHeight) { + style.height = maxHeight + 'px'; + addClass(container, scrolledClass); + } else { + removeClass(container, scrolledClass); + } + + this._containerWidth = this._container.offsetWidth; + }, + + _animateZoom: function (e) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), + anchor = this._getAnchor(); + setPosition(this._container, pos.add(anchor)); + }, + + _adjustPan: function () { + if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; } + + var map = this._map, + marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0, + containerHeight = this._container.offsetHeight + marginBottom, + containerWidth = this._containerWidth, + layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom); + + layerPos._add(getPosition(this._container)); + + var containerPos = map.layerPointToContainerPoint(layerPos), + padding = toPoint(this.options.autoPanPadding), + paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding), + paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding), + size = map.getSize(), + dx = 0, + dy = 0; + + if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right + dx = containerPos.x + containerWidth - size.x + paddingBR.x; + } + if (containerPos.x - dx - paddingTL.x < 0) { // left + dx = containerPos.x - paddingTL.x; + } + if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom + dy = containerPos.y + containerHeight - size.y + paddingBR.y; + } + if (containerPos.y - dy - paddingTL.y < 0) { // top + dy = containerPos.y - paddingTL.y; + } + + // @namespace Map + // @section Popup events + // @event autopanstart: Event + // Fired when the map starts autopanning when opening a popup. + if (dx || dy) { + map + .fire('autopanstart') + .panBy([dx, dy]); + } + }, + + _onCloseButtonClick: function (e) { + this._close(); + stop(e); + }, + + _getAnchor: function () { + // Where should we anchor the popup on the source layer? + return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]); + } + +}); + +// @namespace Popup +// @factory L.popup(options?: Popup options, source?: Layer) +// Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers. +var popup = function (options, source) { + return new Popup(options, source); +}; + + +/* @namespace Map + * @section Interaction Options + * @option closePopupOnClick: Boolean = true + * Set it to `false` if you don't want popups to close when user clicks the map. + */ +Map.mergeOptions({ + closePopupOnClick: true +}); + + +// @namespace Map +// @section Methods for Layers and Controls +Map.include({ + // @method openPopup(popup: Popup): this + // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability). + // @alternative + // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this + // Creates a popup with the specified content and options and opens it in the given point on a map. + openPopup: function (popup, latlng, options) { + if (!(popup instanceof Popup)) { + popup = new Popup(options).setContent(popup); + } + + if (latlng) { + popup.setLatLng(latlng); + } + + if (this.hasLayer(popup)) { + return this; + } + + if (this._popup && this._popup.options.autoClose) { + this.closePopup(); + } + + this._popup = popup; + return this.addLayer(popup); + }, + + // @method closePopup(popup?: Popup): this + // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one). + closePopup: function (popup) { + if (!popup || popup === this._popup) { + popup = this._popup; + this._popup = null; + } + if (popup) { + this.removeLayer(popup); + } + return this; + } +}); + +/* + * @namespace Layer + * @section Popup methods example + * + * All layers share a set of methods convenient for binding popups to it. + * + * ```js + * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map); + * layer.openPopup(); + * layer.closePopup(); + * ``` + * + * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened. + */ + +// @section Popup methods +Layer.include({ + + // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this + // Binds a popup to the layer with the passed `content` and sets up the + // necessary event listeners. If a `Function` is passed it will receive + // the layer as the first argument and should return a `String` or `HTMLElement`. + bindPopup: function (content, options) { + + if (content instanceof Popup) { + setOptions(content, options); + this._popup = content; + content._source = this; + } else { + if (!this._popup || options) { + this._popup = new Popup(options, this); + } + this._popup.setContent(content); + } + + if (!this._popupHandlersAdded) { + this.on({ + click: this._openPopup, + keypress: this._onKeyPress, + remove: this.closePopup, + move: this._movePopup + }); + this._popupHandlersAdded = true; + } + + return this; + }, + + // @method unbindPopup(): this + // Removes the popup previously bound with `bindPopup`. + unbindPopup: function () { + if (this._popup) { + this.off({ + click: this._openPopup, + keypress: this._onKeyPress, + remove: this.closePopup, + move: this._movePopup + }); + this._popupHandlersAdded = false; + this._popup = null; + } + return this; + }, + + // @method openPopup(latlng?: LatLng): this + // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed. + openPopup: function (layer, latlng) { + if (!(layer instanceof Layer)) { + latlng = layer; + layer = this; + } + + if (layer instanceof FeatureGroup) { + for (var id in this._layers) { + layer = this._layers[id]; + break; + } + } + + if (!latlng) { + latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng(); + } + + if (this._popup && this._map) { + // set popup source to this layer + this._popup._source = layer; + + // update the popup (content, layout, ect...) + this._popup.update(); + + // open the popup on the map + this._map.openPopup(this._popup, latlng); + } + + return this; + }, + + // @method closePopup(): this + // Closes the popup bound to this layer if it is open. + closePopup: function () { + if (this._popup) { + this._popup._close(); + } + return this; + }, + + // @method togglePopup(): this + // Opens or closes the popup bound to this layer depending on its current state. + togglePopup: function (target) { + if (this._popup) { + if (this._popup._map) { + this.closePopup(); + } else { + this.openPopup(target); + } + } + return this; + }, + + // @method isPopupOpen(): boolean + // Returns `true` if the popup bound to this layer is currently open. + isPopupOpen: function () { + return (this._popup ? this._popup.isOpen() : false); + }, + + // @method setPopupContent(content: String|HTMLElement|Popup): this + // Sets the content of the popup bound to this layer. + setPopupContent: function (content) { + if (this._popup) { + this._popup.setContent(content); + } + return this; + }, + + // @method getPopup(): Popup + // Returns the popup bound to this layer. + getPopup: function () { + return this._popup; + }, + + _openPopup: function (e) { + var layer = e.layer || e.target; + + if (!this._popup) { + return; + } + + if (!this._map) { + return; + } + + // prevent map click + stop(e); + + // if this inherits from Path its a vector and we can just + // open the popup at the new location + if (layer instanceof Path) { + this.openPopup(e.layer || e.target, e.latlng); + return; + } + + // otherwise treat it like a marker and figure out + // if we should toggle it open/closed + if (this._map.hasLayer(this._popup) && this._popup._source === layer) { + this.closePopup(); + } else { + this.openPopup(layer, e.latlng); + } + }, + + _movePopup: function (e) { + this._popup.setLatLng(e.latlng); + }, + + _onKeyPress: function (e) { + if (e.originalEvent.keyCode === 13) { + this._openPopup(e); + } + } +}); + +/* + * @class Tooltip + * @inherits DivOverlay + * @aka L.Tooltip + * Used to display small texts on top of map layers. + * + * @example + * + * ```js + * marker.bindTooltip("my tooltip text").openTooltip(); + * ``` + * Note about tooltip offset. Leaflet takes two options in consideration + * for computing tooltip offsetting: + * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip. + * Add a positive x offset to move the tooltip to the right, and a positive y offset to + * move it to the bottom. Negatives will move to the left and top. + * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You + * should adapt this value if you use a custom icon. + */ + + +// @namespace Tooltip +var Tooltip = DivOverlay.extend({ + + // @section + // @aka Tooltip options + options: { + // @option pane: String = 'tooltipPane' + // `Map pane` where the tooltip will be added. + pane: 'tooltipPane', + + // @option offset: Point = Point(0, 0) + // Optional offset of the tooltip position. + offset: [0, 0], + + // @option direction: String = 'auto' + // Direction where to open the tooltip. Possible values are: `right`, `left`, + // `top`, `bottom`, `center`, `auto`. + // `auto` will dynamically switch between `right` and `left` according to the tooltip + // position on the map. + direction: 'auto', + + // @option permanent: Boolean = false + // Whether to open the tooltip permanently or only on mouseover. + permanent: false, + + // @option sticky: Boolean = false + // If true, the tooltip will follow the mouse instead of being fixed at the feature center. + sticky: false, + + // @option interactive: Boolean = false + // If true, the tooltip will listen to the feature events. + interactive: false, + + // @option opacity: Number = 0.9 + // Tooltip container opacity. + opacity: 0.9 + }, + + onAdd: function (map) { + DivOverlay.prototype.onAdd.call(this, map); + this.setOpacity(this.options.opacity); + + // @namespace Map + // @section Tooltip events + // @event tooltipopen: TooltipEvent + // Fired when a tooltip is opened in the map. + map.fire('tooltipopen', {tooltip: this}); + + if (this._source) { + // @namespace Layer + // @section Tooltip events + // @event tooltipopen: TooltipEvent + // Fired when a tooltip bound to this layer is opened. + this._source.fire('tooltipopen', {tooltip: this}, true); + } + }, + + onRemove: function (map) { + DivOverlay.prototype.onRemove.call(this, map); + + // @namespace Map + // @section Tooltip events + // @event tooltipclose: TooltipEvent + // Fired when a tooltip in the map is closed. + map.fire('tooltipclose', {tooltip: this}); + + if (this._source) { + // @namespace Layer + // @section Tooltip events + // @event tooltipclose: TooltipEvent + // Fired when a tooltip bound to this layer is closed. + this._source.fire('tooltipclose', {tooltip: this}, true); + } + }, + + getEvents: function () { + var events = DivOverlay.prototype.getEvents.call(this); + + if (touch && !this.options.permanent) { + events.preclick = this._close; + } + + return events; + }, + + _close: function () { + if (this._map) { + this._map.closeTooltip(this); + } + }, + + _initLayout: function () { + var prefix = 'leaflet-tooltip', + className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); + + this._contentNode = this._container = create$1('div', className); + }, + + _updateLayout: function () {}, + + _adjustPan: function () {}, + + _setPosition: function (pos) { + var map = this._map, + container = this._container, + centerPoint = map.latLngToContainerPoint(map.getCenter()), + tooltipPoint = map.layerPointToContainerPoint(pos), + direction = this.options.direction, + tooltipWidth = container.offsetWidth, + tooltipHeight = container.offsetHeight, + offset = toPoint(this.options.offset), + anchor = this._getAnchor(); + + if (direction === 'top') { + pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true)); + } else if (direction === 'bottom') { + pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true)); + } else if (direction === 'center') { + pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true)); + } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) { + direction = 'right'; + pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true)); + } else { + direction = 'left'; + pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true)); + } + + removeClass(container, 'leaflet-tooltip-right'); + removeClass(container, 'leaflet-tooltip-left'); + removeClass(container, 'leaflet-tooltip-top'); + removeClass(container, 'leaflet-tooltip-bottom'); + addClass(container, 'leaflet-tooltip-' + direction); + setPosition(container, pos); + }, + + _updatePosition: function () { + var pos = this._map.latLngToLayerPoint(this._latlng); + this._setPosition(pos); + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._container) { + setOpacity(this._container, opacity); + } + }, + + _animateZoom: function (e) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); + this._setPosition(pos); + }, + + _getAnchor: function () { + // Where should we anchor the tooltip on the source layer? + return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]); + } + +}); + +// @namespace Tooltip +// @factory L.tooltip(options?: Tooltip options, source?: Layer) +// Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers. +var tooltip = function (options, source) { + return new Tooltip(options, source); +}; + +// @namespace Map +// @section Methods for Layers and Controls +Map.include({ + + // @method openTooltip(tooltip: Tooltip): this + // Opens the specified tooltip. + // @alternative + // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this + // Creates a tooltip with the specified content and options and open it. + openTooltip: function (tooltip, latlng, options) { + if (!(tooltip instanceof Tooltip)) { + tooltip = new Tooltip(options).setContent(tooltip); + } + + if (latlng) { + tooltip.setLatLng(latlng); + } + + if (this.hasLayer(tooltip)) { + return this; + } + + return this.addLayer(tooltip); + }, + + // @method closeTooltip(tooltip?: Tooltip): this + // Closes the tooltip given as parameter. + closeTooltip: function (tooltip) { + if (tooltip) { + this.removeLayer(tooltip); + } + return this; + } + +}); + +/* + * @namespace Layer + * @section Tooltip methods example + * + * All layers share a set of methods convenient for binding tooltips to it. + * + * ```js + * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map); + * layer.openTooltip(); + * layer.closeTooltip(); + * ``` + */ + +// @section Tooltip methods +Layer.include({ + + // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this + // Binds a tooltip to the layer with the passed `content` and sets up the + // necessary event listeners. If a `Function` is passed it will receive + // the layer as the first argument and should return a `String` or `HTMLElement`. + bindTooltip: function (content, options) { + + if (content instanceof Tooltip) { + setOptions(content, options); + this._tooltip = content; + content._source = this; + } else { + if (!this._tooltip || options) { + this._tooltip = new Tooltip(options, this); + } + this._tooltip.setContent(content); + + } + + this._initTooltipInteractions(); + + if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) { + this.openTooltip(); + } + + return this; + }, + + // @method unbindTooltip(): this + // Removes the tooltip previously bound with `bindTooltip`. + unbindTooltip: function () { + if (this._tooltip) { + this._initTooltipInteractions(true); + this.closeTooltip(); + this._tooltip = null; + } + return this; + }, + + _initTooltipInteractions: function (remove$$1) { + if (!remove$$1 && this._tooltipHandlersAdded) { return; } + var onOff = remove$$1 ? 'off' : 'on', + events = { + remove: this.closeTooltip, + move: this._moveTooltip + }; + if (!this._tooltip.options.permanent) { + events.mouseover = this._openTooltip; + events.mouseout = this.closeTooltip; + if (this._tooltip.options.sticky) { + events.mousemove = this._moveTooltip; + } + if (touch) { + events.click = this._openTooltip; + } + } else { + events.add = this._openTooltip; + } + this[onOff](events); + this._tooltipHandlersAdded = !remove$$1; + }, + + // @method openTooltip(latlng?: LatLng): this + // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed. + openTooltip: function (layer, latlng) { + if (!(layer instanceof Layer)) { + latlng = layer; + layer = this; + } + + if (layer instanceof FeatureGroup) { + for (var id in this._layers) { + layer = this._layers[id]; + break; + } + } + + if (!latlng) { + latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng(); + } + + if (this._tooltip && this._map) { + + // set tooltip source to this layer + this._tooltip._source = layer; + + // update the tooltip (content, layout, ect...) + this._tooltip.update(); + + // open the tooltip on the map + this._map.openTooltip(this._tooltip, latlng); + + // Tooltip container may not be defined if not permanent and never + // opened. + if (this._tooltip.options.interactive && this._tooltip._container) { + addClass(this._tooltip._container, 'leaflet-clickable'); + this.addInteractiveTarget(this._tooltip._container); + } + } + + return this; + }, + + // @method closeTooltip(): this + // Closes the tooltip bound to this layer if it is open. + closeTooltip: function () { + if (this._tooltip) { + this._tooltip._close(); + if (this._tooltip.options.interactive && this._tooltip._container) { + removeClass(this._tooltip._container, 'leaflet-clickable'); + this.removeInteractiveTarget(this._tooltip._container); + } + } + return this; + }, + + // @method toggleTooltip(): this + // Opens or closes the tooltip bound to this layer depending on its current state. + toggleTooltip: function (target) { + if (this._tooltip) { + if (this._tooltip._map) { + this.closeTooltip(); + } else { + this.openTooltip(target); + } + } + return this; + }, + + // @method isTooltipOpen(): boolean + // Returns `true` if the tooltip bound to this layer is currently open. + isTooltipOpen: function () { + return this._tooltip.isOpen(); + }, + + // @method setTooltipContent(content: String|HTMLElement|Tooltip): this + // Sets the content of the tooltip bound to this layer. + setTooltipContent: function (content) { + if (this._tooltip) { + this._tooltip.setContent(content); + } + return this; + }, + + // @method getTooltip(): Tooltip + // Returns the tooltip bound to this layer. + getTooltip: function () { + return this._tooltip; + }, + + _openTooltip: function (e) { + var layer = e.layer || e.target; + + if (!this._tooltip || !this._map) { + return; + } + this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined); + }, + + _moveTooltip: function (e) { + var latlng = e.latlng, containerPoint, layerPoint; + if (this._tooltip.options.sticky && e.originalEvent) { + containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent); + layerPoint = this._map.containerPointToLayerPoint(containerPoint); + latlng = this._map.layerPointToLatLng(layerPoint); + } + this._tooltip.setLatLng(latlng); + } +}); + +/* + * @class DivIcon + * @aka L.DivIcon + * @inherits Icon + * + * Represents a lightweight icon for markers that uses a simple `<div>` + * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options. + * + * @example + * ```js + * var myIcon = L.divIcon({className: 'my-div-icon'}); + * // you can set .my-div-icon styles in CSS + * + * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); + * ``` + * + * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow. + */ + +var DivIcon = Icon.extend({ + options: { + // @section + // @aka DivIcon options + iconSize: [12, 12], // also can be set through CSS + + // iconAnchor: (Point), + // popupAnchor: (Point), + + // @option html: String = '' + // Custom HTML code to put inside the div element, empty by default. + html: false, + + // @option bgPos: Point = [0, 0] + // Optional relative position of the background, in pixels + bgPos: null, + + className: 'leaflet-div-icon' + }, + + createIcon: function (oldIcon) { + var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), + options = this.options; + + div.innerHTML = options.html !== false ? options.html : ''; + + if (options.bgPos) { + var bgPos = toPoint(options.bgPos); + div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; + } + this._setIconStyles(div, 'icon'); + + return div; + }, + + createShadow: function () { + return null; + } +}); + +// @factory L.divIcon(options: DivIcon options) +// Creates a `DivIcon` instance with the given options. +function divIcon(options) { + return new DivIcon(options); +} + +Icon.Default = IconDefault; + +/* + * @class GridLayer + * @inherits Layer + * @aka L.GridLayer + * + * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`. + * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you. + * + * + * @section Synchronous usage + * @example + * + * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile. + * + * ```js + * var CanvasLayer = L.GridLayer.extend({ + * createTile: function(coords){ + * // create a <canvas> element for drawing + * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); + * + * // setup tile width and height according to the options + * var size = this.getTileSize(); + * tile.width = size.x; + * tile.height = size.y; + * + * // get a canvas context and draw something on it using coords.x, coords.y and coords.z + * var ctx = tile.getContext('2d'); + * + * // return the tile so it can be rendered on screen + * return tile; + * } + * }); + * ``` + * + * @section Asynchronous usage + * @example + * + * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback. + * + * ```js + * var CanvasLayer = L.GridLayer.extend({ + * createTile: function(coords, done){ + * var error; + * + * // create a <canvas> element for drawing + * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); + * + * // setup tile width and height according to the options + * var size = this.getTileSize(); + * tile.width = size.x; + * tile.height = size.y; + * + * // draw something asynchronously and pass the tile to the done() callback + * setTimeout(function() { + * done(error, tile); + * }, 1000); + * + * return tile; + * } + * }); + * ``` + * + * @section + */ + + +var GridLayer = Layer.extend({ + + // @section + // @aka GridLayer options + options: { + // @option tileSize: Number|Point = 256 + // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise. + tileSize: 256, + + // @option opacity: Number = 1.0 + // Opacity of the tiles. Can be used in the `createTile()` function. + opacity: 1, + + // @option updateWhenIdle: Boolean = (depends) + // Load new tiles only when panning ends. + // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation. + // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the + // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers. + updateWhenIdle: mobile, + + // @option updateWhenZooming: Boolean = true + // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends. + updateWhenZooming: true, + + // @option updateInterval: Number = 200 + // Tiles will not update more than once every `updateInterval` milliseconds when panning. + updateInterval: 200, + + // @option zIndex: Number = 1 + // The explicit zIndex of the tile layer. + zIndex: 1, + + // @option bounds: LatLngBounds = undefined + // If set, tiles will only be loaded inside the set `LatLngBounds`. + bounds: null, + + // @option minZoom: Number = 0 + // The minimum zoom level down to which this layer will be displayed (inclusive). + minZoom: 0, + + // @option maxZoom: Number = undefined + // The maximum zoom level up to which this layer will be displayed (inclusive). + maxZoom: undefined, + + // @option maxNativeZoom: Number = undefined + // Maximum zoom number the tile source has available. If it is specified, + // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded + // from `maxNativeZoom` level and auto-scaled. + maxNativeZoom: undefined, + + // @option minNativeZoom: Number = undefined + // Minimum zoom number the tile source has available. If it is specified, + // the tiles on all zoom levels lower than `minNativeZoom` will be loaded + // from `minNativeZoom` level and auto-scaled. + minNativeZoom: undefined, + + // @option noWrap: Boolean = false + // Whether the layer is wrapped around the antimeridian. If `true`, the + // GridLayer will only be displayed once at low zoom levels. Has no + // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used + // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting + // tiles outside the CRS limits. + noWrap: false, + + // @option pane: String = 'tilePane' + // `Map pane` where the grid layer will be added. + pane: 'tilePane', + + // @option className: String = '' + // A custom class name to assign to the tile layer. Empty by default. + className: '', + + // @option keepBuffer: Number = 2 + // When panning the map, keep this many rows and columns of tiles before unloading them. + keepBuffer: 2 + }, + + initialize: function (options) { + setOptions(this, options); + }, + + onAdd: function () { + this._initContainer(); + + this._levels = {}; + this._tiles = {}; + + this._resetView(); + this._update(); + }, + + beforeAdd: function (map) { + map._addZoomLimit(this); + }, + + onRemove: function (map) { + this._removeAllTiles(); + remove(this._container); + map._removeZoomLimit(this); + this._container = null; + this._tileZoom = undefined; + }, + + // @method bringToFront: this + // Brings the tile layer to the top of all tile layers. + bringToFront: function () { + if (this._map) { + toFront(this._container); + this._setAutoZIndex(Math.max); + } + return this; + }, + + // @method bringToBack: this + // Brings the tile layer to the bottom of all tile layers. + bringToBack: function () { + if (this._map) { + toBack(this._container); + this._setAutoZIndex(Math.min); + } + return this; + }, + + // @method getContainer: HTMLElement + // Returns the HTML element that contains the tiles for this layer. + getContainer: function () { + return this._container; + }, + + // @method setOpacity(opacity: Number): this + // Changes the [opacity](#gridlayer-opacity) of the grid layer. + setOpacity: function (opacity) { + this.options.opacity = opacity; + this._updateOpacity(); + return this; + }, + + // @method setZIndex(zIndex: Number): this + // Changes the [zIndex](#gridlayer-zindex) of the grid layer. + setZIndex: function (zIndex) { + this.options.zIndex = zIndex; + this._updateZIndex(); + + return this; + }, + + // @method isLoading: Boolean + // Returns `true` if any tile in the grid layer has not finished loading. + isLoading: function () { + return this._loading; + }, + + // @method redraw: this + // Causes the layer to clear all the tiles and request them again. + redraw: function () { + if (this._map) { + this._removeAllTiles(); + this._update(); + } + return this; + }, + + getEvents: function () { + var events = { + viewprereset: this._invalidateAll, + viewreset: this._resetView, + zoom: this._resetView, + moveend: this._onMoveEnd + }; + + if (!this.options.updateWhenIdle) { + // update tiles on move, but not more often than once per given interval + if (!this._onMove) { + this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this); + } + + events.move = this._onMove; + } + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + + return events; + }, + + // @section Extension methods + // Layers extending `GridLayer` shall reimplement the following method. + // @method createTile(coords: Object, done?: Function): HTMLElement + // Called only internally, must be overridden by classes extending `GridLayer`. + // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback + // is specified, it must be called when the tile has finished loading and drawing. + createTile: function () { + return document.createElement('div'); + }, + + // @section + // @method getTileSize: Point + // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method. + getTileSize: function () { + var s = this.options.tileSize; + return s instanceof Point ? s : new Point(s, s); + }, + + _updateZIndex: function () { + if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) { + this._container.style.zIndex = this.options.zIndex; + } + }, + + _setAutoZIndex: function (compare) { + // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back) + + var layers = this.getPane().children, + edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min + + for (var i = 0, len = layers.length, zIndex; i < len; i++) { + + zIndex = layers[i].style.zIndex; + + if (layers[i] !== this._container && zIndex) { + edgeZIndex = compare(edgeZIndex, +zIndex); + } + } + + if (isFinite(edgeZIndex)) { + this.options.zIndex = edgeZIndex + compare(-1, 1); + this._updateZIndex(); + } + }, + + _updateOpacity: function () { + if (!this._map) { return; } + + // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles + if (ielt9) { return; } + + setOpacity(this._container, this.options.opacity); + + var now = +new Date(), + nextFrame = false, + willPrune = false; + + for (var key in this._tiles) { + var tile = this._tiles[key]; + if (!tile.current || !tile.loaded) { continue; } + + var fade = Math.min(1, (now - tile.loaded) / 200); + + setOpacity(tile.el, fade); + if (fade < 1) { + nextFrame = true; + } else { + if (tile.active) { + willPrune = true; + } else { + this._onOpaqueTile(tile); + } + tile.active = true; + } + } + + if (willPrune && !this._noPrune) { this._pruneTiles(); } + + if (nextFrame) { + cancelAnimFrame(this._fadeFrame); + this._fadeFrame = requestAnimFrame(this._updateOpacity, this); + } + }, + + _onOpaqueTile: falseFn, + + _initContainer: function () { + if (this._container) { return; } + + this._container = create$1('div', 'leaflet-layer ' + (this.options.className || '')); + this._updateZIndex(); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + + this.getPane().appendChild(this._container); + }, + + _updateLevels: function () { + + var zoom = this._tileZoom, + maxZoom = this.options.maxZoom; + + if (zoom === undefined) { return undefined; } + + for (var z in this._levels) { + if (this._levels[z].el.children.length || z === zoom) { + this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z); + this._onUpdateLevel(z); + } else { + remove(this._levels[z].el); + this._removeTilesAtZoom(z); + this._onRemoveLevel(z); + delete this._levels[z]; + } + } + + var level = this._levels[zoom], + map = this._map; + + if (!level) { + level = this._levels[zoom] = {}; + + level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); + level.el.style.zIndex = maxZoom; + + level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); + level.zoom = zoom; + + this._setZoomTransform(level, map.getCenter(), map.getZoom()); + + // force the browser to consider the newly added element for transition + falseFn(level.el.offsetWidth); + + this._onCreateLevel(level); + } + + this._level = level; + + return level; + }, + + _onUpdateLevel: falseFn, + + _onRemoveLevel: falseFn, + + _onCreateLevel: falseFn, + + _pruneTiles: function () { + if (!this._map) { + return; + } + + var key, tile; + + var zoom = this._map.getZoom(); + if (zoom > this.options.maxZoom || + zoom < this.options.minZoom) { + this._removeAllTiles(); + return; + } + + for (key in this._tiles) { + tile = this._tiles[key]; + tile.retain = tile.current; + } + + for (key in this._tiles) { + tile = this._tiles[key]; + if (tile.current && !tile.active) { + var coords = tile.coords; + if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { + this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); + } + } + } + + for (key in this._tiles) { + if (!this._tiles[key].retain) { + this._removeTile(key); + } + } + }, + + _removeTilesAtZoom: function (zoom) { + for (var key in this._tiles) { + if (this._tiles[key].coords.z !== zoom) { + continue; + } + this._removeTile(key); + } + }, + + _removeAllTiles: function () { + for (var key in this._tiles) { + this._removeTile(key); + } + }, + + _invalidateAll: function () { + for (var z in this._levels) { + remove(this._levels[z].el); + this._onRemoveLevel(z); + delete this._levels[z]; + } + this._removeAllTiles(); + + this._tileZoom = undefined; + }, + + _retainParent: function (x, y, z, minZoom) { + var x2 = Math.floor(x / 2), + y2 = Math.floor(y / 2), + z2 = z - 1, + coords2 = new Point(+x2, +y2); + coords2.z = +z2; + + var key = this._tileCoordsToKey(coords2), + tile = this._tiles[key]; + + if (tile && tile.active) { + tile.retain = true; + return true; + + } else if (tile && tile.loaded) { + tile.retain = true; + } + + if (z2 > minZoom) { + return this._retainParent(x2, y2, z2, minZoom); + } + + return false; + }, + + _retainChildren: function (x, y, z, maxZoom) { + + for (var i = 2 * x; i < 2 * x + 2; i++) { + for (var j = 2 * y; j < 2 * y + 2; j++) { + + var coords = new Point(i, j); + coords.z = z + 1; + + var key = this._tileCoordsToKey(coords), + tile = this._tiles[key]; + + if (tile && tile.active) { + tile.retain = true; + continue; + + } else if (tile && tile.loaded) { + tile.retain = true; + } + + if (z + 1 < maxZoom) { + this._retainChildren(i, j, z + 1, maxZoom); + } + } + } + }, + + _resetView: function (e) { + var animating = e && (e.pinch || e.flyTo); + this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); + }, + + _animateZoom: function (e) { + this._setView(e.center, e.zoom, true, e.noUpdate); + }, + + _clampZoom: function (zoom) { + var options = this.options; + + if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) { + return options.minNativeZoom; + } + + if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) { + return options.maxNativeZoom; + } + + return zoom; + }, + + _setView: function (center, zoom, noPrune, noUpdate) { + var tileZoom = this._clampZoom(Math.round(zoom)); + if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) || + (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) { + tileZoom = undefined; + } + + var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom); + + if (!noUpdate || tileZoomChanged) { + + this._tileZoom = tileZoom; + + if (this._abortLoading) { + this._abortLoading(); + } + + this._updateLevels(); + this._resetGrid(); + + if (tileZoom !== undefined) { + this._update(center); + } + + if (!noPrune) { + this._pruneTiles(); + } + + // Flag to prevent _updateOpacity from pruning tiles during + // a zoom anim or a pinch gesture + this._noPrune = !!noPrune; + } + + this._setZoomTransforms(center, zoom); + }, + + _setZoomTransforms: function (center, zoom) { + for (var i in this._levels) { + this._setZoomTransform(this._levels[i], center, zoom); + } + }, + + _setZoomTransform: function (level, center, zoom) { + var scale = this._map.getZoomScale(zoom, level.zoom), + translate = level.origin.multiplyBy(scale) + .subtract(this._map._getNewPixelOrigin(center, zoom)).round(); + + if (any3d) { + setTransform(level.el, translate, scale); + } else { + setPosition(level.el, translate); + } + }, + + _resetGrid: function () { + var map = this._map, + crs = map.options.crs, + tileSize = this._tileSize = this.getTileSize(), + tileZoom = this._tileZoom; + + var bounds = this._map.getPixelWorldBounds(this._tileZoom); + if (bounds) { + this._globalTileRange = this._pxBoundsToTileRange(bounds); + } + + this._wrapX = crs.wrapLng && !this.options.noWrap && [ + Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), + Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) + ]; + this._wrapY = crs.wrapLat && !this.options.noWrap && [ + Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), + Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) + ]; + }, + + _onMoveEnd: function () { + if (!this._map || this._map._animatingZoom) { return; } + + this._update(); + }, + + _getTiledPixelBounds: function (center) { + var map = this._map, + mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(), + scale = map.getZoomScale(mapZoom, this._tileZoom), + pixelCenter = map.project(center, this._tileZoom).floor(), + halfSize = map.getSize().divideBy(scale * 2); + + return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); + }, + + // Private method to load tiles in the grid's active zoom level according to map bounds + _update: function (center) { + var map = this._map; + if (!map) { return; } + var zoom = this._clampZoom(map.getZoom()); + + if (center === undefined) { center = map.getCenter(); } + if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom + + var pixelBounds = this._getTiledPixelBounds(center), + tileRange = this._pxBoundsToTileRange(pixelBounds), + tileCenter = tileRange.getCenter(), + queue = [], + margin = this.options.keepBuffer, + noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]), + tileRange.getTopRight().add([margin, -margin])); + + // Sanity check: panic if the tile range contains Infinity somewhere. + if (!(isFinite(tileRange.min.x) && + isFinite(tileRange.min.y) && + isFinite(tileRange.max.x) && + isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); } + + for (var key in this._tiles) { + var c = this._tiles[key].coords; + if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) { + this._tiles[key].current = false; + } + } + + // _update just loads more tiles. If the tile zoom level differs too much + // from the map's, let _setView reset levels and prune old tiles. + if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; } + + // create a queue of coordinates to load tiles from + for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { + for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { + var coords = new Point(i, j); + coords.z = this._tileZoom; + + if (!this._isValidTile(coords)) { continue; } + + var tile = this._tiles[this._tileCoordsToKey(coords)]; + if (tile) { + tile.current = true; + } else { + queue.push(coords); + } + } + } + + // sort tile queue to load tiles in order of their distance to center + queue.sort(function (a, b) { + return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); + }); + + if (queue.length !== 0) { + // if it's the first batch of tiles to load + if (!this._loading) { + this._loading = true; + // @event loading: Event + // Fired when the grid layer starts loading tiles. + this.fire('loading'); + } + + // create DOM fragment to append tiles in one batch + var fragment = document.createDocumentFragment(); + + for (i = 0; i < queue.length; i++) { + this._addTile(queue[i], fragment); + } + + this._level.el.appendChild(fragment); + } + }, + + _isValidTile: function (coords) { + var crs = this._map.options.crs; + + if (!crs.infinite) { + // don't load tile if it's out of bounds and not wrapped + var bounds = this._globalTileRange; + if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) || + (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; } + } + + if (!this.options.bounds) { return true; } + + // don't load tile if it doesn't intersect the bounds in options + var tileBounds = this._tileCoordsToBounds(coords); + return toLatLngBounds(this.options.bounds).overlaps(tileBounds); + }, + + _keyToBounds: function (key) { + return this._tileCoordsToBounds(this._keyToTileCoords(key)); + }, + + _tileCoordsToNwSe: function (coords) { + var map = this._map, + tileSize = this.getTileSize(), + nwPoint = coords.scaleBy(tileSize), + sePoint = nwPoint.add(tileSize), + nw = map.unproject(nwPoint, coords.z), + se = map.unproject(sePoint, coords.z); + return [nw, se]; + }, + + // converts tile coordinates to its geographical bounds + _tileCoordsToBounds: function (coords) { + var bp = this._tileCoordsToNwSe(coords), + bounds = new LatLngBounds(bp[0], bp[1]); + + if (!this.options.noWrap) { + bounds = this._map.wrapLatLngBounds(bounds); + } + return bounds; + }, + // converts tile coordinates to key for the tile cache + _tileCoordsToKey: function (coords) { + return coords.x + ':' + coords.y + ':' + coords.z; + }, + + // converts tile cache key to coordinates + _keyToTileCoords: function (key) { + var k = key.split(':'), + coords = new Point(+k[0], +k[1]); + coords.z = +k[2]; + return coords; + }, + + _removeTile: function (key) { + var tile = this._tiles[key]; + if (!tile) { return; } + + // Cancels any pending http requests associated with the tile + // unless we're on Android's stock browser, + // see https://github.com/Leaflet/Leaflet/issues/137 + if (!androidStock) { + tile.el.setAttribute('src', emptyImageUrl); + } + remove(tile.el); + + delete this._tiles[key]; + + // @event tileunload: TileEvent + // Fired when a tile is removed (e.g. when a tile goes off the screen). + this.fire('tileunload', { + tile: tile.el, + coords: this._keyToTileCoords(key) + }); + }, + + _initTile: function (tile) { + addClass(tile, 'leaflet-tile'); + + var tileSize = this.getTileSize(); + tile.style.width = tileSize.x + 'px'; + tile.style.height = tileSize.y + 'px'; + + tile.onselectstart = falseFn; + tile.onmousemove = falseFn; + + // update opacity on tiles in IE7-8 because of filter inheritance problems + if (ielt9 && this.options.opacity < 1) { + setOpacity(tile, this.options.opacity); + } + + // without this hack, tiles disappear after zoom on Chrome for Android + // https://github.com/Leaflet/Leaflet/issues/2078 + if (android && !android23) { + tile.style.WebkitBackfaceVisibility = 'hidden'; + } + }, + + _addTile: function (coords, container) { + var tilePos = this._getTilePos(coords), + key = this._tileCoordsToKey(coords); + + var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords)); + + this._initTile(tile); + + // if createTile is defined with a second argument ("done" callback), + // we know that tile is async and will be ready later; otherwise + if (this.createTile.length < 2) { + // mark tile as ready, but delay one frame for opacity animation to happen + requestAnimFrame(bind(this._tileReady, this, coords, null, tile)); + } + + setPosition(tile, tilePos); + + // save tile in cache + this._tiles[key] = { + el: tile, + coords: coords, + current: true + }; + + container.appendChild(tile); + // @event tileloadstart: TileEvent + // Fired when a tile is requested and starts loading. + this.fire('tileloadstart', { + tile: tile, + coords: coords + }); + }, + + _tileReady: function (coords, err, tile) { + if (!this._map) { return; } + + if (err) { + // @event tileerror: TileErrorEvent + // Fired when there is an error loading a tile. + this.fire('tileerror', { + error: err, + tile: tile, + coords: coords + }); + } + + var key = this._tileCoordsToKey(coords); + + tile = this._tiles[key]; + if (!tile) { return; } + + tile.loaded = +new Date(); + if (this._map._fadeAnimated) { + setOpacity(tile.el, 0); + cancelAnimFrame(this._fadeFrame); + this._fadeFrame = requestAnimFrame(this._updateOpacity, this); + } else { + tile.active = true; + this._pruneTiles(); + } + + if (!err) { + addClass(tile.el, 'leaflet-tile-loaded'); + + // @event tileload: TileEvent + // Fired when a tile loads. + this.fire('tileload', { + tile: tile.el, + coords: coords + }); + } + + if (this._noTilesToLoad()) { + this._loading = false; + // @event load: Event + // Fired when the grid layer loaded all visible tiles. + this.fire('load'); + + if (ielt9 || !this._map._fadeAnimated) { + requestAnimFrame(this._pruneTiles, this); + } else { + // Wait a bit more than 0.2 secs (the duration of the tile fade-in) + // to trigger a pruning. + setTimeout(bind(this._pruneTiles, this), 250); + } + } + }, + + _getTilePos: function (coords) { + return coords.scaleBy(this.getTileSize()).subtract(this._level.origin); + }, + + _wrapCoords: function (coords) { + var newCoords = new Point( + this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x, + this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y); + newCoords.z = coords.z; + return newCoords; + }, + + _pxBoundsToTileRange: function (bounds) { + var tileSize = this.getTileSize(); + return new Bounds( + bounds.min.unscaleBy(tileSize).floor(), + bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1])); + }, + + _noTilesToLoad: function () { + for (var key in this._tiles) { + if (!this._tiles[key].loaded) { return false; } + } + return true; + } +}); + +// @factory L.gridLayer(options?: GridLayer options) +// Creates a new instance of GridLayer with the supplied options. +function gridLayer(options) { + return new GridLayer(options); +} + +/* + * @class TileLayer + * @inherits GridLayer + * @aka L.TileLayer + * Used to load and display tile layers on the map. Extends `GridLayer`. + * + * @example + * + * ```js + * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map); + * ``` + * + * @section URL template + * @example + * + * A string of the following form: + * + * ``` + * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png' + * ``` + * + * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "@2x" to the URL to load retina tiles. + * + * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this: + * + * ``` + * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'}); + * ``` + */ + + +var TileLayer = GridLayer.extend({ + + // @section + // @aka TileLayer options + options: { + // @option minZoom: Number = 0 + // The minimum zoom level down to which this layer will be displayed (inclusive). + minZoom: 0, + + // @option maxZoom: Number = 18 + // The maximum zoom level up to which this layer will be displayed (inclusive). + maxZoom: 18, + + // @option subdomains: String|String[] = 'abc' + // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings. + subdomains: 'abc', + + // @option errorTileUrl: String = '' + // URL to the tile image to show in place of the tile that failed to load. + errorTileUrl: '', + + // @option zoomOffset: Number = 0 + // The zoom number used in tile URLs will be offset with this value. + zoomOffset: 0, + + // @option tms: Boolean = false + // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services). + tms: false, + + // @option zoomReverse: Boolean = false + // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`) + zoomReverse: false, + + // @option detectRetina: Boolean = false + // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution. + detectRetina: false, + + // @option crossOrigin: Boolean = false + // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data. + crossOrigin: false + }, + + initialize: function (url, options) { + + this._url = url; + + options = setOptions(this, options); + + // detecting retina displays, adjusting tileSize and zoom levels + if (options.detectRetina && retina && options.maxZoom > 0) { + + options.tileSize = Math.floor(options.tileSize / 2); + + if (!options.zoomReverse) { + options.zoomOffset++; + options.maxZoom--; + } else { + options.zoomOffset--; + options.minZoom++; + } + + options.minZoom = Math.max(0, options.minZoom); + } + + if (typeof options.subdomains === 'string') { + options.subdomains = options.subdomains.split(''); + } + + // for https://github.com/Leaflet/Leaflet/issues/137 + if (!android) { + this.on('tileunload', this._onTileRemove); + } + }, + + // @method setUrl(url: String, noRedraw?: Boolean): this + // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`). + setUrl: function (url, noRedraw) { + this._url = url; + + if (!noRedraw) { + this.redraw(); + } + return this; + }, + + // @method createTile(coords: Object, done?: Function): HTMLElement + // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile) + // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done` + // callback is called when the tile has been loaded. + createTile: function (coords, done) { + var tile = document.createElement('img'); + + on(tile, 'load', bind(this._tileOnLoad, this, done, tile)); + on(tile, 'error', bind(this._tileOnError, this, done, tile)); + + if (this.options.crossOrigin) { + tile.crossOrigin = ''; + } + + /* + Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons + http://www.w3.org/TR/WCAG20-TECHS/H67 + */ + tile.alt = ''; + + /* + Set role="presentation" to force screen readers to ignore this + https://www.w3.org/TR/wai-aria/roles#textalternativecomputation + */ + tile.setAttribute('role', 'presentation'); + + tile.src = this.getTileUrl(coords); + + return tile; + }, + + // @section Extension methods + // @uninheritable + // Layers extending `TileLayer` might reimplement the following method. + // @method getTileUrl(coords: Object): String + // Called only internally, returns the URL for a tile given its coordinates. + // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes. + getTileUrl: function (coords) { + var data = { + r: retina ? '@2x' : '', + s: this._getSubdomain(coords), + x: coords.x, + y: coords.y, + z: this._getZoomForUrl() + }; + if (this._map && !this._map.options.crs.infinite) { + var invertedY = this._globalTileRange.max.y - coords.y; + if (this.options.tms) { + data['y'] = invertedY; + } + data['-y'] = invertedY; + } + + return template(this._url, extend(data, this.options)); + }, + + _tileOnLoad: function (done, tile) { + // For https://github.com/Leaflet/Leaflet/issues/3332 + if (ielt9) { + setTimeout(bind(done, this, null, tile), 0); + } else { + done(null, tile); + } + }, + + _tileOnError: function (done, tile, e) { + var errorUrl = this.options.errorTileUrl; + if (errorUrl && tile.getAttribute('src') !== errorUrl) { + tile.src = errorUrl; + } + done(e, tile); + }, + + _onTileRemove: function (e) { + e.tile.onload = null; + }, + + _getZoomForUrl: function () { + var zoom = this._tileZoom, + maxZoom = this.options.maxZoom, + zoomReverse = this.options.zoomReverse, + zoomOffset = this.options.zoomOffset; + + if (zoomReverse) { + zoom = maxZoom - zoom; + } + + return zoom + zoomOffset; + }, + + _getSubdomain: function (tilePoint) { + var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; + return this.options.subdomains[index]; + }, + + // stops loading all tiles in the background layer + _abortLoading: function () { + var i, tile; + for (i in this._tiles) { + if (this._tiles[i].coords.z !== this._tileZoom) { + tile = this._tiles[i].el; + + tile.onload = falseFn; + tile.onerror = falseFn; + + if (!tile.complete) { + tile.src = emptyImageUrl; + remove(tile); + delete this._tiles[i]; + } + } + } + } +}); + + +// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options) +// Instantiates a tile layer object given a `URL template` and optionally an options object. + +function tileLayer(url, options) { + return new TileLayer(url, options); +} + +/* + * @class TileLayer.WMS + * @inherits TileLayer + * @aka L.TileLayer.WMS + * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`. + * + * @example + * + * ```js + * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", { + * layers: 'nexrad-n0r-900913', + * format: 'image/png', + * transparent: true, + * attribution: "Weather data © 2012 IEM Nexrad" + * }); + * ``` + */ + +var TileLayerWMS = TileLayer.extend({ + + // @section + // @aka TileLayer.WMS options + // If any custom options not documented here are used, they will be sent to the + // WMS server as extra parameters in each request URL. This can be useful for + // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html). + defaultWmsParams: { + service: 'WMS', + request: 'GetMap', + + // @option layers: String = '' + // **(required)** Comma-separated list of WMS layers to show. + layers: '', + + // @option styles: String = '' + // Comma-separated list of WMS styles. + styles: '', + + // @option format: String = 'image/jpeg' + // WMS image format (use `'image/png'` for layers with transparency). + format: 'image/jpeg', + + // @option transparent: Boolean = false + // If `true`, the WMS service will return images with transparency. + transparent: false, + + // @option version: String = '1.1.1' + // Version of the WMS service to use + version: '1.1.1' + }, + + options: { + // @option crs: CRS = null + // Coordinate Reference System to use for the WMS requests, defaults to + // map CRS. Don't change this if you're not sure what it means. + crs: null, + + // @option uppercase: Boolean = false + // If `true`, WMS request parameter keys will be uppercase. + uppercase: false + }, + + initialize: function (url, options) { + + this._url = url; + + var wmsParams = extend({}, this.defaultWmsParams); + + // all keys that are not TileLayer options go to WMS params + for (var i in options) { + if (!(i in this.options)) { + wmsParams[i] = options[i]; + } + } + + options = setOptions(this, options); + + var realRetina = options.detectRetina && retina ? 2 : 1; + var tileSize = this.getTileSize(); + wmsParams.width = tileSize.x * realRetina; + wmsParams.height = tileSize.y * realRetina; + + this.wmsParams = wmsParams; + }, + + onAdd: function (map) { + + this._crs = this.options.crs || map.options.crs; + this._wmsVersion = parseFloat(this.wmsParams.version); + + var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; + this.wmsParams[projectionKey] = this._crs.code; + + TileLayer.prototype.onAdd.call(this, map); + }, + + getTileUrl: function (coords) { + + var tileBounds = this._tileCoordsToNwSe(coords), + crs = this._crs, + bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), + min = bounds.min, + max = bounds.max, + bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ? + [min.y, min.x, max.y, max.x] : + [min.x, min.y, max.x, max.y]).join(','), + url = L.TileLayer.prototype.getTileUrl.call(this, coords); + return url + + getParamString(this.wmsParams, url, this.options.uppercase) + + (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox; + }, + + // @method setParams(params: Object, noRedraw?: Boolean): this + // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true). + setParams: function (params, noRedraw) { + + extend(this.wmsParams, params); + + if (!noRedraw) { + this.redraw(); + } + + return this; + } +}); + + +// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options) +// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object. +function tileLayerWMS(url, options) { + return new TileLayerWMS(url, options); +} + +TileLayer.WMS = TileLayerWMS; +tileLayer.wms = tileLayerWMS; + +/* + * @class Renderer + * @inherits Layer + * @aka L.Renderer + * + * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the + * DOM container of the renderer, its bounds, and its zoom animation. + * + * A `Renderer` works as an implicit layer group for all `Path`s - the renderer + * itself can be added or removed to the map. All paths use a renderer, which can + * be implicit (the map will decide the type of renderer and use it automatically) + * or explicit (using the [`renderer`](#path-renderer) option of the path). + * + * Do not use this class directly, use `SVG` and `Canvas` instead. + * + * @event update: Event + * Fired when the renderer updates its bounds, center and zoom, for example when + * its map has moved + */ + +var Renderer = Layer.extend({ + + // @section + // @aka Renderer options + options: { + // @option padding: Number = 0.1 + // How much to extend the clip area around the map view (relative to its size) + // e.g. 0.1 would be 10% of map view in each direction + padding: 0.1, + + // @option tolerance: Number = 0 + // How much to extend click tolerance round a path/object on the map + tolerance : 0 + }, + + initialize: function (options) { + setOptions(this, options); + stamp(this); + this._layers = this._layers || {}; + }, + + onAdd: function () { + if (!this._container) { + this._initContainer(); // defined by renderer implementations + + if (this._zoomAnimated) { + addClass(this._container, 'leaflet-zoom-animated'); + } + } + + this.getPane().appendChild(this._container); + this._update(); + this.on('update', this._updatePaths, this); + }, + + onRemove: function () { + this.off('update', this._updatePaths, this); + this._destroyContainer(); + }, + + getEvents: function () { + var events = { + viewreset: this._reset, + zoom: this._onZoom, + moveend: this._update, + zoomend: this._onZoomEnd + }; + if (this._zoomAnimated) { + events.zoomanim = this._onAnimZoom; + } + return events; + }, + + _onAnimZoom: function (ev) { + this._updateTransform(ev.center, ev.zoom); + }, + + _onZoom: function () { + this._updateTransform(this._map.getCenter(), this._map.getZoom()); + }, + + _updateTransform: function (center, zoom) { + var scale = this._map.getZoomScale(zoom, this._zoom), + position = getPosition(this._container), + viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding), + currentCenterPoint = this._map.project(this._center, zoom), + destCenterPoint = this._map.project(center, zoom), + centerOffset = destCenterPoint.subtract(currentCenterPoint), + + topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset); + + if (any3d) { + setTransform(this._container, topLeftOffset, scale); + } else { + setPosition(this._container, topLeftOffset); + } + }, + + _reset: function () { + this._update(); + this._updateTransform(this._center, this._zoom); + + for (var id in this._layers) { + this._layers[id]._reset(); + } + }, + + _onZoomEnd: function () { + for (var id in this._layers) { + this._layers[id]._project(); + } + }, + + _updatePaths: function () { + for (var id in this._layers) { + this._layers[id]._update(); + } + }, + + _update: function () { + // Update pixel bounds of renderer container (for positioning/sizing/clipping later) + // Subclasses are responsible of firing the 'update' event. + var p = this.options.padding, + size = this._map.getSize(), + min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round(); + + this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round()); + + this._center = this._map.getCenter(); + this._zoom = this._map.getZoom(); + } +}); + +/* + * @class Canvas + * @inherits Renderer + * @aka L.Canvas + * + * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). + * Inherits `Renderer`. + * + * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not + * available in all web browsers, notably IE8, and overlapping geometries might + * not display properly in some edge cases. + * + * @example + * + * Use Canvas by default for all paths in the map: + * + * ```js + * var map = L.map('map', { + * renderer: L.canvas() + * }); + * ``` + * + * Use a Canvas renderer with extra padding for specific vector geometries: + * + * ```js + * var map = L.map('map'); + * var myRenderer = L.canvas({ padding: 0.5 }); + * var line = L.polyline( coordinates, { renderer: myRenderer } ); + * var circle = L.circle( center, { renderer: myRenderer } ); + * ``` + */ + +var Canvas = Renderer.extend({ + getEvents: function () { + var events = Renderer.prototype.getEvents.call(this); + events.viewprereset = this._onViewPreReset; + return events; + }, + + _onViewPreReset: function () { + // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once + this._postponeUpdatePaths = true; + }, + + onAdd: function () { + Renderer.prototype.onAdd.call(this); + + // Redraw vectors since canvas is cleared upon removal, + // in case of removing the renderer itself from the map. + this._draw(); + }, + + _initContainer: function () { + var container = this._container = document.createElement('canvas'); + + on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this); + on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this); + on(container, 'mouseout', this._handleMouseOut, this); + + this._ctx = container.getContext('2d'); + }, + + _destroyContainer: function () { + delete this._ctx; + remove(this._container); + off(this._container); + delete this._container; + }, + + _updatePaths: function () { + if (this._postponeUpdatePaths) { return; } + + var layer; + this._redrawBounds = null; + for (var id in this._layers) { + layer = this._layers[id]; + layer._update(); + } + this._redraw(); + }, + + _update: function () { + if (this._map._animatingZoom && this._bounds) { return; } + + this._drawnLayers = {}; + + Renderer.prototype._update.call(this); + + var b = this._bounds, + container = this._container, + size = b.getSize(), + m = retina ? 2 : 1; + + setPosition(container, b.min); + + // set canvas size (also clearing it); use double size on retina + container.width = m * size.x; + container.height = m * size.y; + container.style.width = size.x + 'px'; + container.style.height = size.y + 'px'; + + if (retina) { + this._ctx.scale(2, 2); + } + + // translate so we use the same path coordinates after canvas element moves + this._ctx.translate(-b.min.x, -b.min.y); + + // Tell paths to redraw themselves + this.fire('update'); + }, + + _reset: function () { + Renderer.prototype._reset.call(this); + + if (this._postponeUpdatePaths) { + this._postponeUpdatePaths = false; + this._updatePaths(); + } + }, + + _initPath: function (layer) { + this._updateDashArray(layer); + this._layers[stamp(layer)] = layer; + + var order = layer._order = { + layer: layer, + prev: this._drawLast, + next: null + }; + if (this._drawLast) { this._drawLast.next = order; } + this._drawLast = order; + this._drawFirst = this._drawFirst || this._drawLast; + }, + + _addPath: function (layer) { + this._requestRedraw(layer); + }, + + _removePath: function (layer) { + var order = layer._order; + var next = order.next; + var prev = order.prev; + + if (next) { + next.prev = prev; + } else { + this._drawLast = prev; + } + if (prev) { + prev.next = next; + } else { + this._drawFirst = next; + } + + delete layer._order; + + delete this._layers[L.stamp(layer)]; + + this._requestRedraw(layer); + }, + + _updatePath: function (layer) { + // Redraw the union of the layer's old pixel + // bounds and the new pixel bounds. + this._extendRedrawBounds(layer); + layer._project(); + layer._update(); + // The redraw will extend the redraw bounds + // with the new pixel bounds. + this._requestRedraw(layer); + }, + + _updateStyle: function (layer) { + this._updateDashArray(layer); + this._requestRedraw(layer); + }, + + _updateDashArray: function (layer) { + if (layer.options.dashArray) { + var parts = layer.options.dashArray.split(','), + dashArray = [], + i; + for (i = 0; i < parts.length; i++) { + dashArray.push(Number(parts[i])); + } + layer.options._dashArray = dashArray; + } + }, + + _requestRedraw: function (layer) { + if (!this._map) { return; } + + this._extendRedrawBounds(layer); + this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this); + }, + + _extendRedrawBounds: function (layer) { + if (layer._pxBounds) { + var padding = (layer.options.weight || 0) + 1; + this._redrawBounds = this._redrawBounds || new Bounds(); + this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding])); + this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding])); + } + }, + + _redraw: function () { + this._redrawRequest = null; + + if (this._redrawBounds) { + this._redrawBounds.min._floor(); + this._redrawBounds.max._ceil(); + } + + this._clear(); // clear layers in redraw bounds + this._draw(); // draw layers + + this._redrawBounds = null; + }, + + _clear: function () { + var bounds = this._redrawBounds; + if (bounds) { + var size = bounds.getSize(); + this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y); + } else { + this._ctx.clearRect(0, 0, this._container.width, this._container.height); + } + }, + + _draw: function () { + var layer, bounds = this._redrawBounds; + this._ctx.save(); + if (bounds) { + var size = bounds.getSize(); + this._ctx.beginPath(); + this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); + this._ctx.clip(); + } + + this._drawing = true; + + for (var order = this._drawFirst; order; order = order.next) { + layer = order.layer; + if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) { + layer._updatePath(); + } + } + + this._drawing = false; + + this._ctx.restore(); // Restore state before clipping. + }, + + _updatePoly: function (layer, closed) { + if (!this._drawing) { return; } + + var i, j, len2, p, + parts = layer._parts, + len = parts.length, + ctx = this._ctx; + + if (!len) { return; } + + this._drawnLayers[layer._leaflet_id] = layer; + + ctx.beginPath(); + + for (i = 0; i < len; i++) { + for (j = 0, len2 = parts[i].length; j < len2; j++) { + p = parts[i][j]; + ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y); + } + if (closed) { + ctx.closePath(); + } + } + + this._fillStroke(ctx, layer); + + // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature + }, + + _updateCircle: function (layer) { + + if (!this._drawing || layer._empty()) { return; } + + var p = layer._point, + ctx = this._ctx, + r = Math.max(Math.round(layer._radius), 1), + s = (Math.max(Math.round(layer._radiusY), 1) || r) / r; + + this._drawnLayers[layer._leaflet_id] = layer; + + if (s !== 1) { + ctx.save(); + ctx.scale(1, s); + } + + ctx.beginPath(); + ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false); + + if (s !== 1) { + ctx.restore(); + } + + this._fillStroke(ctx, layer); + }, + + _fillStroke: function (ctx, layer) { + var options = layer.options; + + if (options.fill) { + ctx.globalAlpha = options.fillOpacity; + ctx.fillStyle = options.fillColor || options.color; + ctx.fill(options.fillRule || 'evenodd'); + } + + if (options.stroke && options.weight !== 0) { + if (ctx.setLineDash) { + ctx.setLineDash(layer.options && layer.options._dashArray || []); + } + ctx.globalAlpha = options.opacity; + ctx.lineWidth = options.weight; + ctx.strokeStyle = options.color; + ctx.lineCap = options.lineCap; + ctx.lineJoin = options.lineJoin; + ctx.stroke(); + } + }, + + // Canvas obviously doesn't have mouse events for individual drawn objects, + // so we emulate that by calculating what's under the mouse on mousemove/click manually + + _onClick: function (e) { + var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer; + + for (var order = this._drawFirst; order; order = order.next) { + layer = order.layer; + if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) { + clickedLayer = layer; + } + } + if (clickedLayer) { + fakeStop(e); + this._fireEvent([clickedLayer], e); + } + }, + + _onMouseMove: function (e) { + if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; } + + var point = this._map.mouseEventToLayerPoint(e); + this._handleMouseHover(e, point); + }, + + + _handleMouseOut: function (e) { + var layer = this._hoveredLayer; + if (layer) { + // if we're leaving the layer, fire mouseout + removeClass(this._container, 'leaflet-interactive'); + this._fireEvent([layer], e, 'mouseout'); + this._hoveredLayer = null; + } + }, + + _handleMouseHover: function (e, point) { + var layer, candidateHoveredLayer; + + for (var order = this._drawFirst; order; order = order.next) { + layer = order.layer; + if (layer.options.interactive && layer._containsPoint(point)) { + candidateHoveredLayer = layer; + } + } + + if (candidateHoveredLayer !== this._hoveredLayer) { + this._handleMouseOut(e); + + if (candidateHoveredLayer) { + addClass(this._container, 'leaflet-interactive'); // change cursor + this._fireEvent([candidateHoveredLayer], e, 'mouseover'); + this._hoveredLayer = candidateHoveredLayer; + } + } + + if (this._hoveredLayer) { + this._fireEvent([this._hoveredLayer], e); + } + }, + + _fireEvent: function (layers, e, type) { + this._map._fireDOMEvent(e, type || e.type, layers); + }, + + _bringToFront: function (layer) { + var order = layer._order; + var next = order.next; + var prev = order.prev; + + if (next) { + next.prev = prev; + } else { + // Already last + return; + } + if (prev) { + prev.next = next; + } else if (next) { + // Update first entry unless this is the + // single entry + this._drawFirst = next; + } + + order.prev = this._drawLast; + this._drawLast.next = order; + + order.next = null; + this._drawLast = order; + + this._requestRedraw(layer); + }, + + _bringToBack: function (layer) { + var order = layer._order; + var next = order.next; + var prev = order.prev; + + if (prev) { + prev.next = next; + } else { + // Already first + return; + } + if (next) { + next.prev = prev; + } else if (prev) { + // Update last entry unless this is the + // single entry + this._drawLast = prev; + } + + order.prev = null; + + order.next = this._drawFirst; + this._drawFirst.prev = order; + this._drawFirst = order; + + this._requestRedraw(layer); + } +}); + +// @factory L.canvas(options?: Renderer options) +// Creates a Canvas renderer with the given options. +function canvas$1(options) { + return canvas ? new Canvas(options) : null; +} + +/* + * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! + */ + + +var vmlCreate = (function () { + try { + document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); + return function (name) { + return document.createElement('<lvml:' + name + ' class="lvml">'); + }; + } catch (e) { + return function (name) { + return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); + }; + } +})(); + + +/* + * @class SVG + * + * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case. + * + * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility + * with old versions of Internet Explorer. + */ + +// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences +var vmlMixin = { + + _initContainer: function () { + this._container = create$1('div', 'leaflet-vml-container'); + }, + + _update: function () { + if (this._map._animatingZoom) { return; } + Renderer.prototype._update.call(this); + this.fire('update'); + }, + + _initPath: function (layer) { + var container = layer._container = vmlCreate('shape'); + + addClass(container, 'leaflet-vml-shape ' + (this.options.className || '')); + + container.coordsize = '1 1'; + + layer._path = vmlCreate('path'); + container.appendChild(layer._path); + + this._updateStyle(layer); + this._layers[stamp(layer)] = layer; + }, + + _addPath: function (layer) { + var container = layer._container; + this._container.appendChild(container); + + if (layer.options.interactive) { + layer.addInteractiveTarget(container); + } + }, + + _removePath: function (layer) { + var container = layer._container; + remove(container); + layer.removeInteractiveTarget(container); + delete this._layers[stamp(layer)]; + }, + + _updateStyle: function (layer) { + var stroke = layer._stroke, + fill = layer._fill, + options = layer.options, + container = layer._container; + + container.stroked = !!options.stroke; + container.filled = !!options.fill; + + if (options.stroke) { + if (!stroke) { + stroke = layer._stroke = vmlCreate('stroke'); + } + container.appendChild(stroke); + stroke.weight = options.weight + 'px'; + stroke.color = options.color; + stroke.opacity = options.opacity; + + if (options.dashArray) { + stroke.dashStyle = isArray(options.dashArray) ? + options.dashArray.join(' ') : + options.dashArray.replace(/( *, *)/g, ' '); + } else { + stroke.dashStyle = ''; + } + stroke.endcap = options.lineCap.replace('butt', 'flat'); + stroke.joinstyle = options.lineJoin; + + } else if (stroke) { + container.removeChild(stroke); + layer._stroke = null; + } + + if (options.fill) { + if (!fill) { + fill = layer._fill = vmlCreate('fill'); + } + container.appendChild(fill); + fill.color = options.fillColor || options.color; + fill.opacity = options.fillOpacity; + + } else if (fill) { + container.removeChild(fill); + layer._fill = null; + } + }, + + _updateCircle: function (layer) { + var p = layer._point.round(), + r = Math.round(layer._radius), + r2 = Math.round(layer._radiusY || r); + + this._setPath(layer, layer._empty() ? 'M0 0' : + 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360)); + }, + + _setPath: function (layer, path) { + layer._path.v = path; + }, + + _bringToFront: function (layer) { + toFront(layer._container); + }, + + _bringToBack: function (layer) { + toBack(layer._container); + } +}; + +var create$2 = vml ? vmlCreate : svgCreate; + +/* + * @class SVG + * @inherits Renderer + * @aka L.SVG + * + * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). + * Inherits `Renderer`. + * + * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not + * available in all web browsers, notably Android 2.x and 3.x. + * + * Although SVG is not available on IE7 and IE8, these browsers support + * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language) + * (a now deprecated technology), and the SVG renderer will fall back to VML in + * this case. + * + * @example + * + * Use SVG by default for all paths in the map: + * + * ```js + * var map = L.map('map', { + * renderer: L.svg() + * }); + * ``` + * + * Use a SVG renderer with extra padding for specific vector geometries: + * + * ```js + * var map = L.map('map'); + * var myRenderer = L.svg({ padding: 0.5 }); + * var line = L.polyline( coordinates, { renderer: myRenderer } ); + * var circle = L.circle( center, { renderer: myRenderer } ); + * ``` + */ + +var SVG = Renderer.extend({ + + getEvents: function () { + var events = Renderer.prototype.getEvents.call(this); + events.zoomstart = this._onZoomStart; + return events; + }, + + _initContainer: function () { + this._container = create$2('svg'); + + // makes it possible to click through svg root; we'll reset it back in individual paths + this._container.setAttribute('pointer-events', 'none'); + + this._rootGroup = create$2('g'); + this._container.appendChild(this._rootGroup); + }, + + _destroyContainer: function () { + remove(this._container); + off(this._container); + delete this._container; + delete this._rootGroup; + delete this._svgSize; + }, + + _onZoomStart: function () { + // Drag-then-pinch interactions might mess up the center and zoom. + // In this case, the easiest way to prevent this is re-do the renderer + // bounds and padding when the zooming starts. + this._update(); + }, + + _update: function () { + if (this._map._animatingZoom && this._bounds) { return; } + + Renderer.prototype._update.call(this); + + var b = this._bounds, + size = b.getSize(), + container = this._container; + + // set size of svg-container if changed + if (!this._svgSize || !this._svgSize.equals(size)) { + this._svgSize = size; + container.setAttribute('width', size.x); + container.setAttribute('height', size.y); + } + + // movement: update container viewBox so that we don't have to change coordinates of individual layers + setPosition(container, b.min); + container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); + + this.fire('update'); + }, + + // methods below are called by vector layers implementations + + _initPath: function (layer) { + var path = layer._path = create$2('path'); + + // @namespace Path + // @option className: String = null + // Custom class name set on an element. Only for SVG renderer. + if (layer.options.className) { + addClass(path, layer.options.className); + } + + if (layer.options.interactive) { + addClass(path, 'leaflet-interactive'); + } + + this._updateStyle(layer); + this._layers[stamp(layer)] = layer; + }, + + _addPath: function (layer) { + if (!this._rootGroup) { this._initContainer(); } + this._rootGroup.appendChild(layer._path); + layer.addInteractiveTarget(layer._path); + }, + + _removePath: function (layer) { + remove(layer._path); + layer.removeInteractiveTarget(layer._path); + delete this._layers[stamp(layer)]; + }, + + _updatePath: function (layer) { + layer._project(); + layer._update(); + }, + + _updateStyle: function (layer) { + var path = layer._path, + options = layer.options; + + if (!path) { return; } + + if (options.stroke) { + path.setAttribute('stroke', options.color); + path.setAttribute('stroke-opacity', options.opacity); + path.setAttribute('stroke-width', options.weight); + path.setAttribute('stroke-linecap', options.lineCap); + path.setAttribute('stroke-linejoin', options.lineJoin); + + if (options.dashArray) { + path.setAttribute('stroke-dasharray', options.dashArray); + } else { + path.removeAttribute('stroke-dasharray'); + } + + if (options.dashOffset) { + path.setAttribute('stroke-dashoffset', options.dashOffset); + } else { + path.removeAttribute('stroke-dashoffset'); + } + } else { + path.setAttribute('stroke', 'none'); + } + + if (options.fill) { + path.setAttribute('fill', options.fillColor || options.color); + path.setAttribute('fill-opacity', options.fillOpacity); + path.setAttribute('fill-rule', options.fillRule || 'evenodd'); + } else { + path.setAttribute('fill', 'none'); + } + }, + + _updatePoly: function (layer, closed) { + this._setPath(layer, pointsToPath(layer._parts, closed)); + }, + + _updateCircle: function (layer) { + var p = layer._point, + r = Math.max(Math.round(layer._radius), 1), + r2 = Math.max(Math.round(layer._radiusY), 1) || r, + arc = 'a' + r + ',' + r2 + ' 0 1,0 '; + + // drawing a circle with two half-arcs + var d = layer._empty() ? 'M0 0' : + 'M' + (p.x - r) + ',' + p.y + + arc + (r * 2) + ',0 ' + + arc + (-r * 2) + ',0 '; + + this._setPath(layer, d); + }, + + _setPath: function (layer, path) { + layer._path.setAttribute('d', path); + }, + + // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements + _bringToFront: function (layer) { + toFront(layer._path); + }, + + _bringToBack: function (layer) { + toBack(layer._path); + } +}); + +if (vml) { + SVG.include(vmlMixin); +} + +// @namespace SVG +// @factory L.svg(options?: Renderer options) +// Creates a SVG renderer with the given options. +function svg$1(options) { + return svg || vml ? new SVG(options) : null; +} + +Map.include({ + // @namespace Map; @method getRenderer(layer: Path): Renderer + // Returns the instance of `Renderer` that should be used to render the given + // `Path`. It will ensure that the `renderer` options of the map and paths + // are respected, and that the renderers do exist on the map. + getRenderer: function (layer) { + // @namespace Path; @option renderer: Renderer + // Use this specific instance of `Renderer` for this path. Takes + // precedence over the map's [default renderer](#map-renderer). + var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; + + if (!renderer) { + // @namespace Map; @option preferCanvas: Boolean = false + // Whether `Path`s should be rendered on a `Canvas` renderer. + // By default, all `Path`s are rendered in a `SVG` renderer. + renderer = this._renderer = (this.options.preferCanvas && canvas$1()) || svg$1(); + } + + if (!this.hasLayer(renderer)) { + this.addLayer(renderer); + } + return renderer; + }, + + _getPaneRenderer: function (name) { + if (name === 'overlayPane' || name === undefined) { + return false; + } + + var renderer = this._paneRenderers[name]; + if (renderer === undefined) { + renderer = (SVG && svg$1({pane: name})) || (Canvas && canvas$1({pane: name})); + this._paneRenderers[name] = renderer; + } + return renderer; + } +}); + +/* + * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. + */ + +/* + * @class Rectangle + * @aka L.Rectangle + * @inherits Polygon + * + * A class for drawing rectangle overlays on a map. Extends `Polygon`. + * + * @example + * + * ```js + * // define rectangle geographical bounds + * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]]; + * + * // create an orange rectangle + * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map); + * + * // zoom the map to the rectangle bounds + * map.fitBounds(bounds); + * ``` + * + */ + + +var Rectangle = Polygon.extend({ + initialize: function (latLngBounds, options) { + Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); + }, + + // @method setBounds(latLngBounds: LatLngBounds): this + // Redraws the rectangle with the passed bounds. + setBounds: function (latLngBounds) { + return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); + }, + + _boundsToLatLngs: function (latLngBounds) { + latLngBounds = toLatLngBounds(latLngBounds); + return [ + latLngBounds.getSouthWest(), + latLngBounds.getNorthWest(), + latLngBounds.getNorthEast(), + latLngBounds.getSouthEast() + ]; + } +}); + + +// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options) +function rectangle(latLngBounds, options) { + return new Rectangle(latLngBounds, options); +} + +SVG.create = create$2; +SVG.pointsToPath = pointsToPath; + +GeoJSON.geometryToLayer = geometryToLayer; +GeoJSON.coordsToLatLng = coordsToLatLng; +GeoJSON.coordsToLatLngs = coordsToLatLngs; +GeoJSON.latLngToCoords = latLngToCoords; +GeoJSON.latLngsToCoords = latLngsToCoords; +GeoJSON.getFeature = getFeature; +GeoJSON.asFeature = asFeature; + +/* + * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map + * (zoom to a selected bounding box), enabled by default. + */ + +// @namespace Map +// @section Interaction Options +Map.mergeOptions({ + // @option boxZoom: Boolean = true + // Whether the map can be zoomed to a rectangular area specified by + // dragging the mouse while pressing the shift key. + boxZoom: true +}); + +var BoxZoom = Handler.extend({ + initialize: function (map) { + this._map = map; + this._container = map._container; + this._pane = map._panes.overlayPane; + this._resetStateTimeout = 0; + map.on('unload', this._destroy, this); + }, + + addHooks: function () { + on(this._container, 'mousedown', this._onMouseDown, this); + }, + + removeHooks: function () { + off(this._container, 'mousedown', this._onMouseDown, this); + }, + + moved: function () { + return this._moved; + }, + + _destroy: function () { + remove(this._pane); + delete this._pane; + }, + + _resetState: function () { + this._resetStateTimeout = 0; + this._moved = false; + }, + + _clearDeferredResetState: function () { + if (this._resetStateTimeout !== 0) { + clearTimeout(this._resetStateTimeout); + this._resetStateTimeout = 0; + } + }, + + _onMouseDown: function (e) { + if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } + + // Clear the deferred resetState if it hasn't executed yet, otherwise it + // will interrupt the interaction and orphan a box element in the container. + this._clearDeferredResetState(); + this._resetState(); + + disableTextSelection(); + disableImageDrag(); + + this._startPoint = this._map.mouseEventToContainerPoint(e); + + on(document, { + contextmenu: stop, + mousemove: this._onMouseMove, + mouseup: this._onMouseUp, + keydown: this._onKeyDown + }, this); + }, + + _onMouseMove: function (e) { + if (!this._moved) { + this._moved = true; + + this._box = create$1('div', 'leaflet-zoom-box', this._container); + addClass(this._container, 'leaflet-crosshair'); + + this._map.fire('boxzoomstart'); + } + + this._point = this._map.mouseEventToContainerPoint(e); + + var bounds = new Bounds(this._point, this._startPoint), + size = bounds.getSize(); + + setPosition(this._box, bounds.min); + + this._box.style.width = size.x + 'px'; + this._box.style.height = size.y + 'px'; + }, + + _finish: function () { + if (this._moved) { + remove(this._box); + removeClass(this._container, 'leaflet-crosshair'); + } + + enableTextSelection(); + enableImageDrag(); + + off(document, { + contextmenu: stop, + mousemove: this._onMouseMove, + mouseup: this._onMouseUp, + keydown: this._onKeyDown + }, this); + }, + + _onMouseUp: function (e) { + if ((e.which !== 1) && (e.button !== 1)) { return; } + + this._finish(); + + if (!this._moved) { return; } + // Postpone to next JS tick so internal click event handling + // still see it as "moved". + this._clearDeferredResetState(); + this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0); + + var bounds = new LatLngBounds( + this._map.containerPointToLatLng(this._startPoint), + this._map.containerPointToLatLng(this._point)); + + this._map + .fitBounds(bounds) + .fire('boxzoomend', {boxZoomBounds: bounds}); + }, + + _onKeyDown: function (e) { + if (e.keyCode === 27) { + this._finish(); + } + } +}); + +// @section Handlers +// @property boxZoom: Handler +// Box (shift-drag with mouse) zoom handler. +Map.addInitHook('addHandler', 'boxZoom', BoxZoom); + +/* + * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. + */ + +// @namespace Map +// @section Interaction Options + +Map.mergeOptions({ + // @option doubleClickZoom: Boolean|String = true + // Whether the map can be zoomed in by double clicking on it and + // zoomed out by double clicking while holding shift. If passed + // `'center'`, double-click zoom will zoom to the center of the + // view regardless of where the mouse was. + doubleClickZoom: true +}); + +var DoubleClickZoom = Handler.extend({ + addHooks: function () { + this._map.on('dblclick', this._onDoubleClick, this); + }, + + removeHooks: function () { + this._map.off('dblclick', this._onDoubleClick, this); + }, + + _onDoubleClick: function (e) { + var map = this._map, + oldZoom = map.getZoom(), + delta = map.options.zoomDelta, + zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta; + + if (map.options.doubleClickZoom === 'center') { + map.setZoom(zoom); + } else { + map.setZoomAround(e.containerPoint, zoom); + } + } +}); + +// @section Handlers +// +// Map properties include interaction handlers that allow you to control +// interaction behavior in runtime, enabling or disabling certain features such +// as dragging or touch zoom (see `Handler` methods). For example: +// +// ```js +// map.doubleClickZoom.disable(); +// ``` +// +// @property doubleClickZoom: Handler +// Double click zoom handler. +Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom); + +/* + * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. + */ + +// @namespace Map +// @section Interaction Options +Map.mergeOptions({ + // @option dragging: Boolean = true + // Whether the map be draggable with mouse/touch or not. + dragging: true, + + // @section Panning Inertia Options + // @option inertia: Boolean = * + // If enabled, panning of the map will have an inertia effect where + // the map builds momentum while dragging and continues moving in + // the same direction for some time. Feels especially nice on touch + // devices. Enabled by default unless running on old Android devices. + inertia: !android23, + + // @option inertiaDeceleration: Number = 3000 + // The rate with which the inertial movement slows down, in pixels/second². + inertiaDeceleration: 3400, // px/s^2 + + // @option inertiaMaxSpeed: Number = Infinity + // Max speed of the inertial movement, in pixels/second. + inertiaMaxSpeed: Infinity, // px/s + + // @option easeLinearity: Number = 0.2 + easeLinearity: 0.2, + + // TODO refactor, move to CRS + // @option worldCopyJump: Boolean = false + // With this option enabled, the map tracks when you pan to another "copy" + // of the world and seamlessly jumps to the original one so that all overlays + // like markers and vector layers are still visible. + worldCopyJump: false, + + // @option maxBoundsViscosity: Number = 0.0 + // If `maxBounds` is set, this option will control how solid the bounds + // are when dragging the map around. The default value of `0.0` allows the + // user to drag outside the bounds at normal speed, higher values will + // slow down map dragging outside bounds, and `1.0` makes the bounds fully + // solid, preventing the user from dragging outside the bounds. + maxBoundsViscosity: 0.0 +}); + +var Drag = Handler.extend({ + addHooks: function () { + if (!this._draggable) { + var map = this._map; + + this._draggable = new Draggable(map._mapPane, map._container); + + this._draggable.on({ + dragstart: this._onDragStart, + drag: this._onDrag, + dragend: this._onDragEnd + }, this); + + this._draggable.on('predrag', this._onPreDragLimit, this); + if (map.options.worldCopyJump) { + this._draggable.on('predrag', this._onPreDragWrap, this); + map.on('zoomend', this._onZoomEnd, this); + + map.whenReady(this._onZoomEnd, this); + } + } + addClass(this._map._container, 'leaflet-grab leaflet-touch-drag'); + this._draggable.enable(); + this._positions = []; + this._times = []; + }, + + removeHooks: function () { + removeClass(this._map._container, 'leaflet-grab'); + removeClass(this._map._container, 'leaflet-touch-drag'); + this._draggable.disable(); + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + moving: function () { + return this._draggable && this._draggable._moving; + }, + + _onDragStart: function () { + var map = this._map; + + map._stop(); + if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { + var bounds = toLatLngBounds(this._map.options.maxBounds); + + this._offsetLimit = toBounds( + this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1), + this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1) + .add(this._map.getSize())); + + this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity)); + } else { + this._offsetLimit = null; + } + + map + .fire('movestart') + .fire('dragstart'); + + if (map.options.inertia) { + this._positions = []; + this._times = []; + } + }, + + _onDrag: function (e) { + if (this._map.options.inertia) { + var time = this._lastTime = +new Date(), + pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; + + this._positions.push(pos); + this._times.push(time); + + this._prunePositions(time); + } + + this._map + .fire('move', e) + .fire('drag', e); + }, + + _prunePositions: function (time) { + while (this._positions.length > 1 && time - this._times[0] > 50) { + this._positions.shift(); + this._times.shift(); + } + }, + + _onZoomEnd: function () { + var pxCenter = this._map.getSize().divideBy(2), + pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); + + this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; + this._worldWidth = this._map.getPixelWorldBounds().getSize().x; + }, + + _viscousLimit: function (value, threshold) { + return value - (value - threshold) * this._viscosity; + }, + + _onPreDragLimit: function () { + if (!this._viscosity || !this._offsetLimit) { return; } + + var offset = this._draggable._newPos.subtract(this._draggable._startPos); + + var limit = this._offsetLimit; + if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); } + if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); } + if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); } + if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); } + + this._draggable._newPos = this._draggable._startPos.add(offset); + }, + + _onPreDragWrap: function () { + // TODO refactor to be able to adjust map pane position after zoom + var worldWidth = this._worldWidth, + halfWidth = Math.round(worldWidth / 2), + dx = this._initialWorldOffset, + x = this._draggable._newPos.x, + newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, + newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, + newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; + + this._draggable._absPos = this._draggable._newPos.clone(); + this._draggable._newPos.x = newX; + }, + + _onDragEnd: function (e) { + var map = this._map, + options = map.options, + + noInertia = !options.inertia || this._times.length < 2; + + map.fire('dragend', e); + + if (noInertia) { + map.fire('moveend'); + + } else { + this._prunePositions(+new Date()); + + var direction = this._lastPos.subtract(this._positions[0]), + duration = (this._lastTime - this._times[0]) / 1000, + ease = options.easeLinearity, + + speedVector = direction.multiplyBy(ease / duration), + speed = speedVector.distanceTo([0, 0]), + + limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), + limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), + + decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), + offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); + + if (!offset.x && !offset.y) { + map.fire('moveend'); + + } else { + offset = map._limitOffset(offset, map.options.maxBounds); + + requestAnimFrame(function () { + map.panBy(offset, { + duration: decelerationDuration, + easeLinearity: ease, + noMoveStart: true, + animate: true + }); + }); + } + } + } +}); + +// @section Handlers +// @property dragging: Handler +// Map dragging handler (by both mouse and touch). +Map.addInitHook('addHandler', 'dragging', Drag); + +/* + * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. + */ + +// @namespace Map +// @section Keyboard Navigation Options +Map.mergeOptions({ + // @option keyboard: Boolean = true + // Makes the map focusable and allows users to navigate the map with keyboard + // arrows and `+`/`-` keys. + keyboard: true, + + // @option keyboardPanDelta: Number = 80 + // Amount of pixels to pan when pressing an arrow key. + keyboardPanDelta: 80 +}); + +var Keyboard = Handler.extend({ + + keyCodes: { + left: [37], + right: [39], + down: [40], + up: [38], + zoomIn: [187, 107, 61, 171], + zoomOut: [189, 109, 54, 173] + }, + + initialize: function (map) { + this._map = map; + + this._setPanDelta(map.options.keyboardPanDelta); + this._setZoomDelta(map.options.zoomDelta); + }, + + addHooks: function () { + var container = this._map._container; + + // make the container focusable by tabbing + if (container.tabIndex <= 0) { + container.tabIndex = '0'; + } + + on(container, { + focus: this._onFocus, + blur: this._onBlur, + mousedown: this._onMouseDown + }, this); + + this._map.on({ + focus: this._addHooks, + blur: this._removeHooks + }, this); + }, + + removeHooks: function () { + this._removeHooks(); + + off(this._map._container, { + focus: this._onFocus, + blur: this._onBlur, + mousedown: this._onMouseDown + }, this); + + this._map.off({ + focus: this._addHooks, + blur: this._removeHooks + }, this); + }, + + _onMouseDown: function () { + if (this._focused) { return; } + + var body = document.body, + docEl = document.documentElement, + top = body.scrollTop || docEl.scrollTop, + left = body.scrollLeft || docEl.scrollLeft; + + this._map._container.focus(); + + window.scrollTo(left, top); + }, + + _onFocus: function () { + this._focused = true; + this._map.fire('focus'); + }, + + _onBlur: function () { + this._focused = false; + this._map.fire('blur'); + }, + + _setPanDelta: function (panDelta) { + var keys = this._panKeys = {}, + codes = this.keyCodes, + i, len; + + for (i = 0, len = codes.left.length; i < len; i++) { + keys[codes.left[i]] = [-1 * panDelta, 0]; + } + for (i = 0, len = codes.right.length; i < len; i++) { + keys[codes.right[i]] = [panDelta, 0]; + } + for (i = 0, len = codes.down.length; i < len; i++) { + keys[codes.down[i]] = [0, panDelta]; + } + for (i = 0, len = codes.up.length; i < len; i++) { + keys[codes.up[i]] = [0, -1 * panDelta]; + } + }, + + _setZoomDelta: function (zoomDelta) { + var keys = this._zoomKeys = {}, + codes = this.keyCodes, + i, len; + + for (i = 0, len = codes.zoomIn.length; i < len; i++) { + keys[codes.zoomIn[i]] = zoomDelta; + } + for (i = 0, len = codes.zoomOut.length; i < len; i++) { + keys[codes.zoomOut[i]] = -zoomDelta; + } + }, + + _addHooks: function () { + on(document, 'keydown', this._onKeyDown, this); + }, + + _removeHooks: function () { + off(document, 'keydown', this._onKeyDown, this); + }, + + _onKeyDown: function (e) { + if (e.altKey || e.ctrlKey || e.metaKey) { return; } + + var key = e.keyCode, + map = this._map, + offset; + + if (key in this._panKeys) { + + if (map._panAnim && map._panAnim._inProgress) { return; } + + offset = this._panKeys[key]; + if (e.shiftKey) { + offset = toPoint(offset).multiplyBy(3); + } + + map.panBy(offset); + + if (map.options.maxBounds) { + map.panInsideBounds(map.options.maxBounds); + } + + } else if (key in this._zoomKeys) { + map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]); + + } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) { + map.closePopup(); + + } else { + return; + } + + stop(e); + } +}); + +// @section Handlers +// @section Handlers +// @property keyboard: Handler +// Keyboard navigation handler. +Map.addInitHook('addHandler', 'keyboard', Keyboard); + +/* + * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. + */ + +// @namespace Map +// @section Interaction Options +Map.mergeOptions({ + // @section Mousewheel options + // @option scrollWheelZoom: Boolean|String = true + // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`, + // it will zoom to the center of the view regardless of where the mouse was. + scrollWheelZoom: true, + + // @option wheelDebounceTime: Number = 40 + // Limits the rate at which a wheel can fire (in milliseconds). By default + // user can't zoom via wheel more often than once per 40 ms. + wheelDebounceTime: 40, + + // @option wheelPxPerZoomLevel: Number = 60 + // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta)) + // mean a change of one full zoom level. Smaller values will make wheel-zooming + // faster (and vice versa). + wheelPxPerZoomLevel: 60 +}); + +var ScrollWheelZoom = Handler.extend({ + addHooks: function () { + on(this._map._container, 'mousewheel', this._onWheelScroll, this); + + this._delta = 0; + }, + + removeHooks: function () { + off(this._map._container, 'mousewheel', this._onWheelScroll, this); + }, + + _onWheelScroll: function (e) { + var delta = getWheelDelta(e); + + var debounce = this._map.options.wheelDebounceTime; + + this._delta += delta; + this._lastMousePos = this._map.mouseEventToContainerPoint(e); + + if (!this._startTime) { + this._startTime = +new Date(); + } + + var left = Math.max(debounce - (+new Date() - this._startTime), 0); + + clearTimeout(this._timer); + this._timer = setTimeout(bind(this._performZoom, this), left); + + stop(e); + }, + + _performZoom: function () { + var map = this._map, + zoom = map.getZoom(), + snap = this._map.options.zoomSnap || 0; + + map._stop(); // stop panning and fly animations if any + + // map the delta with a sigmoid function to -4..4 range leaning on -1..1 + var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4), + d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2, + d4 = snap ? Math.ceil(d3 / snap) * snap : d3, + delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom; + + this._delta = 0; + this._startTime = null; + + if (!delta) { return; } + + if (map.options.scrollWheelZoom === 'center') { + map.setZoom(zoom + delta); + } else { + map.setZoomAround(this._lastMousePos, zoom + delta); + } + } +}); + +// @section Handlers +// @property scrollWheelZoom: Handler +// Scroll wheel zoom handler. +Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom); + +/* + * L.Map.Tap is used to enable mobile hacks like quick taps and long hold. + */ + +// @namespace Map +// @section Interaction Options +Map.mergeOptions({ + // @section Touch interaction options + // @option tap: Boolean = true + // Enables mobile hacks for supporting instant taps (fixing 200ms click + // delay on iOS/Android) and touch holds (fired as `contextmenu` events). + tap: true, + + // @option tapTolerance: Number = 15 + // The max number of pixels a user can shift his finger during touch + // for it to be considered a valid tap. + tapTolerance: 15 +}); + +var Tap = Handler.extend({ + addHooks: function () { + on(this._map._container, 'touchstart', this._onDown, this); + }, + + removeHooks: function () { + off(this._map._container, 'touchstart', this._onDown, this); + }, + + _onDown: function (e) { + if (!e.touches) { return; } + + preventDefault(e); + + this._fireClick = true; + + // don't simulate click or track longpress if more than 1 touch + if (e.touches.length > 1) { + this._fireClick = false; + clearTimeout(this._holdTimeout); + return; + } + + var first = e.touches[0], + el = first.target; + + this._startPos = this._newPos = new Point(first.clientX, first.clientY); + + // if touching a link, highlight it + if (el.tagName && el.tagName.toLowerCase() === 'a') { + addClass(el, 'leaflet-active'); + } + + // simulate long hold but setting a timeout + this._holdTimeout = setTimeout(bind(function () { + if (this._isTapValid()) { + this._fireClick = false; + this._onUp(); + this._simulateEvent('contextmenu', first); + } + }, this), 1000); + + this._simulateEvent('mousedown', first); + + on(document, { + touchmove: this._onMove, + touchend: this._onUp + }, this); + }, + + _onUp: function (e) { + clearTimeout(this._holdTimeout); + + off(document, { + touchmove: this._onMove, + touchend: this._onUp + }, this); + + if (this._fireClick && e && e.changedTouches) { + + var first = e.changedTouches[0], + el = first.target; + + if (el && el.tagName && el.tagName.toLowerCase() === 'a') { + removeClass(el, 'leaflet-active'); + } + + this._simulateEvent('mouseup', first); + + // simulate click if the touch didn't move too much + if (this._isTapValid()) { + this._simulateEvent('click', first); + } + } + }, + + _isTapValid: function () { + return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; + }, + + _onMove: function (e) { + var first = e.touches[0]; + this._newPos = new Point(first.clientX, first.clientY); + this._simulateEvent('mousemove', first); + }, + + _simulateEvent: function (type, e) { + var simulatedEvent = document.createEvent('MouseEvents'); + + simulatedEvent._simulated = true; + e.target._simulatedClick = true; + + simulatedEvent.initMouseEvent( + type, true, true, window, 1, + e.screenX, e.screenY, + e.clientX, e.clientY, + false, false, false, false, 0, null); + + e.target.dispatchEvent(simulatedEvent); + } +}); + +// @section Handlers +// @property tap: Handler +// Mobile touch hacks (quick tap and touch hold) handler. +if (touch && !pointer) { + Map.addInitHook('addHandler', 'tap', Tap); +} + +/* + * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers. + */ + +// @namespace Map +// @section Interaction Options +Map.mergeOptions({ + // @section Touch interaction options + // @option touchZoom: Boolean|String = * + // Whether the map can be zoomed by touch-dragging with two fingers. If + // passed `'center'`, it will zoom to the center of the view regardless of + // where the touch events (fingers) were. Enabled for touch-capable web + // browsers except for old Androids. + touchZoom: touch && !android23, + + // @option bounceAtZoomLimits: Boolean = true + // Set it to false if you don't want the map to zoom beyond min/max zoom + // and then bounce back when pinch-zooming. + bounceAtZoomLimits: true +}); + +var TouchZoom = Handler.extend({ + addHooks: function () { + addClass(this._map._container, 'leaflet-touch-zoom'); + on(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + removeHooks: function () { + removeClass(this._map._container, 'leaflet-touch-zoom'); + off(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + _onTouchStart: function (e) { + var map = this._map; + if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } + + var p1 = map.mouseEventToContainerPoint(e.touches[0]), + p2 = map.mouseEventToContainerPoint(e.touches[1]); + + this._centerPoint = map.getSize()._divideBy(2); + this._startLatLng = map.containerPointToLatLng(this._centerPoint); + if (map.options.touchZoom !== 'center') { + this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2)); + } + + this._startDist = p1.distanceTo(p2); + this._startZoom = map.getZoom(); + + this._moved = false; + this._zooming = true; + + map._stop(); + + on(document, 'touchmove', this._onTouchMove, this); + on(document, 'touchend', this._onTouchEnd, this); + + preventDefault(e); + }, + + _onTouchMove: function (e) { + if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; } + + var map = this._map, + p1 = map.mouseEventToContainerPoint(e.touches[0]), + p2 = map.mouseEventToContainerPoint(e.touches[1]), + scale = p1.distanceTo(p2) / this._startDist; + + this._zoom = map.getScaleZoom(scale, this._startZoom); + + if (!map.options.bounceAtZoomLimits && ( + (this._zoom < map.getMinZoom() && scale < 1) || + (this._zoom > map.getMaxZoom() && scale > 1))) { + this._zoom = map._limitZoom(this._zoom); + } + + if (map.options.touchZoom === 'center') { + this._center = this._startLatLng; + if (scale === 1) { return; } + } else { + // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng + var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); + if (scale === 1 && delta.x === 0 && delta.y === 0) { return; } + this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom); + } + + if (!this._moved) { + map._moveStart(true, false); + this._moved = true; + } + + cancelAnimFrame(this._animRequest); + + var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}); + this._animRequest = requestAnimFrame(moveFn, this, true); + + preventDefault(e); + }, + + _onTouchEnd: function () { + if (!this._moved || !this._zooming) { + this._zooming = false; + return; + } + + this._zooming = false; + cancelAnimFrame(this._animRequest); + + off(document, 'touchmove', this._onTouchMove); + off(document, 'touchend', this._onTouchEnd); + + // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate. + if (this._map.options.zoomAnimation) { + this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap); + } else { + this._map._resetView(this._center, this._map._limitZoom(this._zoom)); + } + } +}); + +// @section Handlers +// @property touchZoom: Handler +// Touch zoom handler. +Map.addInitHook('addHandler', 'touchZoom', TouchZoom); + +Map.BoxZoom = BoxZoom; +Map.DoubleClickZoom = DoubleClickZoom; +Map.Drag = Drag; +Map.Keyboard = Keyboard; +Map.ScrollWheelZoom = ScrollWheelZoom; +Map.Tap = Tap; +Map.TouchZoom = TouchZoom; + +// misc + +var oldL = window.L; +function noConflict() { + window.L = oldL; + return this; +} + +// Always export us to window global (see #2364) +window.L = exports; + +Object.freeze = freeze; + +exports.version = version; +exports.noConflict = noConflict; +exports.Control = Control; +exports.control = control; +exports.Browser = Browser; +exports.Evented = Evented; +exports.Mixin = Mixin; +exports.Util = Util; +exports.Class = Class; +exports.Handler = Handler; +exports.extend = extend; +exports.bind = bind; +exports.stamp = stamp; +exports.setOptions = setOptions; +exports.DomEvent = DomEvent; +exports.DomUtil = DomUtil; +exports.PosAnimation = PosAnimation; +exports.Draggable = Draggable; +exports.LineUtil = LineUtil; +exports.PolyUtil = PolyUtil; +exports.Point = Point; +exports.point = toPoint; +exports.Bounds = Bounds; +exports.bounds = toBounds; +exports.Transformation = Transformation; +exports.transformation = toTransformation; +exports.Projection = index; +exports.LatLng = LatLng; +exports.latLng = toLatLng; +exports.LatLngBounds = LatLngBounds; +exports.latLngBounds = toLatLngBounds; +exports.CRS = CRS; +exports.GeoJSON = GeoJSON; +exports.geoJSON = geoJSON; +exports.geoJson = geoJson; +exports.Layer = Layer; +exports.LayerGroup = LayerGroup; +exports.layerGroup = layerGroup; +exports.FeatureGroup = FeatureGroup; +exports.featureGroup = featureGroup; +exports.ImageOverlay = ImageOverlay; +exports.imageOverlay = imageOverlay; +exports.VideoOverlay = VideoOverlay; +exports.videoOverlay = videoOverlay; +exports.DivOverlay = DivOverlay; +exports.Popup = Popup; +exports.popup = popup; +exports.Tooltip = Tooltip; +exports.tooltip = tooltip; +exports.Icon = Icon; +exports.icon = icon; +exports.DivIcon = DivIcon; +exports.divIcon = divIcon; +exports.Marker = Marker; +exports.marker = marker; +exports.TileLayer = TileLayer; +exports.tileLayer = tileLayer; +exports.GridLayer = GridLayer; +exports.gridLayer = gridLayer; +exports.SVG = SVG; +exports.svg = svg$1; +exports.Renderer = Renderer; +exports.Canvas = Canvas; +exports.canvas = canvas$1; +exports.Path = Path; +exports.CircleMarker = CircleMarker; +exports.circleMarker = circleMarker; +exports.Circle = Circle; +exports.circle = circle; +exports.Polyline = Polyline; +exports.polyline = polyline; +exports.Polygon = Polygon; +exports.polygon = polygon; +exports.Rectangle = Rectangle; +exports.rectangle = rectangle; +exports.Map = Map; +exports.map = createMap; + +}))); +//# sourceMappingURL=leaflet-src.js.map diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet.draw-src.js b/themes/bootstrap3/js/vendor/leaflet/leaflet.draw-src.js new file mode 100644 index 0000000000000000000000000000000000000000..3b56e3344446fd403d4134861c3b908e11eb3335 --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet.draw-src.js @@ -0,0 +1,4782 @@ +/* + Leaflet.draw 0.4.13, a plugin that adds drawing and editing tools to Leaflet powered maps. + (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet + + Copyright 2012-2017 Jacob Toye and Leaflet + + 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. + + https://github.com/Leaflet/Leaflet.draw + http://leafletjs.com + */ +(function (window, document, undefined) {/** + * Leaflet.draw assumes that you have already included the Leaflet library. + */ +L.drawVersion = "0.4.13"; +/** + * @class L.Draw + * @aka Draw + * + * + * To add the draw toolbar set the option drawControl: true in the map options. + * + * @example + * ```js + * var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13); + * + * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + * attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' + * }).addTo(map); + * ``` + * + * ### Adding the edit toolbar + * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map. + * + * ```js + * var map = L.map('map').setView([51.505, -0.09], 13); + * + * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + * attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' + * }).addTo(map); + * + * // FeatureGroup is to store editable layers + * var drawnItems = new L.FeatureGroup(); + * map.addLayer(drawnItems); + * + * var drawControl = new L.Control.Draw({ + * edit: { + * featureGroup: drawnItems + * } + * }); + * map.addControl(drawControl); + * ``` + * + * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that + * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon. + * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon, + * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a + * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons). + */ +L.Draw = {}; + +/** + * @class L.drawLocal + * @aka L.drawLocal + * + * The core toolbar class of the API — it is used to create the toolbar ui + * + * @example + * ```js + * var modifiedDraw = L.drawLocal.extend({ + * draw: { + * toolbar: { + * buttons: { + * polygon: 'Draw an awesome polygon' + * } + * } + * } + * }); + * ``` + * + * The default state for the control is the draw toolbar just below the zoom control. + * This will allow map users to draw vectors and markers. + * **Please note the edit toolbar is not enabled by default.** + */ +L.drawLocal = { + // format: { + // numeric: { + // delimiters: { + // thousands: ',', + // decimal: '.' + // } + // } + // }, + draw: { + toolbar: { + // #TODO: this should be reorganized where actions are nested in actions + // ex: actions.undo or actions.cancel + actions: { + title: 'Cancel drawing', + text: 'Cancel' + }, + finish: { + title: 'Finish drawing', + text: 'Finish' + }, + undo: { + title: 'Delete last point drawn', + text: 'Delete last point' + }, + buttons: { + polyline: 'Draw a polyline', + polygon: 'Draw a polygon', + rectangle: 'Draw a rectangle', + circle: 'Draw a circle', + marker: 'Draw a marker', + circlemarker: 'Draw a circlemarker' + } + }, + handlers: { + circle: { + tooltip: { + start: 'Click and drag to draw circle.' + }, + radius: 'Radius' + }, + circlemarker: { + tooltip: { + start: 'Click map to place circle marker.' + } + }, + marker: { + tooltip: { + start: 'Click map to place marker.' + } + }, + polygon: { + tooltip: { + start: 'Click to start drawing shape.', + cont: 'Click to continue drawing shape.', + end: 'Click first point to close this shape.' + } + }, + polyline: { + error: '<strong>Error:</strong> shape edges cannot cross!', + tooltip: { + start: 'Click to start drawing line.', + cont: 'Click to continue drawing line.', + end: 'Click last point to finish line.' + } + }, + rectangle: { + tooltip: { + start: 'Click and drag to draw rectangle.' + } + }, + simpleshape: { + tooltip: { + end: 'Release mouse to finish drawing.' + } + } + } + }, + edit: { + toolbar: { + actions: { + save: { + title: 'Save changes', + text: 'Save' + }, + cancel: { + title: 'Cancel editing, discards all changes', + text: 'Cancel' + }, + clearAll:{ + title: 'Clear all layers', + text: 'Clear All' + } + }, + buttons: { + edit: 'Edit layers', + editDisabled: 'No layers to edit', + remove: 'Delete layers', + removeDisabled: 'No layers to delete' + } + }, + handlers: { + edit: { + tooltip: { + text: 'Drag handles or markers to edit features.', + subtext: 'Click cancel to undo changes.' + } + }, + remove: { + tooltip: { + text: 'Click on a feature to remove.' + } + } + } + } +}; + + + +/** + * ### Events + * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different + * actions users can initiate. The following events will be triggered on the map: + * + * @class L.Draw.Event + * @aka Draw.Event + * + * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct. + * + * @example + * ```js + * map.on(L.Draw.Event.CREATED; function (e) { + * var type = e.layerType, + * layer = e.layer; + * + * if (type === 'marker') { + * // Do marker specific actions + * } + * + * // Do whatever else you need to. (save to db; add to map etc) + * map.addLayer(layer); + *}); + * ``` + */ +L.Draw.Event = {}; +/** + * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String + * + * Layer that was just created. + * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker` + * Triggered when a new vector or marker has been created. + * + */ +L.Draw.Event.CREATED = 'draw:created'; + +/** + * @event draw:edited: LayerGroup + * + * List of all layers just edited on the map. + * + * + * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved. + * + * @example + * ```js + * map.on('draw:edited', function (e) { + * var layers = e.layers; + * layers.eachLayer(function (layer) { + * //do whatever you want; most likely save back to db + * }); + * }); + * ``` + */ +L.Draw.Event.EDITED = 'draw:edited'; + +/** + * @event draw:deleted: LayerGroup + * + * List of all layers just removed from the map. + * + * Triggered when layers have been removed (and saved) from the FeatureGroup. + */ +L.Draw.Event.DELETED = 'draw:deleted'; + +/** + * @event draw:drawstart: String + * + * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker` + * + * Triggered when the user has chosen to draw a particular vector or marker. + */ +L.Draw.Event.DRAWSTART = 'draw:drawstart'; + +/** + * @event draw:drawstop: String + * + * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker` + * + * Triggered when the user has finished a particular vector or marker. + */ + +L.Draw.Event.DRAWSTOP = 'draw:drawstop'; + +/** + * @event draw:drawvertex: LayerGroup + * + * List of all layers just being added from the map. + * + * Triggered when a vertex is created on a polyline or polygon. + */ +L.Draw.Event.DRAWVERTEX = 'draw:drawvertex'; + +/** + * @event draw:editstart: String + * + * The type of edit this is. One of: `edit` + * + * Triggered when the user starts edit mode by clicking the edit tool button. + */ + +L.Draw.Event.EDITSTART = 'draw:editstart'; + +/** + * @event draw:editmove: ILayer + * + * Layer that was just moved. + * + * Triggered as the user moves a rectangle; circle or marker. + */ +L.Draw.Event.EDITMOVE = 'draw:editmove'; + +/** + * @event draw:editresize: ILayer + * + * Layer that was just moved. + * + * Triggered as the user resizes a rectangle or circle. + */ +L.Draw.Event.EDITRESIZE = 'draw:editresize'; + +/** + * @event draw:editvertex: LayerGroup + * + * List of all layers just being edited from the map. + * + * Triggered when a vertex is edited on a polyline or polygon. + */ +L.Draw.Event.EDITVERTEX = 'draw:editvertex'; + +/** + * @event draw:editstop: String + * + * The type of edit this is. One of: `edit` + * + * Triggered when the user has finshed editing (edit mode) and saves edits. + */ +L.Draw.Event.EDITSTOP = 'draw:editstop'; + +/** + * @event draw:deletestart: String + * + * The type of edit this is. One of: `remove` + * + * Triggered when the user starts remove mode by clicking the remove tool button. + */ +L.Draw.Event.DELETESTART = 'draw:deletestart'; + +/** + * @event draw:deletestop: String + * + * The type of edit this is. One of: `remove` + * + * Triggered when the user has finished removing shapes (remove mode) and saves. + */ +L.Draw.Event.DELETESTOP = 'draw:deletestop'; + +/** + * @event draw:toolbaropened: String + * + * Triggered when a toolbar is opened. + */ +L.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened'; + +/** + * @event draw:toolbarclosed: String + * + * Triggered when a toolbar is closed. + */ +L.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed'; + + +L.Draw = L.Draw || {}; + +/** + * @class L.Draw.Feature + * @aka Draw.Feature + */ +L.Draw.Feature = L.Handler.extend({ + + // @method initialize(): void + initialize: function (map, options) { + this._map = map; + this._container = map._container; + this._overlayPane = map._panes.overlayPane; + this._popupPane = map._panes.popupPane; + + // Merge default shapeOptions options with custom shapeOptions + if (options && options.shapeOptions) { + options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions); + } + L.setOptions(this, options); + + var version = L.version.split("."); + //If Version is >= 1.2.0 + if(parseInt(version[0],10) === 1 && parseInt(version[1],10) >= 2 ) { + L.Draw.Feature.include(L.Evented.prototype); + } else { + L.Draw.Feature.include(L.Mixin.Events); + } + }, + + // @method enable(): void + // Enables this handler + enable: function () { + if (this._enabled) { + return; + } + + L.Handler.prototype.enable.call(this); + + this.fire('enabled', { handler: this.type }); + + this._map.fire(L.Draw.Event.DRAWSTART, { layerType: this.type }); + }, + + // @method disable(): void + disable: function () { + if (!this._enabled) { + return; + } + + L.Handler.prototype.disable.call(this); + + this._map.fire(L.Draw.Event.DRAWSTOP, { layerType: this.type }); + + this.fire('disabled', { handler: this.type }); + }, + + // @method addHooks(): void + // Add's event listeners to this handler + addHooks: function () { + var map = this._map; + + if (map) { + L.DomUtil.disableTextSelection(); + + map.getContainer().focus(); + + this._tooltip = new L.Draw.Tooltip(this._map); + + L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this); + } + }, + + // @method removeHooks(): void + // Removes event listeners from this handler + removeHooks: function () { + if (this._map) { + L.DomUtil.enableTextSelection(); + + this._tooltip.dispose(); + this._tooltip = null; + + L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this); + } + }, + + // @method setOptions(object): void + // Sets new options to this handler + setOptions: function (options) { + L.setOptions(this, options); + }, + + _fireCreatedEvent: function (layer) { + this._map.fire(L.Draw.Event.CREATED, { layer: layer, layerType: this.type }); + }, + + // Cancel drawing when the escape key is pressed + _cancelDrawing: function (e) { + if (e.keyCode === 27) { + this._map.fire('draw:canceled', { layerType: this.type }); + this.disable(); + } + } +}); + + + +/** + * @class L.Draw.Polyline + * @aka Draw.Polyline + * @inherits L.Draw.Feature + */ +L.Draw.Polyline = L.Draw.Feature.extend({ + statics: { + TYPE: 'polyline' + }, + + Poly: L.Polyline, + + options: { + allowIntersection: true, + repeatMode: false, + drawError: { + color: '#b00b00', + timeout: 2500 + }, + icon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: 'leaflet-div-icon leaflet-editing-icon' + }), + touchIcon: new L.DivIcon({ + iconSize: new L.Point(20, 20), + className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon' + }), + guidelineDistance: 20, + maxGuideLineLength: 4000, + shapeOptions: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: false, + clickable: true + }, + metric: true, // Whether to use the metric measurement system or imperial + feet: true, // When not metric, to use feet instead of yards for display. + nautic: false, // When not metric, not feet use nautic mile for display + showLength: true, // Whether to display distance in the tooltip + zIndexOffset: 2000, // This should be > than the highest z-index any map layers + factor: 1, // To change distance calculation + maxPoints: 0 // Once this number of points are placed, finish shape + }, + + // @method initialize(): void + initialize: function (map, options) { + // if touch, switch to touch icon + if (L.Browser.touch) { + this.options.icon = this.options.touchIcon; + } + + // Need to set this here to ensure the correct message is used. + this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error; + + // Merge default drawError options with custom options + if (options && options.drawError) { + options.drawError = L.Util.extend({}, this.options.drawError, options.drawError); + } + + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.Polyline.TYPE; + + L.Draw.Feature.prototype.initialize.call(this, map, options); + }, + + // @method addHooks(): void + // Add listener hooks to this handler + addHooks: function () { + L.Draw.Feature.prototype.addHooks.call(this); + if (this._map) { + this._markers = []; + + this._markerGroup = new L.LayerGroup(); + this._map.addLayer(this._markerGroup); + + this._poly = new L.Polyline([], this.options.shapeOptions); + + this._tooltip.updateContent(this._getTooltipText()); + + // Make a transparent marker that will used to catch click events. These click + // events will create the vertices. We need to do this so we can ensure that + // we can create vertices over other map layers (markers, vector layers). We + // also do not want to trigger any click handlers of objects we are clicking on + // while drawing. + if (!this._mouseMarker) { + this._mouseMarker = L.marker(this._map.getCenter(), { + icon: L.divIcon({ + className: 'leaflet-mouse-marker', + iconAnchor: [20, 20], + iconSize: [40, 40] + }), + opacity: 0, + zIndexOffset: this.options.zIndexOffset + }); + } + + this._mouseMarker + .on('mouseout', this._onMouseOut, this) + .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter + .on('mousedown', this._onMouseDown, this) + .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility + .addTo(this._map); + + this._map + .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility + .on('mousemove', this._onMouseMove, this) + .on('zoomlevelschange', this._onZoomEnd, this) + .on('touchstart', this._onTouch, this) + .on('zoomend', this._onZoomEnd, this); + + } + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler. + removeHooks: function () { + L.Draw.Feature.prototype.removeHooks.call(this); + + this._clearHideErrorTimeout(); + + this._cleanUpShape(); + + // remove markers from map + this._map.removeLayer(this._markerGroup); + delete this._markerGroup; + delete this._markers; + + this._map.removeLayer(this._poly); + delete this._poly; + + this._mouseMarker + .off('mousedown', this._onMouseDown, this) + .off('mouseout', this._onMouseOut, this) + .off('mouseup', this._onMouseUp, this) + .off('mousemove', this._onMouseMove, this); + this._map.removeLayer(this._mouseMarker); + delete this._mouseMarker; + + // clean up DOM + this._clearGuides(); + + this._map + .off('mouseup', this._onMouseUp, this) + .off('mousemove', this._onMouseMove, this) + .off('zoomlevelschange', this._onZoomEnd, this) + .off('zoomend', this._onZoomEnd, this) + .off('touchstart', this._onTouch, this) + .off('click', this._onTouch, this); + }, + + // @method deleteLastVertex(): void + // Remove the last vertex from the polyline, removes polyline from map if only one point exists. + deleteLastVertex: function () { + if (this._markers.length <= 1) { + return; + } + + var lastMarker = this._markers.pop(), + poly = this._poly, + // Replaces .spliceLatLngs() + latlngs = poly.getLatLngs(), + latlng = latlngs.splice(-1, 1)[0]; + this._poly.setLatLngs(latlngs); + + this._markerGroup.removeLayer(lastMarker); + + if (poly.getLatLngs().length < 2) { + this._map.removeLayer(poly); + } + + this._vertexChanged(latlng, false); + }, + + // @method addVertex(): void + // Add a vertex to the end of the polyline + addVertex: function (latlng) { + var markersLength = this._markers.length; + // markersLength must be greater than or equal to 2 before intersections can occur + if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) { + this._showErrorTooltip(); + return; + } + else if (this._errorShown) { + this._hideErrorTooltip(); + } + + this._markers.push(this._createMarker(latlng)); + + this._poly.addLatLng(latlng); + + if (this._poly.getLatLngs().length === 2) { + this._map.addLayer(this._poly); + } + + this._vertexChanged(latlng, true); + }, + + // @method completeShape(): void + // Closes the polyline between the first and last points + completeShape: function () { + if (this._markers.length <= 1) { + return; + } + + this._fireCreatedEvent(); + this.disable(); + + if (this.options.repeatMode) { + this.enable(); + } + }, + + _finishShape: function () { + var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs(); + var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]); + + if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) { + this._showErrorTooltip(); + return; + } + + this._fireCreatedEvent(); + this.disable(); + if (this.options.repeatMode) { + this.enable(); + } + }, + + // Called to verify the shape is valid when the user tries to finish it + // Return false if the shape is not valid + _shapeIsValid: function () { + return true; + }, + + _onZoomEnd: function () { + if (this._markers !== null) { + this._updateGuide(); + } + }, + + _onMouseMove: function (e) { + var newPos = this._map.mouseEventToLayerPoint(e.originalEvent); + var latlng = this._map.layerPointToLatLng(newPos); + + // Save latlng + // should this be moved to _updateGuide() ? + this._currentLatLng = latlng; + + this._updateTooltip(latlng); + + // Update the guide line + this._updateGuide(newPos); + + // Update the mouse marker position + this._mouseMarker.setLatLng(latlng); + + L.DomEvent.preventDefault(e.originalEvent); + }, + + _vertexChanged: function (latlng, added) { + this._map.fire(L.Draw.Event.DRAWVERTEX, { layers: this._markerGroup }); + this._updateFinishHandler(); + + this._updateRunningMeasure(latlng, added); + + this._clearGuides(); + + this._updateTooltip(); + }, + + _onMouseDown: function (e) { + if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) { + this._onMouseMove(e); + this._clickHandled = true; + this._disableNewMarkers(); + var originalEvent = e.originalEvent; + var clientX = originalEvent.clientX; + var clientY = originalEvent.clientY; + this._startPoint.call(this, clientX, clientY); + } + }, + + _startPoint: function (clientX, clientY) { + this._mouseDownOrigin = L.point(clientX, clientY); + }, + + _onMouseUp: function (e) { + var originalEvent = e.originalEvent; + var clientX = originalEvent.clientX; + var clientY = originalEvent.clientY; + this._endPoint.call(this, clientX, clientY, e); + this._clickHandled = null; + }, + + _endPoint: function (clientX, clientY, e) { + if (this._mouseDownOrigin) { + var dragCheckDistance = L.point(clientX, clientY) + .distanceTo(this._mouseDownOrigin); + var lastPtDistance = this._calculateFinishDistance(e.latlng); + if(this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length+1) { + this.addVertex(e.latlng); + this._finishShape(); + } else if (lastPtDistance < 10 && L.Browser.touch) { + this._finishShape(); + } else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) { + this.addVertex(e.latlng); + } + this._enableNewMarkers(); // after a short pause, enable new markers + } + this._mouseDownOrigin = null; + }, + + // ontouch prevented by clickHandled flag because some browsers fire both click/touch events, + // causing unwanted behavior + _onTouch: function (e) { + var originalEvent = e.originalEvent; + var clientX; + var clientY; + if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) { + clientX = originalEvent.touches[0].clientX; + clientY = originalEvent.touches[0].clientY; + this._disableNewMarkers(); + this._touchHandled = true; + this._startPoint.call(this, clientX, clientY); + this._endPoint.call(this, clientX, clientY, e); + this._touchHandled = null; + } + this._clickHandled = null; + }, + + _onMouseOut: function () { + if (this._tooltip) { + this._tooltip._onMouseOut.call(this._tooltip); + } + }, + + // calculate if we are currently within close enough distance + // of the closing point (first point for shapes, last point for lines) + // this is semi-ugly code but the only reliable way i found to get the job done + // note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work + _calculateFinishDistance: function (potentialLatLng) { + var lastPtDistance + if (this._markers.length > 0) { + var finishMarker; + if (this.type === L.Draw.Polyline.TYPE) { + finishMarker = this._markers[this._markers.length - 1]; + } else if (this.type === L.Draw.Polygon.TYPE) { + finishMarker = this._markers[0]; + } else { + return Infinity; + } + var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()), + potentialMarker = new L.Marker(potentialLatLng, { + icon: this.options.icon, + zIndexOffset: this.options.zIndexOffset * 2 + }); + var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng()); + lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint); + } else { + lastPtDistance = Infinity; + } + return lastPtDistance; + }, + + _updateFinishHandler: function () { + var markerCount = this._markers.length; + // The last marker should have a click handler to close the polyline + if (markerCount > 1) { + this._markers[markerCount - 1].on('click', this._finishShape, this); + } + + // Remove the old marker click handler (as only the last point should close the polyline) + if (markerCount > 2) { + this._markers[markerCount - 2].off('click', this._finishShape, this); + } + }, + + _createMarker: function (latlng) { + var marker = new L.Marker(latlng, { + icon: this.options.icon, + zIndexOffset: this.options.zIndexOffset * 2 + }); + + this._markerGroup.addLayer(marker); + + return marker; + }, + + _updateGuide: function (newPos) { + var markerCount = this._markers ? this._markers.length : 0; + + if (markerCount > 0) { + newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng); + + // draw the guide line + this._clearGuides(); + this._drawGuide( + this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()), + newPos + ); + } + }, + + _updateTooltip: function (latLng) { + var text = this._getTooltipText(); + + if (latLng) { + this._tooltip.updatePosition(latLng); + } + + if (!this._errorShown) { + this._tooltip.updateContent(text); + } + }, + + _drawGuide: function (pointA, pointB) { + var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))), + guidelineDistance = this.options.guidelineDistance, + maxGuideLineLength = this.options.maxGuideLineLength, + // Only draw a guideline with a max length + i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance, + fraction, + dashPoint, + dash; + + //create the guides container if we haven't yet + if (!this._guidesContainer) { + this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane); + } + + //draw a dash every GuildeLineDistance + for (; i < length; i += this.options.guidelineDistance) { + //work out fraction along line we are + fraction = i / length; + + //calculate new x,y point + dashPoint = { + x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)), + y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y)) + }; + + //add guide dash to guide container + dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer); + dash.style.backgroundColor = + !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color; + + L.DomUtil.setPosition(dash, dashPoint); + } + }, + + _updateGuideColor: function (color) { + if (this._guidesContainer) { + for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) { + this._guidesContainer.childNodes[i].style.backgroundColor = color; + } + } + }, + + // removes all child elements (guide dashes) from the guides container + _clearGuides: function () { + if (this._guidesContainer) { + while (this._guidesContainer.firstChild) { + this._guidesContainer.removeChild(this._guidesContainer.firstChild); + } + } + }, + + _getTooltipText: function () { + var showLength = this.options.showLength, + labelText, distanceStr; + if (L.Browser.touch) { + showLength = false; // if there's a better place to put this, feel free to move it + } + if (this._markers.length === 0) { + labelText = { + text: L.drawLocal.draw.handlers.polyline.tooltip.start + }; + } else { + distanceStr = showLength ? this._getMeasurementString() : ''; + + if (this._markers.length === 1) { + labelText = { + text: L.drawLocal.draw.handlers.polyline.tooltip.cont, + subtext: distanceStr + }; + } else { + labelText = { + text: L.drawLocal.draw.handlers.polyline.tooltip.end, + subtext: distanceStr + }; + } + } + return labelText; + }, + + _updateRunningMeasure: function (latlng, added) { + var markersLength = this._markers.length, + previousMarkerIndex, distance; + + if (this._markers.length === 1) { + this._measurementRunningTotal = 0; + } else { + previousMarkerIndex = markersLength - (added ? 2 : 1); + + // Calculate the distance based on the version + if(L.GeometryUtil.isVersion07x()){ + distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); + } else { + distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1); + } + + this._measurementRunningTotal += distance * (added ? 1 : -1); + } + }, + + _getMeasurementString: function () { + var currentLatLng = this._currentLatLng, + previousLatLng = this._markers[this._markers.length - 1].getLatLng(), + distance; + + // Calculate the distance from the last fixed point to the mouse position based on the version + if(L.GeometryUtil.isVersion07x()){ + distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0 ; + } else { + distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0 ; + } + + return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision); + }, + + _showErrorTooltip: function () { + this._errorShown = true; + + // Update tooltip + this._tooltip + .showAsError() + .updateContent({ text: this.options.drawError.message }); + + // Update shape + this._updateGuideColor(this.options.drawError.color); + this._poly.setStyle({ color: this.options.drawError.color }); + + // Hide the error after 2 seconds + this._clearHideErrorTimeout(); + this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout); + }, + + _hideErrorTooltip: function () { + this._errorShown = false; + + this._clearHideErrorTimeout(); + + // Revert tooltip + this._tooltip + .removeError() + .updateContent(this._getTooltipText()); + + // Revert shape + this._updateGuideColor(this.options.shapeOptions.color); + this._poly.setStyle({ color: this.options.shapeOptions.color }); + }, + + _clearHideErrorTimeout: function () { + if (this._hideErrorTimeout) { + clearTimeout(this._hideErrorTimeout); + this._hideErrorTimeout = null; + } + }, + + // disable new markers temporarily; + // this is to prevent duplicated touch/click events in some browsers + _disableNewMarkers: function () { + this._disableMarkers = true; + }, + + // see _disableNewMarkers + _enableNewMarkers: function () { + setTimeout(function() { + this._disableMarkers = false; + }.bind(this), 50); + }, + + _cleanUpShape: function () { + if (this._markers.length > 1) { + this._markers[this._markers.length - 1].off('click', this._finishShape, this); + } + }, + + _fireCreatedEvent: function () { + var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions); + L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly); + } +}); + + + +/** + * @class L.Draw.Polygon + * @aka Draw.Polygon + * @inherits L.Draw.Polyline + */ +L.Draw.Polygon = L.Draw.Polyline.extend({ + statics: { + TYPE: 'polygon' + }, + + Poly: L.Polygon, + + options: { + showArea: false, + showLength: false, + shapeOptions: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: true, + fillColor: null, //same as color by default + fillOpacity: 0.2, + clickable: true + }, + // Whether to use the metric measurement system (truthy) or not (falsy). + // Also defines the units to use for the metric system as an array of + // strings (e.g. `['ha', 'm']`). + metric: true, + feet: true, // When not metric, to use feet instead of yards for display. + nautic: false, // When not metric, not feet use nautic mile for display + // Defines the precision for each type of unit (e.g. {km: 2, ft: 0} + precision: {} + }, + + // @method initialize(): void + initialize: function (map, options) { + L.Draw.Polyline.prototype.initialize.call(this, map, options); + + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.Polygon.TYPE; + }, + + _updateFinishHandler: function () { + var markerCount = this._markers.length; + + // The first marker should have a click handler to close the polygon + if (markerCount === 1) { + this._markers[0].on('click', this._finishShape, this); + } + + // Add and update the double click handler + if (markerCount > 2) { + this._markers[markerCount - 1].on('dblclick', this._finishShape, this); + // Only need to remove handler if has been added before + if (markerCount > 3) { + this._markers[markerCount - 2].off('dblclick', this._finishShape, this); + } + } + }, + + _getTooltipText: function () { + var text, subtext; + + if (this._markers.length === 0) { + text = L.drawLocal.draw.handlers.polygon.tooltip.start; + } else if (this._markers.length < 3) { + text = L.drawLocal.draw.handlers.polygon.tooltip.cont; + subtext = this._getMeasurementString(); + } else { + text = L.drawLocal.draw.handlers.polygon.tooltip.end; + subtext = this._getMeasurementString(); + } + + return { + text: text, + subtext: subtext + }; + }, + + _getMeasurementString: function () { + var area = this._area, + measurementString = ''; + + + if (!area && !this.options.showLength) { + return null; + } + + if (this.options.showLength) { + measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this); + } + + if (area) { + measurementString += '<br>' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision); + } + + return measurementString; + }, + + _shapeIsValid: function () { + return this._markers.length >= 3; + }, + + _vertexChanged: function (latlng, added) { + var latLngs; + + // Check to see if we should show the area + if (!this.options.allowIntersection && this.options.showArea) { + latLngs = this._poly.getLatLngs(); + + this._area = L.GeometryUtil.geodesicArea(latLngs); + } + + L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added); + }, + + _cleanUpShape: function () { + var markerCount = this._markers.length; + + if (markerCount > 0) { + this._markers[0].off('click', this._finishShape, this); + + if (markerCount > 2) { + this._markers[markerCount - 1].off('dblclick', this._finishShape, this); + } + } + } +}); + + + +L.SimpleShape = {}; +/** + * @class L.Draw.SimpleShape + * @aka Draw.SimpleShape + * @inherits L.Draw.Feature + */ +L.Draw.SimpleShape = L.Draw.Feature.extend({ + options: { + repeatMode: false + }, + + // @method initialize(): void + initialize: function (map, options) { + this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end; + + L.Draw.Feature.prototype.initialize.call(this, map, options); + }, + + // @method addHooks(): void + // Add listener hooks to this handler. + addHooks: function () { + L.Draw.Feature.prototype.addHooks.call(this); + if (this._map) { + this._mapDraggable = this._map.dragging.enabled(); + + if (this._mapDraggable) { + this._map.dragging.disable(); + } + + //TODO refactor: move cursor to styles + this._container.style.cursor = 'crosshair'; + + this._tooltip.updateContent({ text: this._initialLabelText }); + + this._map + .on('mousedown', this._onMouseDown, this) + .on('mousemove', this._onMouseMove, this) + .on('touchstart', this._onMouseDown, this) + .on('touchmove', this._onMouseMove, this); + + // we should prevent default, otherwise default behavior (scrolling) will fire, + // and that will cause document.touchend to fire and will stop the drawing + // (circle, rectangle) in touch mode. + // (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means, + // handler can't event.preventDefault + // check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners + document.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false}); + } + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler. + removeHooks: function () { + L.Draw.Feature.prototype.removeHooks.call(this); + if (this._map) { + if (this._mapDraggable) { + this._map.dragging.enable(); + } + + //TODO refactor: move cursor to styles + this._container.style.cursor = ''; + + this._map + .off('mousedown', this._onMouseDown, this) + .off('mousemove', this._onMouseMove, this) + .off('touchstart', this._onMouseDown, this) + .off('touchmove', this._onMouseMove, this); + + L.DomEvent.off(document, 'mouseup', this._onMouseUp, this); + L.DomEvent.off(document, 'touchend', this._onMouseUp, this); + + document.removeEventListener('touchstart', L.DomEvent.preventDefault); + + // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return + if (this._shape) { + this._map.removeLayer(this._shape); + delete this._shape; + } + } + this._isDrawing = false; + }, + + _getTooltipText: function () { + return { + text: this._endLabelText + }; + }, + + _onMouseDown: function (e) { + this._isDrawing = true; + this._startLatLng = e.latlng; + + L.DomEvent + .on(document, 'mouseup', this._onMouseUp, this) + .on(document, 'touchend', this._onMouseUp, this) + .preventDefault(e.originalEvent); + }, + + _onMouseMove: function (e) { + var latlng = e.latlng; + + this._tooltip.updatePosition(latlng); + if (this._isDrawing) { + this._tooltip.updateContent(this._getTooltipText()); + this._drawShape(latlng); + } + }, + + _onMouseUp: function () { + if (this._shape) { + this._fireCreatedEvent(); + } + + this.disable(); + if (this.options.repeatMode) { + this.enable(); + } + } +}); + + +/** + * @class L.Draw.Rectangle + * @aka Draw.Rectangle + * @inherits L.Draw.SimpleShape + */ +L.Draw.Rectangle = L.Draw.SimpleShape.extend({ + statics: { + TYPE: 'rectangle' + }, + + options: { + shapeOptions: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: true, + fillColor: null, //same as color by default + fillOpacity: 0.2, + showArea: true, + clickable: true + }, + metric: true // Whether to use the metric measurement system or imperial + }, + + // @method initialize(): void + initialize: function (map, options) { + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.Rectangle.TYPE; + + this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start; + + L.Draw.SimpleShape.prototype.initialize.call(this, map, options); + }, + + // @method disable(): void + disable: function () { + if (!this._enabled) { + return; + } + + this._isCurrentlyTwoClickDrawing = false; + L.Draw.SimpleShape.prototype.disable.call(this); + }, + + _onMouseUp: function (e) { + if (!this._shape && !this._isCurrentlyTwoClickDrawing) { + this._isCurrentlyTwoClickDrawing = true; + return; + } + + // Make sure closing click is on map + if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) { + return; + } + + L.Draw.SimpleShape.prototype._onMouseUp.call(this); + }, + + _drawShape: function (latlng) { + if (!this._shape) { + this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions); + this._map.addLayer(this._shape); + } else { + this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng)); + } + }, + + _fireCreatedEvent: function () { + var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions); + L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle); + }, + + _getTooltipText: function () { + var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this), + shape = this._shape, + showArea = this.options.showArea, + latLngs, area, subtext; + + if (shape) { + latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(); + area = L.GeometryUtil.geodesicArea(latLngs); + subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : '' + } + + return { + text: tooltipText.text, + subtext: subtext + }; + } +}); + +function _hasAncestor (el, cls) { + while ((el = el.parentElement) && !el.classList.contains(cls)); + return el; +} + + + +/** + * @class L.Draw.Marker + * @aka Draw.Marker + * @inherits L.Draw.Feature + */ +L.Draw.Marker = L.Draw.Feature.extend({ + statics: { + TYPE: 'marker' + }, + + options: { + icon: new L.Icon.Default(), + repeatMode: false, + zIndexOffset: 2000 // This should be > than the highest z-index any markers + }, + + // @method initialize(): void + initialize: function (map, options) { + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.Marker.TYPE; + + this._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start; + + L.Draw.Feature.prototype.initialize.call(this, map, options); + }, + + // @method addHooks(): void + // Add listener hooks to this handler. + addHooks: function () { + L.Draw.Feature.prototype.addHooks.call(this); + + if (this._map) { + this._tooltip.updateContent({ text: this._initialLabelText }); + + // Same mouseMarker as in Draw.Polyline + if (!this._mouseMarker) { + this._mouseMarker = L.marker(this._map.getCenter(), { + icon: L.divIcon({ + className: 'leaflet-mouse-marker', + iconAnchor: [20, 20], + iconSize: [40, 40] + }), + opacity: 0, + zIndexOffset: this.options.zIndexOffset + }); + } + + this._mouseMarker + .on('click', this._onClick, this) + .addTo(this._map); + + this._map.on('mousemove', this._onMouseMove, this); + this._map.on('click', this._onTouch, this); + } + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler. + removeHooks: function () { + L.Draw.Feature.prototype.removeHooks.call(this); + + if (this._map) { + if (this._marker) { + this._marker.off('click', this._onClick, this); + this._map + .off('click', this._onClick, this) + .off('click', this._onTouch, this) + .removeLayer(this._marker); + delete this._marker; + } + + this._mouseMarker.off('click', this._onClick, this); + this._map.removeLayer(this._mouseMarker); + delete this._mouseMarker; + + this._map.off('mousemove', this._onMouseMove, this); + } + }, + + _onMouseMove: function (e) { + var latlng = e.latlng; + + this._tooltip.updatePosition(latlng); + this._mouseMarker.setLatLng(latlng); + + if (!this._marker) { + this._marker = this._createMarker(latlng); + // Bind to both marker and map to make sure we get the click event. + this._marker.on('click', this._onClick, this); + this._map + .on('click', this._onClick, this) + .addLayer(this._marker); + } + else { + latlng = this._mouseMarker.getLatLng(); + this._marker.setLatLng(latlng); + } + }, + + _createMarker: function (latlng) { + return new L.Marker(latlng, { + icon: this.options.icon, + zIndexOffset: this.options.zIndexOffset + }); + }, + + _onClick: function () { + this._fireCreatedEvent(); + + this.disable(); + if (this.options.repeatMode) { + this.enable(); + } + }, + + _onTouch: function (e) { + // called on click & tap, only really does any thing on tap + this._onMouseMove(e); // creates & places marker + this._onClick(); // permanently places marker & ends interaction + }, + + _fireCreatedEvent: function () { + var marker = new L.Marker.Touch(this._marker.getLatLng(), { icon: this.options.icon }); + L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker); + } +}); + + + +/** + * @class L.Draw.CircleMarker + * @aka Draw.CircleMarker + * @inherits L.Draw.Marker + */ +L.Draw.CircleMarker = L.Draw.Marker.extend({ + statics: { + TYPE: 'circlemarker' + }, + + options: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: true, + fillColor: null, //same as color by default + fillOpacity: 0.2, + clickable: true, + zIndexOffset: 2000 // This should be > than the highest z-index any markers + }, + + // @method initialize(): void + initialize: function (map, options) { + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.CircleMarker.TYPE; + + this._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start; + + L.Draw.Feature.prototype.initialize.call(this, map, options); + }, + + + _fireCreatedEvent: function () { + var circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options); + L.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker); + }, + + _createMarker: function (latlng) { + return new L.CircleMarker(latlng, this.options); + } +}); + + + +/** + * @class L.Draw.Circle + * @aka Draw.Circle + * @inherits L.Draw.SimpleShape + */ +L.Draw.Circle = L.Draw.SimpleShape.extend({ + statics: { + TYPE: 'circle' + }, + + options: { + shapeOptions: { + stroke: true, + color: '#3388ff', + weight: 4, + opacity: 0.5, + fill: true, + fillColor: null, //same as color by default + fillOpacity: 0.2, + clickable: true + }, + showRadius: true, + metric: true, // Whether to use the metric measurement system or imperial + feet: true, // When not metric, use feet instead of yards for display + nautic: false // When not metric, not feet use nautic mile for display + }, + + // @method initialize(): void + initialize: function (map, options) { + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.Draw.Circle.TYPE; + + this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start; + + L.Draw.SimpleShape.prototype.initialize.call(this, map, options); + }, + + _drawShape: function (latlng) { + // Calculate the distance based on the version + if(L.GeometryUtil.isVersion07x()){ + var distance = this._startLatLng.distanceTo(latlng); + } else { + var distance = this._map.distance(this._startLatLng, latlng); + } + + if (!this._shape) { + this._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions); + this._map.addLayer(this._shape); + } else { + this._shape.setRadius(distance); + } + }, + + _fireCreatedEvent: function () { + var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions); + L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle); + }, + + _onMouseMove: function (e) { + var latlng = e.latlng, + showRadius = this.options.showRadius, + useMetric = this.options.metric, + radius; + + this._tooltip.updatePosition(latlng); + if (this._isDrawing) { + this._drawShape(latlng); + + // Get the new radius (rounded to 1 dp) + radius = this._shape.getRadius().toFixed(1); + + var subtext = ''; + if (showRadius) { + subtext = L.drawLocal.draw.handlers.circle.radius + ': ' + + L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic); + } + this._tooltip.updateContent({ + text: this._endLabelText, + subtext: subtext + }); + } + } +}); + + + +L.Edit = L.Edit || {}; + +/** + * @class L.Edit.Marker + * @aka Edit.Marker + */ +L.Edit.Marker = L.Handler.extend({ + // @method initialize(): void + initialize: function (marker, options) { + this._marker = marker; + L.setOptions(this, options); + }, + + // @method addHooks(): void + // Add listener hooks to this handler + addHooks: function () { + var marker = this._marker; + + marker.dragging.enable(); + marker.on('dragend', this._onDragEnd, marker); + this._toggleMarkerHighlight(); + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler + removeHooks: function () { + var marker = this._marker; + + marker.dragging.disable(); + marker.off('dragend', this._onDragEnd, marker); + this._toggleMarkerHighlight(); + }, + + _onDragEnd: function (e) { + var layer = e.target; + layer.edited = true; + this._map.fire(L.Draw.Event.EDITMOVE, { layer: layer }); + }, + + _toggleMarkerHighlight: function () { + var icon = this._marker._icon; + + // Don't do anything if this layer is a marker but doesn't have an icon. Markers + // should usually have icons. If using Leaflet.draw with Leaflet.markercluster there + // is a chance that a marker doesn't. + if (!icon) { + return; + } + + // This is quite naughty, but I don't see another way of doing it. (short of setting a new icon) + icon.style.display = 'none'; + + if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) { + L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected'); + // Offset as the border will make the icon move. + this._offsetMarker(icon, -4); + + } else { + L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected'); + // Offset as the border will make the icon move. + this._offsetMarker(icon, 4); + } + + icon.style.display = ''; + }, + + _offsetMarker: function (icon, offset) { + var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset, + iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset; + + icon.style.marginTop = iconMarginTop + 'px'; + icon.style.marginLeft = iconMarginLeft + 'px'; + } +}); + +L.Marker.addInitHook(function () { + if (L.Edit.Marker) { + this.editing = new L.Edit.Marker(this); + + if (this.options.editable) { + this.editing.enable(); + } + } +}); + + + +L.Edit = L.Edit || {}; + +/** + * @class L.Edit.Polyline + * @aka L.Edit.Poly + * @aka Edit.Poly + */ +L.Edit.Poly = L.Handler.extend({ + // @method initialize(): void + initialize: function (poly) { + + this.latlngs = [poly._latlngs]; + if (poly._holes) { + this.latlngs = this.latlngs.concat(poly._holes); + } + + this._poly = poly; + + this._poly.on('revert-edited', this._updateLatLngs, this); + }, + + // Compatibility method to normalize Poly* objects + // between 0.7.x and 1.0+ + _defaultShape: function () { + if (!L.Polyline._flat) { + return this._poly._latlngs; + } + return L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0]; + }, + + _eachVertexHandler: function (callback) { + for (var i = 0; i < this._verticesHandlers.length; i++) { + callback(this._verticesHandlers[i]); + } + }, + + // @method addHooks(): void + // Add listener hooks to this handler + addHooks: function () { + this._initHandlers(); + this._eachVertexHandler(function (handler) { + handler.addHooks(); + }); + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler + removeHooks: function () { + this._eachVertexHandler(function (handler) { + handler.removeHooks(); + }); + }, + + // @method updateMarkers(): void + // Fire an update for each vertex handler + updateMarkers: function () { + this._eachVertexHandler(function (handler) { + handler.updateMarkers(); + }); + }, + + _initHandlers: function () { + this._verticesHandlers = []; + for (var i = 0; i < this.latlngs.length; i++) { + this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly)); + } + }, + + _updateLatLngs: function (e) { + this.latlngs = [e.layer._latlngs]; + if (e.layer._holes) { + this.latlngs = this.latlngs.concat(e.layer._holes); + } + } + +}); + +/** + * @class L.Edit.PolyVerticesEdit + * @aka Edit.PolyVerticesEdit + */ +L.Edit.PolyVerticesEdit = L.Handler.extend({ + options: { + icon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: 'leaflet-div-icon leaflet-editing-icon' + }), + touchIcon: new L.DivIcon({ + iconSize: new L.Point(20, 20), + className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon' + }), + drawError: { + color: '#b00b00', + timeout: 1000 + } + + + }, + + // @method intialize(): void + initialize: function (poly, latlngs, options) { + // if touch, switch to touch icon + if (L.Browser.touch) { + this.options.icon = this.options.touchIcon; + } + this._poly = poly; + + if (options && options.drawError) { + options.drawError = L.Util.extend({}, this.options.drawError, options.drawError); + } + + this._latlngs = latlngs; + + L.setOptions(this, options); + }, + + // Compatibility method to normalize Poly* objects + // between 0.7.x and 1.0+ + _defaultShape: function () { + if (!L.Polyline._flat) { + return this._latlngs; + } + return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + + // @method addHooks(): void + // Add listener hooks to this handler. + addHooks: function () { + var poly = this._poly; + var path = poly._path; + + if (!(poly instanceof L.Polygon)) { + poly.options.fill = false; + if (poly.options.editing) { + poly.options.editing.fill = false; + } + } + + if (path) { + if (poly.options.editing.className) { + if (poly.options.original.className) { + poly.options.original.className.split(' ').forEach(function(className) { + L.DomUtil.removeClass(path, className); + }); + } + poly.options.editing.className.split(' ').forEach(function(className) { + L.DomUtil.addClass(path, className); + }); + } + } + + poly.setStyle(poly.options.editing); + + if (this._poly._map) { + + this._map = this._poly._map; // Set map + + if (!this._markerGroup) { + this._initMarkers(); + } + this._poly._map.addLayer(this._markerGroup); + } + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler. + removeHooks: function () { + var poly = this._poly; + var path = poly._path; + + if (path) { + if (poly.options.editing.className) { + poly.options.editing.className.split(' ').forEach(function(className) { + L.DomUtil.removeClass(path, className); + }); + if (poly.options.original.className) { + poly.options.original.className.split(' ').forEach(function(className) { + L.DomUtil.addClass(path, className); + }); + } + } + } + + poly.setStyle(poly.options.original); + + if (poly._map) { + poly._map.removeLayer(this._markerGroup); + delete this._markerGroup; + delete this._markers; + } + }, + + // @method updateMarkers(): void + // Clear markers and update their location + updateMarkers: function () { + this._markerGroup.clearLayers(); + this._initMarkers(); + }, + + _initMarkers: function () { + if (!this._markerGroup) { + this._markerGroup = new L.LayerGroup(); + } + this._markers = []; + + var latlngs = this._defaultShape(), + i, j, len, marker; + + for (i = 0, len = latlngs.length; i < len; i++) { + + marker = this._createMarker(latlngs[i], i); + marker.on('click', this._onMarkerClick, this); + this._markers.push(marker); + } + + var markerLeft, markerRight; + + for (i = 0, j = len - 1; i < len; j = i++) { + if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) { + continue; + } + + markerLeft = this._markers[j]; + markerRight = this._markers[i]; + + this._createMiddleMarker(markerLeft, markerRight); + this._updatePrevNext(markerLeft, markerRight); + } + }, + + _createMarker: function (latlng, index) { + // Extending L.Marker in TouchEvents.js to include touch. + var marker = new L.Marker.Touch(latlng, { + draggable: true, + icon: this.options.icon, + }); + + marker._origLatLng = latlng; + marker._index = index; + + marker + .on('dragstart', this._onMarkerDragStart, this) + .on('drag', this._onMarkerDrag, this) + .on('dragend', this._fireEdit, this) + .on('touchmove', this._onTouchMove, this) + .on('touchend', this._fireEdit, this) + .on('MSPointerMove', this._onTouchMove, this) + .on('MSPointerUp', this._fireEdit, this); + + this._markerGroup.addLayer(marker); + + return marker; + }, + + _onMarkerDragStart: function () { + this._poly.fire('editstart'); + }, + + _spliceLatLngs: function () { + var latlngs = this._defaultShape(); + var removed = [].splice.apply(latlngs, arguments); + this._poly._convertLatLngs(latlngs, true); + this._poly.redraw(); + return removed; + }, + + _removeMarker: function (marker) { + var i = marker._index; + + this._markerGroup.removeLayer(marker); + this._markers.splice(i, 1); + this._spliceLatLngs(i, 1); + this._updateIndexes(i, -1); + + marker + .off('dragstart', this._onMarkerDragStart, this) + .off('drag', this._onMarkerDrag, this) + .off('dragend', this._fireEdit, this) + .off('touchmove', this._onMarkerDrag, this) + .off('touchend', this._fireEdit, this) + .off('click', this._onMarkerClick, this) + .off('MSPointerMove', this._onTouchMove, this) + .off('MSPointerUp', this._fireEdit, this); + }, + + _fireEdit: function () { + this._poly.edited = true; + this._poly.fire('edit'); + this._poly._map.fire(L.Draw.Event.EDITVERTEX, { layers: this._markerGroup, poly: this._poly }); + }, + + _onMarkerDrag: function (e) { + var marker = e.target; + var poly = this._poly; + + L.extend(marker._origLatLng, marker._latlng); + + if (marker._middleLeft) { + marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker)); + } + if (marker._middleRight) { + marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next)); + } + + if (poly.options.poly) { + var tooltip = poly._map._editTooltip; // Access the tooltip + + // If we don't allow intersections and the polygon intersects + if (!poly.options.poly.allowIntersection && poly.intersects()) { + + var originalColor = poly.options.color; + poly.setStyle({ color: this.options.drawError.color }); + + // Manually trigger 'dragend' behavior on marker we are about to remove + // WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484 + if (L.version.indexOf('0.7') !== 0) { + marker.dragging._draggable._onUp(e); + } + this._onMarkerClick(e); // Remove violating marker + // FIXME: Reset the marker to it's original position (instead of remove) + + if (tooltip) { + tooltip.updateContent({ + text: L.drawLocal.draw.handlers.polyline.error + }); + } + + // Reset everything back to normal after a second + setTimeout(function () { + poly.setStyle({ color: originalColor }); + if (tooltip) { + tooltip.updateContent({ + text: L.drawLocal.edit.handlers.edit.tooltip.text, + subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext + }); + } + }, 1000); + } + } + + this._poly.redraw(); + this._poly.fire('editdrag'); + }, + + _onMarkerClick: function (e) { + + var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3, + marker = e.target; + + // If removing this point would create an invalid polyline/polygon don't remove + if (this._defaultShape().length < minPoints) { + return; + } + + // remove the marker + this._removeMarker(marker); + + // update prev/next links of adjacent markers + this._updatePrevNext(marker._prev, marker._next); + + // remove ghost markers near the removed marker + if (marker._middleLeft) { + this._markerGroup.removeLayer(marker._middleLeft); + } + if (marker._middleRight) { + this._markerGroup.removeLayer(marker._middleRight); + } + + // create a ghost marker in place of the removed one + if (marker._prev && marker._next) { + this._createMiddleMarker(marker._prev, marker._next); + + } else if (!marker._prev) { + marker._next._middleLeft = null; + + } else if (!marker._next) { + marker._prev._middleRight = null; + } + + this._fireEdit(); + }, + + _onTouchMove: function (e) { + + var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]), + latlng = this._map.layerPointToLatLng(layerPoint), + marker = e.target; + + L.extend(marker._origLatLng, latlng); + + if (marker._middleLeft) { + marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker)); + } + if (marker._middleRight) { + marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next)); + } + + this._poly.redraw(); + this.updateMarkers(); + }, + + _updateIndexes: function (index, delta) { + this._markerGroup.eachLayer(function (marker) { + if (marker._index > index) { + marker._index += delta; + } + }); + }, + + _createMiddleMarker: function (marker1, marker2) { + var latlng = this._getMiddleLatLng(marker1, marker2), + marker = this._createMarker(latlng), + onClick, + onDragStart, + onDragEnd; + + marker.setOpacity(0.6); + + marker1._middleRight = marker2._middleLeft = marker; + + onDragStart = function () { + marker.off('touchmove', onDragStart, this); + var i = marker2._index; + + marker._index = i; + + marker + .off('click', onClick, this) + .on('click', this._onMarkerClick, this); + + latlng.lat = marker.getLatLng().lat; + latlng.lng = marker.getLatLng().lng; + this._spliceLatLngs(i, 0, latlng); + this._markers.splice(i, 0, marker); + + marker.setOpacity(1); + + this._updateIndexes(i, 1); + marker2._index++; + this._updatePrevNext(marker1, marker); + this._updatePrevNext(marker, marker2); + + this._poly.fire('editstart'); + }; + + onDragEnd = function () { + marker.off('dragstart', onDragStart, this); + marker.off('dragend', onDragEnd, this); + marker.off('touchmove', onDragStart, this); + + this._createMiddleMarker(marker1, marker); + this._createMiddleMarker(marker, marker2); + }; + + onClick = function () { + onDragStart.call(this); + onDragEnd.call(this); + this._fireEdit(); + }; + + marker + .on('click', onClick, this) + .on('dragstart', onDragStart, this) + .on('dragend', onDragEnd, this) + .on('touchmove', onDragStart, this); + + this._markerGroup.addLayer(marker); + }, + + _updatePrevNext: function (marker1, marker2) { + if (marker1) { + marker1._next = marker2; + } + if (marker2) { + marker2._prev = marker1; + } + }, + + _getMiddleLatLng: function (marker1, marker2) { + var map = this._poly._map, + p1 = map.project(marker1.getLatLng()), + p2 = map.project(marker2.getLatLng()); + + return map.unproject(p1._add(p2)._divideBy(2)); + } +}); + +L.Polyline.addInitHook(function () { + + // Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit + if (this.editing) { + return; + } + + if (L.Edit.Poly) { + + this.editing = new L.Edit.Poly(this); + + if (this.options.editable) { + this.editing.enable(); + } + } + + this.on('add', function () { + if (this.editing && this.editing.enabled()) { + this.editing.addHooks(); + } + }); + + this.on('remove', function () { + if (this.editing && this.editing.enabled()) { + this.editing.removeHooks(); + } + }); +}); + + + +L.Edit = L.Edit || {}; +/** + * @class L.Edit.SimpleShape + * @aka Edit.SimpleShape + */ +L.Edit.SimpleShape = L.Handler.extend({ + options: { + moveIcon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move' + }), + resizeIcon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize' + }), + touchMoveIcon: new L.DivIcon({ + iconSize: new L.Point(20, 20), + className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon' + }), + touchResizeIcon: new L.DivIcon({ + iconSize: new L.Point(20, 20), + className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon' + }), + }, + + // @method intialize(): void + initialize: function (shape, options) { + // if touch, switch to touch icon + if (L.Browser.touch) { + this.options.moveIcon = this.options.touchMoveIcon; + this.options.resizeIcon = this.options.touchResizeIcon; + } + + this._shape = shape; + L.Util.setOptions(this, options); + }, + + // @method addHooks(): void + // Add listener hooks to this handler + addHooks: function () { + var shape = this._shape; + if (this._shape._map) { + this._map = this._shape._map; + shape.setStyle(shape.options.editing); + + if (shape._map) { + this._map = shape._map; + if (!this._markerGroup) { + this._initMarkers(); + } + this._map.addLayer(this._markerGroup); + } + } + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler + removeHooks: function () { + var shape = this._shape; + + shape.setStyle(shape.options.original); + + if (shape._map) { + this._unbindMarker(this._moveMarker); + + for (var i = 0, l = this._resizeMarkers.length; i < l; i++) { + this._unbindMarker(this._resizeMarkers[i]); + } + this._resizeMarkers = null; + + this._map.removeLayer(this._markerGroup); + delete this._markerGroup; + } + + this._map = null; + }, + + // @method updateMarkers(): void + // Remove the edit markers from this layer + updateMarkers: function () { + this._markerGroup.clearLayers(); + this._initMarkers(); + }, + + _initMarkers: function () { + if (!this._markerGroup) { + this._markerGroup = new L.LayerGroup(); + } + + // Create center marker + this._createMoveMarker(); + + // Create edge marker + this._createResizeMarker(); + }, + + _createMoveMarker: function () { + // Children override + }, + + _createResizeMarker: function () { + // Children override + }, + + _createMarker: function (latlng, icon) { + // Extending L.Marker in TouchEvents.js to include touch. + var marker = new L.Marker.Touch(latlng, { + draggable: true, + icon: icon, + zIndexOffset: 10 + }); + + this._bindMarker(marker); + + this._markerGroup.addLayer(marker); + + return marker; + }, + + _bindMarker: function (marker) { + marker + .on('dragstart', this._onMarkerDragStart, this) + .on('drag', this._onMarkerDrag, this) + .on('dragend', this._onMarkerDragEnd, this) + .on('touchstart', this._onTouchStart, this) + .on('touchmove', this._onTouchMove, this) + .on('MSPointerMove', this._onTouchMove, this) + .on('touchend', this._onTouchEnd, this) + .on('MSPointerUp', this._onTouchEnd, this); + }, + + _unbindMarker: function (marker) { + marker + .off('dragstart', this._onMarkerDragStart, this) + .off('drag', this._onMarkerDrag, this) + .off('dragend', this._onMarkerDragEnd, this) + .off('touchstart', this._onTouchStart, this) + .off('touchmove', this._onTouchMove, this) + .off('MSPointerMove', this._onTouchMove, this) + .off('touchend', this._onTouchEnd, this) + .off('MSPointerUp', this._onTouchEnd, this); + }, + + _onMarkerDragStart: function (e) { + var marker = e.target; + marker.setOpacity(0); + + this._shape.fire('editstart'); + }, + + _fireEdit: function () { + this._shape.edited = true; + this._shape.fire('edit'); + }, + + _onMarkerDrag: function (e) { + var marker = e.target, + latlng = marker.getLatLng(); + + if (marker === this._moveMarker) { + this._move(latlng); + } else { + this._resize(latlng); + } + + this._shape.redraw(); + this._shape.fire('editdrag'); + }, + + _onMarkerDragEnd: function (e) { + var marker = e.target; + marker.setOpacity(1); + + this._fireEdit(); + }, + + _onTouchStart: function (e) { + L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e); + + if (typeof(this._getCorners) === 'function') { + // Save a reference to the opposite point + var corners = this._getCorners(), + marker = e.target, + currentCornerIndex = marker._cornerIndex; + + marker.setOpacity(0); + + // Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart() + // Latlng is null otherwise. + this._oppositeCorner = corners[(currentCornerIndex + 2) % 4]; + this._toggleCornerMarkers(0, currentCornerIndex); + } + + this._shape.fire('editstart'); + }, + + _onTouchMove: function (e) { + var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]), + latlng = this._map.layerPointToLatLng(layerPoint), + marker = e.target; + + if (marker === this._moveMarker) { + this._move(latlng); + } else { + this._resize(latlng); + } + + this._shape.redraw(); + + // prevent touchcancel in IOS + // e.preventDefault(); + return false; + }, + + _onTouchEnd: function (e) { + var marker = e.target; + marker.setOpacity(1); + this.updateMarkers(); + this._fireEdit(); + }, + + _move: function () { + // Children override + }, + + _resize: function () { + // Children override + } +}); + + + +L.Edit = L.Edit || {}; +/** + * @class L.Edit.Rectangle + * @aka Edit.Rectangle + * @inherits L.Edit.SimpleShape + */ +L.Edit.Rectangle = L.Edit.SimpleShape.extend({ + _createMoveMarker: function () { + var bounds = this._shape.getBounds(), + center = bounds.getCenter(); + + this._moveMarker = this._createMarker(center, this.options.moveIcon); + }, + + _createResizeMarker: function () { + var corners = this._getCorners(); + + this._resizeMarkers = []; + + for (var i = 0, l = corners.length; i < l; i++) { + this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon)); + // Monkey in the corner index as we will need to know this for dragging + this._resizeMarkers[i]._cornerIndex = i; + } + }, + + _onMarkerDragStart: function (e) { + L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e); + + // Save a reference to the opposite point + var corners = this._getCorners(), + marker = e.target, + currentCornerIndex = marker._cornerIndex; + + this._oppositeCorner = corners[(currentCornerIndex + 2) % 4]; + + this._toggleCornerMarkers(0, currentCornerIndex); + }, + + _onMarkerDragEnd: function (e) { + var marker = e.target, + bounds, center; + + // Reset move marker position to the center + if (marker === this._moveMarker) { + bounds = this._shape.getBounds(); + center = bounds.getCenter(); + + marker.setLatLng(center); + } + + this._toggleCornerMarkers(1); + + this._repositionCornerMarkers(); + + L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e); + }, + + _move: function (newCenter) { + var latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(), + bounds = this._shape.getBounds(), + center = bounds.getCenter(), + offset, newLatLngs = []; + + // Offset the latlngs to the new center + for (var i = 0, l = latlngs.length; i < l; i++) { + offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng]; + newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]); + } + + this._shape.setLatLngs(newLatLngs); + + // Reposition the resize markers + this._repositionCornerMarkers(); + + this._map.fire(L.Draw.Event.EDITMOVE, { layer: this._shape }); + }, + + _resize: function (latlng) { + var bounds; + + // Update the shape based on the current position of this corner and the opposite point + this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner)); + + // Reposition the move marker + bounds = this._shape.getBounds(); + this._moveMarker.setLatLng(bounds.getCenter()); + + this._map.fire(L.Draw.Event.EDITRESIZE, { layer: this._shape }); + }, + + _getCorners: function () { + var bounds = this._shape.getBounds(), + nw = bounds.getNorthWest(), + ne = bounds.getNorthEast(), + se = bounds.getSouthEast(), + sw = bounds.getSouthWest(); + + return [nw, ne, se, sw]; + }, + + _toggleCornerMarkers: function (opacity) { + for (var i = 0, l = this._resizeMarkers.length; i < l; i++) { + this._resizeMarkers[i].setOpacity(opacity); + } + }, + + _repositionCornerMarkers: function () { + var corners = this._getCorners(); + + for (var i = 0, l = this._resizeMarkers.length; i < l; i++) { + this._resizeMarkers[i].setLatLng(corners[i]); + } + } +}); + +L.Rectangle.addInitHook(function () { + if (L.Edit.Rectangle) { + this.editing = new L.Edit.Rectangle(this); + + if (this.options.editable) { + this.editing.enable(); + } + } +}); + + + +L.Edit = L.Edit || {}; +/** + * @class L.Edit.CircleMarker + * @aka Edit.Circle + * @inherits L.Edit.SimpleShape + */ +L.Edit.CircleMarker = L.Edit.SimpleShape.extend({ + _createMoveMarker: function () { + var center = this._shape.getLatLng(); + + this._moveMarker = this._createMarker(center, this.options.moveIcon); + }, + + _createResizeMarker: function () { + // To avoid an undefined check in L.Edit.SimpleShape.removeHooks + this._resizeMarkers = []; + }, + + _move: function (latlng) { + if (this._resizeMarkers.length) { + var resizemarkerPoint = this._getResizeMarkerPoint(latlng); + // Move the resize marker + this._resizeMarkers[0].setLatLng(resizemarkerPoint); + } + + // Move the circle + this._shape.setLatLng(latlng); + + this._map.fire(L.Draw.Event.EDITMOVE, { layer: this._shape }); + }, +}); + +L.CircleMarker.addInitHook(function () { + if (L.Edit.CircleMarker) { + this.editing = new L.Edit.CircleMarker(this); + + if (this.options.editable) { + this.editing.enable(); + } + } + + this.on('add', function () { + if (this.editing && this.editing.enabled()) { + this.editing.addHooks(); + } + }); + + this.on('remove', function () { + if (this.editing && this.editing.enabled()) { + this.editing.removeHooks(); + } + }); +}); + + + +L.Edit = L.Edit || {}; +/** + * @class L.Edit.Circle + * @aka Edit.Circle + * @inherits L.Edit.CircleMarker + */ +L.Edit.Circle = L.Edit.CircleMarker.extend({ + + _createResizeMarker: function () { + var center = this._shape.getLatLng(), + resizemarkerPoint = this._getResizeMarkerPoint(center); + + this._resizeMarkers = []; + this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon)); + }, + + _getResizeMarkerPoint: function (latlng) { + // From L.shape.getBounds() + var delta = this._shape._radius * Math.cos(Math.PI / 4), + point = this._map.project(latlng); + return this._map.unproject([point.x + delta, point.y - delta]); + }, + + _resize: function (latlng) { + var moveLatLng = this._moveMarker.getLatLng(); + + // Calculate the radius based on the version + if(L.GeometryUtil.isVersion07x()){ + radius = moveLatLng.distanceTo(latlng); + } else { + radius = this._map.distance(moveLatLng, latlng); + } + + this._map.fire(L.Draw.Event.EDITRESIZE, { layer: this._shape }); + } +}); + +L.Circle.addInitHook(function () { + if (L.Edit.Circle) { + this.editing = new L.Edit.Circle(this); + + if (this.options.editable) { + this.editing.enable(); + } + } + + this.on('add', function () { + if (this.editing && this.editing.enabled()) { + this.editing.addHooks(); + } + }); + + this.on('remove', function () { + if (this.editing && this.editing.enabled()) { + this.editing.removeHooks(); + } + }); +}); + + + +L.Map.mergeOptions({ + touchExtend: true +}); + +/** + * @class L.Map.TouchExtend + * @aka TouchExtend + */ +L.Map.TouchExtend = L.Handler.extend({ + + // @method initialize(): void + // Sets TouchExtend private accessor variables + initialize: function (map) { + this._map = map; + this._container = map._container; + this._pane = map._panes.overlayPane; + }, + + // @method addHooks(): void + // Adds dom listener events to the map container + addHooks: function () { + L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this); + L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this); + L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this); + if (this._detectIE()) { + L.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this); + L.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this); + L.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this); + L.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this); + + } else { + L.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this); + L.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this); + } + }, + + // @method removeHooks(): void + // Removes dom listener events from the map container + removeHooks: function () { + L.DomEvent.off(this._container, 'touchstart', this._onTouchStart); + L.DomEvent.off(this._container, 'touchend', this._onTouchEnd); + L.DomEvent.off(this._container, 'touchmove', this._onTouchMove); + if (this._detectIE()) { + L.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart); + L.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd); + L.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove); + L.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel); + } else { + L.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel); + L.DomEvent.off(this._container, 'touchleave', this._onTouchLeave); + } + }, + + _touchEvent: function (e, type) { + // #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events + // _filterClick is what leaflet uses as a workaround. + // This is a problem with more things than just android. Another problem is touchEnd has no touches in + // its touch list. + var touchEvent = {}; + if (typeof e.touches !== 'undefined') { + if (!e.touches.length) { + return; + } + touchEvent = e.touches[0]; + } else if (e.pointerType === 'touch') { + touchEvent = e; + if (!this._filterClick(e)) { + return; + } + } else { + return; + } + + var containerPoint = this._map.mouseEventToContainerPoint(touchEvent), + layerPoint = this._map.mouseEventToLayerPoint(touchEvent), + latlng = this._map.layerPointToLatLng(layerPoint); + + this._map.fire(type, { + latlng: latlng, + layerPoint: layerPoint, + containerPoint: containerPoint, + pageX: touchEvent.pageX, + pageY: touchEvent.pageY, + originalEvent: e + }); + }, + + /** Borrowed from Leaflet and modified for bool ops **/ + _filterClick: function (e) { + var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), + elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); + + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + L.DomEvent.stop(e); + return false; + } + L.DomEvent._lastClick = timeStamp; + return true; + }, + + _onTouchStart: function (e) { + if (!this._map._loaded) { + return; + } + + var type = 'touchstart'; + this._touchEvent(e, type); + + }, + + _onTouchEnd: function (e) { + if (!this._map._loaded) { + return; + } + + var type = 'touchend'; + this._touchEvent(e, type); + }, + + _onTouchCancel: function (e) { + if (!this._map._loaded) { + return; + } + + var type = 'touchcancel'; + if (this._detectIE()) { + type = 'pointercancel'; + } + this._touchEvent(e, type); + }, + + _onTouchLeave: function (e) { + if (!this._map._loaded) { + return; + } + + var type = 'touchleave'; + this._touchEvent(e, type); + }, + + _onTouchMove: function (e) { + if (!this._map._loaded) { + return; + } + + var type = 'touchmove'; + this._touchEvent(e, type); + }, + + _detectIE: function () { + var ua = window.navigator.userAgent; + + var msie = ua.indexOf('MSIE '); + if (msie > 0) { + // IE 10 or older => return version number + return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); + } + + var trident = ua.indexOf('Trident/'); + if (trident > 0) { + // IE 11 => return version number + var rv = ua.indexOf('rv:'); + return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); + } + + var edge = ua.indexOf('Edge/'); + if (edge > 0) { + // IE 12 => return version number + return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); + } + + // other browser + return false; + } +}); + +L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend); + + +/** + * @class L.Marker.Touch + * @aka Marker.Touch + * + * This isn't full Touch support. This is just to get markers to also support dom touch events after creation + * #TODO: find a better way of getting markers to support touch. + */ +L.Marker.Touch = L.Marker.extend({ + + _initInteraction: function () { + if (!this.addInteractiveTarget) { + // 0.7.x support + return this._initInteractionLegacy(); + } + // TODO this may need be updated to re-add touch events for 1.0+ + return L.Marker.prototype._initInteraction.apply(this); + }, + + // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js + // with the addition of the touch events + _initInteractionLegacy: function () { + + if (!this.options.clickable) { + return; + } + + // TODO refactor into something shared with Map/Path/etc. to DRY it up + + var icon = this._icon, + events = ['dblclick', + 'mousedown', + 'mouseover', + 'mouseout', + 'contextmenu', + 'touchstart', + 'touchend', + 'touchmove']; + if (this._detectIE) { + events.concat(['MSPointerDown', + 'MSPointerUp', + 'MSPointerMove', + 'MSPointerCancel']); + } else { + events.concat(['touchcancel']); + } + + L.DomUtil.addClass(icon, 'leaflet-clickable'); + L.DomEvent.on(icon, 'click', this._onMouseClick, this); + L.DomEvent.on(icon, 'keypress', this._onKeyPress, this); + + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(icon, events[i], this._fireMouseEvent, this); + } + + if (L.Handler.MarkerDrag) { + this.dragging = new L.Handler.MarkerDrag(this); + + if (this.options.draggable) { + this.dragging.enable(); + } + } + }, + + _detectIE: function () { + var ua = window.navigator.userAgent; + + var msie = ua.indexOf('MSIE '); + if (msie > 0) { + // IE 10 or older => return version number + return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); + } + + var trident = ua.indexOf('Trident/'); + if (trident > 0) { + // IE 11 => return version number + var rv = ua.indexOf('rv:'); + return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); + } + + var edge = ua.indexOf('Edge/'); + if (edge > 0) { + // IE 12 => return version number + return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); + } + + // other browser + return false; + } +}); + + + +/** + * @class L.LatLngUtil + * @aka LatLngUtil + */ +L.LatLngUtil = { + // Clones a LatLngs[], returns [][] + + // @method cloneLatLngs(LatLngs[]): L.LatLngs[] + // Clone the latLng point or points or nested points and return an array with those points + cloneLatLngs: function (latlngs) { + var clone = []; + for (var i = 0, l = latlngs.length; i < l; i++) { + // Check for nested array (Polyline/Polygon) + if (Array.isArray(latlngs[i])) { + clone.push(L.LatLngUtil.cloneLatLngs(latlngs[i])); + } else { + clone.push(this.cloneLatLng(latlngs[i])); + } + } + return clone; + }, + + // @method cloneLatLng(LatLng): L.LatLng + // Clone the latLng and return a new LatLng object. + cloneLatLng: function (latlng) { + return L.latLng(latlng.lat, latlng.lng); + } +}; + + + +(function() { + +var defaultPrecision = { + km: 2, + ha: 2, + m: 0, + mi: 2, + ac: 2, + yd: 0, + ft: 0, + nm: 2 +}; + + +/** + * @class L.GeometryUtil + * @aka GeometryUtil + */ +L.GeometryUtil = L.extend(L.GeometryUtil || {}, { + // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270 + + // @method geodesicArea(): number + geodesicArea: function (latLngs) { + var pointsCount = latLngs.length, + area = 0.0, + d2r = Math.PI / 180, + p1, p2; + + if (pointsCount > 2) { + for (var i = 0; i < pointsCount; i++) { + p1 = latLngs[i]; + p2 = latLngs[(i + 1) % pointsCount]; + area += ((p2.lng - p1.lng) * d2r) * + (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r)); + } + area = area * 6378137.0 * 6378137.0 / 2.0; + } + + return Math.abs(area); + }, + + // @method formattedNumber(n, precision): string + // Returns n in specified number format (if defined) and precision + formattedNumber: function (n, precision) { + var formatted = parseFloat(n).toFixed(precision), + format = L.drawLocal.format && L.drawLocal.format.numeric, + delimiters = format && format.delimiters, + thousands = delimiters && delimiters.thousands, + decimal = delimiters && delimiters.decimal; + + if (thousands || decimal) { + var splitValue = formatted.split('.'); + formatted = thousands ? splitValue[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousands) : splitValue[0]; + decimal = decimal || '.'; + if (splitValue.length > 1) { + formatted = formatted + decimal + splitValue[1]; + } + } + + return formatted; + }, + + // @method readableArea(area, isMetric, precision): string + // Returns a readable area string in yards or metric. + // The value will be rounded as defined by the precision option object. + readableArea: function (area, isMetric, precision) { + var areaStr, + units, + precision = L.Util.extend({}, defaultPrecision, precision); + + if (isMetric) { + units = ['ha', 'm']; + type = typeof isMetric; + if (type === 'string') { + units = [isMetric]; + } else if (type !== 'boolean') { + units = isMetric; + } + + if (area >= 1000000 && units.indexOf('km') !== -1) { + areaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²'; + } else if (area >= 10000 && units.indexOf('ha') !== -1) { + areaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha'; + } else { + areaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²'; + } + } else { + area /= 0.836127; // Square yards in 1 meter + + if (area >= 3097600) { //3097600 square yards in 1 square mile + areaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²'; + } else if (area >= 4840) { //4840 square yards in 1 acre + areaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres'; + } else { + areaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²'; + } + } + + return areaStr; + }, + + // @method readableDistance(distance, units): string + // Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string + // + // @alternative + // @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string + // Converts metric distance to distance string. + // The value will be rounded as defined by the precision option object. + readableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) { + var distanceStr, + units, + precision = L.Util.extend({}, defaultPrecision, precision); + + if (isMetric) { + units = typeof isMetric == 'string' ? isMetric : 'metric'; + } else if (isFeet) { + units = 'feet'; + } else if (isNauticalMile) { + units = 'nauticalMile'; + } else { + units = 'yards'; + } + + switch (units) { + case 'metric': + // show metres when distance is < 1km, then show km + if (distance > 1000) { + distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km'; + } else { + distanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m'; + } + break; + case 'feet': + distance *= 1.09361 * 3; + distanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft'; + + break; + case 'nauticalMile': + distance *= 0.53996; + distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm'; + break; + case 'yards': + default: + distance *= 1.09361; + + if (distance > 1760) { + distanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles'; + } else { + distanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd'; + } + break; + } + return distanceStr; + }, + + // @method isVersion07x(): boolean + // Returns true if the Leaflet version is 0.7.x, false otherwise. + isVersion07x: function(){ + var version = L.version.split("."); + //If Version is == 0.7.* + return parseInt(version[0], 10) === 0 && parseInt(version[1], 10) === 7; + }, +}); + +})(); + + + +/** + * @class L.LineUtil + * @aka Util + * @aka L.Utils + */ +L.Util.extend(L.LineUtil, { + + // @method segmentsIntersect(): boolean + // Checks to see if two line segments intersect. Does not handle degenerate cases. + // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf + segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) { + return this._checkCounterclockwise(p, p2, p3) !== + this._checkCounterclockwise(p1, p2, p3) && + this._checkCounterclockwise(p, p1, p2) !== + this._checkCounterclockwise(p, p1, p3); + }, + + // check to see if points are in counterclockwise order + _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x); + } +}); + + +/** + * @class L.Polyline + * @aka Polyline + */ +L.Polyline.include({ + + // @method intersects(): boolean + // Check to see if this polyline has any linesegments that intersect. + // NOTE: does not support detecting intersection for degenerate cases. + intersects: function () { + var points = this._getProjectedPoints(), + len = points ? points.length : 0, + i, p, p1; + + if (this._tooFewPointsForIntersection()) { + return false; + } + + for (i = len - 1; i >= 3; i--) { + p = points[i - 1]; + p1 = points[i]; + + + if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) { + return true; + } + } + + return false; + }, + + // @method newLatLngIntersects(): boolean + // Check for intersection if new latlng was added to this polyline. + // NOTE: does not support detecting intersection for degenerate cases. + newLatLngIntersects: function (latlng, skipFirst) { + // Cannot check a polyline for intersecting lats/lngs when not added to the map + if (!this._map) { + return false; + } + + return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst); + }, + + // @method newPointIntersects(): boolean + // Check for intersection if new point was added to this polyline. + // newPoint must be a layer point. + // NOTE: does not support detecting intersection for degenerate cases. + newPointIntersects: function (newPoint, skipFirst) { + var points = this._getProjectedPoints(), + len = points ? points.length : 0, + lastPoint = points ? points[len - 1] : null, + // The previous previous line segment. Previous line segment doesn't need testing. + maxIndex = len - 2; + + if (this._tooFewPointsForIntersection(1)) { + return false; + } + + return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0); + }, + + // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these). + // Cannot have intersection when < 3 line segments (< 4 points) + _tooFewPointsForIntersection: function (extraPoints) { + var points = this._getProjectedPoints(), + len = points ? points.length : 0; + // Increment length by extraPoints if present + len += extraPoints || 0; + + return !points || len <= 3; + }, + + // Checks a line segment intersections with any line segments before its predecessor. + // Don't need to check the predecessor as will never intersect. + _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) { + var points = this._getProjectedPoints(), + p2, p3; + + minIndex = minIndex || 0; + + // Check all previous line segments (beside the immediately previous) for intersections + for (var j = maxIndex; j > minIndex; j--) { + p2 = points[j - 1]; + p3 = points[j]; + + if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) { + return true; + } + } + + return false; + }, + + _getProjectedPoints: function () { + if (!this._defaultShape) { + return this._originalPoints; + } + var points = [], + _shape = this._defaultShape(); + + for (var i = 0; i < _shape.length; i++) { + points.push(this._map.latLngToLayerPoint(_shape[i])); + } + return points; + } +}); + + + +/** + * @class L.Polygon + * @aka Polygon + */ +L.Polygon.include({ + + // @method intersects(): boolean + // Checks a polygon for any intersecting line segments. Ignores holes. + intersects: function () { + var polylineIntersects, + points = this._getProjectedPoints(), + len, firstPoint, lastPoint, maxIndex; + + if (this._tooFewPointsForIntersection()) { + return false; + } + + polylineIntersects = L.Polyline.prototype.intersects.call(this); + + // If already found an intersection don't need to check for any more. + if (polylineIntersects) { + return true; + } + + len = points.length; + firstPoint = points[0]; + lastPoint = points[len - 1]; + maxIndex = len - 2; + + // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1) + return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1); + } +}); + + + +/** + * @class L.Control.Draw + * @aka L.Draw + */ +L.Control.Draw = L.Control.extend({ + + // Options + options: { + position: 'topleft', + draw: {}, + edit: false + }, + + // @method initialize(): void + // Initializes draw control, toolbars from the options + initialize: function (options) { + if (L.version < '0.7') { + throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/'); + } + + L.Control.prototype.initialize.call(this, options); + + var toolbar; + + this._toolbars = {}; + + // Initialize toolbars + if (L.DrawToolbar && this.options.draw) { + toolbar = new L.DrawToolbar(this.options.draw); + + this._toolbars[L.DrawToolbar.TYPE] = toolbar; + + // Listen for when toolbar is enabled + this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this); + } + + if (L.EditToolbar && this.options.edit) { + toolbar = new L.EditToolbar(this.options.edit); + + this._toolbars[L.EditToolbar.TYPE] = toolbar; + + // Listen for when toolbar is enabled + this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this); + } + L.toolbar = this; //set global var for editing the toolbar + }, + + // @method onAdd(): container + // Adds the toolbar container to the map + onAdd: function (map) { + var container = L.DomUtil.create('div', 'leaflet-draw'), + addedTopClass = false, + topClassName = 'leaflet-draw-toolbar-top', + toolbarContainer; + + for (var toolbarId in this._toolbars) { + if (this._toolbars.hasOwnProperty(toolbarId)) { + toolbarContainer = this._toolbars[toolbarId].addToolbar(map); + + if (toolbarContainer) { + // Add class to the first toolbar to remove the margin + if (!addedTopClass) { + if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) { + L.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName); + } + addedTopClass = true; + } + + container.appendChild(toolbarContainer); + } + } + } + + return container; + }, + + // @method onRemove(): void + // Removes the toolbars from the map toolbar container + onRemove: function () { + for (var toolbarId in this._toolbars) { + if (this._toolbars.hasOwnProperty(toolbarId)) { + this._toolbars[toolbarId].removeToolbar(); + } + } + }, + + // @method setDrawingOptions(options): void + // Sets options to all toolbar instances + setDrawingOptions: function (options) { + for (var toolbarId in this._toolbars) { + if (this._toolbars[toolbarId] instanceof L.DrawToolbar) { + this._toolbars[toolbarId].setOptions(options); + } + } + }, + + _toolbarEnabled: function (e) { + var enabledToolbar = e.target; + + for (var toolbarId in this._toolbars) { + if (this._toolbars[toolbarId] !== enabledToolbar) { + this._toolbars[toolbarId].disable(); + } + } + } +}); + +L.Map.mergeOptions({ + drawControlTooltips: true, + drawControl: false +}); + +L.Map.addInitHook(function () { + if (this.options.drawControl) { + this.drawControl = new L.Control.Draw(); + this.addControl(this.drawControl); + } +}); + + + +/** + * @class L.Draw.Toolbar + * @aka Toolbar + * + * The toolbar class of the API — it is used to create the ui + * This will be depreciated + * + * @example + * + * ```js + * var toolbar = L.Toolbar(); + * toolbar.addToolbar(map); + * ``` + * + * ### Disabling a toolbar + * + * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false. + * + * ```js + * var drawControl = new L.Control.Draw({ + * draw: false, + * edit: { + * featureGroup: editableLayers + * } + * }); + * ``` + * + * ### Disabling a toolbar item + * + * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and + * markers. It also turns off the ability to edit layers. + * + * ```js + * var drawControl = new L.Control.Draw({ + * draw: { + * polygon: false, + * marker: false + * }, + * edit: { + * featureGroup: editableLayers, + * edit: false + * } + * }); + * ``` + */ +L.Toolbar = L.Class.extend({ + // @section Methods for modifying the toolbar + + // @method initialize(options): void + // Toolbar constructor + initialize: function (options) { + L.setOptions(this, options); + + this._modes = {}; + this._actionButtons = []; + this._activeMode = null; + + var version = L.version.split("."); + //If Version is >= 1.2.0 + if(parseInt(version[0],10) === 1 && parseInt(version[1],10) >= 2 ) { + L.Toolbar.include(L.Evented.prototype); + } else { + L.Toolbar.include(L.Mixin.Events); + } + }, + + // @method enabled(): boolean + // Gets a true/false of whether the toolbar is enabled + enabled: function () { + return this._activeMode !== null; + }, + + // @method disable(): void + // Disables the toolbar + disable: function () { + if (!this.enabled()) { + return; + } + + this._activeMode.handler.disable(); + }, + + // @method addToolbar(map): L.DomUtil + // Adds the toolbar to the map and returns the toolbar dom element + addToolbar: function (map) { + var container = L.DomUtil.create('div', 'leaflet-draw-section'), + buttonIndex = 0, + buttonClassPrefix = this._toolbarClass || '', + modeHandlers = this.getModeHandlers(map), + i; + + this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar'); + this._map = map; + + for (i = 0; i < modeHandlers.length; i++) { + if (modeHandlers[i].enabled) { + this._initModeHandler( + modeHandlers[i].handler, + this._toolbarContainer, + buttonIndex++, + buttonClassPrefix, + modeHandlers[i].title + ); + } + } + + // if no buttons were added, do not add the toolbar + if (!buttonIndex) { + return; + } + + // Save button index of the last button, -1 as we would have ++ after the last button + this._lastButtonIndex = --buttonIndex; + + // Create empty actions part of the toolbar + this._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions'); + + // Add draw and cancel containers to the control container + container.appendChild(this._toolbarContainer); + container.appendChild(this._actionsContainer); + + return container; + }, + + // @method removeToolbar(): void + // Removes the toolbar and drops the handler event listeners + removeToolbar: function () { + // Dispose each handler + for (var handlerId in this._modes) { + if (this._modes.hasOwnProperty(handlerId)) { + // Unbind handler button + this._disposeButton( + this._modes[handlerId].button, + this._modes[handlerId].handler.enable, + this._modes[handlerId].handler + ); + + // Make sure is disabled + this._modes[handlerId].handler.disable(); + + // Unbind handler + this._modes[handlerId].handler + .off('enabled', this._handlerActivated, this) + .off('disabled', this._handlerDeactivated, this); + } + } + this._modes = {}; + + // Dispose the actions toolbar + for (var i = 0, l = this._actionButtons.length; i < l; i++) { + this._disposeButton( + this._actionButtons[i].button, + this._actionButtons[i].callback, + this + ); + } + this._actionButtons = []; + this._actionsContainer = null; + }, + + _initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) { + var type = handler.type; + + this._modes[type] = {}; + + this._modes[type].handler = handler; + + this._modes[type].button = this._createButton({ + type: type, + title: buttonTitle, + className: classNamePredix + '-' + type, + container: container, + callback: this._modes[type].handler.enable, + context: this._modes[type].handler + }); + + this._modes[type].buttonIndex = buttonIndex; + + this._modes[type].handler + .on('enabled', this._handlerActivated, this) + .on('disabled', this._handlerDeactivated, this); + }, + + /* Detect iOS based on browser User Agent, based on: + * http://stackoverflow.com/a/9039885 */ + _detectIOS: function () { + var iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream); + return iOS; + }, + + _createButton: function (options) { + + var link = L.DomUtil.create('a', options.className || '', options.container); + // Screen reader tag + var sr = L.DomUtil.create('span', 'sr-only', options.container); + + link.href = '#'; + link.appendChild(sr); + + if (options.title) { + link.title = options.title; + sr.innerHTML = options.title; + } + + if (options.text) { + link.innerHTML = options.text; + sr.innerHTML = options.text; + } + + /* iOS does not use click events */ + var buttonEvent = this._detectIOS() ? 'touchstart' : 'click'; + + L.DomEvent + .on(link, 'click', L.DomEvent.stopPropagation) + .on(link, 'mousedown', L.DomEvent.stopPropagation) + .on(link, 'dblclick', L.DomEvent.stopPropagation) + .on(link, 'touchstart', L.DomEvent.stopPropagation) + .on(link, 'click', L.DomEvent.preventDefault) + .on(link, buttonEvent, options.callback, options.context); + + return link; + }, + + _disposeButton: function (button, callback) { + /* iOS does not use click events */ + var buttonEvent = this._detectIOS() ? 'touchstart' : 'click'; + + L.DomEvent + .off(button, 'click', L.DomEvent.stopPropagation) + .off(button, 'mousedown', L.DomEvent.stopPropagation) + .off(button, 'dblclick', L.DomEvent.stopPropagation) + .off(button, 'touchstart', L.DomEvent.stopPropagation) + .off(button, 'click', L.DomEvent.preventDefault) + .off(button, buttonEvent, callback); + }, + + _handlerActivated: function (e) { + // Disable active mode (if present) + this.disable(); + + // Cache new active feature + this._activeMode = this._modes[e.handler]; + + L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled'); + + this._showActionsToolbar(); + + this.fire('enable'); + }, + + _handlerDeactivated: function () { + this._hideActionsToolbar(); + + L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled'); + + this._activeMode = null; + + this.fire('disable'); + }, + + _createActions: function (handler) { + var container = this._actionsContainer, + buttons = this.getActions(handler), + l = buttons.length, + li, di, dl, button; + + // Dispose the actions toolbar (todo: dispose only not used buttons) + for (di = 0, dl = this._actionButtons.length; di < dl; di++) { + this._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback); + } + this._actionButtons = []; + + // Remove all old buttons + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + for (var i = 0; i < l; i++) { + if ('enabled' in buttons[i] && !buttons[i].enabled) { + continue; + } + + li = L.DomUtil.create('li', '', container); + + button = this._createButton({ + title: buttons[i].title, + text: buttons[i].text, + container: li, + callback: buttons[i].callback, + context: buttons[i].context + }); + + this._actionButtons.push({ + button: button, + callback: buttons[i].callback + }); + } + }, + + _showActionsToolbar: function () { + var buttonIndex = this._activeMode.buttonIndex, + lastButtonIndex = this._lastButtonIndex, + toolbarPosition = this._activeMode.button.offsetTop - 1; + + // Recreate action buttons on every click + this._createActions(this._activeMode.handler); + + // Correctly position the cancel button + this._actionsContainer.style.top = toolbarPosition + 'px'; + + if (buttonIndex === 0) { + L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop'); + L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top'); + } + + if (buttonIndex === lastButtonIndex) { + L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); + L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); + } + + this._actionsContainer.style.display = 'block'; + this._map.fire(L.Draw.Event.TOOLBAROPENED); + }, + + _hideActionsToolbar: function () { + this._actionsContainer.style.display = 'none'; + + L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop'); + L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom'); + L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top'); + L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom'); + this._map.fire(L.Draw.Event.TOOLBARCLOSED); + } +}); + + + +L.Draw = L.Draw || {}; +/** + * @class L.Draw.Tooltip + * @aka Tooltip + * + * The tooltip class — it is used to display the tooltip while drawing + * This will be depreciated + * + * @example + * + * ```js + * var tooltip = L.Draw.Tooltip(); + * ``` + * + */ +L.Draw.Tooltip = L.Class.extend({ + + // @section Methods for modifying draw state + + // @method initialize(map): void + // Tooltip constructor + initialize: function (map) { + this._map = map; + this._popupPane = map._panes.popupPane; + this._visible = false; + + this._container = map.options.drawControlTooltips ? + L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null; + this._singleLineLabel = false; + + this._map.on('mouseout', this._onMouseOut, this); + }, + + // @method dispose(): void + // Remove Tooltip DOM and unbind events + dispose: function () { + this._map.off('mouseout', this._onMouseOut, this); + + if (this._container) { + this._popupPane.removeChild(this._container); + this._container = null; + } + }, + + // @method updateContent(labelText): this + // Changes the tooltip text to string in function call + updateContent: function (labelText) { + if (!this._container) { + return this; + } + labelText.subtext = labelText.subtext || ''; + + // update the vertical position (only if changed) + if (labelText.subtext.length === 0 && !this._singleLineLabel) { + L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single'); + this._singleLineLabel = true; + } + else if (labelText.subtext.length > 0 && this._singleLineLabel) { + L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single'); + this._singleLineLabel = false; + } + + this._container.innerHTML = + (labelText.subtext.length > 0 ? + '<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') + + '<span>' + labelText.text + '</span>'; + + if (!labelText.text && !labelText.subtext) { + this._visible = false; + this._container.style.visibility = 'hidden'; + } else { + this._visible = true; + this._container.style.visibility = 'inherit'; + } + + return this; + }, + + // @method updatePosition(latlng): this + // Changes the location of the tooltip + updatePosition: function (latlng) { + var pos = this._map.latLngToLayerPoint(latlng), + tooltipContainer = this._container; + + if (this._container) { + if (this._visible) { + tooltipContainer.style.visibility = 'inherit'; + } + L.DomUtil.setPosition(tooltipContainer, pos); + } + + return this; + }, + + // @method showAsError(): this + // Applies error class to tooltip + showAsError: function () { + if (this._container) { + L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip'); + } + return this; + }, + + // @method removeError(): this + // Removes the error class from the tooltip + removeError: function () { + if (this._container) { + L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip'); + } + return this; + }, + + _onMouseOut: function () { + if (this._container) { + this._container.style.visibility = 'hidden'; + } + } +}); + + + +/** + * @class L.DrawToolbar + * @aka Toolbar + */ +L.DrawToolbar = L.Toolbar.extend({ + + statics: { + TYPE: 'draw' + }, + + options: { + polyline: {}, + polygon: {}, + rectangle: {}, + circle: {}, + marker: {}, + circlemarker: {} + }, + + // @method initialize(): void + initialize: function (options) { + // Ensure that the options are merged correctly since L.extend is only shallow + for (var type in this.options) { + if (this.options.hasOwnProperty(type)) { + if (options[type]) { + options[type] = L.extend({}, this.options[type], options[type]); + } + } + } + + this._toolbarClass = 'leaflet-draw-draw'; + L.Toolbar.prototype.initialize.call(this, options); + }, + + // @method getModeHandlers(): object + // Get mode handlers information + getModeHandlers: function (map) { + return [ + { + enabled: this.options.polyline, + handler: new L.Draw.Polyline(map, this.options.polyline), + title: L.drawLocal.draw.toolbar.buttons.polyline + }, + { + enabled: this.options.polygon, + handler: new L.Draw.Polygon(map, this.options.polygon), + title: L.drawLocal.draw.toolbar.buttons.polygon + }, + { + enabled: this.options.rectangle, + handler: new L.Draw.Rectangle(map, this.options.rectangle), + title: L.drawLocal.draw.toolbar.buttons.rectangle + }, + { + enabled: this.options.circle, + handler: new L.Draw.Circle(map, this.options.circle), + title: L.drawLocal.draw.toolbar.buttons.circle + }, + { + enabled: this.options.marker, + handler: new L.Draw.Marker(map, this.options.marker), + title: L.drawLocal.draw.toolbar.buttons.marker + }, + { + enabled: this.options.circlemarker, + handler: new L.Draw.CircleMarker(map, this.options.circlemarker), + title: L.drawLocal.draw.toolbar.buttons.circlemarker + } + ]; + }, + + // @method getActions(): object + // Get action information + getActions: function (handler) { + return [ + { + enabled: handler.completeShape, + title: L.drawLocal.draw.toolbar.finish.title, + text: L.drawLocal.draw.toolbar.finish.text, + callback: handler.completeShape, + context: handler + }, + { + enabled: handler.deleteLastVertex, + title: L.drawLocal.draw.toolbar.undo.title, + text: L.drawLocal.draw.toolbar.undo.text, + callback: handler.deleteLastVertex, + context: handler + }, + { + title: L.drawLocal.draw.toolbar.actions.title, + text: L.drawLocal.draw.toolbar.actions.text, + callback: this.disable, + context: this + } + ]; + }, + + // @method setOptions(): void + // Sets the options to the toolbar + setOptions: function (options) { + L.setOptions(this, options); + + for (var type in this._modes) { + if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) { + this._modes[type].handler.setOptions(options[type]); + } + } + } +}); + + + +/*L.Map.mergeOptions({ + editControl: true + });*/ +/** + * @class L.EditToolbar + * @aka EditToolbar + */ +L.EditToolbar = L.Toolbar.extend({ + statics: { + TYPE: 'edit' + }, + + options: { + edit: { + selectedPathOptions: { + dashArray: '10, 10', + + fill: true, + fillColor: '#fe57a1', + fillOpacity: 0.1, + + // Whether to user the existing layers color + maintainColor: false + } + }, + remove: {}, + poly: null, + featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */ + }, + + // @method intialize(): void + initialize: function (options) { + // Need to set this manually since null is an acceptable value here + if (options.edit) { + if (typeof options.edit.selectedPathOptions === 'undefined') { + options.edit.selectedPathOptions = this.options.edit.selectedPathOptions; + } + options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions); + } + + if (options.remove) { + options.remove = L.extend({}, this.options.remove, options.remove); + } + + if (options.poly) { + options.poly = L.extend({}, this.options.poly, options.poly); + } + + this._toolbarClass = 'leaflet-draw-edit'; + L.Toolbar.prototype.initialize.call(this, options); + + this._selectedFeatureCount = 0; + }, + + // @method getModeHandlers(): object + // Get mode handlers information + getModeHandlers: function (map) { + var featureGroup = this.options.featureGroup; + return [ + { + enabled: this.options.edit, + handler: new L.EditToolbar.Edit(map, { + featureGroup: featureGroup, + selectedPathOptions: this.options.edit.selectedPathOptions, + poly: this.options.poly + }), + title: L.drawLocal.edit.toolbar.buttons.edit + }, + { + enabled: this.options.remove, + handler: new L.EditToolbar.Delete(map, { + featureGroup: featureGroup + }), + title: L.drawLocal.edit.toolbar.buttons.remove + } + ]; + }, + + // @method getActions(): object + // Get actions information + getActions: function (handler) { + var actions = [ + { + title: L.drawLocal.edit.toolbar.actions.save.title, + text: L.drawLocal.edit.toolbar.actions.save.text, + callback: this._save, + context: this + }, + { + title: L.drawLocal.edit.toolbar.actions.cancel.title, + text: L.drawLocal.edit.toolbar.actions.cancel.text, + callback: this.disable, + context: this + } + ]; + + if (handler.removeAllLayers) { + actions.push({ + title: L.drawLocal.edit.toolbar.actions.clearAll.title, + text: L.drawLocal.edit.toolbar.actions.clearAll.text, + callback: this._clearAllLayers, + context: this + }); + } + + return actions; + }, + + // @method addToolbar(map): L.DomUtil + // Adds the toolbar to the map + addToolbar: function (map) { + var container = L.Toolbar.prototype.addToolbar.call(this, map); + + this._checkDisabled(); + + this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this); + + return container; + }, + + // @method removeToolbar(): void + // Removes the toolbar from the map + removeToolbar: function () { + this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this); + + L.Toolbar.prototype.removeToolbar.call(this); + }, + + // @method disable(): void + // Disables the toolbar + disable: function () { + if (!this.enabled()) { + return; + } + + this._activeMode.handler.revertLayers(); + + L.Toolbar.prototype.disable.call(this); + }, + + _save: function () { + this._activeMode.handler.save(); + if (this._activeMode) { + this._activeMode.handler.disable(); + } + }, + + _clearAllLayers:function(){ + this._activeMode.handler.removeAllLayers(); + if (this._activeMode) { + this._activeMode.handler.disable(); + } + }, + + _checkDisabled: function () { + var featureGroup = this.options.featureGroup, + hasLayers = featureGroup.getLayers().length !== 0, + button; + + if (this.options.edit) { + button = this._modes[L.EditToolbar.Edit.TYPE].button; + + if (hasLayers) { + L.DomUtil.removeClass(button, 'leaflet-disabled'); + } else { + L.DomUtil.addClass(button, 'leaflet-disabled'); + } + + button.setAttribute( + 'title', + hasLayers ? + L.drawLocal.edit.toolbar.buttons.edit + : L.drawLocal.edit.toolbar.buttons.editDisabled + ); + } + + if (this.options.remove) { + button = this._modes[L.EditToolbar.Delete.TYPE].button; + + if (hasLayers) { + L.DomUtil.removeClass(button, 'leaflet-disabled'); + } else { + L.DomUtil.addClass(button, 'leaflet-disabled'); + } + + button.setAttribute( + 'title', + hasLayers ? + L.drawLocal.edit.toolbar.buttons.remove + : L.drawLocal.edit.toolbar.buttons.removeDisabled + ); + } + } +}); + + + +/** + * @class L.EditToolbar.Edit + * @aka EditToolbar.Edit + */ +L.EditToolbar.Edit = L.Handler.extend({ + statics: { + TYPE: 'edit' + }, + + // @method intialize(): void + initialize: function (map, options) { + L.Handler.prototype.initialize.call(this, map); + + L.setOptions(this, options); + + // Store the selectable layer group for ease of access + this._featureGroup = options.featureGroup; + + if (!(this._featureGroup instanceof L.FeatureGroup)) { + throw new Error('options.featureGroup must be a L.FeatureGroup'); + } + + this._uneditedLayerProps = {}; + + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.EditToolbar.Edit.TYPE; + + var version = L.version.split("."); + //If Version is >= 1.2.0 + if(parseInt(version[0],10) === 1 && parseInt(version[1],10) >= 2 ) { + L.EditToolbar.Edit.include(L.Evented.prototype); + } else { + L.EditToolbar.Edit.include(L.Mixin.Events); + } + }, + + // @method enable(): void + // Enable the edit toolbar + enable: function () { + if (this._enabled || !this._hasAvailableLayers()) { + return; + } + this.fire('enabled', { handler: this.type }); + //this disable other handlers + + this._map.fire(L.Draw.Event.EDITSTART, { handler: this.type }); + //allow drawLayer to be updated before beginning edition. + + L.Handler.prototype.enable.call(this); + this._featureGroup + .on('layeradd', this._enableLayerEdit, this) + .on('layerremove', this._disableLayerEdit, this); + }, + + // @method disable(): void + // Disable the edit toolbar + disable: function () { + if (!this._enabled) { + return; + } + this._featureGroup + .off('layeradd', this._enableLayerEdit, this) + .off('layerremove', this._disableLayerEdit, this); + L.Handler.prototype.disable.call(this); + this._map.fire(L.Draw.Event.EDITSTOP, { handler: this.type }); + this.fire('disabled', { handler: this.type }); + }, + + // @method addHooks(): void + // Add listener hooks for this handler + addHooks: function () { + var map = this._map; + + if (map) { + map.getContainer().focus(); + + this._featureGroup.eachLayer(this._enableLayerEdit, this); + + this._tooltip = new L.Draw.Tooltip(this._map); + this._tooltip.updateContent({ + text: L.drawLocal.edit.handlers.edit.tooltip.text, + subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext + }); + + // Quickly access the tooltip to update for intersection checking + map._editTooltip = this._tooltip; + + this._updateTooltip(); + + this._map + .on('mousemove', this._onMouseMove, this) + .on('touchmove', this._onMouseMove, this) + .on('MSPointerMove', this._onMouseMove, this) + .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); + } + }, + + // @method removeHooks(): void + // Remove listener hooks for this handler + removeHooks: function () { + if (this._map) { + // Clean up selected layers. + this._featureGroup.eachLayer(this._disableLayerEdit, this); + + // Clear the backups of the original layers + this._uneditedLayerProps = {}; + + this._tooltip.dispose(); + this._tooltip = null; + + this._map + .off('mousemove', this._onMouseMove, this) + .off('touchmove', this._onMouseMove, this) + .off('MSPointerMove', this._onMouseMove, this) + .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); + } + }, + + // @method revertLayers(): void + // Revert each layer's geometry changes + revertLayers: function () { + this._featureGroup.eachLayer(function (layer) { + this._revertLayer(layer); + }, this); + }, + + // @method save(): void + // Save the layer geometries + save: function () { + var editedLayers = new L.LayerGroup(); + this._featureGroup.eachLayer(function (layer) { + if (layer.edited) { + editedLayers.addLayer(layer); + layer.edited = false; + } + }); + this._map.fire(L.Draw.Event.EDITED, { layers: editedLayers }); + }, + + _backupLayer: function (layer) { + var id = L.Util.stamp(layer); + + if (!this._uneditedLayerProps[id]) { + // Polyline, Polygon or Rectangle + if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { + this._uneditedLayerProps[id] = { + latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs()) + }; + } else if (layer instanceof L.Circle) { + this._uneditedLayerProps[id] = { + latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()), + radius: layer.getRadius() + }; + } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker + this._uneditedLayerProps[id] = { + latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()) + }; + } + } + }, + + _getTooltipText: function () { + return ({ + text: L.drawLocal.edit.handlers.edit.tooltip.text, + subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext + }); + }, + + _updateTooltip: function () { + this._tooltip.updateContent(this._getTooltipText()); + }, + + _revertLayer: function (layer) { + var id = L.Util.stamp(layer); + layer.edited = false; + if (this._uneditedLayerProps.hasOwnProperty(id)) { + // Polyline, Polygon or Rectangle + if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { + layer.setLatLngs(this._uneditedLayerProps[id].latlngs); + } else if (layer instanceof L.Circle) { + layer.setLatLng(this._uneditedLayerProps[id].latlng); + layer.setRadius(this._uneditedLayerProps[id].radius); + } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker + layer.setLatLng(this._uneditedLayerProps[id].latlng); + } + + layer.fire('revert-edited', { layer: layer }); + } + }, + + _enableLayerEdit: function (e) { + var layer = e.layer || e.target || e, + pathOptions, poly; + + // Back up this layer (if haven't before) + this._backupLayer(layer); + + if (this.options.poly) { + poly = L.Util.extend({}, this.options.poly); + layer.options.poly = poly; + } + + // Set different style for editing mode + if (this.options.selectedPathOptions) { + pathOptions = L.Util.extend({}, this.options.selectedPathOptions); + + // Use the existing color of the layer + if (pathOptions.maintainColor) { + pathOptions.color = layer.options.color; + pathOptions.fillColor = layer.options.fillColor; + } + + layer.options.original = L.extend({}, layer.options); + layer.options.editing = pathOptions; + + } + + if (layer instanceof L.Marker) { + if (layer.editing) { + layer.editing.enable(); + } + layer.dragging.enable(); + layer + .on('dragend', this._onMarkerDragEnd) + // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again. + .on('touchmove', this._onTouchMove, this) + .on('MSPointerMove', this._onTouchMove, this) + .on('touchend', this._onMarkerDragEnd, this) + .on('MSPointerUp', this._onMarkerDragEnd, this); + } else { + layer.editing.enable(); + } + }, + + _disableLayerEdit: function (e) { + var layer = e.layer || e.target || e; + + layer.edited = false; + if (layer.editing) { + layer.editing.disable(); + } + + delete layer.options.editing; + delete layer.options.original; + // Reset layer styles to that of before select + if (this._selectedPathOptions) { + if (layer instanceof L.Marker) { + this._toggleMarkerHighlight(layer); + } else { + // reset the layer style to what is was before being selected + layer.setStyle(layer.options.previousOptions); + // remove the cached options for the layer object + delete layer.options.previousOptions; + } + } + + if (layer instanceof L.Marker) { + layer.dragging.disable(); + layer + .off('dragend', this._onMarkerDragEnd, this) + .off('touchmove', this._onTouchMove, this) + .off('MSPointerMove', this._onTouchMove, this) + .off('touchend', this._onMarkerDragEnd, this) + .off('MSPointerUp', this._onMarkerDragEnd, this); + } else { + layer.editing.disable(); + } + }, + + _onMouseMove: function (e) { + this._tooltip.updatePosition(e.latlng); + }, + + _onMarkerDragEnd: function (e) { + var layer = e.target; + layer.edited = true; + this._map.fire(L.Draw.Event.EDITMOVE, { layer: layer }); + }, + + _onTouchMove: function (e) { + var touchEvent = e.originalEvent.changedTouches[0], + layerPoint = this._map.mouseEventToLayerPoint(touchEvent), + latlng = this._map.layerPointToLatLng(layerPoint); + e.target.setLatLng(latlng); + }, + + _hasAvailableLayers: function () { + return this._featureGroup.getLayers().length !== 0; + } +}); + + + +/** + * @class L.EditToolbar.Delete + * @aka EditToolbar.Delete + */ +L.EditToolbar.Delete = L.Handler.extend({ + statics: { + TYPE: 'remove' // not delete as delete is reserved in js + }, + + // @method intialize(): void + initialize: function (map, options) { + L.Handler.prototype.initialize.call(this, map); + + L.Util.setOptions(this, options); + + // Store the selectable layer group for ease of access + this._deletableLayers = this.options.featureGroup; + + if (!(this._deletableLayers instanceof L.FeatureGroup)) { + throw new Error('options.featureGroup must be a L.FeatureGroup'); + } + + // Save the type so super can fire, need to do this as cannot do this.TYPE :( + this.type = L.EditToolbar.Delete.TYPE; + + var version = L.version.split("."); + //If Version is >= 1.2.0 + if(parseInt(version[0],10) === 1 && parseInt(version[1],10) >= 2 ) { + L.EditToolbar.Delete.include(L.Evented.prototype); + } else { + L.EditToolbar.Delete.include(L.Mixin.Events); + } + + }, + + // @method enable(): void + // Enable the delete toolbar + enable: function () { + if (this._enabled || !this._hasAvailableLayers()) { + return; + } + this.fire('enabled', { handler: this.type }); + + this._map.fire(L.Draw.Event.DELETESTART, { handler: this.type }); + + L.Handler.prototype.enable.call(this); + + this._deletableLayers + .on('layeradd', this._enableLayerDelete, this) + .on('layerremove', this._disableLayerDelete, this); + }, + + // @method disable(): void + // Disable the delete toolbar + disable: function () { + if (!this._enabled) { + return; + } + + this._deletableLayers + .off('layeradd', this._enableLayerDelete, this) + .off('layerremove', this._disableLayerDelete, this); + + L.Handler.prototype.disable.call(this); + + this._map.fire(L.Draw.Event.DELETESTOP, { handler: this.type }); + + this.fire('disabled', { handler: this.type }); + }, + + // @method addHooks(): void + // Add listener hooks to this handler + addHooks: function () { + var map = this._map; + + if (map) { + map.getContainer().focus(); + + this._deletableLayers.eachLayer(this._enableLayerDelete, this); + this._deletedLayers = new L.LayerGroup(); + + this._tooltip = new L.Draw.Tooltip(this._map); + this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text }); + + this._map.on('mousemove', this._onMouseMove, this); + } + }, + + // @method removeHooks(): void + // Remove listener hooks from this handler + removeHooks: function () { + if (this._map) { + this._deletableLayers.eachLayer(this._disableLayerDelete, this); + this._deletedLayers = null; + + this._tooltip.dispose(); + this._tooltip = null; + + this._map.off('mousemove', this._onMouseMove, this); + } + }, + + // @method revertLayers(): void + // Revert the deleted layers back to their prior state. + revertLayers: function () { + // Iterate of the deleted layers and add them back into the featureGroup + this._deletedLayers.eachLayer(function (layer) { + this._deletableLayers.addLayer(layer); + layer.fire('revert-deleted', { layer: layer }); + }, this); + }, + + // @method save(): void + // Save deleted layers + save: function () { + this._map.fire(L.Draw.Event.DELETED, { layers: this._deletedLayers }); + }, + + // @method removeAllLayers(): void + // Remove all delateable layers + removeAllLayers: function(){ + // Iterate of the delateable layers and add remove them + this._deletableLayers.eachLayer(function (layer) { + this._removeLayer({layer:layer}); + }, this); + this.save(); + }, + + _enableLayerDelete: function (e) { + var layer = e.layer || e.target || e; + + layer.on('click', this._removeLayer, this); + }, + + _disableLayerDelete: function (e) { + var layer = e.layer || e.target || e; + + layer.off('click', this._removeLayer, this); + + // Remove from the deleted layers so we can't accidentally revert if the user presses cancel + this._deletedLayers.removeLayer(layer); + }, + + _removeLayer: function (e) { + var layer = e.layer || e.target || e; + + this._deletableLayers.removeLayer(layer); + + this._deletedLayers.addLayer(layer); + + layer.fire('deleted'); + }, + + _onMouseMove: function (e) { + this._tooltip.updatePosition(e.latlng); + }, + + _hasAvailableLayers: function () { + return this._deletableLayers.getLayers().length !== 0; + } +}); + + + +}(window, document)); +//# sourceMappingURL=leaflet.draw-src.map diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet.draw.js b/themes/bootstrap3/js/vendor/leaflet/leaflet.draw.js new file mode 100644 index 0000000000000000000000000000000000000000..f8d1d67360b07dfbc14babc0a554de6785e7e4eb --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet.draw.js @@ -0,0 +1,31 @@ +/* + Leaflet.draw 0.4.13, a plugin that adds drawing and editing tools to Leaflet powered maps. + (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet + + Copyright 2012-2017 Jacob Toye and Leaflet + + 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. + + https://github.com/Leaflet/Leaflet.draw + http://leafletjs.com + */ +!function(t,e,i){function o(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}L.drawVersion="0.4.13",L.Draw={},L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},finish:{title:"Finish drawing",text:"Finish"},undo:{title:"Delete last point drawn",text:"Delete last point"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker",circlemarker:"Draw a circlemarker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."},radius:"Radius"},circlemarker:{tooltip:{start:"Click map to place circle marker."}},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"<strong>Error:</strong> shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes",text:"Save"},cancel:{title:"Cancel editing, discards all changes",text:"Cancel"},clearAll:{title:"Clear all layers",text:"Clear All"}},buttons:{edit:"Edit layers",editDisabled:"No layers to edit",remove:"Delete layers",removeDisabled:"No layers to delete"}},handlers:{edit:{tooltip:{text:"Drag handles or markers to edit features.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove."}}}}},L.Draw.Event={},L.Draw.Event.CREATED="draw:created",L.Draw.Event.EDITED="draw:edited",L.Draw.Event.DELETED="draw:deleted",L.Draw.Event.DRAWSTART="draw:drawstart",L.Draw.Event.DRAWSTOP="draw:drawstop",L.Draw.Event.DRAWVERTEX="draw:drawvertex",L.Draw.Event.EDITSTART="draw:editstart",L.Draw.Event.EDITMOVE="draw:editmove",L.Draw.Event.EDITRESIZE="draw:editresize",L.Draw.Event.EDITVERTEX="draw:editvertex",L.Draw.Event.EDITSTOP="draw:editstop",L.Draw.Event.DELETESTART="draw:deletestart",L.Draw.Event.DELETESTOP="draw:deletestop",L.Draw.Event.TOOLBAROPENED="draw:toolbaropened",L.Draw.Event.TOOLBARCLOSED="draw:toolbarclosed",L.Draw=L.Draw||{},L.Draw.Feature=L.Handler.extend({initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.setOptions(this,e);var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.Draw.Feature.include(L.Evented.prototype):L.Draw.Feature.include(L.Mixin.Events)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DRAWSTART,{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DRAWSTOP,{layerType:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Draw.Tooltip(this._map),L.DomEvent.on(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.off(this._container,"keyup",this._cancelDrawing,this))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire(L.Draw.Event.CREATED,{layer:t,layerType:this.type})},_cancelDrawing:function(t){27===t.keyCode&&(this._map.fire("draw:canceled",{layerType:this.type}),this.disable())}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),guidelineDistance:20,maxGuideLineLength:4e3,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,feet:!0,nautic:!1,showLength:!0,zIndexOffset:2e3,factor:1,maxPoints:0},initialize:function(t,e){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("mouseout",this._onMouseOut,this).on("mousemove",this._onMouseMove,this).on("mousedown",this._onMouseDown,this).on("mouseup",this._onMouseUp,this).addTo(this._map),this._map.on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).on("zoomlevelschange",this._onZoomEnd,this).on("touchstart",this._onTouch,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("mousedown",this._onMouseDown,this).off("mouseout",this._onMouseOut,this).off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this).off("zoomlevelschange",this._onZoomEnd,this).off("zoomend",this._onZoomEnd,this).off("touchstart",this._onTouch,this).off("click",this._onTouch,this)},deleteLastVertex:function(){if(!(this._markers.length<=1)){var t=this._markers.pop(),e=this._poly,i=e.getLatLngs(),o=i.splice(-1,1)[0];this._poly.setLatLngs(i),this._markerGroup.removeLayer(t),e.getLatLngs().length<2&&this._map.removeLayer(e),this._vertexChanged(o,!1)}},addVertex:function(t){if(this._markers.length>=2&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t))return void this._showErrorTooltip();this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),this._vertexChanged(t,!0)},completeShape:function(){this._markers.length<=1||(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_finishShape:function(){var t=this._poly._defaultShape?this._poly._defaultShape():this._poly.getLatLngs(),e=this._poly.newLatLngIntersects(t[t.length-1]);if(!this.options.allowIntersection&&e||!this._shapeIsValid())return void this._showErrorTooltip();this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_shapeIsValid:function(){return!0},_onZoomEnd:function(){null!==this._markers&&this._updateGuide()},_onMouseMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent),i=this._map.layerPointToLatLng(e);this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_vertexChanged:function(t,e){this._map.fire(L.Draw.Event.DRAWVERTEX,{layers:this._markerGroup}),this._updateFinishHandler(),this._updateRunningMeasure(t,e),this._clearGuides(),this._updateTooltip()},_onMouseDown:function(t){if(!this._clickHandled&&!this._touchHandled&&!this._disableMarkers){this._onMouseMove(t),this._clickHandled=!0,this._disableNewMarkers();var e=t.originalEvent,i=e.clientX,o=e.clientY;this._startPoint.call(this,i,o)}},_startPoint:function(t,e){this._mouseDownOrigin=L.point(t,e)},_onMouseUp:function(t){var e=t.originalEvent,i=e.clientX,o=e.clientY;this._endPoint.call(this,i,o,t),this._clickHandled=null},_endPoint:function(e,i,o){if(this._mouseDownOrigin){var n=L.point(e,i).distanceTo(this._mouseDownOrigin),a=this._calculateFinishDistance(o.latlng);this.options.maxPoints>1&&this.options.maxPoints==this._markers.length+1?(this.addVertex(o.latlng),this._finishShape()):a<10&&L.Browser.touch?this._finishShape():Math.abs(n)<9*(t.devicePixelRatio||1)&&this.addVertex(o.latlng),this._enableNewMarkers()}this._mouseDownOrigin=null},_onTouch:function(t){var e,i,o=t.originalEvent;!o.touches||!o.touches[0]||this._clickHandled||this._touchHandled||this._disableMarkers||(e=o.touches[0].clientX,i=o.touches[0].clientY,this._disableNewMarkers(),this._touchHandled=!0,this._startPoint.call(this,e,i),this._endPoint.call(this,e,i,t),this._touchHandled=null),this._clickHandled=null},_onMouseOut:function(){this._tooltip&&this._tooltip._onMouseOut.call(this._tooltip)},_calculateFinishDistance:function(t){var e;if(this._markers.length>0){var i;if(this.type===L.Draw.Polyline.TYPE)i=this._markers[this._markers.length-1];else{if(this.type!==L.Draw.Polygon.TYPE)return 1/0;i=this._markers[0]}var o=this._map.latLngToContainerPoint(i.getLatLng()),n=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset}),a=this._map.latLngToContainerPoint(n.getLatLng());e=o.distanceTo(a)}else e=1/0;return e},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers?this._markers.length:0;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,n,a=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))),s=this.options.guidelineDistance,r=this.options.maxGuideLineLength,l=a>r?a-r:s;for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));l<a;l+=this.options.guidelineDistance)i=l/a,o={x:Math.floor(t.x*(1-i)+i*e.x),y:Math.floor(t.y*(1-i)+i*e.y)},n=L.DomUtil.create("div","leaflet-draw-guide-dash",this._guidesContainer),n.style.backgroundColor=this._errorShown?this.options.drawError.color:this.options.shapeOptions.color,L.DomUtil.setPosition(n,o)},_updateGuideColor:function(t){if(this._guidesContainer)for(var e=0,i=this._guidesContainer.childNodes.length;e<i;e++)this._guidesContainer.childNodes[e].style.backgroundColor=t},_clearGuides:function(){if(this._guidesContainer)for(;this._guidesContainer.firstChild;)this._guidesContainer.removeChild(this._guidesContainer.firstChild)},_getTooltipText:function(){var t,e,i=this.options.showLength;return L.Browser.touch&&(i=!1),0===this._markers.length?t={text:L.drawLocal.draw.handlers.polyline.tooltip.start}:(e=i?this._getMeasurementString():"",t=1===this._markers.length?{text:L.drawLocal.draw.handlers.polyline.tooltip.cont,subtext:e}:{text:L.drawLocal.draw.handlers.polyline.tooltip.end,subtext:e}),t},_updateRunningMeasure:function(t,e){var i,o,n=this._markers.length;1===this._markers.length?this._measurementRunningTotal=0:(i=n-(e?2:1),o=L.GeometryUtil.isVersion07x()?t.distanceTo(this._markers[i].getLatLng())*(this.options.factor||1):this._map.distance(t,this._markers[i].getLatLng())*(this.options.factor||1),this._measurementRunningTotal+=o*(e?1:-1))},_getMeasurementString:function(){var t,e=this._currentLatLng,i=this._markers[this._markers.length-1].getLatLng();return t=L.GeometryUtil.isVersion07x()?i&&e&&e.distanceTo?this._measurementRunningTotal+e.distanceTo(i)*(this.options.factor||1):this._measurementRunningTotal||0:i&&e?this._measurementRunningTotal+this._map.distance(e,i)*(this.options.factor||1):this._measurementRunningTotal||0,L.GeometryUtil.readableDistance(t,this.options.metric,this.options.feet,this.options.nautic,this.options.precision)},_showErrorTooltip:function(){this._errorShown=!0,this._tooltip.showAsError().updateContent({text:this.options.drawError.message}),this._updateGuideColor(this.options.drawError.color),this._poly.setStyle({color:this.options.drawError.color}),this._clearHideErrorTimeout(),this._hideErrorTimeout=setTimeout(L.Util.bind(this._hideErrorTooltip,this),this.options.drawError.timeout)},_hideErrorTooltip:function(){this._errorShown=!1,this._clearHideErrorTimeout(),this._tooltip.removeError().updateContent(this._getTooltipText()),this._updateGuideColor(this.options.shapeOptions.color),this._poly.setStyle({color:this.options.shapeOptions.color})},_clearHideErrorTimeout:function(){this._hideErrorTimeout&&(clearTimeout(this._hideErrorTimeout),this._hideErrorTimeout=null)},_disableNewMarkers:function(){this._disableMarkers=!0},_enableNewMarkers:function(){setTimeout(function(){this._disableMarkers=!1}.bind(this),50)},_cleanUpShape:function(){this._markers.length>1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,showLength:!1,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0,feet:!0,nautic:!1,precision:{}},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:this._markers.length<3?(t=L.drawLocal.draw.handlers.polygon.tooltip.cont,e=this._getMeasurementString()):(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area,e="";return t||this.options.showLength?(this.options.showLength&&(e=L.Draw.Polyline.prototype._getMeasurementString.call(this)),t&&(e+="<br>"+L.GeometryUtil.readableArea(t,this.options.metric,this.options.precision)),e):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexChanged:function(t,e){var i;!this.options.allowIntersection&&this.options.showArea&&(i=this._poly.getLatLngs(),this._area=L.GeometryUtil.geodesicArea(i)),L.Draw.Polyline.prototype._vertexChanged.call(this,t,e)},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._mapDraggable=this._map.dragging.enabled(),this._mapDraggable&&this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this).on("touchstart",this._onMouseDown,this).on("touchmove",this._onMouseMove,this),e.addEventListener("touchstart",L.DomEvent.preventDefault,{passive:!1}))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._mapDraggable&&this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this).off("touchstart",this._onMouseDown,this).off("touchmove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp,this),L.DomEvent.off(e,"touchend",this._onMouseUp,this),e.removeEventListener("touchstart",L.DomEvent.preventDefault),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_getTooltipText:function(){return{text:this._endLabelText}},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).on(e,"touchend",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent(this._getTooltipText()),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,showArea:!0,clickable:!0},metric:!0},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},disable:function(){this._enabled&&(this._isCurrentlyTwoClickDrawing=!1,L.Draw.SimpleShape.prototype.disable.call(this))},_onMouseUp:function(t){if(!this._shape&&!this._isCurrentlyTwoClickDrawing)return void(this._isCurrentlyTwoClickDrawing=!0);this._isCurrentlyTwoClickDrawing&&!o(t.target,"leaflet-pane")||L.Draw.SimpleShape.prototype._onMouseUp.call(this)},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_getTooltipText:function(){var t,e,i,o=L.Draw.SimpleShape.prototype._getTooltipText.call(this),n=this._shape,a=this.options.showArea;return n&&(t=this._shape._defaultShape?this._shape._defaultShape():this._shape.getLatLngs(),e=L.GeometryUtil.geodesicArea(t),i=a?L.GeometryUtil.readableArea(e,this.options.metric):""),{text:o.text,subtext:i}}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.marker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:this._initialLabelText}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onTouch,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._marker&&(this._marker.off("click",this._onClick,this),this._map.off("click",this._onClick,this).off("click",this._onTouch,this).removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?(e=this._mouseMarker.getLatLng(),this._marker.setLatLng(e)):(this._marker=this._createMarker(e),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_createMarker:function(t){return new L.Marker(t,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset})},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_onTouch:function(t){this._onMouseMove(t),this._onClick()},_fireCreatedEvent:function(){var t=new L.Marker.Touch(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.CircleMarker=L.Draw.Marker.extend({statics:{TYPE:"circlemarker"},options:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.CircleMarker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circlemarker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},_fireCreatedEvent:function(){var t=new L.CircleMarker(this._marker.getLatLng(),this.options);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)},_createMarker:function(t){return new L.CircleMarker(t,this.options)}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0,feet:!0,nautic:!1},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){if(L.GeometryUtil.isVersion07x())var e=this._startLatLng.distanceTo(t);else var e=this._map.distance(this._startLatLng,t);this._shape?this._shape.setRadius(e):(this._shape=new L.Circle(this._startLatLng,e,this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=this.options.showRadius,n=this.options.metric;if(this._tooltip.updatePosition(i),this._isDrawing){this._drawShape(i),e=this._shape.getRadius().toFixed(1);var a="";o&&(a=L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(e,n,this.options.feet,this.options.nautic)),this._tooltip.updateContent({text:this._endLabelText,subtext:a})}}}),L.Edit=L.Edit||{},L.Edit.Marker=L.Handler.extend({initialize:function(t,e){this._marker=t,L.setOptions(this,e)},addHooks:function(){var t=this._marker;t.dragging.enable(),t.on("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},removeHooks:function(){var t=this._marker;t.dragging.disable(),t.off("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},_onDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_toggleMarkerHighlight:function(){var t=this._marker._icon;t&&(t.style.display="none",L.DomUtil.hasClass(t,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,-4)):(L.DomUtil.addClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,4)),t.style.display="")},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"}}),L.Marker.addInitHook(function(){L.Edit.Marker&&(this.editing=new L.Edit.Marker(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({initialize:function(t){this.latlngs=[t._latlngs],t._holes&&(this.latlngs=this.latlngs.concat(t._holes)),this._poly=t,this._poly.on("revert-edited",this._updateLatLngs,this)},_defaultShape:function(){return L.Polyline._flat?L.Polyline._flat(this._poly._latlngs)?this._poly._latlngs:this._poly._latlngs[0]:this._poly._latlngs},_eachVertexHandler:function(t){for(var e=0;e<this._verticesHandlers.length;e++)t(this._verticesHandlers[e])},addHooks:function(){this._initHandlers(),this._eachVertexHandler(function(t){t.addHooks()})},removeHooks:function(){this._eachVertexHandler(function(t){t.removeHooks()})},updateMarkers:function(){this._eachVertexHandler(function(t){t.updateMarkers()})},_initHandlers:function(){this._verticesHandlers=[];for(var t=0;t<this.latlngs.length;t++)this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly,this.latlngs[t],this._poly.options.poly))},_updateLatLngs:function(t){this.latlngs=[t.layer._latlngs],t.layer._holes&&(this.latlngs=this.latlngs.concat(t.layer._holes))}}),L.Edit.PolyVerticesEdit=L.Handler.extend({options:{icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),drawError:{color:"#b00b00",timeout:1e3}},initialize:function(t,e,i){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this._poly=t,i&&i.drawError&&(i.drawError=L.Util.extend({},this.options.drawError,i.drawError)),this._latlngs=e,L.setOptions(this,i)},_defaultShape:function(){return L.Polyline._flat?L.Polyline._flat(this._latlngs)?this._latlngs:this._latlngs[0]:this._latlngs},addHooks:function(){var t=this._poly,e=t._path;t instanceof L.Polygon||(t.options.fill=!1,t.options.editing&&(t.options.editing.fill=!1)),e&&t.options.editing.className&&(t.options.original.className&&t.options.original.className.split(" ").forEach(function(t){L.DomUtil.removeClass(e,t)}),t.options.editing.className.split(" ").forEach(function(t){L.DomUtil.addClass(e,t)})),t.setStyle(t.options.editing),this._poly._map&&(this._map=this._poly._map,this._markerGroup||this._initMarkers(),this._poly._map.addLayer(this._markerGroup))},removeHooks:function(){var t=this._poly,e=t._path;e&&t.options.editing.className&&(t.options.editing.className.split(" ").forEach(function(t){L.DomUtil.removeClass(e,t)}),t.options.original.className&&t.options.original.className.split(" ").forEach(function(t){L.DomUtil.addClass(e,t)})),t.setStyle(t.options.original),t._map&&(t._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers)},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new L.LayerGroup),this._markers=[];var t,e,i,o,n=this._defaultShape();for(t=0,i=n.length;t<i;t++)o=this._createMarker(n[t],t),o.on("click",this._onMarkerClick,this),this._markers.push(o);var a,s;for(t=0,e=i-1;t<i;e=t++)(0!==t||L.Polygon&&this._poly instanceof L.Polygon)&&(a=this._markers[e],s=this._markers[t],this._createMiddleMarker(a,s),this._updatePrevNext(a,s))},_createMarker:function(t,e){var i=new L.Marker.Touch(t,{draggable:!0,icon:this.options.icon});return i._origLatLng=t,i._index=e,i.on("dragstart",this._onMarkerDragStart,this).on("drag",this._onMarkerDrag,this).on("dragend",this._fireEdit,this).on("touchmove",this._onTouchMove,this).on("touchend",this._fireEdit,this).on("MSPointerMove",this._onTouchMove,this).on("MSPointerUp",this._fireEdit,this),this._markerGroup.addLayer(i),i},_onMarkerDragStart:function(){this._poly.fire("editstart")},_spliceLatLngs:function(){var t=this._defaultShape(),e=[].splice.apply(t,arguments);return this._poly._convertLatLngs(t,!0),this._poly.redraw(),e},_removeMarker:function(t){var e=t._index;this._markerGroup.removeLayer(t),this._markers.splice(e,1),this._spliceLatLngs(e,1),this._updateIndexes(e,-1),t.off("dragstart",this._onMarkerDragStart,this).off("drag",this._onMarkerDrag,this).off("dragend",this._fireEdit,this).off("touchmove",this._onMarkerDrag,this).off("touchend",this._fireEdit,this).off("click",this._onMarkerClick,this).off("MSPointerMove",this._onTouchMove,this).off("MSPointerUp",this._fireEdit,this)},_fireEdit:function(){this._poly.edited=!0,this._poly.fire("edit"),this._poly._map.fire(L.Draw.Event.EDITVERTEX,{layers:this._markerGroup,poly:this._poly})},_onMarkerDrag:function(t){var e=t.target,i=this._poly;if(L.extend(e._origLatLng,e._latlng),e._middleLeft&&e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev,e)),e._middleRight&&e._middleRight.setLatLng(this._getMiddleLatLng(e,e._next)),i.options.poly){var o=i._map._editTooltip;if(!i.options.poly.allowIntersection&&i.intersects()){var n=i.options.color;i.setStyle({color:this.options.drawError.color}),0!==L.version.indexOf("0.7")&&e.dragging._draggable._onUp(t),this._onMarkerClick(t),o&&o.updateContent({text:L.drawLocal.draw.handlers.polyline.error}),setTimeout(function(){i.setStyle({color:n}),o&&o.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext})},1e3)}}this._poly.redraw(),this._poly.fire("editdrag")},_onMarkerClick:function(t){var e=L.Polygon&&this._poly instanceof L.Polygon?4:3,i=t.target;this._defaultShape().length<e||(this._removeMarker(i),this._updatePrevNext(i._prev,i._next),i._middleLeft&&this._markerGroup.removeLayer(i._middleLeft),i._middleRight&&this._markerGroup.removeLayer(i._middleRight),i._prev&&i._next?this._createMiddleMarker(i._prev,i._next):i._prev?i._next||(i._prev._middleRight=null):i._next._middleLeft=null,this._fireEdit())},_onTouchMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent.touches[0]),i=this._map.layerPointToLatLng(e),o=t.target;L.extend(o._origLatLng,i),o._middleLeft&&o._middleLeft.setLatLng(this._getMiddleLatLng(o._prev,o)),o._middleRight&&o._middleRight.setLatLng(this._getMiddleLatLng(o,o._next)),this._poly.redraw(),this.updateMarkers()},_updateIndexes:function(t,e){this._markerGroup.eachLayer(function(i){i._index>t&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,o,n,a=this._getMiddleLatLng(t,e),s=this._createMarker(a);s.setOpacity(.6),t._middleRight=e._middleLeft=s,o=function(){s.off("touchmove",o,this);var n=e._index;s._index=n,s.off("click",i,this).on("click",this._onMarkerClick,this),a.lat=s.getLatLng().lat,a.lng=s.getLatLng().lng,this._spliceLatLngs(n,0,a),this._markers.splice(n,0,s),s.setOpacity(1),this._updateIndexes(n,1),e._index++,this._updatePrevNext(t,s),this._updatePrevNext(s,e),this._poly.fire("editstart")},n=function(){s.off("dragstart",o,this),s.off("dragend",n,this),s.off("touchmove",o,this),this._createMiddleMarker(t,s),this._createMiddleMarker(s,e)},i=function(){o.call(this),n.call(this),this._fireEdit()},s.on("click",i,this).on("dragstart",o,this).on("dragend",n,this).on("touchmove",o,this),this._markerGroup.addLayer(s)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),n=i.project(e.getLatLng());return i.unproject(o._add(n)._divideBy(2))}}),L.Polyline.addInitHook(function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()}))}),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"}),touchMoveIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon"}),touchResizeIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon"})},initialize:function(t,e){L.Browser.touch&&(this.options.moveIcon=this.options.touchMoveIcon,this.options.resizeIcon=this.options.touchResizeIcon),this._shape=t, +L.Util.setOptions(this,e)},addHooks:function(){var t=this._shape;this._shape._map&&(this._map=this._shape._map,t.setStyle(t.options.editing),t._map&&(this._map=t._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup)))},removeHooks:function(){var t=this._shape;if(t.setStyle(t.options.original),t._map){this._unbindMarker(this._moveMarker);for(var e=0,i=this._resizeMarkers.length;e<i;e++)this._unbindMarker(this._resizeMarkers[e]);this._resizeMarkers=null,this._map.removeLayer(this._markerGroup),delete this._markerGroup}this._map=null},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new L.LayerGroup),this._createMoveMarker(),this._createResizeMarker()},_createMoveMarker:function(){},_createResizeMarker:function(){},_createMarker:function(t,e){var i=new L.Marker.Touch(t,{draggable:!0,icon:e,zIndexOffset:10});return this._bindMarker(i),this._markerGroup.addLayer(i),i},_bindMarker:function(t){t.on("dragstart",this._onMarkerDragStart,this).on("drag",this._onMarkerDrag,this).on("dragend",this._onMarkerDragEnd,this).on("touchstart",this._onTouchStart,this).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onTouchEnd,this).on("MSPointerUp",this._onTouchEnd,this)},_unbindMarker:function(t){t.off("dragstart",this._onMarkerDragStart,this).off("drag",this._onMarkerDrag,this).off("dragend",this._onMarkerDragEnd,this).off("touchstart",this._onTouchStart,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onTouchEnd,this).off("MSPointerUp",this._onTouchEnd,this)},_onMarkerDragStart:function(t){t.target.setOpacity(0),this._shape.fire("editstart")},_fireEdit:function(){this._shape.edited=!0,this._shape.fire("edit")},_onMarkerDrag:function(t){var e=t.target,i=e.getLatLng();e===this._moveMarker?this._move(i):this._resize(i),this._shape.redraw(),this._shape.fire("editdrag")},_onMarkerDragEnd:function(t){t.target.setOpacity(1),this._fireEdit()},_onTouchStart:function(t){if(L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this,t),"function"==typeof this._getCorners){var e=this._getCorners(),i=t.target,o=i._cornerIndex;i.setOpacity(0),this._oppositeCorner=e[(o+2)%4],this._toggleCornerMarkers(0,o)}this._shape.fire("editstart")},_onTouchMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent.touches[0]),i=this._map.layerPointToLatLng(e);return t.target===this._moveMarker?this._move(i):this._resize(i),this._shape.redraw(),!1},_onTouchEnd:function(t){t.target.setOpacity(1),this.updateMarkers(),this._fireEdit()},_move:function(){},_resize:function(){}}),L.Edit=L.Edit||{},L.Edit.Rectangle=L.Edit.SimpleShape.extend({_createMoveMarker:function(){var t=this._shape.getBounds(),e=t.getCenter();this._moveMarker=this._createMarker(e,this.options.moveIcon)},_createResizeMarker:function(){var t=this._getCorners();this._resizeMarkers=[];for(var e=0,i=t.length;e<i;e++)this._resizeMarkers.push(this._createMarker(t[e],this.options.resizeIcon)),this._resizeMarkers[e]._cornerIndex=e},_onMarkerDragStart:function(t){L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this,t);var e=this._getCorners(),i=t.target,o=i._cornerIndex;this._oppositeCorner=e[(o+2)%4],this._toggleCornerMarkers(0,o)},_onMarkerDragEnd:function(t){var e,i,o=t.target;o===this._moveMarker&&(e=this._shape.getBounds(),i=e.getCenter(),o.setLatLng(i)),this._toggleCornerMarkers(1),this._repositionCornerMarkers(),L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this,t)},_move:function(t){for(var e,i=this._shape._defaultShape?this._shape._defaultShape():this._shape.getLatLngs(),o=this._shape.getBounds(),n=o.getCenter(),a=[],s=0,r=i.length;s<r;s++)e=[i[s].lat-n.lat,i[s].lng-n.lng],a.push([t.lat+e[0],t.lng+e[1]]);this._shape.setLatLngs(a),this._repositionCornerMarkers(),this._map.fire(L.Draw.Event.EDITMOVE,{layer:this._shape})},_resize:function(t){var e;this._shape.setBounds(L.latLngBounds(t,this._oppositeCorner)),e=this._shape.getBounds(),this._moveMarker.setLatLng(e.getCenter()),this._map.fire(L.Draw.Event.EDITRESIZE,{layer:this._shape})},_getCorners:function(){var t=this._shape.getBounds();return[t.getNorthWest(),t.getNorthEast(),t.getSouthEast(),t.getSouthWest()]},_toggleCornerMarkers:function(t){for(var e=0,i=this._resizeMarkers.length;e<i;e++)this._resizeMarkers[e].setOpacity(t)},_repositionCornerMarkers:function(){for(var t=this._getCorners(),e=0,i=this._resizeMarkers.length;e<i;e++)this._resizeMarkers[e].setLatLng(t[e])}}),L.Rectangle.addInitHook(function(){L.Edit.Rectangle&&(this.editing=new L.Edit.Rectangle(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.CircleMarker=L.Edit.SimpleShape.extend({_createMoveMarker:function(){var t=this._shape.getLatLng();this._moveMarker=this._createMarker(t,this.options.moveIcon)},_createResizeMarker:function(){this._resizeMarkers=[]},_move:function(t){if(this._resizeMarkers.length){var e=this._getResizeMarkerPoint(t);this._resizeMarkers[0].setLatLng(e)}this._shape.setLatLng(t),this._map.fire(L.Draw.Event.EDITMOVE,{layer:this._shape})}}),L.CircleMarker.addInitHook(function(){L.Edit.CircleMarker&&(this.editing=new L.Edit.CircleMarker(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),L.Edit=L.Edit||{},L.Edit.Circle=L.Edit.CircleMarker.extend({_createResizeMarker:function(){var t=this._shape.getLatLng(),e=this._getResizeMarkerPoint(t);this._resizeMarkers=[],this._resizeMarkers.push(this._createMarker(e,this.options.resizeIcon))},_getResizeMarkerPoint:function(t){var e=this._shape._radius*Math.cos(Math.PI/4),i=this._map.project(t);return this._map.unproject([i.x+e,i.y-e])},_resize:function(t){var e=this._moveMarker.getLatLng();L.GeometryUtil.isVersion07x()?radius=e.distanceTo(t):radius=this._map.distance(e,t),this._map.fire(L.Draw.Event.EDITRESIZE,{layer:this._shape})}}),L.Circle.addInitHook(function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),L.Map.mergeOptions({touchExtend:!0}),L.Map.TouchExtend=L.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){L.DomEvent.on(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.on(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.on(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.on(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.on(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.on(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.on(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.on(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.on(this._container,"touchleave",this._onTouchLeave,this))},removeHooks:function(){L.DomEvent.off(this._container,"touchstart",this._onTouchStart),L.DomEvent.off(this._container,"touchend",this._onTouchEnd),L.DomEvent.off(this._container,"touchmove",this._onTouchMove),this._detectIE()?(L.DomEvent.off(this._container,"MSPointerDowm",this._onTouchStart),L.DomEvent.off(this._container,"MSPointerUp",this._onTouchEnd),L.DomEvent.off(this._container,"MSPointerMove",this._onTouchMove),L.DomEvent.off(this._container,"MSPointerCancel",this._onTouchCancel)):(L.DomEvent.off(this._container,"touchcancel",this._onTouchCancel),L.DomEvent.off(this._container,"touchleave",this._onTouchLeave))},_touchEvent:function(t,e){var i={};if(void 0!==t.touches){if(!t.touches.length)return;i=t.touches[0]}else{if("touch"!==t.pointerType)return;if(i=t,!this._filterClick(t))return}var o=this._map.mouseEventToContainerPoint(i),n=this._map.mouseEventToLayerPoint(i),a=this._map.layerPointToLatLng(n);this._map.fire(e,{latlng:a,layerPoint:n,containerPoint:o,pageX:i.pageX,pageY:i.pageY,originalEvent:t})},_filterClick:function(t){var e=t.timeStamp||t.originalEvent.timeStamp,i=L.DomEvent._lastClick&&e-L.DomEvent._lastClick;return i&&i>100&&i<500||t.target._simulatedClick&&!t._simulated?(L.DomEvent.stop(t),!1):(L.DomEvent._lastClick=e,!0)},_onTouchStart:function(t){if(this._map._loaded){this._touchEvent(t,"touchstart")}},_onTouchEnd:function(t){if(this._map._loaded){this._touchEvent(t,"touchend")}},_onTouchCancel:function(t){if(this._map._loaded){var e="touchcancel";this._detectIE()&&(e="pointercancel"),this._touchEvent(t,e)}},_onTouchLeave:function(t){if(this._map._loaded){this._touchEvent(t,"touchleave")}},_onTouchMove:function(t){if(this._map._loaded){this._touchEvent(t,"touchmove")}},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var n=e.indexOf("Edge/");return n>0&&parseInt(e.substring(n+5,e.indexOf(".",n)),10)}}),L.Map.addInitHook("addHandler","touchExtend",L.Map.TouchExtend),L.Marker.Touch=L.Marker.extend({_initInteraction:function(){return this.addInteractiveTarget?L.Marker.prototype._initInteraction.apply(this):this._initInteractionLegacy()},_initInteractionLegacy:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu","touchstart","touchend","touchmove"];this._detectIE?e.concat(["MSPointerDown","MSPointerUp","MSPointerMove","MSPointerCancel"]):e.concat(["touchcancel"]),L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this),L.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i<e.length;i++)L.DomEvent.on(t,e[i],this._fireMouseEvent,this);L.Handler.MarkerDrag&&(this.dragging=new L.Handler.MarkerDrag(this),this.options.draggable&&this.dragging.enable())}},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var n=e.indexOf("Edge/");return n>0&&parseInt(e.substring(n+5,e.indexOf(".",n)),10)}}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;i<o;i++)Array.isArray(t[i])?e.push(L.LatLngUtil.cloneLatLngs(t[i])):e.push(this.cloneLatLng(t[i]));return e},cloneLatLng:function(t){return L.latLng(t.lat,t.lng)}},function(){var t={km:2,ha:2,m:0,mi:2,ac:2,yd:0,ft:0,nm:2};L.GeometryUtil=L.extend(L.GeometryUtil||{},{geodesicArea:function(t){var e,i,o=t.length,n=0,a=Math.PI/180;if(o>2){for(var s=0;s<o;s++)e=t[s],i=t[(s+1)%o],n+=(i.lng-e.lng)*a*(2+Math.sin(e.lat*a)+Math.sin(i.lat*a));n=6378137*n*6378137/2}return Math.abs(n)},formattedNumber:function(t,e){var i=parseFloat(t).toFixed(e),o=L.drawLocal.format&&L.drawLocal.format.numeric,n=o&&o.delimiters,a=n&&n.thousands,s=n&&n.decimal;if(a||s){var r=i.split(".");i=a?r[0].replace(/(\d)(?=(\d{3})+(?!\d))/g,"$1"+a):r[0],s=s||".",r.length>1&&(i=i+s+r[1])}return i},readableArea:function(e,i,o){var n,a,o=L.Util.extend({},t,o);return i?(a=["ha","m"],type=typeof i,"string"===type?a=[i]:"boolean"!==type&&(a=i),n=e>=1e6&&-1!==a.indexOf("km")?L.GeometryUtil.formattedNumber(1e-6*e,o.km)+" km²":e>=1e4&&-1!==a.indexOf("ha")?L.GeometryUtil.formattedNumber(1e-4*e,o.ha)+" ha":L.GeometryUtil.formattedNumber(e,o.m)+" m²"):(e/=.836127,n=e>=3097600?L.GeometryUtil.formattedNumber(e/3097600,o.mi)+" mi²":e>=4840?L.GeometryUtil.formattedNumber(e/4840,o.ac)+" acres":L.GeometryUtil.formattedNumber(e,o.yd)+" yd²"),n},readableDistance:function(e,i,o,n,a){var s,a=L.Util.extend({},t,a);switch(i?"string"==typeof i?i:"metric":o?"feet":n?"nauticalMile":"yards"){case"metric":s=e>1e3?L.GeometryUtil.formattedNumber(e/1e3,a.km)+" km":L.GeometryUtil.formattedNumber(e,a.m)+" m";break;case"feet":e*=3.28083,s=L.GeometryUtil.formattedNumber(e,a.ft)+" ft";break;case"nauticalMile":e*=.53996,s=L.GeometryUtil.formattedNumber(e/1e3,a.nm)+" nm";break;case"yards":default:e*=1.09361,s=e>1760?L.GeometryUtil.formattedNumber(e/1760,a.mi)+" miles":L.GeometryUtil.formattedNumber(e,a.yd)+" yd"}return s},isVersion07x:function(){var t=L.version.split(".");return 0===parseInt(t[0],10)&&7===parseInt(t[1],10)}})}(),L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._getProjectedPoints(),n=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=n-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return!!this._map&&this.newPointIntersects(this._map.latLngToLayerPoint(t),e)},newPointIntersects:function(t,e){var i=this._getProjectedPoints(),o=i?i.length:0,n=i?i[o-1]:null,a=o-2;return!this._tooFewPointsForIntersection(1)&&this._lineSegmentsIntersectsRange(n,t,a,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._getProjectedPoints(),i=e?e.length:0;return i+=t||0,!e||i<=3},_lineSegmentsIntersectsRange:function(t,e,i,o){var n,a,s=this._getProjectedPoints();o=o||0;for(var r=i;r>o;r--)if(n=s[r-1],a=s[r],L.LineUtil.segmentsIntersect(t,e,n,a))return!0;return!1},_getProjectedPoints:function(){if(!this._defaultShape)return this._originalPoints;for(var t=[],e=this._defaultShape(),i=0;i<e.length;i++)t.push(this._map.latLngToLayerPoint(e[i]));return t}}),L.Polygon.include({intersects:function(){var t,e,i,o,n=this._getProjectedPoints();return!this._tooFewPointsForIntersection()&&(!!L.Polyline.prototype.intersects.call(this)||(t=n.length,e=n[0],i=n[t-1],o=t-2,this._lineSegmentsIntersectsRange(i,e,o,1)))}}),L.Control.Draw=L.Control.extend({options:{position:"topleft",draw:{},edit:!1},initialize:function(t){if(L.version<"0.7")throw new Error("Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/");L.Control.prototype.initialize.call(this,t);var e;this._toolbars={},L.DrawToolbar&&this.options.draw&&(e=new L.DrawToolbar(this.options.draw),this._toolbars[L.DrawToolbar.TYPE]=e,this._toolbars[L.DrawToolbar.TYPE].on("enable",this._toolbarEnabled,this)),L.EditToolbar&&this.options.edit&&(e=new L.EditToolbar(this.options.edit),this._toolbars[L.EditToolbar.TYPE]=e,this._toolbars[L.EditToolbar.TYPE].on("enable",this._toolbarEnabled,this)),L.toolbar=this},onAdd:function(t){var e,i=L.DomUtil.create("div","leaflet-draw"),o=!1;for(var n in this._toolbars)this._toolbars.hasOwnProperty(n)&&(e=this._toolbars[n].addToolbar(t))&&(o||(L.DomUtil.hasClass(e,"leaflet-draw-toolbar-top")||L.DomUtil.addClass(e.childNodes[0],"leaflet-draw-toolbar-top"),o=!0),i.appendChild(e));return i},onRemove:function(){for(var t in this._toolbars)this._toolbars.hasOwnProperty(t)&&this._toolbars[t].removeToolbar()},setDrawingOptions:function(t){for(var e in this._toolbars)this._toolbars[e]instanceof L.DrawToolbar&&this._toolbars[e].setOptions(t)},_toolbarEnabled:function(t){var e=t.target;for(var i in this._toolbars)this._toolbars[i]!==e&&this._toolbars[i].disable()}}),L.Map.mergeOptions({drawControlTooltips:!0,drawControl:!1}),L.Map.addInitHook(function(){this.options.drawControl&&(this.drawControl=new L.Control.Draw,this.addControl(this.drawControl))}),L.Toolbar=L.Class.extend({initialize:function(t){L.setOptions(this,t),this._modes={},this._actionButtons=[],this._activeMode=null;var e=L.version.split(".");1===parseInt(e[0],10)&&parseInt(e[1],10)>=2?L.Toolbar.include(L.Evented.prototype):L.Toolbar.include(L.Mixin.Events)},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},addToolbar:function(t){var e,i=L.DomUtil.create("div","leaflet-draw-section"),o=0,n=this._toolbarClass||"",a=this.getModeHandlers(t);for(this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,e=0;e<a.length;e++)a[e].enabled&&this._initModeHandler(a[e].handler,this._toolbarContainer,o++,n,a[e].title);if(o)return this._lastButtonIndex=--o,this._actionsContainer=L.DomUtil.create("ul","leaflet-draw-actions"),i.appendChild(this._toolbarContainer),i.appendChild(this._actionsContainer),i},removeToolbar:function(){for(var t in this._modes)this._modes.hasOwnProperty(t)&&(this._disposeButton(this._modes[t].button,this._modes[t].handler.enable,this._modes[t].handler),this._modes[t].handler.disable(),this._modes[t].handler.off("enabled",this._handlerActivated,this).off("disabled",this._handlerDeactivated,this));this._modes={};for(var e=0,i=this._actionButtons.length;e<i;e++)this._disposeButton(this._actionButtons[e].button,this._actionButtons[e].callback,this);this._actionButtons=[],this._actionsContainer=null},_initModeHandler:function(t,e,i,o,n){var a=t.type;this._modes[a]={},this._modes[a].handler=t,this._modes[a].button=this._createButton({type:a,title:n,className:o+"-"+a,container:e,callback:this._modes[a].handler.enable,context:this._modes[a].handler}),this._modes[a].buttonIndex=i,this._modes[a].handler.on("enabled",this._handlerActivated,this).on("disabled",this._handlerDeactivated,this)},_detectIOS:function(){return/iPad|iPhone|iPod/.test(navigator.userAgent)&&!t.MSStream},_createButton:function(t){var e=L.DomUtil.create("a",t.className||"",t.container),i=L.DomUtil.create("span","sr-only",t.container);e.href="#",e.appendChild(i),t.title&&(e.title=t.title,i.innerHTML=t.title),t.text&&(e.innerHTML=t.text,i.innerHTML=t.text);var o=this._detectIOS()?"touchstart":"click";return L.DomEvent.on(e,"click",L.DomEvent.stopPropagation).on(e,"mousedown",L.DomEvent.stopPropagation).on(e,"dblclick",L.DomEvent.stopPropagation).on(e,"touchstart",L.DomEvent.stopPropagation).on(e,"click",L.DomEvent.preventDefault).on(e,o,t.callback,t.context),e},_disposeButton:function(t,e){var i=this._detectIOS()?"touchstart":"click";L.DomEvent.off(t,"click",L.DomEvent.stopPropagation).off(t,"mousedown",L.DomEvent.stopPropagation).off(t,"dblclick",L.DomEvent.stopPropagation).off(t,"touchstart",L.DomEvent.stopPropagation).off(t,"click",L.DomEvent.preventDefault).off(t,i,e)},_handlerActivated:function(t){this.disable(),this._activeMode=this._modes[t.handler],L.DomUtil.addClass(this._activeMode.button,"leaflet-draw-toolbar-button-enabled"),this._showActionsToolbar(),this.fire("enable")},_handlerDeactivated:function(){this._hideActionsToolbar(),L.DomUtil.removeClass(this._activeMode.button,"leaflet-draw-toolbar-button-enabled"),this._activeMode=null,this.fire("disable")},_createActions:function(t){var e,i,o,n,a=this._actionsContainer,s=this.getActions(t),r=s.length;for(i=0,o=this._actionButtons.length;i<o;i++)this._disposeButton(this._actionButtons[i].button,this._actionButtons[i].callback);for(this._actionButtons=[];a.firstChild;)a.removeChild(a.firstChild);for(var l=0;l<r;l++)"enabled"in s[l]&&!s[l].enabled||(e=L.DomUtil.create("li","",a),n=this._createButton({title:s[l].title,text:s[l].text,container:e,callback:s[l].callback,context:s[l].context}),this._actionButtons.push({button:n,callback:s[l].callback}))},_showActionsToolbar:function(){var t=this._activeMode.buttonIndex,e=this._lastButtonIndex,i=this._activeMode.button.offsetTop-1;this._createActions(this._activeMode.handler),this._actionsContainer.style.top=i+"px",0===t&&(L.DomUtil.addClass(this._toolbarContainer,"leaflet-draw-toolbar-notop"),L.DomUtil.addClass(this._actionsContainer,"leaflet-draw-actions-top")),t===e&&(L.DomUtil.addClass(this._toolbarContainer,"leaflet-draw-toolbar-nobottom"),L.DomUtil.addClass(this._actionsContainer,"leaflet-draw-actions-bottom")),this._actionsContainer.style.display="block",this._map.fire(L.Draw.Event.TOOLBAROPENED)},_hideActionsToolbar:function(){this._actionsContainer.style.display="none",L.DomUtil.removeClass(this._toolbarContainer,"leaflet-draw-toolbar-notop"),L.DomUtil.removeClass(this._toolbarContainer,"leaflet-draw-toolbar-nobottom"),L.DomUtil.removeClass(this._actionsContainer,"leaflet-draw-actions-top"),L.DomUtil.removeClass(this._actionsContainer,"leaflet-draw-actions-bottom"),this._map.fire(L.Draw.Event.TOOLBARCLOSED)}}),L.Draw=L.Draw||{},L.Draw.Tooltip=L.Class.extend({initialize:function(t){this._map=t,this._popupPane=t._panes.popupPane,this._visible=!1,this._container=t.options.drawControlTooltips?L.DomUtil.create("div","leaflet-draw-tooltip",this._popupPane):null,this._singleLineLabel=!1,this._map.on("mouseout",this._onMouseOut,this)},dispose:function(){this._map.off("mouseout",this._onMouseOut,this),this._container&&(this._popupPane.removeChild(this._container),this._container=null)},updateContent:function(t){return this._container?(t.subtext=t.subtext||"",0!==t.subtext.length||this._singleLineLabel?t.subtext.length>0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?'<span class="leaflet-draw-tooltip-subtext">'+t.subtext+"</span><br />":"")+"<span>"+t.text+"</span>",t.text||t.subtext?(this._visible=!0,this._container.style.visibility="inherit"):(this._visible=!1,this._container.style.visibility="hidden"),this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(this._visible&&(i.style.visibility="inherit"),L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this},_onMouseOut:function(){this._container&&(this._container.style.visibility="hidden")}}),L.DrawToolbar=L.Toolbar.extend({statics:{TYPE:"draw"},options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{},circlemarker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e]));this._toolbarClass="leaflet-draw-draw",L.Toolbar.prototype.initialize.call(this,t)},getModeHandlers:function(t){return[{enabled:this.options.polyline,handler:new L.Draw.Polyline(t,this.options.polyline),title:L.drawLocal.draw.toolbar.buttons.polyline},{enabled:this.options.polygon,handler:new L.Draw.Polygon(t,this.options.polygon),title:L.drawLocal.draw.toolbar.buttons.polygon},{enabled:this.options.rectangle,handler:new L.Draw.Rectangle(t,this.options.rectangle),title:L.drawLocal.draw.toolbar.buttons.rectangle},{enabled:this.options.circle,handler:new L.Draw.Circle(t,this.options.circle),title:L.drawLocal.draw.toolbar.buttons.circle},{enabled:this.options.marker,handler:new L.Draw.Marker(t,this.options.marker),title:L.drawLocal.draw.toolbar.buttons.marker},{enabled:this.options.circlemarker,handler:new L.Draw.CircleMarker(t,this.options.circlemarker),title:L.drawLocal.draw.toolbar.buttons.circlemarker}]},getActions:function(t){return[{enabled:t.completeShape,title:L.drawLocal.draw.toolbar.finish.title,text:L.drawLocal.draw.toolbar.finish.text,callback:t.completeShape,context:t},{enabled:t.deleteLastVertex,title:L.drawLocal.draw.toolbar.undo.title,text:L.drawLocal.draw.toolbar.undo.text,callback:t.deleteLastVertex,context:t},{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]},setOptions:function(t){L.setOptions(this,t);for(var e in this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({statics:{TYPE:"edit"},options:{edit:{selectedPathOptions:{dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1,maintainColor:!1}},remove:{},poly:null,featureGroup:null},initialize:function(t){t.edit&&(void 0===t.edit.selectedPathOptions&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit.selectedPathOptions=L.extend({},this.options.edit.selectedPathOptions,t.edit.selectedPathOptions)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),t.poly&&(t.poly=L.extend({},this.options.poly,t.poly)),this._toolbarClass="leaflet-draw-edit",L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},getModeHandlers:function(t){var e=this.options.featureGroup;return[{enabled:this.options.edit,handler:new L.EditToolbar.Edit(t,{featureGroup:e,selectedPathOptions:this.options.edit.selectedPathOptions,poly:this.options.poly}),title:L.drawLocal.edit.toolbar.buttons.edit},{enabled:this.options.remove,handler:new L.EditToolbar.Delete(t,{featureGroup:e}),title:L.drawLocal.edit.toolbar.buttons.remove}]},getActions:function(t){var e=[{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}];return t.removeAllLayers&&e.push({title:L.drawLocal.edit.toolbar.actions.clearAll.title,text:L.drawLocal.edit.toolbar.actions.clearAll.text,callback:this._clearAllLayers,context:this}),e},addToolbar:function(t){var e=L.Toolbar.prototype.addToolbar.call(this,t);return this._checkDisabled(),this.options.featureGroup.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this),L.Toolbar.prototype.removeToolbar.call(this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode&&this._activeMode.handler.disable()},_clearAllLayers:function(){this._activeMode.handler.removeAllLayers(),this._activeMode&&this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=this.options.featureGroup,i=0!==e.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.setOptions(this,e),this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Edit.include(L.Evented.prototype):L.EditToolbar.Edit.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.EDITSTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this))},disable:function(){this._enabled&&(this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.EDITSTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),t._editTooltip=this._tooltip,this._updateTooltip(),this._map.on("mousemove",this._onMouseMove,this).on("touchmove",this._onMouseMove,this).on("MSPointerMove",this._onMouseMove,this).on(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this).off("touchmove",this._onMouseMove,this).off("MSPointerMove",this._onMouseMove,this).off(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},revertLayers:function(){this._featureGroup.eachLayer(function(t){this._revertLayer(t)},this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer(function(e){e.edited&&(t.addLayer(e),e.edited=!1)}),this._map.fire(L.Draw.Event.EDITED,{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?this._uneditedLayerProps[e]={latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:(t instanceof L.Marker||t instanceof L.CircleMarker)&&(this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())}))},_getTooltipText:function(){return{text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}},_updateTooltip:function(){this._tooltip.updateContent(this._getTooltipText())},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):(t instanceof L.Marker||t instanceof L.CircleMarker)&&t.setLatLng(this._uneditedLayerProps[e].latlng),t.fire("revert-edited",{layer:t}))},_enableLayerEdit:function(t){var e,i,o=t.layer||t.target||t;this._backupLayer(o),this.options.poly&&(i=L.Util.extend({},this.options.poly),o.options.poly=i),this.options.selectedPathOptions&&(e=L.Util.extend({},this.options.selectedPathOptions),e.maintainColor&&(e.color=o.options.color,e.fillColor=o.options.fillColor),o.options.original=L.extend({},o.options),o.options.editing=e),o instanceof L.Marker?(o.editing&&o.editing.enable(),o.dragging.enable(),o.on("dragend",this._onMarkerDragEnd).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onMarkerDragEnd,this).on("MSPointerUp",this._onMarkerDragEnd,this)):o.editing.enable()},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,e.editing&&e.editing.disable(),delete e.options.editing,delete e.options.original,this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onMarkerDragEnd,this).off("MSPointerUp",this._onMarkerDragEnd,this)):e.editing.disable()},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_onMarkerDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_onTouchMove:function(t){var e=t.originalEvent.changedTouches[0],i=this._map.mouseEventToLayerPoint(e),o=this._map.layerPointToLatLng(i);t.target.setLatLng(o)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}), +L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Delete.include(L.Evented.prototype):L.EditToolbar.Delete.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DELETESTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this))},disable:function(){this._enabled&&(this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DELETESTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.LayerGroup,this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer(function(t){this._deletableLayers.addLayer(t),t.fire("revert-deleted",{layer:t})},this)},save:function(){this._map.fire(L.Draw.Event.DELETED,{layers:this._deletedLayers})},removeAllLayers:function(){this._deletableLayers.eachLayer(function(t){this._removeLayer({layer:t})},this),this.save()},_enableLayerDelete:function(t){(t.layer||t.target||t).on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e),e.fire("deleted")},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})}(window,document); diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet.js b/themes/bootstrap3/js/vendor/leaflet/leaflet.js new file mode 100644 index 0000000000000000000000000000000000000000..764fbb887df5a26c3411272f9eb5f2b9aefe94fb --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet.js @@ -0,0 +1,5 @@ +/* @preserve + * Leaflet 1.3.1+Detached: ba6f97fff8647e724e4dfe66d2ed7da11f908989.ba6f97f, a JS library for interactive maps. http://leafletjs.com + * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ +!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e<n;e++){o=arguments[e];for(i in o)t[i]=o[i]}return t}function e(t,i){var e=Array.prototype.slice;if(t.bind)return t.bind.apply(t,e.call(arguments,1));var n=e.call(arguments,2);return function(){return t.apply(i,n.length?n.concat(e.call(arguments)):arguments)}}function n(t){return t._leaflet_id=t._leaflet_id||++ti,t._leaflet_id}function o(t,i,e){var n,o,s,r;return r=function(){n=!1,o&&(s.apply(e,o),o=!1)},s=function(){n?o=arguments:(t.apply(e,arguments),setTimeout(r,i),n=!0)}}function s(t,i,e){var n=i[1],o=i[0],s=n-o;return t===n&&e?t:((t-o)%s+s)%s+o}function r(){return!1}function a(t,i){var e=Math.pow(10,void 0===i?6:i);return Math.round(t*e)/e}function h(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function u(t){return h(t).split(/\s+/)}function l(t,i){t.hasOwnProperty("options")||(t.options=t.options?Qt(t.options):{});for(var e in i)t.options[e]=i[e];return t.options}function c(t,i,e){var n=[];for(var o in t)n.push(encodeURIComponent(e?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(i&&-1!==i.indexOf("?")?"&":"?")+n.join("&")}function _(t,i){return t.replace(ii,function(t,e){var n=i[e];if(void 0===n)throw new Error("No value provided for variable "+t);return"function"==typeof n&&(n=n(i)),n})}function d(t,i){for(var e=0;e<t.length;e++)if(t[e]===i)return e;return-1}function p(t){return window["webkit"+t]||window["moz"+t]||window["ms"+t]}function m(t){var i=+new Date,e=Math.max(0,16-(i-oi));return oi=i+e,window.setTimeout(t,e)}function f(t,i,n){if(!n||si!==m)return si.call(window,e(t,i));t.call(i)}function g(t){t&&ri.call(window,t)}function v(){}function y(t){if("undefined"!=typeof L&&L&&L.Mixin){t=ei(t)?t:[t];for(var i=0;i<t.length;i++)t[i]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",(new Error).stack)}}function x(t,i,e){this.x=e?Math.round(t):t,this.y=e?Math.round(i):i}function w(t,i,e){return t instanceof x?t:ei(t)?new x(t[0],t[1]):void 0===t||null===t?t:"object"==typeof t&&"x"in t&&"y"in t?new x(t.x,t.y):new x(t,i,e)}function P(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function b(t,i){return!t||t instanceof P?t:new P(t,i)}function T(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function z(t,i){return t instanceof T?t:new T(t,i)}function M(t,i,e){if(isNaN(t)||isNaN(i))throw new Error("Invalid LatLng object: ("+t+", "+i+")");this.lat=+t,this.lng=+i,void 0!==e&&(this.alt=+e)}function C(t,i,e){return t instanceof M?t:ei(t)&&"object"!=typeof t[0]?3===t.length?new M(t[0],t[1],t[2]):2===t.length?new M(t[0],t[1]):null:void 0===t||null===t?t:"object"==typeof t&&"lat"in t?new M(t.lat,"lng"in t?t.lng:t.lon,t.alt):void 0===i?null:new M(t,i,e)}function Z(t,i,e,n){if(ei(t))return this._a=t[0],this._b=t[1],this._c=t[2],void(this._d=t[3]);this._a=t,this._b=i,this._c=e,this._d=n}function S(t,i,e,n){return new Z(t,i,e,n)}function E(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function k(t,i){var e,n,o,s,r,a,h="";for(e=0,o=t.length;e<o;e++){for(n=0,s=(r=t[e]).length;n<s;n++)a=r[n],h+=(n?"L":"M")+a.x+" "+a.y;h+=i?Xi?"z":"x":""}return h||"M0 0"}function A(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}function I(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function B(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(Qi,n,!1):"touchmove"===i?t.removeEventListener(te,n,!1):"touchend"===i&&(t.removeEventListener(ie,n,!1),t.removeEventListener(ee,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(ne.indexOf(t.target.tagName)<0))return;$(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(Qi,o,!1),se||(document.documentElement.addEventListener(Qi,R,!0),document.documentElement.addEventListener(te,D,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,N,!0),se=!0)}function R(t){oe[t.pointerId]=t,re++}function D(t){oe[t.pointerId]&&(oe[t.pointerId]=t)}function N(t){delete oe[t.pointerId],re--}function j(t,i){t.touches=[];for(var e in oe)t.touches.push(oe[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(te,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ie,n,!1),t.addEventListener(ee,n,!1)}function F(t,i,e){function n(t){var i;if(Ui){if(!Pi||"mouse"===t.pointerType)return;i=re}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Ui){if(!Pi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[ue+ae+e]=n,t[ue+he+e]=o,t[ue+"dblclick"+e]=i,t.addEventListener(ae,n,!1),t.addEventListener(he,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[ue+ae+i],n=t[ue+he+i],o=t[ue+"dblclick"+i];return t.removeEventListener(ae,e,!1),t.removeEventListener(he,n,!1),Pi||t.removeEventListener("dblclick",o,!1),this}function V(t,i,e,n){if("object"==typeof i)for(var o in i)G(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s<r;s++)G(t,i[s],e,n);return this}function q(t,i,e,n){if("object"==typeof i)for(var o in i)K(t,o,i[o],e);else if(i)for(var s=0,r=(i=u(i)).length;s<r;s++)K(t,i[s],e,n);else{for(var a in t[le])K(t,a,t[le][a]);delete t[le]}return this}function G(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):"");if(t[le]&&t[le][s])return this;var r=function(i){return e.call(o||t,i||window.event)},a=r;Ui&&0===i.indexOf("touch")?I(t,i,r,s):!Vi||"dblclick"!==i||!F||Ui&&Si?"addEventListener"in t?"mousewheel"===i?t.addEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):"mouseenter"===i||"mouseleave"===i?(r=function(i){i=i||window.event,ot(t,i)&&a(i)},t.addEventListener("mouseenter"===i?"mouseover":"mouseout",r,!1)):("click"===i&&Ti&&(r=function(t){st(t,a)}),t.addEventListener(i,r,!1)):"attachEvent"in t&&t.attachEvent("on"+i,r):F(t,r,s),t[le]=t[le]||{},t[le][s]=r}function K(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):""),r=t[le]&&t[le][s];if(!r)return this;Ui&&0===i.indexOf("touch")?B(t,i,s):!Vi||"dblclick"!==i||!U||Ui&&Si?"removeEventListener"in t?"mousewheel"===i?t.removeEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):t.removeEventListener("mouseenter"===i?"mouseover":"mouseleave"===i?"mouseout":i,r,!1):"detachEvent"in t&&t.detachEvent("on"+i,r):U(t,s),t[le][s]=null}function Y(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,nt(t),this}function X(t){return G(t,"mousewheel",Y),this}function J(t){return V(t,"mousedown touchstart dblclick",Y),G(t,"click",et),this}function $(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this}function Q(t){return $(t),Y(t),this}function tt(t,i){if(!i)return new x(t.clientX,t.clientY);var e=i.getBoundingClientRect(),n=e.width/i.offsetWidth||1,o=e.height/i.offsetHeight||1;return new x(t.clientX/n-e.left-i.clientLeft,t.clientY/o-e.top-i.clientTop)}function it(t){return Pi?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/ce:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function et(t){_e[t.type]=!0}function nt(t){var i=_e[t.type];return _e[t.type]=!1,i}function ot(t,i){var e=i.relatedTarget;if(!e)return!0;try{for(;e&&e!==t;)e=e.parentNode}catch(t){return!1}return e!==t}function st(t,i){var e=t.timeStamp||t.originalEvent&&t.originalEvent.timeStamp,n=pi&&e-pi;n&&n>100&&n<500||t.target._simulatedClick&&!t._simulated?Q(t):(pi=e,i(t))}function rt(t){return"string"==typeof t?document.getElementById(t):t}function at(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function ht(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function ut(t){var i=t.parentNode;i&&i.removeChild(t)}function lt(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ct(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function _t(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function dt(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=gt(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function pt(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;n<o;n++)t.classList.add(e[n]);else if(!dt(t,i)){var s=gt(t);ft(t,(s?s+" ":"")+i)}}function mt(t,i){void 0!==t.classList?t.classList.remove(i):ft(t,h((" "+gt(t)+" ").replace(" "+i+" "," ")))}function ft(t,i){void 0===t.className.baseVal?t.className=i:t.className.baseVal=i}function gt(t){return void 0===t.className.baseVal?t.className:t.className.baseVal}function vt(t,i){"opacity"in t.style?t.style.opacity=i:"filter"in t.style&&yt(t,i)}function yt(t,i){var e=!1,n="DXImageTransform.Microsoft.Alpha";try{e=t.filters.item(n)}catch(t){if(1===i)return}i=Math.round(100*i),e?(e.Enabled=100!==i,e.Opacity=i):t.style.filter+=" progid:"+n+"(opacity="+i+")"}function xt(t){for(var i=document.documentElement.style,e=0;e<t.length;e++)if(t[e]in i)return t[e];return!1}function wt(t,i,e){var n=i||new x(0,0);t.style[pe]=(Oi?"translate("+n.x+"px,"+n.y+"px)":"translate3d("+n.x+"px,"+n.y+"px,0)")+(e?" scale("+e+")":"")}function Lt(t,i){t._leaflet_pos=i,Ni?wt(t,i):(t.style.left=i.x+"px",t.style.top=i.y+"px")}function Pt(t){return t._leaflet_pos||new x(0,0)}function bt(){V(window,"dragstart",$)}function Tt(){q(window,"dragstart",$)}function zt(t){for(;-1===t.tabIndex;)t=t.parentNode;t.style&&(Mt(),ve=t,ye=t.style.outline,t.style.outline="none",V(window,"keydown",Mt))}function Mt(){ve&&(ve.style.outline=ye,ve=void 0,ye=void 0,q(window,"keydown",Mt))}function Ct(t,i){if(!i||!t.length)return t.slice();var e=i*i;return t=kt(t,e),t=St(t,e)}function Zt(t,i,e){return Math.sqrt(Rt(t,i,e,!0))}function St(t,i){var e=t.length,n=new(typeof Uint8Array!=void 0+""?Uint8Array:Array)(e);n[0]=n[e-1]=1,Et(t,n,i,0,e-1);var o,s=[];for(o=0;o<e;o++)n[o]&&s.push(t[o]);return s}function Et(t,i,e,n,o){var s,r,a,h=0;for(r=n+1;r<=o-1;r++)(a=Rt(t[r],t[n],t[o],!0))>h&&(s=r,h=a);h>e&&(i[s]=1,Et(t,i,e,n,s),Et(t,i,e,s,o))}function kt(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;n<s;n++)Ot(t[n],t[o])>i&&(e.push(t[n]),o=n);return o<s-1&&e.push(t[s-1]),e}function At(t,i,e,n,o){var s,r,a,h=n?Se:Bt(t,e),u=Bt(i,e);for(Se=u;;){if(!(h|u))return[t,i];if(h&u)return!1;a=Bt(r=It(t,i,s=h||u,e,o),e),s===h?(t=r,h=a):(i=r,u=a)}}function It(t,i,e,n,o){var s,r,a=i.x-t.x,h=i.y-t.y,u=n.min,l=n.max;return 8&e?(s=t.x+a*(l.y-t.y)/h,r=l.y):4&e?(s=t.x+a*(u.y-t.y)/h,r=u.y):2&e?(s=l.x,r=t.y+h*(l.x-t.x)/a):1&e&&(s=u.x,r=t.y+h*(u.x-t.x)/a),new x(s,r,o)}function Bt(t,i){var e=0;return t.x<i.min.x?e|=1:t.x>i.max.x&&(e|=2),t.y<i.min.y?e|=4:t.y>i.max.y&&(e|=8),e}function Ot(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Rt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&((o=((t.x-s)*a+(t.y-r)*h)/u)>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function Dt(t){return!ei(t[0])||"object"!=typeof t[0][0]&&void 0!==t[0][0]}function Nt(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),Dt(t)}function jt(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o<u;o++)t[o]._code=Bt(t[o],i);for(r=0;r<4;r++){for(l=_[r],n=[],o=0,s=(u=t.length)-1;o<u;s=o++)a=t[o],h=t[s],a._code&l?h._code&l||((c=It(h,a,l,i,e))._code=Bt(c,i),n.push(c)):(h._code&l&&((c=It(h,a,l,i,e))._code=Bt(c,i),n.push(c)),n.push(a));t=n}return t}function Wt(t,i){var e,n,o,s,r="Feature"===t.type?t.geometry:t,a=r?r.coordinates:null,h=[],u=i&&i.pointToLayer,l=i&&i.coordsToLatLng||Ht;if(!a&&!r)return null;switch(r.type){case"Point":return e=l(a),u?u(t,e):new Xe(e);case"MultiPoint":for(o=0,s=a.length;o<s;o++)e=l(a[o]),h.push(u?u(t,e):new Xe(e));return new qe(h);case"LineString":case"MultiLineString":return n=Ft(a,"LineString"===r.type?0:1,l),new tn(n,i);case"Polygon":case"MultiPolygon":return n=Ft(a,"Polygon"===r.type?1:2,l),new en(n,i);case"GeometryCollection":for(o=0,s=r.geometries.length;o<s;o++){var c=Wt({geometry:r.geometries[o],type:"Feature",properties:t.properties},i);c&&h.push(c)}return new qe(h);default:throw new Error("Invalid GeoJSON object.")}}function Ht(t){return new M(t[1],t[0],t[2])}function Ft(t,i,e){for(var n,o=[],s=0,r=t.length;s<r;s++)n=i?Ft(t[s],i-1,e):(e||Ht)(t[s]),o.push(n);return o}function Ut(t,i){return i="number"==typeof i?i:6,void 0!==t.alt?[a(t.lng,i),a(t.lat,i),a(t.alt,i)]:[a(t.lng,i),a(t.lat,i)]}function Vt(t,i,e,n){for(var o=[],s=0,r=t.length;s<r;s++)o.push(i?Vt(t[s],i-1,e,n):Ut(t[s],n));return!i&&e&&o.push(o[0]),o}function qt(t,e){return t.feature?i({},t.feature,{geometry:e}):Gt(e)}function Gt(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}function Kt(t,i){return new nn(t,i)}function Yt(t,i){return new dn(t,i)}function Xt(t){return Yi?new fn(t):null}function Jt(t){return Xi||Ji?new xn(t):null}var $t=Object.freeze;Object.freeze=function(t){return t};var Qt=Object.create||function(){function t(){}return function(i){return t.prototype=i,new t}}(),ti=0,ii=/\{ *([\w_-]+) *\}/g,ei=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},ni="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",oi=0,si=window.requestAnimationFrame||p("RequestAnimationFrame")||m,ri=window.cancelAnimationFrame||p("CancelAnimationFrame")||p("CancelRequestAnimationFrame")||function(t){window.clearTimeout(t)},ai=(Object.freeze||Object)({freeze:$t,extend:i,create:Qt,bind:e,lastId:ti,stamp:n,throttle:o,wrapNum:s,falseFn:r,formatNum:a,trim:h,splitWords:u,setOptions:l,getParamString:c,template:_,isArray:ei,indexOf:d,emptyImageUrl:ni,requestFn:si,cancelFn:ri,requestAnimFrame:f,cancelAnimFrame:g});v.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},n=e.__super__=this.prototype,o=Qt(n);o.constructor=e,e.prototype=o;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&"__super__"!==s&&(e[s]=this[s]);return t.statics&&(i(e,t.statics),delete t.statics),t.includes&&(y(t.includes),i.apply(null,[o].concat(t.includes)),delete t.includes),o.options&&(t.options=i(Qt(o.options),t.options)),i(o,t),o._initHooks=[],o.callInitHooks=function(){if(!this._initHooksCalled){n.callInitHooks&&n.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,i=o._initHooks.length;t<i;t++)o._initHooks[t].call(this)}},e},v.include=function(t){return i(this.prototype,t),this},v.mergeOptions=function(t){return i(this.prototype.options,t),this},v.addInitHook=function(t){var i=Array.prototype.slice.call(arguments,1),e="function"==typeof t?t:function(){this[t].apply(this,i)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(e),this};var hi={on:function(t,i,e){if("object"==typeof t)for(var n in t)this._on(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._on(t[o],i,e);return this},off:function(t,i,e){if(t)if("object"==typeof t)for(var n in t)this._off(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._off(t[o],i,e);else delete this._events;return this},_on:function(t,i,e){this._events=this._events||{};var n=this._events[t];n||(n=[],this._events[t]=n),e===this&&(e=void 0);for(var o={fn:i,ctx:e},s=n,r=0,a=s.length;r<a;r++)if(s[r].fn===i&&s[r].ctx===e)return;s.push(o)},_off:function(t,i,e){var n,o,s;if(this._events&&(n=this._events[t]))if(i){if(e===this&&(e=void 0),n)for(o=0,s=n.length;o<s;o++){var a=n[o];if(a.ctx===e&&a.fn===i)return a.fn=r,this._firingCount&&(this._events[t]=n=n.slice()),void n.splice(o,1)}}else{for(o=0,s=n.length;o<s;o++)n[o].fn=r;delete this._events[t]}},fire:function(t,e,n){if(!this.listens(t,n))return this;var o=i({},e,{type:t,target:this,sourceTarget:e&&e.sourceTarget||this});if(this._events){var s=this._events[t];if(s){this._firingCount=this._firingCount+1||1;for(var r=0,a=s.length;r<a;r++){var h=s[r];h.fn.call(h.ctx||this,o)}this._firingCount--}}return n&&this._propagateEvent(o),this},listens:function(t,i){var e=this._events&&this._events[t];if(e&&e.length)return!0;if(i)for(var n in this._eventParents)if(this._eventParents[n].listens(t,i))return!0;return!1},once:function(t,i,n){if("object"==typeof t){for(var o in t)this.once(o,t[o],i);return this}var s=e(function(){this.off(t,i,n).off(t,s,n)},this);return this.on(t,i,n).on(t,s,n)},addEventParent:function(t){return this._eventParents=this._eventParents||{},this._eventParents[n(t)]=t,this},removeEventParent:function(t){return this._eventParents&&delete this._eventParents[n(t)],this},_propagateEvent:function(t){for(var e in this._eventParents)this._eventParents[e].fire(t.type,i({layer:t.target,propagatedFrom:t.target},t),!0)}};hi.addEventListener=hi.on,hi.removeEventListener=hi.clearAllEventListeners=hi.off,hi.addOneTimeEventListener=hi.once,hi.fireEvent=hi.fire,hi.hasEventListeners=hi.listens;var ui=v.extend(hi),li=Math.trunc||function(t){return t>0?Math.floor(t):Math.ceil(t)};x.prototype={clone:function(){return new x(this.x,this.y)},add:function(t){return this.clone()._add(w(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(w(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new x(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new x(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=li(this.x),this.y=li(this.y),this},distanceTo:function(t){var i=(t=w(t)).x-this.x,e=t.y-this.y;return Math.sqrt(i*i+e*e)},equals:function(t){return(t=w(t)).x===this.x&&t.y===this.y},contains:function(t){return t=w(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+a(this.x)+", "+a(this.y)+")"}},P.prototype={extend:function(t){return t=w(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new x((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new x(this.min.x,this.max.y)},getTopRight:function(){return new x(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var i,e;return(t="number"==typeof t[0]||t instanceof x?w(t):b(t))instanceof P?(i=t.min,e=t.max):i=e=t,i.x>=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.x<e.x,r=o.y>i.y&&n.y<e.y;return s&&r},isValid:function(){return!(!this.min||!this.max)}},T.prototype={extend:function(t){var i,e,n=this._southWest,o=this._northEast;if(t instanceof M)i=t,e=t;else{if(!(t instanceof T))return t?this.extend(C(t)||z(t)):this;if(i=t._southWest,e=t._northEast,!i||!e)return this}return n||o?(n.lat=Math.min(i.lat,n.lat),n.lng=Math.min(i.lng,n.lng),o.lat=Math.max(e.lat,o.lat),o.lng=Math.max(e.lng,o.lng)):(this._southWest=new M(i.lat,i.lng),this._northEast=new M(e.lat,e.lng)),this},pad:function(t){var i=this._southWest,e=this._northEast,n=Math.abs(i.lat-e.lat)*t,o=Math.abs(i.lng-e.lng)*t;return new T(new M(i.lat-n,i.lng-o),new M(e.lat+n,e.lng+o))},getCenter:function(){return new M((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new M(this.getNorth(),this.getWest())},getSouthEast:function(){return new M(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof M||"lat"in t?C(t):z(t);var i,e,n=this._southWest,o=this._northEast;return t instanceof T?(i=t.getSouthWest(),e=t.getNorthEast()):i=e=t,i.lat>=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lat<e.lat,r=o.lng>i.lng&&n.lng<e.lng;return s&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t,i){return!!t&&(t=z(t),this._southWest.equals(t.getSouthWest(),i)&&this._northEast.equals(t.getNorthEast(),i))},isValid:function(){return!(!this._southWest||!this._northEast)}},M.prototype={equals:function(t,i){return!!t&&(t=C(t),Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng))<=(void 0===i?1e-9:i))},toString:function(t){return"LatLng("+a(this.lat,t)+", "+a(this.lng,t)+")"},distanceTo:function(t){return _i.distance(this,C(t))},wrap:function(){return _i.wrapLatLng(this)},toBounds:function(t){var i=180*t/40075017,e=i/Math.cos(Math.PI/180*this.lat);return z([this.lat-i,this.lng-e],[this.lat+i,this.lng+e])},clone:function(){return new M(this.lat,this.lng,this.alt)}};var ci={latLngToPoint:function(t,i){var e=this.projection.project(t),n=this.scale(i);return this.transformation._transform(e,n)},pointToLatLng:function(t,i){var e=this.scale(i),n=this.transformation.untransform(t,e);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},unproject:function(t){return this.projection.unproject(t)},scale:function(t){return 256*Math.pow(2,t)},zoom:function(t){return Math.log(t/256)/Math.LN2},getProjectedBounds:function(t){if(this.infinite)return null;var i=this.projection.bounds,e=this.scale(t);return new P(this.transformation.transform(i.min,e),this.transformation.transform(i.max,e))},infinite:!1,wrapLatLng:function(t){var i=this.wrapLng?s(t.lng,this.wrapLng,!0):t.lng;return new M(this.wrapLat?s(t.lat,this.wrapLat,!0):t.lat,i,t.alt)},wrapLatLngBounds:function(t){var i=t.getCenter(),e=this.wrapLatLng(i),n=i.lat-e.lat,o=i.lng-e.lng;if(0===n&&0===o)return t;var s=t.getSouthWest(),r=t.getNorthEast();return new T(new M(s.lat-n,s.lng-o),new M(r.lat-n,r.lng-o))}},_i=i({},ci,{wrapLng:[-180,180],R:6371e3,distance:function(t,i){var e=Math.PI/180,n=t.lat*e,o=i.lat*e,s=Math.sin((i.lat-t.lat)*e/2),r=Math.sin((i.lng-t.lng)*e/2),a=s*s+Math.cos(n)*Math.cos(o)*r*r,h=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));return this.R*h}}),di={R:6378137,MAX_LATITUDE:85.0511287798,project:function(t){var i=Math.PI/180,e=this.MAX_LATITUDE,n=Math.max(Math.min(e,t.lat),-e),o=Math.sin(n*i);return new x(this.R*t.lng*i,this.R*Math.log((1+o)/(1-o))/2)},unproject:function(t){var i=180/Math.PI;return new M((2*Math.atan(Math.exp(t.y/this.R))-Math.PI/2)*i,t.x*i/this.R)},bounds:function(){var t=6378137*Math.PI;return new P([-t,-t],[t,t])}()};Z.prototype={transform:function(t,i){return this._transform(t.clone(),i)},_transform:function(t,i){return i=i||1,t.x=i*(this._a*t.x+this._b),t.y=i*(this._c*t.y+this._d),t},untransform:function(t,i){return i=i||1,new x((t.x/i-this._b)/this._a,(t.y/i-this._d)/this._c)}};var pi,mi,fi,gi,vi=i({},_i,{code:"EPSG:3857",projection:di,transformation:function(){var t=.5/(Math.PI*di.R);return S(t,.5,-t,.5)}()}),yi=i({},vi,{code:"EPSG:900913"}),xi=document.documentElement.style,wi="ActiveXObject"in window,Li=wi&&!document.addEventListener,Pi="msLaunchUri"in navigator&&!("documentMode"in document),bi=A("webkit"),Ti=A("android"),zi=A("android 2")||A("android 3"),Mi=parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1],10),Ci=Ti&&A("Google")&&Mi<537&&!("AudioNode"in window),Zi=!!window.opera,Si=A("chrome"),Ei=A("gecko")&&!bi&&!Zi&&!wi,ki=!Si&&A("safari"),Ai=A("phantom"),Ii="OTransition"in xi,Bi=0===navigator.platform.indexOf("Win"),Oi=wi&&"transition"in xi,Ri="WebKitCSSMatrix"in window&&"m11"in new window.WebKitCSSMatrix&&!zi,Di="MozPerspective"in xi,Ni=!window.L_DISABLE_3D&&(Oi||Ri||Di)&&!Ii&&!Ai,ji="undefined"!=typeof orientation||A("mobile"),Wi=ji&&bi,Hi=ji&&Ri,Fi=!window.PointerEvent&&window.MSPointerEvent,Ui=!(!window.PointerEvent&&!Fi),Vi=!window.L_NO_TOUCH&&(Ui||"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch),qi=ji&&Zi,Gi=ji&&Ei,Ki=(window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI)>1,Yi=!!document.createElement("canvas").getContext,Xi=!(!document.createElementNS||!E("svg").createSVGRect),Ji=!Xi&&function(){try{var t=document.createElement("div");t.innerHTML='<v:shape adj="1"/>';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),$i=(Object.freeze||Object)({ie:wi,ielt9:Li,edge:Pi,webkit:bi,android:Ti,android23:zi,androidStock:Ci,opera:Zi,chrome:Si,gecko:Ei,safari:ki,phantom:Ai,opera12:Ii,win:Bi,ie3d:Oi,webkit3d:Ri,gecko3d:Di,any3d:Ni,mobile:ji,mobileWebkit:Wi,mobileWebkit3d:Hi,msPointer:Fi,pointer:Ui,touch:Vi,mobileOpera:qi,mobileGecko:Gi,retina:Ki,canvas:Yi,svg:Xi,vml:Ji}),Qi=Fi?"MSPointerDown":"pointerdown",te=Fi?"MSPointerMove":"pointermove",ie=Fi?"MSPointerUp":"pointerup",ee=Fi?"MSPointerCancel":"pointercancel",ne=["INPUT","SELECT","OPTION"],oe={},se=!1,re=0,ae=Fi?"MSPointerDown":Ui?"pointerdown":"touchstart",he=Fi?"MSPointerUp":Ui?"pointerup":"touchend",ue="_leaflet_",le="_leaflet_events",ce=Bi&&Si?2*window.devicePixelRatio:Ei?window.devicePixelRatio:1,_e={},de=(Object.freeze||Object)({on:V,off:q,stopPropagation:Y,disableScrollPropagation:X,disableClickPropagation:J,preventDefault:$,stop:Q,getMousePosition:tt,getWheelDelta:it,fakeStop:et,skipped:nt,isExternalTarget:ot,addListener:V,removeListener:q}),pe=xt(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),me=xt(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),fe="webkitTransition"===me||"OTransition"===me?me+"End":"transitionend";if("onselectstart"in document)mi=function(){V(window,"selectstart",$)},fi=function(){q(window,"selectstart",$)};else{var ge=xt(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);mi=function(){if(ge){var t=document.documentElement.style;gi=t[ge],t[ge]="none"}},fi=function(){ge&&(document.documentElement.style[ge]=gi,gi=void 0)}}var ve,ye,xe=(Object.freeze||Object)({TRANSFORM:pe,TRANSITION:me,TRANSITION_END:fe,get:rt,getStyle:at,create:ht,remove:ut,empty:lt,toFront:ct,toBack:_t,hasClass:dt,addClass:pt,removeClass:mt,setClass:ft,getClass:gt,setOpacity:vt,testProp:xt,setTransform:wt,setPosition:Lt,getPosition:Pt,disableTextSelection:mi,enableTextSelection:fi,disableImageDrag:bt,enableImageDrag:Tt,preventOutline:zt,restoreOutline:Mt}),we=ui.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=Pt(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;i<e?this._runFrame(this._easeOut(i/e),t):(this._runFrame(1),this._complete())},_runFrame:function(t,i){var e=this._startPos.add(this._offset.multiplyBy(t));i&&e._round(),Lt(this._el,e),this.fire("step")},_complete:function(){g(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),Le=ui.extend({options:{crs:vi,center:void 0,zoom:void 0,minZoom:void 0,maxZoom:void 0,layers:[],maxBounds:void 0,renderer:void 0,zoomAnimation:!0,zoomAnimationThreshold:4,fadeAnimation:!0,markerZoomAnimation:!0,transform3DLimit:8388608,zoomSnap:1,zoomDelta:1,trackResize:!0},initialize:function(t,i){i=l(this,i),this._initContainer(t),this._initLayout(),this._onResize=e(this._onResize,this),this._initEvents(),i.maxBounds&&this.setMaxBounds(i.maxBounds),void 0!==i.zoom&&(this._zoom=this._limitZoom(i.zoom)),i.center&&void 0!==i.zoom&&this.setView(C(i.center),i.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._sizeChanged=!0,this.callInitHooks(),this._zoomAnimated=me&&Ni&&!qi&&this.options.zoomAnimation,this._zoomAnimated&&(this._createAnimProxy(),V(this._proxy,fe,this._catchTransitionEnd,this)),this._addLayers(this.options.layers)},setView:function(t,e,n){return e=void 0===e?this._zoom:this._limitZoom(e),t=this._limitCenter(C(t),e,this.options.maxBounds),n=n||{},this._stop(),this._loaded&&!n.reset&&!0!==n&&(void 0!==n.animate&&(n.zoom=i({animate:n.animate},n.zoom),n.pan=i({animate:n.animate,duration:n.duration},n.pan)),this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan))?(clearTimeout(this._sizeTimer),this):(this._resetView(t,e),this)},setZoom:function(t,i){return this._loaded?this.setView(this.getCenter(),t,{zoom:i}):(this._zoom=t,this)},zoomIn:function(t,i){return t=t||(Ni?this.options.zoomDelta:1),this.setZoom(this._zoom+t,i)},zoomOut:function(t,i){return t=t||(Ni?this.options.zoomDelta:1),this.setZoom(this._zoom-t,i)},setZoomAround:function(t,i,e){var n=this.getZoomScale(i),o=this.getSize().divideBy(2),s=(t instanceof x?t:this.latLngToContainerPoint(t)).subtract(o).multiplyBy(1-1/n),r=this.containerPointToLatLng(o.add(s));return this.setView(r,i,{zoom:e})},_getBoundsCenterZoom:function(t,i){i=i||{},t=t.getBounds?t.getBounds():z(t);var e=w(i.paddingTopLeft||i.padding||[0,0]),n=w(i.paddingBottomRight||i.padding||[0,0]),o=this.getBoundsZoom(t,!1,e.add(n));if((o="number"==typeof i.maxZoom?Math.min(i.maxZoom,o):o)===1/0)return{center:t.getCenter(),zoom:o};var s=n.subtract(e).divideBy(2),r=this.project(t.getSouthWest(),o),a=this.project(t.getNorthEast(),o);return{center:this.unproject(r.add(a).divideBy(2).add(s),o),zoom:o}},fitBounds:function(t,i){if(!(t=z(t)).isValid())throw new Error("Bounds are not valid.");var e=this._getBoundsCenterZoom(t,i);return this.setView(e.center,e.zoom,i)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,i){return this.setView(t,this._zoom,{pan:i})},panBy:function(t,i){if(t=w(t).round(),i=i||{},!t.x&&!t.y)return this.fire("moveend");if(!0!==i.animate&&!this.getSize().contains(t))return this._resetView(this.unproject(this.project(this.getCenter()).add(t)),this.getZoom()),this;if(this._panAnim||(this._panAnim=new we,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),i.noMoveStart||this.fire("movestart"),!1!==i.animate){pt(this._mapPane,"leaflet-pan-anim");var e=this._getMapPanePos().subtract(t).round();this._panAnim.run(this._mapPane,e,i.duration||.25,i.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},flyTo:function(t,i,e){function n(t){var i=(g*g-m*m+(t?-1:1)*x*x*v*v)/(2*(t?g:m)*x*v),e=Math.sqrt(i*i+1)-i;return e<1e-9?-18:Math.log(e)}function o(t){return(Math.exp(t)-Math.exp(-t))/2}function s(t){return(Math.exp(t)+Math.exp(-t))/2}function r(t){return o(t)/s(t)}function a(t){return m*(s(w)/s(w+y*t))}function h(t){return m*(s(w)*r(w+y*t)-o(w))/x}function u(t){return 1-Math.pow(1-t,1.5)}function l(){var e=(Date.now()-L)/b,n=u(e)*P;e<=1?(this._flyToFrame=f(l,this),this._move(this.unproject(c.add(_.subtract(c).multiplyBy(h(n)/v)),p),this.getScaleZoom(m/a(n),p),{flyTo:!0})):this._move(t,i)._moveEnd(!0)}if(!1===(e=e||{}).animate||!Ni)return this.setView(t,i,e);this._stop();var c=this.project(this.getCenter()),_=this.project(t),d=this.getSize(),p=this._zoom;t=C(t),i=void 0===i?p:i;var m=Math.max(d.x,d.y),g=m*this.getZoomScale(p,i),v=_.distanceTo(c)||1,y=1.42,x=y*y,w=n(0),L=Date.now(),P=(n(1)-w)/y,b=e.duration?1e3*e.duration:1e3*P*.8;return this._moveStart(!0,e.noMoveStart),l.call(this),this},flyToBounds:function(t,i){var e=this._getBoundsCenterZoom(t,i);return this.flyTo(e.center,e.zoom,i)},setMaxBounds:function(t){return(t=z(t)).isValid()?(this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this.options.maxBounds=t,this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds)):(this.options.maxBounds=null,this.off("moveend",this._panInsideMaxBounds))},setMinZoom:function(t){var i=this.options.minZoom;return this.options.minZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()<this.options.minZoom)?this.setZoom(t):this},setMaxZoom:function(t){var i=this.options.maxZoom;return this.options.maxZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()>this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=new M(t.coords.latitude,t.coords.longitude),e=i.toBounds(t.coords.accuracy),n=this._locateOptions;if(n.setView){var o=this.getBoundsZoom(e);this.setView(i,n.maxZoom?Math.min(o,n.maxZoom):o)}var s={latlng:i,bounds:e,timestamp:t.timestamp};for(var r in t.coords)"number"==typeof t.coords[r]&&(s[r]=t.coords[r]);this.fire("locationfound",s)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),ut(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)ut(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e=ht("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new T(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=Ni?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){return this.project(C(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return tt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=rt(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");V(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&Ni,pt(t,"leaflet-container"+(Vi?" leaflet-touch":"")+(Ki?" leaflet-retina":"")+(Li?" leaflet-oldie":"")+(ki?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=at(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Lt(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(pt(t.markerPane,"leaflet-zoom-hide"),pt(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){Lt(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,!1)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Lt(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?q:V;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),Ni&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if((e=this._targets[n(r)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!ot(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!ot(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!nt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||zt(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e))).length){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&$(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.getLatLng&&(!s._radius||s._radius<=10);r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h<n.length;h++)if(n[h].fire(e,r,!0),r.originalEvent._stopped||!1===n[h].options.bubblingMouseEvents&&-1!==d(this._mouseEvents,e))return}},_draggableMoved:function(t){return(t=t.dragging&&t.dragging.enabled()?t:this).dragging&&t.dragging.moved()||this.boxZoom&&this.boxZoom.moved()},_clearHandlers:function(){for(var t=0,i=this._handlers.length;t<i;t++)this._handlers[t].disable()},whenReady:function(t,i){return this._loaded?t.call(i||this,{target:this}):this.on("load",t,i),this},_getMapPanePos:function(){return Pt(this._mapPane)||new x(0,0)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(t,i){return(t&&void 0!==i?this._getNewPixelOrigin(t,i):this.getPixelOrigin()).subtract(this._getMapPanePos())},_getNewPixelOrigin:function(t,i){var e=this.getSize()._divideBy(2);return this.project(t,i)._subtract(e)._add(this._getMapPanePos())._round()},_latLngToNewLayerPoint:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return this.project(t,i)._subtract(n)},_latLngBoundsToNewLayerBounds:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return b([this.project(t.getSouthWest(),i)._subtract(n),this.project(t.getNorthWest(),i)._subtract(n),this.project(t.getSouthEast(),i)._subtract(n),this.project(t.getNorthEast(),i)._subtract(n)])},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,i,e){if(!e)return t;var n=this.project(t,i),o=this.getSize().divideBy(2),s=new P(n.subtract(o),n.add(o)),r=this._getBoundsOffset(s,e,i);return r.round().equals([0,0])?t:this.unproject(n.add(r),i)},_limitOffset:function(t,i){if(!i)return t;var e=this.getPixelBounds(),n=new P(e.min.add(t),e.max.add(t));return t.add(this._getBoundsOffset(n,i))},_getBoundsOffset:function(t,i,e){var n=b(this.project(i.getNorthEast(),e),this.project(i.getSouthWest(),e)),o=n.min.subtract(t.min),s=n.max.subtract(t.max);return new x(this._rebound(o.x,-s.x),this._rebound(o.y,-s.y))},_rebound:function(t,i){return t+i>0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=Ni?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){mt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._trunc();return!(!0!==(i&&i.animate)&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=ht("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=pe,e=this._proxy.style[i];wt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();wt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){ut(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||!1===e.animate||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,pt(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&mt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Pe=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return pt(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(ut(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),be=function(t){return new Pe(t)};Le.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=ht("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=ht("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)ut(this._controlCorners[t]);ut(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Te=Pe.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e<n?-1:n<e?1:0}},initialize:function(t,i,e){l(this,e),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in i)this._addLayer(i[n],n,!0)},onAdd:function(t){this._initLayout(),this._update(),this._map=t,t.on("zoomend",this._checkDisabledLayers,this);for(var i=0;i<this._layers.length;i++)this._layers[i].layer.on("add remove",this._onLayerChange,this);return this._container},addTo:function(t){return Pe.prototype.addTo.call(this,t),this._expandIfNotCollapsed()},onRemove:function(){this._map.off("zoomend",this._checkDisabledLayers,this);for(var t=0;t<this._layers.length;t++)this._layers[t].layer.off("add remove",this._onLayerChange,this)},addBaseLayer:function(t,i){return this._addLayer(t,i),this._map?this._update():this},addOverlay:function(t,i){return this._addLayer(t,i,!0),this._map?this._update():this},removeLayer:function(t){t.off("add remove",this._onLayerChange,this);var i=this._getLayer(n(t));return i&&this._layers.splice(this._layers.indexOf(i),1),this._map?this._update():this},expand:function(){pt(this._container,"leaflet-control-layers-expanded"),this._form.style.height=null;var t=this._map.getSize().y-(this._container.offsetTop+50);return t<this._form.clientHeight?(pt(this._form,"leaflet-control-layers-scrollbar"),this._form.style.height=t+"px"):mt(this._form,"leaflet-control-layers-scrollbar"),this._checkDisabledLayers(),this},collapse:function(){return mt(this._container,"leaflet-control-layers-expanded"),this},_initLayout:function(){var t="leaflet-control-layers",i=this._container=ht("div",t),e=this.options.collapsed;i.setAttribute("aria-haspopup",!0),J(i),X(i);var n=this._form=ht("form",t+"-list");e&&(this._map.on("click",this.collapse,this),Ti||V(i,{mouseenter:this.expand,mouseleave:this.collapse},this));var o=this._layersLink=ht("a",t+"-toggle",i);o.href="#",o.title="Layers",Vi?(V(o,"click",Q),V(o,"click",this.expand,this)):V(o,"focus",this.expand,this),e||this.expand(),this._baseLayersList=ht("div",t+"-base",n),this._separator=ht("div",t+"-separator",n),this._overlaysList=ht("div",t+"-overlays",n),i.appendChild(n)},_getLayer:function(t){for(var i=0;i<this._layers.length;i++)if(this._layers[i]&&n(this._layers[i].layer)===t)return this._layers[i]},_addLayer:function(t,i,n){this._map&&t.on("add remove",this._onLayerChange,this),this._layers.push({layer:t,name:i,overlay:n}),this.options.sortLayers&&this._layers.sort(e(function(t,i){return this.options.sortFunction(t.layer,i.layer,t.name,i.name)},this)),this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex)),this._expandIfNotCollapsed()},_update:function(){if(!this._container)return this;lt(this._baseLayersList),lt(this._overlaysList),this._layerControlInputs=[];var t,i,e,n,o=0;for(e=0;e<this._layers.length;e++)n=this._layers[e],this._addItem(n),i=i||n.overlay,t=t||!n.overlay,o+=n.overlay?0:1;return this.options.hideSingleBase&&(t=t&&o>1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='<input type="radio" class="leaflet-control-layers-selector" name="'+t+'"'+(i?' checked="checked"':"")+"/>",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),V(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");return e.appendChild(r),r.appendChild(i),r.appendChild(s),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;s>=0;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;s<o.length;s++)this._map.hasLayer(o[s])&&this._map.removeLayer(o[s]);for(s=0;s<n.length;s++)this._map.hasLayer(n[s])||this._map.addLayer(n[s]);this._handlingClick=!1,this._refocusOnMap()},_checkDisabledLayers:function(){for(var t,i,e=this._layerControlInputs,n=this._map.getZoom(),o=e.length-1;o>=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&n<i.options.minZoom||void 0!==i.options.maxZoom&&n>i.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),ze=Pe.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ht("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoom<this._map.getMaxZoom()&&this._map.zoomIn(this._map.options.zoomDelta*(t.shiftKey?3:1))},_zoomOut:function(t){!this._disabled&&this._map._zoom>this._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=ht("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),J(s),V(s,"click",Q),V(s,"click",o,this),V(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";mt(this._zoomInButton,i),mt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&pt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&pt(this._zoomInButton,i)}});Le.mergeOptions({zoomControl:!0}),Le.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new ze,this.addControl(this.zoomControl))});var Me=Pe.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i=ht("div","leaflet-control-scale"),e=this.options;return this._addScales(e,"leaflet-control-scale-line",i),t.on(e.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ht("div",i,e)),t.imperial&&(this._iScale=ht("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),Ce=Pe.extend({options:{position:"bottomright",prefix:'<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=ht("div","leaflet-control-attribution"),J(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});Le.mergeOptions({attributionControl:!0}),Le.addInitHook(function(){this.options.attributionControl&&(new Ce).addTo(this)});Pe.Layers=Te,Pe.Zoom=ze,Pe.Scale=Me,Pe.Attribution=Ce,be.layers=function(t,i,e){return new Te(t,i,e)},be.zoom=function(t){return new ze(t)},be.scale=function(t){return new Me(t)},be.attribution=function(t){return new Ce(t)};var Ze=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});Ze.addTo=function(t,i){return t.addHandler(i,this),this};var Se,Ee={Events:hi},ke=Vi?"touchstart mousedown":"mousedown",Ae={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Ie={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Be=ui.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(V(this._dragStartTarget,ke,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Be._dragging===this&&this.finishDrag(),q(this._dragStartTarget,ke,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!dt(this._element,"leaflet-zoom-anim")&&!(Be._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Be._dragging=this,this._preventOutline&&zt(this._element),bt(),mi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t;this._startPoint=new x(i.clientX,i.clientY),V(document,Ie[t.type],this._onMove,this),V(document,Ae[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY).subtract(this._startPoint);(e.x||e.y)&&(Math.abs(e.x)+Math.abs(e.y)<this.options.clickTolerance||($(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=Pt(this._element).subtract(e),pt(document.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,window.SVGElementInstance&&this._lastTarget instanceof SVGElementInstance&&(this._lastTarget=this._lastTarget.correspondingUseElement),pt(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(e),this._moving=!0,g(this._animRequest),this._lastEvent=t,this._animRequest=f(this._updatePosition,this,!0)))}},_updatePosition:function(){var t={originalEvent:this._lastEvent};this.fire("predrag",t),Lt(this._element,this._newPos),this.fire("drag",t)},_onUp:function(t){!t._simulated&&this._enabled&&this.finishDrag()},finishDrag:function(){mt(document.body,"leaflet-dragging"),this._lastTarget&&(mt(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in Ie)q(document,Ie[t],this._onMove,this),q(document,Ae[t],this._onUp,this);Tt(),fi(),this._moved&&this._moving&&(g(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1,Be._dragging=!1}}),Oe=(Object.freeze||Object)({simplify:Ct,pointToSegmentDistance:Zt,closestPointOnSegment:function(t,i,e){return Rt(t,i,e)},clipSegment:At,_getEdgeIntersection:It,_getBitCode:Bt,_sqClosestPointOnSegment:Rt,isFlat:Dt,_flat:Nt}),Re=(Object.freeze||Object)({clipPolygon:jt}),De={project:function(t){return new x(t.lng,t.lat)},unproject:function(t){return new M(t.y,t.x)},bounds:new P([-180,-90],[180,90])},Ne={R:6378137,R_MINOR:6356752.314245179,bounds:new P([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var i=Math.PI/180,e=this.R,n=t.lat*i,o=this.R_MINOR/e,s=Math.sqrt(1-o*o),r=s*Math.sin(n),a=Math.tan(Math.PI/4-n/2)/Math.pow((1-r)/(1+r),s/2);return n=-e*Math.log(Math.max(a,1e-10)),new x(t.lng*i*e,n)},unproject:function(t){for(var i,e=180/Math.PI,n=this.R,o=this.R_MINOR/n,s=Math.sqrt(1-o*o),r=Math.exp(-t.y/n),a=Math.PI/2-2*Math.atan(r),h=0,u=.1;h<15&&Math.abs(u)>1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),a+=u=Math.PI/2-2*Math.atan(r*i)-a;return new M(a*e,t.x*e/n)}},je=(Object.freeze||Object)({LonLat:De,Mercator:Ne,SphericalMercator:di}),We=i({},_i,{code:"EPSG:3395",projection:Ne,transformation:function(){var t=.5/(Math.PI*Ne.R);return S(t,.5,-t,.5)}()}),He=i({},_i,{code:"EPSG:4326",projection:De,transformation:S(1/180,1,-1/180,.5)}),Fe=i({},ci,{projection:De,transformation:S(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});ci.Earth=_i,ci.EPSG3395=We,ci.EPSG3857=vi,ci.EPSG900913=yi,ci.EPSG4326=He,ci.Simple=Fe;var Ue=ui.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});Le.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){for(var i=0,e=(t=t?ei(t)?t:[t]:[]).length;i<e;i++)this.addLayer(t[i])},_addZoomLimit:function(t){!isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[n(t)]=t,this._updateZoomLevels())},_removeZoomLimit:function(t){var i=n(t);this._zoomBoundLayers[i]&&(delete this._zoomBoundLayers[i],this._updateZoomLevels())},_updateZoomLevels:function(){var t=1/0,i=-1/0,e=this._getZoomSpan();for(var n in this._zoomBoundLayers){var o=this._zoomBoundLayers[n].options;t=void 0===o.minZoom?t:Math.min(t,o.minZoom),i=void 0===o.maxZoom?i:Math.max(i,o.maxZoom)}this._layersMaxZoom=i===-1/0?void 0:i,this._layersMinZoom=t===1/0?void 0:t,e!==this._getZoomSpan()&&this.fire("zoomlevelschange"),void 0===this.options.maxZoom&&this._layersMaxZoom&&this.getZoom()>this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()<this._layersMinZoom&&this.setZoom(this._layersMinZoom)}});var Ve=Ue.extend({initialize:function(t,i){l(this,i),this._layers={};var e,n;if(t)for(e=0,n=t.length;e<n;e++)this.addLayer(t[e])},addLayer:function(t){var i=this.getLayerId(t);return this._layers[i]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var i=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[i]&&this._map.removeLayer(this._layers[i]),delete this._layers[i],this},hasLayer:function(t){return!!t&&(t in this._layers||this.getLayerId(t)in this._layers)},clearLayers:function(){return this.eachLayer(this.removeLayer,this)},invoke:function(t){var i,e,n=Array.prototype.slice.call(arguments,1);for(i in this._layers)(e=this._layers[i])[t]&&e[t].apply(e,n);return this},onAdd:function(t){this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t)},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];return this.eachLayer(t.push,t),t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return n(t)}}),qe=Ve.extend({addLayer:function(t){return this.hasLayer(t)?this:(t.addEventParent(this),Ve.prototype.addLayer.call(this,t),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.removeEventParent(this),Ve.prototype.removeLayer.call(this,t),this.fire("layerremove",{layer:t})):this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new T;for(var i in this._layers){var e=this._layers[i];t.extend(e.getBounds?e.getBounds():e.getLatLng())}return t}}),Ge=v.extend({options:{popupAnchor:[0,0],tooltipAnchor:[0,0]},initialize:function(t){l(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,i){var e=this._getIconUrl(t);if(!e){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(e,i&&"IMG"===i.tagName?i:null);return this._setIconStyles(n,t),n},_setIconStyles:function(t,i){var e=this.options,n=e[i+"Size"];"number"==typeof n&&(n=[n,n]);var o=w(n),s=w("shadow"===i&&e.shadowAnchor||e.iconAnchor||o&&o.divideBy(2,!0));t.className="leaflet-marker-"+i+" "+(e.className||""),s&&(t.style.marginLeft=-s.x+"px",t.style.marginTop=-s.y+"px"),o&&(t.style.width=o.x+"px",t.style.height=o.y+"px")},_createImg:function(t,i){return i=i||document.createElement("img"),i.src=t,i},_getIconUrl:function(t){return Ki&&this.options[t+"RetinaUrl"]||this.options[t+"Url"]}}),Ke=Ge.extend({options:{iconUrl:"marker-icon.png",iconRetinaUrl:"marker-icon-2x.png",shadowUrl:"marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28],shadowSize:[41,41]},_getIconUrl:function(t){return Ke.imagePath||(Ke.imagePath=this._detectIconPath()),(this.options.imagePath||Ke.imagePath)+Ge.prototype._getIconUrl.call(this,t)},_detectIconPath:function(){var t=ht("div","leaflet-default-icon-path",document.body),i=at(t,"background-image")||at(t,"backgroundImage");return document.body.removeChild(t),i=null===i||0!==i.indexOf("url")?"":i.replace(/^url\(["']?/,"").replace(/marker-icon\.png["']?\)$/,"")}}),Ye=Ze.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new Be(t,t,!0)),this._draggable.on({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).enable(),pt(t,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).disable(),this._marker._icon&&mt(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_adjustPan:function(t){var i=this._marker,e=i._map,n=this._marker.options.autoPanSpeed,o=this._marker.options.autoPanPadding,s=L.DomUtil.getPosition(i._icon),r=e.getPixelBounds(),a=e.getPixelOrigin(),h=b(r.min._subtract(a).add(o),r.max._subtract(a).subtract(o));if(!h.contains(s)){var u=w((Math.max(h.max.x,s.x)-h.max.x)/(r.max.x-h.max.x)-(Math.min(h.min.x,s.x)-h.min.x)/(r.min.x-h.min.x),(Math.max(h.max.y,s.y)-h.max.y)/(r.max.y-h.max.y)-(Math.min(h.min.y,s.y)-h.min.y)/(r.min.y-h.min.y)).multiplyBy(n);e.panBy(u,{animate:!1}),this._draggable._newPos._add(u),this._draggable._startPos._add(u),L.DomUtil.setPosition(i._icon,this._draggable._newPos),this._onDrag(t),this._panRequest=f(this._adjustPan.bind(this,t))}},_onDragStart:function(){this._oldLatLng=this._marker.getLatLng(),this._marker.closePopup().fire("movestart").fire("dragstart")},_onPreDrag:function(t){this._marker.options.autoPan&&(g(this._panRequest),this._panRequest=f(this._adjustPan.bind(this,t)))},_onDrag:function(t){var i=this._marker,e=i._shadow,n=Pt(i._icon),o=i._map.layerPointToLatLng(n);e&&Lt(e,n),i._latlng=o,t.latlng=o,t.oldLatLng=this._oldLatLng,i.fire("move",t).fire("drag",t)},_onDragEnd:function(t){g(this._panRequest),delete this._oldLatLng,this._marker.fire("moveend").fire("dragend",t)}}),Xe=Ue.extend({options:{icon:new Ke,interactive:!0,draggable:!1,autoPan:!1,autoPanPadding:[50,50],autoPanSpeed:10,keyboard:!0,title:"",alt:"",zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250,pane:"markerPane",bubblingMouseEvents:!1},initialize:function(t,i){l(this,i),this._latlng=C(t)},onAdd:function(t){this._zoomAnimated=this._zoomAnimated&&t.options.markerZoomAnimation,this._zoomAnimated&&t.on("zoomanim",this._animateZoom,this),this._initIcon(),this.update()},onRemove:function(t){this.dragging&&this.dragging.enabled()&&(this.options.draggable=!0,this.dragging.removeHooks()),delete this.dragging,this._zoomAnimated&&t.off("zoomanim",this._animateZoom,this),this._removeIcon(),this._removeShadow()},getEvents:function(){return{zoom:this.update,viewreset:this.update}},getLatLng:function(){return this._latlng},setLatLng:function(t){var i=this._latlng;return this._latlng=C(t),this.update(),this.fire("move",{oldLatLng:i,latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update()},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup,this._popup.options),this},getElement:function(){return this._icon},update:function(){if(this._icon&&this._map){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,i="leaflet-zoom-"+(this._zoomAnimated?"animated":"hide"),e=t.icon.createIcon(this._icon),n=!1;e!==this._icon&&(this._icon&&this._removeIcon(),n=!0,t.title&&(e.title=t.title),"IMG"===e.tagName&&(e.alt=t.alt||"")),pt(e,i),t.keyboard&&(e.tabIndex="0"),this._icon=e,t.riseOnHover&&this.on({mouseover:this._bringToFront,mouseout:this._resetZIndex});var o=t.icon.createShadow(this._shadow),s=!1;o!==this._shadow&&(this._removeShadow(),s=!0),o&&(pt(o,i),o.alt=""),this._shadow=o,t.opacity<1&&this._updateOpacity(),n&&this.getPane().appendChild(this._icon),this._initInteraction(),o&&s&&this.getPane("shadowPane").appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&this.off({mouseover:this._bringToFront,mouseout:this._resetZIndex}),ut(this._icon),this.removeInteractiveTarget(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&ut(this._shadow),this._shadow=null},_setPos:function(t){Lt(this._icon,t),this._shadow&&Lt(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(i)},_initInteraction:function(){if(this.options.interactive&&(pt(this._icon,"leaflet-interactive"),this.addInteractiveTarget(this._icon),Ye)){var t=this.options.draggable;this.dragging&&(t=this.dragging.enabled(),this.dragging.disable()),this.dragging=new Ye(this),t&&this.dragging.enable()}},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){var t=this.options.opacity;vt(this._icon,t),this._shadow&&vt(this._shadow,t)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)},_getPopupAnchor:function(){return this.options.icon.options.popupAnchor},_getTooltipAnchor:function(){return this.options.icon.options.tooltipAnchor}}),Je=Ue.extend({options:{stroke:!0,color:"#3388ff",weight:3,opacity:1,lineCap:"round",lineJoin:"round",dashArray:null,dashOffset:null,fill:!1,fillColor:null,fillOpacity:.2,fillRule:"evenodd",interactive:!0,bubblingMouseEvents:!0},beforeAdd:function(t){this._renderer=t.getRenderer(this)},onAdd:function(){this._renderer._initPath(this),this._reset(),this._renderer._addPath(this)},onRemove:function(){this._renderer._removePath(this)},redraw:function(){return this._map&&this._renderer._updatePath(this),this},setStyle:function(t){return l(this,t),this._renderer&&this._renderer._updateStyle(this),this},bringToFront:function(){return this._renderer&&this._renderer._bringToFront(this),this},bringToBack:function(){return this._renderer&&this._renderer._bringToBack(this),this},getElement:function(){return this._path},_reset:function(){this._project(),this._update()},_clickTolerance:function(){return(this.options.stroke?this.options.weight/2:0)+this._renderer.options.tolerance}}),$e=Je.extend({options:{fill:!0,radius:10},initialize:function(t,i){l(this,i),this._latlng=C(t),this._radius=this.options.radius},setLatLng:function(t){return this._latlng=C(t),this.redraw(),this.fire("move",{latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var i=t&&t.radius||this._radius;return Je.prototype.setStyle.call(this,t),this.setRadius(i),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,i=this._radiusY||t,e=this._clickTolerance(),n=[t+e,i+e];this._pxBounds=new P(this._point.subtract(n),this._point.add(n))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateCircle(this)},_empty:function(){return this._radius&&!this._renderer._bounds.intersects(this._pxBounds)},_containsPoint:function(t){return t.distanceTo(this._point)<=this._radius+this._clickTolerance()}}),Qe=$e.extend({initialize:function(t,e,n){if("number"==typeof e&&(e=i({},n,{radius:e})),l(this,e),this._latlng=C(t),isNaN(this.options.radius))throw new Error("Circle radius cannot be NaN");this._mRadius=this.options.radius},setRadius:function(t){return this._mRadius=t,this.redraw()},getRadius:function(){return this._mRadius},getBounds:function(){var t=[this._radius,this._radiusY||this._radius];return new T(this._map.layerPointToLatLng(this._point.subtract(t)),this._map.layerPointToLatLng(this._point.add(t)))},setStyle:Je.prototype.setStyle,_project:function(){var t=this._latlng.lng,i=this._latlng.lat,e=this._map,n=e.options.crs;if(n.distance===_i.distance){var o=Math.PI/180,s=this._mRadius/_i.R/o,r=e.project([i+s,t]),a=e.project([i-s,t]),h=r.add(a).divideBy(2),u=e.unproject(h).lat,l=Math.acos((Math.cos(s*o)-Math.sin(i*o)*Math.sin(u*o))/(Math.cos(i*o)*Math.cos(u*o)))/o;(isNaN(l)||0===l)&&(l=s/Math.cos(Math.PI/180*i)),this._point=h.subtract(e.getPixelOrigin()),this._radius=isNaN(l)?0:h.x-e.project([u,t-l]).x,this._radiusY=h.y-r.y}else{var c=n.unproject(n.project(this._latlng).subtract([this._mRadius,0]));this._point=e.latLngToLayerPoint(this._latlng),this._radius=this._point.x-e.latLngToLayerPoint(c).x}this._updateBounds()}}),tn=Je.extend({options:{smoothFactor:1,noClip:!1},initialize:function(t,i){l(this,i),this._setLatLngs(t)},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._setLatLngs(t),this.redraw()},isEmpty:function(){return!this._latlngs.length},closestLayerPoint:function(t){for(var i,e,n=1/0,o=null,s=Rt,r=0,a=this._parts.length;r<a;r++)for(var h=this._parts[r],u=1,l=h.length;u<l;u++){var c=s(t,i=h[u-1],e=h[u],!0);c<n&&(n=c,o=s(t,i,e))}return o&&(o.distance=Math.sqrt(n)),o},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a=this._rings[0],h=a.length;if(!h)return null;for(t=0,i=0;t<h-1;t++)i+=a[t].distanceTo(a[t+1])/2;if(0===i)return this._map.layerPointToLatLng(a[0]);for(t=0,n=0;t<h-1;t++)if(o=a[t],s=a[t+1],e=o.distanceTo(s),(n+=e)>i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return Dt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=Dt(t),n=0,o=t.length;n<o;n++)e?(i[n]=C(t[n]),this._bounds.extend(i[n])):i[n]=this._convertLatLngs(t[n]);return i},_project:function(){var t=new P;this._rings=[],this._projectLatlngs(this._latlngs,this._rings,t);var i=this._clickTolerance(),e=new x(i,i);this._bounds.isValid()&&t.isValid()&&(t.min._subtract(e),t.max._add(e),this._pxBounds=t)},_projectLatlngs:function(t,i,e){var n,o,s=t[0]instanceof M,r=t.length;if(s){for(o=[],n=0;n<r;n++)o[n]=this._map.latLngToLayerPoint(t[n]),e.extend(o[n]);i.push(o)}else for(n=0;n<r;n++)this._projectLatlngs(t[n],i,e)},_clipPoints:function(){var t=this._renderer._bounds;if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else{var i,e,n,o,s,r,a,h=this._parts;for(i=0,n=0,o=this._rings.length;i<o;i++)for(e=0,s=(a=this._rings[i]).length;e<s-1;e++)(r=At(a[e],a[e+1],t,e,!0))&&(h[n]=h[n]||[],h[n].push(r[0]),r[1]===a[e+1]&&e!==s-2||(h[n].push(r[1]),n++))}},_simplifyPoints:function(){for(var t=this._parts,i=this.options.smoothFactor,e=0,n=t.length;e<n;e++)t[e]=Ct(t[e],i)},_update:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),this._updatePath())},_updatePath:function(){this._renderer._updatePoly(this)},_containsPoint:function(t,i){var e,n,o,s,r,a,h=this._clickTolerance();if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(e=0,s=this._parts.length;e<s;e++)for(n=0,o=(r=(a=this._parts[e]).length)-1;n<r;o=n++)if((i||0!==n)&&Zt(t,a[o],a[n])<=h)return!0;return!1}});tn._flat=Nt;var en=tn.extend({options:{fill:!0},isEmpty:function(){return!this._latlngs.length||!this._latlngs[0].length},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a,h,u=this._rings[0],l=u.length;if(!l)return null;for(s=r=a=0,t=0,i=l-1;t<l;i=t++)e=u[t],n=u[i],o=e.y*n.x-n.y*e.x,r+=(e.x+n.x)*o,a+=(e.y+n.y)*o,s+=3*o;return h=0===s?u[0]:[r/s,a/s],this._map.layerPointToLatLng(h)},_convertLatLngs:function(t){var i=tn.prototype._convertLatLngs.call(this,t),e=i.length;return e>=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){tn.prototype._setLatLngs.call(this,t),Dt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return Dt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var n,o=0,s=this._rings.length;o<s;o++)(n=jt(this._rings[o],t,!0)).length&&this._parts.push(n)},_updatePath:function(){this._renderer._updatePoly(this,!0)},_containsPoint:function(t){var i,e,n,o,s,r,a,h,u=!1;if(!this._pxBounds.contains(t))return!1;for(o=0,a=this._parts.length;o<a;o++)for(s=0,r=(h=(i=this._parts[o]).length)-1;s<h;r=s++)e=i[s],n=i[r],e.y>t.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||tn.prototype._containsPoint.call(this,t,!0)}}),nn=qe.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=ei(t)?t:t.features;if(o){for(i=0,e=o.length;i<e;i++)((n=o[i]).geometries||n.geometry||n.features||n.coordinates)&&this.addData(n);return this}var s=this.options;if(s.filter&&!s.filter(t))return this;var r=Wt(t,s);return r?(r.feature=Gt(t),r.defaultOptions=r.options,this.resetStyle(r),s.onEachFeature&&s.onEachFeature(t,r),this.addLayer(r)):this},resetStyle:function(t){return t.options=i({},t.defaultOptions),this._setLayerStyle(t,this.options.style),this},setStyle:function(t){return this.eachLayer(function(i){this._setLayerStyle(i,t)},this)},_setLayerStyle:function(t,i){"function"==typeof i&&(i=i(t.feature)),t.setStyle&&t.setStyle(i)}}),on={toGeoJSON:function(t){return qt(this,{type:"Point",coordinates:Ut(this.getLatLng(),t)})}};Xe.include(on),Qe.include(on),$e.include(on),tn.include({toGeoJSON:function(t){var i=!Dt(this._latlngs),e=Vt(this._latlngs,i?1:0,!1,t);return qt(this,{type:(i?"Multi":"")+"LineString",coordinates:e})}}),en.include({toGeoJSON:function(t){var i=!Dt(this._latlngs),e=i&&!Dt(this._latlngs[0]),n=Vt(this._latlngs,e?2:i?1:0,!0,t);return i||(n=[n]),qt(this,{type:(e?"Multi":"")+"Polygon",coordinates:n})}}),Ve.include({toMultiPoint:function(t){var i=[];return this.eachLayer(function(e){i.push(e.toGeoJSON(t).geometry.coordinates)}),qt(this,{type:"MultiPoint",coordinates:i})},toGeoJSON:function(t){var i=this.feature&&this.feature.geometry&&this.feature.geometry.type;if("MultiPoint"===i)return this.toMultiPoint(t);var e="GeometryCollection"===i,n=[];return this.eachLayer(function(i){if(i.toGeoJSON){var o=i.toGeoJSON(t);if(e)n.push(o.geometry);else{var s=Gt(o);"FeatureCollection"===s.type?n.push.apply(n,s.features):n.push(s)}}}),e?qt(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}});var sn=Kt,rn=Ue.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(t,i,e){this._url=t,this._bounds=z(i),l(this,e)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(pt(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){ut(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&ct(this._image),this},bringToBack:function(){return this._map&&_t(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=z(t),this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t="IMG"===this._url.tagName,i=this._image=t?this._url:ht("img");pt(i,"leaflet-image-layer"),this._zoomAnimated&&pt(i,"leaflet-zoom-animated"),this.options.className&&pt(i,this.options.className),i.onselectstart=r,i.onmousemove=r,i.onload=e(this.fire,this,"load"),i.onerror=e(this._overlayOnError,this,"error"),this.options.crossOrigin&&(i.crossOrigin=""),this.options.zIndex&&this._updateZIndex(),t?this._url=i.src:(i.src=this._url,i.alt=this.options.alt)},_animateZoom:function(t){var i=this._map.getZoomScale(t.zoom),e=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;wt(this._image,e,i)},_reset:function(){var t=this._image,i=new P(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),e=i.getSize();Lt(t,i.min),t.style.width=e.x+"px",t.style.height=e.y+"px"},_updateOpacity:function(){vt(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var t=this.options.errorOverlayUrl;t&&this._url!==t&&(this._url=t,this._image.src=t)}}),an=rn.extend({options:{autoplay:!0,loop:!0},_initImage:function(){var t="VIDEO"===this._url.tagName,i=this._image=t?this._url:ht("video");if(pt(i,"leaflet-image-layer"),this._zoomAnimated&&pt(i,"leaflet-zoom-animated"),i.onselectstart=r,i.onmousemove=r,i.onloadeddata=e(this.fire,this,"load"),t){for(var n=i.getElementsByTagName("source"),o=[],s=0;s<n.length;s++)o.push(n[s].src);this._url=n.length>0?o:[i.src]}else{ei(this._url)||(this._url=[this._url]),i.autoplay=!!this.options.autoplay,i.loop=!!this.options.loop;for(var a=0;a<this._url.length;a++){var h=ht("source");h.src=this._url[a],i.appendChild(h)}}}}),hn=Ue.extend({options:{offset:[0,7],className:"",pane:"popupPane"},initialize:function(t,i){l(this,t),this._source=i},onAdd:function(t){this._zoomAnimated=t._zoomAnimated,this._container||this._initLayout(),t._fadeAnimated&&vt(this._container,0),clearTimeout(this._removeTimeout),this.getPane().appendChild(this._container),this.update(),t._fadeAnimated&&vt(this._container,1),this.bringToFront()},onRemove:function(t){t._fadeAnimated?(vt(this._container,0),this._removeTimeout=setTimeout(e(ut,void 0,this._container),200)):ut(this._container)},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=C(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},getElement:function(){return this._container},update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},getEvents:function(){var t={zoom:this._updatePosition,viewreset:this._updatePosition};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},isOpen:function(){return!!this._map&&this._map.hasLayer(this)},bringToFront:function(){return this._map&&ct(this._container),this},bringToBack:function(){return this._map&&_t(this._container),this},_updateContent:function(){if(this._content){var t=this._contentNode,i="function"==typeof this._content?this._content(this._source||this):this._content;if("string"==typeof i)t.innerHTML=i;else{for(;t.hasChildNodes();)t.removeChild(t.firstChild);t.appendChild(i)}this.fire("contentupdate")}},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),i=w(this.options.offset),e=this._getAnchor();this._zoomAnimated?Lt(this._container,t.add(e)):i=i.add(t).add(e);var n=this._containerBottom=-i.y,o=this._containerLeft=-Math.round(this._containerWidth/2)+i.x;this._container.style.bottom=n+"px",this._container.style.left=o+"px"}},_getAnchor:function(){return[0,0]}}),un=hn.extend({options:{maxWidth:300,minWidth:50,maxHeight:null,autoPan:!0,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:!1,closeButton:!0,autoClose:!0,closeOnEscapeKey:!0,className:""},openOn:function(t){return t.openPopup(this),this},onAdd:function(t){hn.prototype.onAdd.call(this,t),t.fire("popupopen",{popup:this}),this._source&&(this._source.fire("popupopen",{popup:this},!0),this._source instanceof Je||this._source.on("preclick",Y))},onRemove:function(t){hn.prototype.onRemove.call(this,t),t.fire("popupclose",{popup:this}),this._source&&(this._source.fire("popupclose",{popup:this},!0),this._source instanceof Je||this._source.off("preclick",Y))},getEvents:function(){var t=hn.prototype.getEvents.call(this);return(void 0!==this.options.closeOnClick?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this._close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_close:function(){this._map&&this._map.closePopup(this)},_initLayout:function(){var t="leaflet-popup",i=this._container=ht("div",t+" "+(this.options.className||"")+" leaflet-zoom-animated"),e=this._wrapper=ht("div",t+"-content-wrapper",i);if(this._contentNode=ht("div",t+"-content",e),J(e),X(this._contentNode),V(e,"contextmenu",Y),this._tipContainer=ht("div",t+"-tip-container",i),this._tip=ht("div",t+"-tip",this._tipContainer),this.options.closeButton){var n=this._closeButton=ht("a",t+"-close-button",i);n.href="#close",n.innerHTML="×",V(n,"click",this._onCloseButtonClick,this)}},_updateLayout:function(){var t=this._contentNode,i=t.style;i.width="",i.whiteSpace="nowrap";var e=t.offsetWidth;e=Math.min(e,this.options.maxWidth),e=Math.max(e,this.options.minWidth),i.width=e+1+"px",i.whiteSpace="",i.height="";var n=t.offsetHeight,o=this.options.maxHeight;o&&n>o?(i.height=o+"px",pt(t,"leaflet-popup-scrolled")):mt(t,"leaflet-popup-scrolled"),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();Lt(this._container,i.add(e))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,i=parseInt(at(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(Pt(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Q(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Le.mergeOptions({closePopupOnClick:!0}),Le.include({openPopup:function(t,i,e){return t instanceof un||(t=new un(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),Ue.include({bindPopup:function(t,i){return t instanceof un?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new un(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof Ue||(i=t,t=this),t instanceof qe)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Q(t),i instanceof Je?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var ln=hn.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){hn.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){hn.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=hn.prototype.getEvents.call(this);return Vi&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ht("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.x<n.x?(s="right",t=t.add(w(h.x+u.x,u.y-a/2+h.y,!0))):(s="left",t=t.subtract(w(r+u.x-h.x,a/2-u.y-h.y,!0))),mt(e,"leaflet-tooltip-right"),mt(e,"leaflet-tooltip-left"),mt(e,"leaflet-tooltip-top"),mt(e,"leaflet-tooltip-bottom"),pt(e,"leaflet-tooltip-"+s),Lt(e,t)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},setOpacity:function(t){this.options.opacity=t,this._container&&vt(this._container,t)},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(i)},_getAnchor:function(){return w(this._source&&this._source._getTooltipAnchor&&!this.options.sticky?this._source._getTooltipAnchor():[0,0])}});Le.include({openTooltip:function(t,i,e){return t instanceof ln||(t=new ln(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:this.addLayer(t)},closeTooltip:function(t){return t&&this.removeLayer(t),this}}),Ue.include({bindTooltip:function(t,i){return t instanceof ln?(l(t,i),this._tooltip=t,t._source=this):(this._tooltip&&!i||(this._tooltip=new ln(i,this)),this._tooltip.setContent(t)),this._initTooltipInteractions(),this._tooltip.options.permanent&&this._map&&this._map.hasLayer(this)&&this.openTooltip(),this},unbindTooltip:function(){return this._tooltip&&(this._initTooltipInteractions(!0),this.closeTooltip(),this._tooltip=null),this},_initTooltipInteractions:function(t){if(t||!this._tooltipHandlersAdded){var i=t?"off":"on",e={remove:this.closeTooltip,move:this._moveTooltip};this._tooltip.options.permanent?e.add=this._openTooltip:(e.mouseover=this._openTooltip,e.mouseout=this.closeTooltip,this._tooltip.options.sticky&&(e.mousemove=this._moveTooltip),Vi&&(e.click=this._openTooltip)),this[i](e),this._tooltipHandlersAdded=!t}},openTooltip:function(t,i){if(t instanceof Ue||(i=t,t=this),t instanceof qe)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._tooltip&&this._map&&(this._tooltip._source=t,this._tooltip.update(),this._map.openTooltip(this._tooltip,i),this._tooltip.options.interactive&&this._tooltip._container&&(pt(this._tooltip._container,"leaflet-clickable"),this.addInteractiveTarget(this._tooltip._container))),this},closeTooltip:function(){return this._tooltip&&(this._tooltip._close(),this._tooltip.options.interactive&&this._tooltip._container&&(mt(this._tooltip._container,"leaflet-clickable"),this.removeInteractiveTarget(this._tooltip._container))),this},toggleTooltip:function(t){return this._tooltip&&(this._tooltip._map?this.closeTooltip():this.openTooltip(t)),this},isTooltipOpen:function(){return this._tooltip.isOpen()},setTooltipContent:function(t){return this._tooltip&&this._tooltip.setContent(t),this},getTooltip:function(){return this._tooltip},_openTooltip:function(t){var i=t.layer||t.target;this._tooltip&&this._map&&this.openTooltip(i,this._tooltip.options.sticky?t.latlng:void 0)},_moveTooltip:function(t){var i,e,n=t.latlng;this._tooltip.options.sticky&&t.originalEvent&&(i=this._map.mouseEventToContainerPoint(t.originalEvent),e=this._map.containerPointToLayerPoint(i),n=this._map.layerPointToLatLng(e)),this._tooltip.setLatLng(n)}});var cn=Ge.extend({options:{iconSize:[12,12],html:!1,bgPos:null,className:"leaflet-div-icon"},createIcon:function(t){var i=t&&"DIV"===t.tagName?t:document.createElement("div"),e=this.options;if(i.innerHTML=!1!==e.html?e.html:"",e.bgPos){var n=w(e.bgPos);i.style.backgroundPosition=-n.x+"px "+-n.y+"px"}return this._setIconStyles(i,"icon"),i},createShadow:function(){return null}});Ge.Default=Ke;var _n=Ue.extend({options:{tileSize:256,opacity:1,updateWhenIdle:ji,updateWhenZooming:!0,updateInterval:200,zIndex:1,bounds:null,minZoom:0,maxZoom:void 0,maxNativeZoom:void 0,minNativeZoom:void 0,noWrap:!1,pane:"tilePane",className:"",keepBuffer:2},initialize:function(t){l(this,t)},onAdd:function(){this._initContainer(),this._levels={},this._tiles={},this._resetView(),this._update()},beforeAdd:function(t){t._addZoomLimit(this)},onRemove:function(t){this._removeAllTiles(),ut(this._container),t._removeZoomLimit(this),this._container=null,this._tileZoom=void 0},bringToFront:function(){return this._map&&(ct(this._container),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this._map&&(_t(this._container),this._setAutoZIndex(Math.min)),this},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},isLoading:function(){return this._loading},redraw:function(){return this._map&&(this._removeAllTiles(),this._update()),this},getEvents:function(){var t={viewprereset:this._invalidateAll,viewreset:this._resetView,zoom:this._resetView,moveend:this._onMoveEnd};return this.options.updateWhenIdle||(this._onMove||(this._onMove=o(this._onMoveEnd,this.options.updateInterval,this)),t.move=this._onMove),this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},createTile:function(){return document.createElement("div")},getTileSize:function(){var t=this.options.tileSize;return t instanceof x?t:new x(t,t)},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t){for(var i,e=this.getPane().children,n=-t(-1/0,1/0),o=0,s=e.length;o<s;o++)i=e[o].style.zIndex,e[o]!==this._container&&i&&(n=t(n,+i));isFinite(n)&&(this.options.zIndex=n+t(-1,1),this._updateZIndex())},_updateOpacity:function(){if(this._map&&!Li){vt(this._container,this.options.opacity);var t=+new Date,i=!1,e=!1;for(var n in this._tiles){var o=this._tiles[n];if(o.current&&o.loaded){var s=Math.min(1,(t-o.loaded)/200);vt(o.el,s),s<1?i=!0:(o.active?e=!0:this._onOpaqueTile(o),o.active=!0)}}e&&!this._noPrune&&this._pruneTiles(),i&&(g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this))}},_onOpaqueTile:r,_initContainer:function(){this._container||(this._container=ht("div","leaflet-layer "+(this.options.className||"")),this._updateZIndex(),this.options.opacity<1&&this._updateOpacity(),this.getPane().appendChild(this._container))},_updateLevels:function(){var t=this._tileZoom,i=this.options.maxZoom;if(void 0!==t){for(var e in this._levels)this._levels[e].el.children.length||e===t?(this._levels[e].el.style.zIndex=i-Math.abs(t-e),this._onUpdateLevel(e)):(ut(this._levels[e].el),this._removeTilesAtZoom(e),this._onRemoveLevel(e),delete this._levels[e]);var n=this._levels[t],o=this._map;return n||((n=this._levels[t]={}).el=ht("div","leaflet-tile-container leaflet-zoom-animated",this._container),n.el.style.zIndex=i,n.origin=o.project(o.unproject(o.getPixelOrigin()),t).round(),n.zoom=t,this._setZoomTransform(n,o.getCenter(),o.getZoom()),n.el.offsetWidth,this._onCreateLevel(n)),this._level=n,n}},_onUpdateLevel:r,_onRemoveLevel:r,_onCreateLevel:r,_pruneTiles:function(){if(this._map){var t,i,e=this._map.getZoom();if(e>this.options.maxZoom||e<this.options.minZoom)this._removeAllTiles();else{for(t in this._tiles)(i=this._tiles[t]).retain=i.current;for(t in this._tiles)if((i=this._tiles[t]).current&&!i.active){var n=i.coords;this._retainParent(n.x,n.y,n.z,n.z-5)||this._retainChildren(n.x,n.y,n.z,n.z+2)}for(t in this._tiles)this._tiles[t].retain||this._removeTile(t)}}},_removeTilesAtZoom:function(t){for(var i in this._tiles)this._tiles[i].coords.z===t&&this._removeTile(i)},_removeAllTiles:function(){for(var t in this._tiles)this._removeTile(t)},_invalidateAll:function(){for(var t in this._levels)ut(this._levels[t].el),this._onRemoveLevel(t),delete this._levels[t];this._removeAllTiles(),this._tileZoom=void 0},_retainParent:function(t,i,e,n){var o=Math.floor(t/2),s=Math.floor(i/2),r=e-1,a=new x(+o,+s);a.z=+r;var h=this._tileCoordsToKey(a),u=this._tiles[h];return u&&u.active?(u.retain=!0,!0):(u&&u.loaded&&(u.retain=!0),r>n&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1<n&&this._retainChildren(o,s,e+1,n))}},_resetView:function(t){var i=t&&(t.pinch||t.flyTo);this._setView(this._map.getCenter(),this._map.getZoom(),i,i)},_animateZoom:function(t){this._setView(t.center,t.zoom,!0,t.noUpdate)},_clampZoom:function(t){var i=this.options;return void 0!==i.minNativeZoom&&t<i.minNativeZoom?i.minNativeZoom:void 0!==i.maxNativeZoom&&i.maxNativeZoom<t?i.maxNativeZoom:t},_setView:function(t,i,e,n){var o=this._clampZoom(Math.round(i));(void 0!==this.options.maxZoom&&o>this.options.maxZoom||void 0!==this.options.minZoom&&o<this.options.minZoom)&&(o=void 0);var s=this.options.updateWhenZooming&&o!==this._tileZoom;n&&!s||(this._tileZoom=o,this._abortLoading&&this._abortLoading(),this._updateLevels(),this._resetGrid(),void 0!==o&&this._update(t),e||this._pruneTiles(),this._noPrune=!!e),this._setZoomTransforms(t,i)},_setZoomTransforms:function(t,i){for(var e in this._levels)this._setZoomTransform(this._levels[e],t,i)},_setZoomTransform:function(t,i,e){var n=this._map.getZoomScale(e,t.zoom),o=t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(i,e)).round();Ni?wt(t.el,o,n):Lt(t.el,o)},_resetGrid:function(){var t=this._map,i=t.options.crs,e=this._tileSize=this.getTileSize(),n=this._tileZoom,o=this._map.getPixelWorldBounds(this._tileZoom);o&&(this._globalTileRange=this._pxBoundsToTileRange(o)),this._wrapX=i.wrapLng&&!this.options.noWrap&&[Math.floor(t.project([0,i.wrapLng[0]],n).x/e.x),Math.ceil(t.project([0,i.wrapLng[1]],n).x/e.y)],this._wrapY=i.wrapLat&&!this.options.noWrap&&[Math.floor(t.project([i.wrapLat[0],0],n).y/e.x),Math.ceil(t.project([i.wrapLat[1],0],n).y/e.y)]},_onMoveEnd:function(){this._map&&!this._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(t){var i=this._map,e=i._animatingZoom?Math.max(i._animateToZoom,i.getZoom()):i.getZoom(),n=i.getZoomScale(e,this._tileZoom),o=i.project(t,this._tileZoom).floor(),s=i.getSize().divideBy(2*n);return new P(o.subtract(s),o.add(s))},_update:function(t){var i=this._map;if(i){var e=this._clampZoom(i.getZoom());if(void 0===t&&(t=i.getCenter()),void 0!==this._tileZoom){var n=this._getTiledPixelBounds(t),o=this._pxBoundsToTileRange(n),s=o.getCenter(),r=[],a=this.options.keepBuffer,h=new P(o.getBottomLeft().subtract([a,-a]),o.getTopRight().add([a,-a]));if(!(isFinite(o.min.x)&&isFinite(o.min.y)&&isFinite(o.max.x)&&isFinite(o.max.y)))throw new Error("Attempted to load an infinite number of tiles");for(var u in this._tiles){var l=this._tiles[u].coords;l.z===this._tileZoom&&h.contains(new x(l.x,l.y))||(this._tiles[u].current=!1)}if(Math.abs(e-this._tileZoom)>1)this._setView(t,e);else{for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);if(d.z=this._tileZoom,this._isValidTile(d)){var p=this._tiles[this._tileCoordsToKey(d)];p?p.current=!0:r.push(d)}}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(_=0;_<r.length;_++)this._addTile(r[_],m);this._level.el.appendChild(m)}}}}},_isValidTile:function(t){var i=this._map.options.crs;if(!i.infinite){var e=this._globalTileRange;if(!i.wrapLng&&(t.x<e.min.x||t.x>e.max.x)||!i.wrapLat&&(t.y<e.min.y||t.y>e.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new T(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(Ci||i.el.setAttribute("src",ni),ut(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){pt(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Li&&this.options.opacity<1&&vt(t,this.options.opacity),Ti&&!zi&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),Lt(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){if(this._map){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(vt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(pt(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Li||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))}},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),dn=_n.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=l(this,i)).detectRetina&&Ki&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),Ti||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return V(n,"load",e(this._tileOnLoad,this,i,n)),V(n,"error",e(this._tileOnError,this,i,n)),this.options.crossOrigin&&(n.crossOrigin=""),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:Ki?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Li?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.getAttribute("src")!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=r,i.onerror=r,i.complete||(i.src=ni,ut(i),delete this._tiles[t]))}}),pn=dn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);var s=(e=l(this,e)).detectRetina&&Ki?2:1,r=this.getTileSize();n.width=r.x*s,n.height=r.y*s,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,dn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToNwSe(t),e=this._crs,n=b(e.project(i[0]),e.project(i[1])),o=n.min,s=n.max,r=(this._wmsVersion>=1.3&&this._crs===He?[o.y,o.x,s.y,s.x]:[o.x,o.y,s.x,s.y]).join(","),a=L.TileLayer.prototype.getTileUrl.call(this,t);return a+c(this.wmsParams,a,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+r},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});dn.WMS=pn,Yt.wms=function(t,i){return new pn(t,i)};var mn=Ue.extend({options:{padding:.1,tolerance:0},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&pt(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=Pt(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i).subtract(s),a=o.multiplyBy(-e).add(n).add(o).subtract(r);Ni?wt(this._container,a,e):Lt(this._container,a)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),fn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){mn.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");V(t,"mousemove",o(this._onMouseMove,32,this),this),V(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),V(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){delete this._ctx,ut(this._container),q(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){this._redrawBounds=null;for(var t in this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},mn.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=Ki?2:1;Lt(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",Ki&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){mn.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,n=i.prev;e?e.prev=n:this._drawLast=n,n?n.next=e:this._drawFirst=e,delete t._order,delete this._layers[L.stamp(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(t.options.dashArray){var i,e=t.options.dashArray.split(","),n=[];for(i=0;i<e.length;i++)n.push(Number(e[i]));t.options._dashArray=n}},_requestRedraw:function(t){this._map&&(this._extendRedrawBounds(t),this._redrawRequest=this._redrawRequest||f(this._redraw,this))},_extendRedrawBounds:function(t){if(t._pxBounds){var i=(t.options.weight||0)+1;this._redrawBounds=this._redrawBounds||new P,this._redrawBounds.extend(t._pxBounds.min.subtract([i,i])),this._redrawBounds.extend(t._pxBounds.max.add([i,i]))}},_redraw:function(){this._redrawRequest=null,this._redrawBounds&&(this._redrawBounds.min._floor(),this._redrawBounds.max._ceil()),this._clear(),this._draw(),this._redrawBounds=null},_clear:function(){var t=this._redrawBounds;if(t){var i=t.getSize();this._ctx.clearRect(t.min.x,t.min.y,i.x,i.y)}else this._ctx.clearRect(0,0,this._container.width,this._container.height)},_draw:function(){var t,i=this._redrawBounds;if(this._ctx.save(),i){var e=i.getSize();this._ctx.beginPath(),this._ctx.rect(i.min.x,i.min.y,e.x,e.y),this._ctx.clip()}this._drawing=!0;for(var n=this._drawFirst;n;n=n.next)t=n.layer,(!i||t._pxBounds&&t._pxBounds.intersects(i))&&t._updatePath();this._drawing=!1,this._ctx.restore()},_updatePoly:function(t,i){if(this._drawing){var e,n,o,s,r=t._parts,a=r.length,h=this._ctx;if(a){for(this._drawnLayers[t._leaflet_id]=t,h.beginPath(),e=0;e<a;e++){for(n=0,o=r[e].length;n<o;n++)s=r[e][n],h[n?"lineTo":"moveTo"](s.x,s.y);i&&h.closePath()}this._fillStroke(h,t)}}},_updateCircle:function(t){if(this._drawing&&!t._empty()){var i=t._point,e=this._ctx,n=Math.max(Math.round(t._radius),1),o=(Math.max(Math.round(t._radiusY),1)||n)/n;this._drawnLayers[t._leaflet_id]=t,1!==o&&(e.save(),e.scale(1,o)),e.beginPath(),e.arc(i.x,i.y/o,n,0,2*Math.PI,!1),1!==o&&e.restore(),this._fillStroke(e,t)}},_fillStroke:function(t,i){var e=i.options;e.fill&&(t.globalAlpha=e.fillOpacity,t.fillStyle=e.fillColor||e.color,t.fill(e.fillRule||"evenodd")),e.stroke&&0!==e.weight&&(t.setLineDash&&t.setLineDash(i.options&&i.options._dashArray||[]),t.globalAlpha=e.opacity,t.lineWidth=e.weight,t.strokeStyle=e.color,t.lineCap=e.lineCap,t.lineJoin=e.lineJoin,t.stroke())},_onClick:function(t){for(var i,e,n=this._map.mouseEventToLayerPoint(t),o=this._drawFirst;o;o=o.next)(i=o.layer).options.interactive&&i._containsPoint(n)&&!this._map._draggableMoved(i)&&(e=i);e&&(et(t),this._fireEvent([e],t))},_onMouseMove:function(t){if(this._map&&!this._map.dragging.moving()&&!this._map._animatingZoom){var i=this._map.mouseEventToLayerPoint(t);this._handleMouseHover(t,i)}},_handleMouseOut:function(t){var i=this._hoveredLayer;i&&(mt(this._container,"leaflet-interactive"),this._fireEvent([i],t,"mouseout"),this._hoveredLayer=null)},_handleMouseHover:function(t,i){for(var e,n,o=this._drawFirst;o;o=o.next)(e=o.layer).options.interactive&&e._containsPoint(i)&&(n=e);n!==this._hoveredLayer&&(this._handleMouseOut(t),n&&(pt(this._container,"leaflet-interactive"),this._fireEvent([n],t,"mouseover"),this._hoveredLayer=n)),this._hoveredLayer&&this._fireEvent([this._hoveredLayer],t)},_fireEvent:function(t,i,e){this._map._fireDOMEvent(i,e||i.type,t)},_bringToFront:function(t){var i=t._order,e=i.next,n=i.prev;e&&(e.prev=n,n?n.next=e:e&&(this._drawFirst=e),i.prev=this._drawLast,this._drawLast.next=i,i.next=null,this._drawLast=i,this._requestRedraw(t))},_bringToBack:function(t){var i=t._order,e=i.next,n=i.prev;n&&(n.next=e,e?e.prev=n:n&&(this._drawLast=n),i.prev=null,i.next=this._drawFirst,this._drawFirst.prev=i,this._drawFirst=i,this._requestRedraw(t))}}),gn=function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return document.createElement("<lvml:"+t+' class="lvml">')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),vn={_initContainer:function(){this._container=ht("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(mn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=gn("shape");pt(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=gn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;ut(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=gn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=ei(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=gn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){ct(t._container)},_bringToBack:function(t){_t(t._container)}},yn=Ji?gn:E,xn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=yn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=yn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ut(this._container),q(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){mn.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),Lt(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=yn("path");t.options.className&&pt(i,t.options.className),t.options.interactive&&pt(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ut(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){ct(t._path)},_bringToBack:function(t){_t(t._path)}});Ji&&xn.include(vn),Le.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this.options.preferCanvas&&Xt()||Jt()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=xn&&Jt({pane:t})||fn&&Xt({pane:t}),this._paneRenderers[t]=i),i}});var wn=en.extend({initialize:function(t,i){en.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});xn.create=yn,xn.pointsToPath=k,nn.geometryToLayer=Wt,nn.coordsToLatLng=Ht,nn.coordsToLatLngs=Ft,nn.latLngToCoords=Ut,nn.latLngsToCoords=Vt,nn.getFeature=qt,nn.asFeature=Gt,Le.mergeOptions({boxZoom:!0});var Ln=Ze.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){V(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){q(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ut(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),mi(),bt(),this._startPoint=this._map.mouseEventToContainerPoint(t),V(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ht("div","leaflet-zoom-box",this._container),pt(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();Lt(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ut(this._box),mt(this._container,"leaflet-crosshair")),fi(),Tt(),q(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});Le.addInitHook("addHandler","boxZoom",Ln),Le.mergeOptions({doubleClickZoom:!0});var Pn=Ze.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});Le.addInitHook("addHandler","doubleClickZoom",Pn),Le.mergeOptions({dragging:!0,inertia:!zi,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var bn=Ze.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Be(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}pt(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){mt(this._map._container,"leaflet-grab"),mt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.x<i.min.x&&(t.x=this._viscousLimit(t.x,i.min.x)),t.y<i.min.y&&(t.y=this._viscousLimit(t.y,i.min.y)),t.x>i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)<Math.abs(s+e)?o:s;this._draggable._absPos=this._draggable._newPos.clone(),this._draggable._newPos.x=r},_onDragEnd:function(t){var i=this._map,e=i.options,n=!e.inertia||this._times.length<2;if(i.fire("dragend",t),n)i.fire("moveend");else{this._prunePositions(+new Date);var o=this._lastPos.subtract(this._positions[0]),s=(this._lastTime-this._times[0])/1e3,r=e.easeLinearity,a=o.multiplyBy(r/s),h=a.distanceTo([0,0]),u=Math.min(e.inertiaMaxSpeed,h),l=a.multiplyBy(u/h),c=u/(e.inertiaDeceleration*r),_=l.multiplyBy(-c/2).round();_.x||_.y?(_=i._limitOffset(_,i.options.maxBounds),f(function(){i.panBy(_,{duration:c,easeLinearity:r,noMoveStart:!0,animate:!0})})):i.fire("moveend")}}});Le.addInitHook("addHandler","dragging",bn),Le.mergeOptions({keyboard:!0,keyboardPanDelta:80});var Tn=Ze.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,54,173]},initialize:function(t){this._map=t,this._setPanDelta(t.options.keyboardPanDelta),this._setZoomDelta(t.options.zoomDelta)},addHooks:function(){var t=this._map._container;t.tabIndex<=0&&(t.tabIndex="0"),V(t,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.on({focus:this._addHooks,blur:this._removeHooks},this)},removeHooks:function(){this._removeHooks(),q(this._map._container,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.off({focus:this._addHooks,blur:this._removeHooks},this)},_onMouseDown:function(){if(!this._focused){var t=document.body,i=document.documentElement,e=t.scrollTop||i.scrollTop,n=t.scrollLeft||i.scrollLeft;this._map._container.focus(),window.scrollTo(n,e)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanDelta:function(t){var i,e,n=this._panKeys={},o=this.keyCodes;for(i=0,e=o.left.length;i<e;i++)n[o.left[i]]=[-1*t,0];for(i=0,e=o.right.length;i<e;i++)n[o.right[i]]=[t,0];for(i=0,e=o.down.length;i<e;i++)n[o.down[i]]=[0,t];for(i=0,e=o.up.length;i<e;i++)n[o.up[i]]=[0,-1*t]},_setZoomDelta:function(t){var i,e,n=this._zoomKeys={},o=this.keyCodes;for(i=0,e=o.zoomIn.length;i<e;i++)n[o.zoomIn[i]]=t;for(i=0,e=o.zoomOut.length;i<e;i++)n[o.zoomOut[i]]=-t},_addHooks:function(){V(document,"keydown",this._onKeyDown,this)},_removeHooks:function(){q(document,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var i,e=t.keyCode,n=this._map;if(e in this._panKeys){if(n._panAnim&&n._panAnim._inProgress)return;i=this._panKeys[e],t.shiftKey&&(i=w(i).multiplyBy(3)),n.panBy(i),n.options.maxBounds&&n.panInsideBounds(n.options.maxBounds)}else if(e in this._zoomKeys)n.setZoom(n.getZoom()+(t.shiftKey?3:1)*this._zoomKeys[e]);else{if(27!==e||!n._popup||!n._popup.options.closeOnEscapeKey)return;n.closePopup()}Q(t)}}});Le.addInitHook("addHandler","keyboard",Tn),Le.mergeOptions({scrollWheelZoom:!0,wheelDebounceTime:40,wheelPxPerZoomLevel:60});var zn=Ze.extend({addHooks:function(){V(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){q(this._map._container,"mousewheel",this._onWheelScroll,this)},_onWheelScroll:function(t){var i=it(t),n=this._map.options.wheelDebounceTime;this._delta+=i,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var o=Math.max(n-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(e(this._performZoom,this),o),Q(t)},_performZoom:function(){var t=this._map,i=t.getZoom(),e=this._map.options.zoomSnap||0;t._stop();var n=this._delta/(4*this._map.options.wheelPxPerZoomLevel),o=4*Math.log(2/(1+Math.exp(-Math.abs(n))))/Math.LN2,s=e?Math.ceil(o/e)*e:o,r=t._limitZoom(i+(this._delta>0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});Le.addInitHook("addHandler","scrollWheelZoom",zn),Le.mergeOptions({tap:!0,tapTolerance:15});var Mn=Ze.extend({addHooks:function(){V(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){q(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if($(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&pt(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),V(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),q(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&mt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});Vi&&!Ui&&Le.addInitHook("addHandler","tap",Mn),Le.mergeOptions({touchZoom:Vi&&!zi,bounceAtZoomLimits:!0});var Cn=Ze.extend({addHooks:function(){pt(this._map._container,"leaflet-touch-zoom"),V(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){mt(this._map._container,"leaflet-touch-zoom"),q(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),V(document,"touchmove",this._onTouchMove,this),V(document,"touchend",this._onTouchEnd,this),$(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoom<i.getMinZoom()&&s<1||this._zoom>i.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0,!1),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),$(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),q(document,"touchmove",this._onTouchMove),q(document,"touchend",this._onTouchEnd),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});Le.addInitHook("addHandler","touchZoom",Cn),Le.BoxZoom=Ln,Le.DoubleClickZoom=Pn,Le.Drag=bn,Le.Keyboard=Tn,Le.ScrollWheelZoom=zn,Le.Tap=Mn,Le.TouchZoom=Cn;var Zn=window.L;window.L=t,Object.freeze=$t,t.version="1.3.1+HEAD.ba6f97f",t.noConflict=function(){return window.L=Zn,this},t.Control=Pe,t.control=be,t.Browser=$i,t.Evented=ui,t.Mixin=Ee,t.Util=ai,t.Class=v,t.Handler=Ze,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=de,t.DomUtil=xe,t.PosAnimation=we,t.Draggable=Be,t.LineUtil=Oe,t.PolyUtil=Re,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=Z,t.transformation=S,t.Projection=je,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=ci,t.GeoJSON=nn,t.geoJSON=Kt,t.geoJson=sn,t.Layer=Ue,t.LayerGroup=Ve,t.layerGroup=function(t,i){return new Ve(t,i)},t.FeatureGroup=qe,t.featureGroup=function(t){return new qe(t)},t.ImageOverlay=rn,t.imageOverlay=function(t,i,e){return new rn(t,i,e)},t.VideoOverlay=an,t.videoOverlay=function(t,i,e){return new an(t,i,e)},t.DivOverlay=hn,t.Popup=un,t.popup=function(t,i){return new un(t,i)},t.Tooltip=ln,t.tooltip=function(t,i){return new ln(t,i)},t.Icon=Ge,t.icon=function(t){return new Ge(t)},t.DivIcon=cn,t.divIcon=function(t){return new cn(t)},t.Marker=Xe,t.marker=function(t,i){return new Xe(t,i)},t.TileLayer=dn,t.tileLayer=Yt,t.GridLayer=_n,t.gridLayer=function(t){return new _n(t)},t.SVG=xn,t.svg=Jt,t.Renderer=mn,t.Canvas=fn,t.canvas=Xt,t.Path=Je,t.CircleMarker=$e,t.circleMarker=function(t,i){return new $e(t,i)},t.Circle=Qe,t.circle=function(t,i,e){return new Qe(t,i,e)},t.Polyline=tn,t.polyline=function(t,i){return new tn(t,i)},t.Polygon=en,t.polygon=function(t,i){return new en(t,i)},t.Rectangle=wn,t.rectangle=function(t,i){return new wn(t,i)},t.Map=Le,t.map=function(t,i){return new Le(t,i)}}); \ No newline at end of file diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet.latlng-graticule.js b/themes/bootstrap3/js/vendor/leaflet/leaflet.latlng-graticule.js new file mode 100644 index 0000000000000000000000000000000000000000..b6033bd4f2cb8d20fdf9a41738fc5660383858b0 --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet.latlng-graticule.js @@ -0,0 +1,546 @@ +/** + * Create a Canvas as ImageOverlay to draw the Lat/Lon Graticule, + * and show the axis tick label on the edge of the map. + * Author: lanwei@cloudybay.com.tw + * Github Repo: https://github.com/cloudybay/leaflet.latlng-graticule + * + * Edits to original code - lmgonzales 2017-12-28 + * changed L:9 from L.Layer.extend to L.LayerGroup.extend + * removed L:10 includes: L.Mixin.Events, + * + * Notes about versioning: + * Source code is version 1.1 + * https://github.com/cloudybay/leaflet.latlng-graticule/releases/tag/v1.1 + * + */ + +(function (window, document, undefined) { + +L.LatLngGraticule = L.LayerGroup.extend({ + + options: { + showLabel: true, + opacity: 1, + weight: 0.8, + color: '#aaa', + font: '12px Verdana', + lngLineCurved: 0, + latLineCurved: 0, + zoomInterval: [ + {start: 2, end: 2, interval: 40}, + {start: 3, end: 3, interval: 20}, + {start: 4, end: 4, interval: 10}, + {start: 5, end: 7, interval: 5}, + {start: 8, end: 20, interval: 1} + ] + }, + + initialize: function (options) { + L.setOptions(this, options); + + var defaultFontName = 'Verdana'; + var _ff = this.options.font.split(' '); + if (_ff.length < 2) { + this.options.font += ' ' + defaultFontName; + } + + if (!this.options.fontColor) { + this.options.fontColor = this.options.color; + } + + if (this.options.zoomInterval) { + if (this.options.zoomInterval.latitude) { + this.options.latInterval = this.options.zoomInterval.latitude; + if (!this.options.zoomInterval.longitude) { + this.options.lngInterval = this.options.zoomInterval.latitude; + } + } + if (this.options.zoomInterval.longitude) { + this.options.lngInterval = this.options.zoomInterval.longitude; + if (!this.options.zoomInterval.latitude) { + this.options.latInterval = this.options.zoomInterval.longitude; + } + } + if (!this.options.latInterval) { + this.options.latInterval = this.options.zoomInterval; + } + if (!this.options.lngInterval) { + this.options.lngInterval = this.options.zoomInterval; + } + } + }, + + onAdd: function (map) { + this._map = map; + + if (!this._container) { + this._initCanvas(); + } + + map._panes.overlayPane.appendChild(this._container); + + map.on('viewreset', this._reset, this); + map.on('move', this._reset, this); + map.on('moveend', this._reset, this); + + if (map.options.zoomAnimation && L.Browser.any3d) { + map.on('zoomanim', this._animateZoom, this); + } + + this._reset(); + }, + + onRemove: function (map) { + map.getPanes().overlayPane.removeChild(this._container); + + map.off('viewreset', this._reset, this); + map.off('move', this._reset, this); + map.off('moveend', this._reset, this); + + if (map.options.zoomAnimation) { + map.off('zoomanim', this._animateZoom, this); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + this._updateOpacity(); + return this; + }, + + bringToFront: function () { + if (this._canvas) { + this._map._panes.overlayPane.appendChild(this._canvas); + } + return this; + }, + + bringToBack: function () { + var pane = this._map._panes.overlayPane; + if (this._canvas) { + pane.insertBefore(this._canvas, pane.firstChild); + } + return this; + }, + + getAttribution: function () { + return this.options.attribution; + }, + + _initCanvas: function () { + this._container = L.DomUtil.create('div', 'leaflet-image-layer'); + + this._canvas = L.DomUtil.create('canvas', ''); + + if (this._map.options.zoomAnimation && L.Browser.any3d) { + L.DomUtil.addClass(this._canvas, 'leaflet-zoom-animated'); + } else { + L.DomUtil.addClass(this._canvas, 'leaflet-zoom-hide'); + } + + this._updateOpacity(); + + this._container.appendChild(this._canvas); + + L.extend(this._canvas, { + onselectstart: L.Util.falseFn, + onmousemove: L.Util.falseFn, + onload: L.bind(this._onCanvasLoad, this) + }); + }, + + _animateZoom: function (e) { + var map = this._map, + container = this._container, + canvas = this._canvas, + scale = map.getZoomScale(e.zoom), + nw = map.containerPointToLatLng([0, 0]), + se = map.containerPointToLatLng([canvas.width, canvas.height]), + + topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center), + size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft), + origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale))); + + container.style[L.DomUtil.TRANSFORM] = + L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') '; + }, + + _reset: function () { + var container = this._container, + canvas = this._canvas, + size = this._map.getSize(), + lt = this._map.containerPointToLayerPoint([0, 0]); + + L.DomUtil.setPosition(container, lt); + + container.style.width = size.x + 'px'; + container.style.height = size.y + 'px'; + + canvas.width = size.x; + canvas.height = size.y; + canvas.style.width = size.x + 'px'; + canvas.style.height = size.y + 'px'; + + this.__calcInterval(); + + this.__draw(true); + }, + + _onCanvasLoad: function () { + this.fire('load'); + }, + + _updateOpacity: function () { + L.DomUtil.setOpacity(this._canvas, this.options.opacity); + }, + + __format_lat: function(lat) { + if (this.options.latFormatTickLabel) { + return this.options.latFormatTickLabel(lat); + } + + // todo: format type of float + if (lat < 0) { + return '' + (lat*-1) + 'S'; + } + else if (lat > 0) { + return '' + lat + 'N'; + } + return '' + lat; + }, + + __format_lng: function(lng) { + if (this.options.lngFormatTickLabel) { + return this.options.lngFormatTickLabel(lng); + } + + // todo: format type of float + if (lng > 180) { + return '' + (360 - lng) + 'W'; + } + else if (lng > 0 && lng < 180) { + return '' + lng + 'E'; + } + else if (lng < 0 && lng > -180) { + return '' + (lng*-1) + 'W'; + } + else if (lng == -180) { + return '' + (lng*-1); + } + else if (lng < -180) { + return '' + (360 + lng) + 'W'; + } + return '' + lng; + }, + + __calcInterval: function() { + var zoom = this._map.getZoom(); + if (this._currZoom != zoom) { + this._currLngInterval = 0; + this._currLatInterval = 0; + this._currZoom = zoom; + } + + var interv; + + if (!this._currLngInterval) { + try { + for (var idx in this.options.lngInterval) { + var dict = this.options.lngInterval[idx]; + if (dict.start <= zoom) { + if (dict.end && dict.end >= zoom) { + this._currLngInterval = dict.interval; + break; + } + } + } + } + catch(e) { + this._currLngInterval = 0; + } + } + + if (!this._currLatInterval) { + try { + for (var idx in this.options.latInterval) { + var dict = this.options.latInterval[idx]; + if (dict.start <= zoom) { + if (dict.end && dict.end >= zoom) { + this._currLatInterval = dict.interval; + break; + } + } + } + } + catch(e) { + this._currLatInterval = 0; + } + } + }, + + __draw: function(label) { + function _parse_px_to_int(txt) { + if (txt.length > 2) { + if (txt.charAt(txt.length-2) == 'p') { + txt = txt.substr(0, txt.length-2); + } + } + try { + return parseInt(txt, 10); + } + catch(e) {} + return 0; + }; + + var self = this, + canvas = this._canvas, + map = this._map, + curvedLon = this.options.lngLineCurved, + curvedLat = this.options.latLineCurved; + + if (L.Browser.canvas && map) { + if (!this._currLngInterval || !this._currLatInterval) { + this.__calcInterval(); + } + + var latInterval = this._currLatInterval, + lngInterval = this._currLngInterval; + + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.lineWidth = this.options.weight; + ctx.strokeStyle = this.options.color; + ctx.fillStyle = this.options.fontColor; + + if (this.options.font) { + ctx.font = this.options.font; + } + var txtWidth = ctx.measureText('0').width; + var txtHeight = 12; + try { + var _font_size = ctx.font.split(' ')[0]; + txtHeight = _parse_px_to_int(_font_size); + } + catch(e) {} + + var ww = canvas.width, + hh = canvas.height; + + var lt = map.containerPointToLatLng(L.point(0, 0)); + var rt = map.containerPointToLatLng(L.point(ww, 0)); + var rb = map.containerPointToLatLng(L.point(ww, hh)); + + var _lat_b = rb.lat, + _lat_t = lt.lat; + var _lon_l = lt.lng, + _lon_r = rt.lng; + + var _point_per_lat = (_lat_t - _lat_b) / (hh * 0.2); + if (isNaN(_point_per_lat)) { + return; + } + + if (_point_per_lat < 1) { _point_per_lat = 1; } + if (_lat_b < -90) { + _lat_b = -90; + } + else { + _lat_b = parseInt(_lat_b - _point_per_lat, 10); + } + + if (_lat_t > 90) { + _lat_t = 90; + } + else { + _lat_t = parseInt(_lat_t + _point_per_lat, 10); + } + + var _point_per_lon = (_lon_r - _lon_l) / (ww * 0.2); + if (_point_per_lon < 1) { _point_per_lon = 1; } + if (_lon_l > 0 && _lon_r < 0) { + _lon_r += 360; + } + _lon_r = parseInt(_lon_r + _point_per_lon, 10); + _lon_l = parseInt(_lon_l - _point_per_lon, 10); + + var ll, latstr, lngstr, _lon_delta = 0.5; + function __draw_lat_line(self, lat_tick) { + ll = self._latLngToCanvasPoint(L.latLng(lat_tick, _lon_l)); + latstr = self.__format_lat(lat_tick); + txtWidth = ctx.measureText(latstr).width; + + if (curvedLat) { + if (typeof(curvedLat) == 'number') { + _lon_delta = curvedLat; + } + + var __lon_left = _lon_l, __lon_right = _lon_r; + if (ll.x > 0) { + var __lon_left = map.containerPointToLatLng(L.point(0, ll.y)); + __lon_left = __lon_left.lng - _point_per_lon; + ll.x = 0; + } + var rr = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_right)); + if (rr.x < ww) { + __lon_right = map.containerPointToLatLng(L.point(ww, rr.y)); + __lon_right = __lon_right.lng + _point_per_lon; + if (__lon_left > 0 && __lon_right < 0) { + __lon_right += 360; + } + } + + ctx.beginPath(); + ctx.moveTo(ll.x, ll.y); + var _prev_p = null; + for (var j=__lon_left; j<=__lon_right; j+=_lon_delta) { + rr = self._latLngToCanvasPoint(L.latLng(lat_tick, j)); + ctx.lineTo(rr.x, rr.y); + + if (self.options.showLabel && label && _prev_p != null) { + if (_prev_p.x < 0 && rr.x >= 0) { + var _s = (rr.x - 0) / (rr.x - _prev_p.x); + var _y = rr.y - ((rr.y - _prev_p.y) * _s); + ctx.fillText(latstr, 0, _y + (txtHeight/2)); + } + else if (_prev_p.x <= (ww-txtWidth) && rr.x > (ww-txtWidth)) { + var _s = (rr.x - ww) / (rr.x - _prev_p.x); + var _y = rr.y - ((rr.y - _prev_p.y) * _s); + ctx.fillText(latstr, ww-txtWidth, _y + (txtHeight/2)-2); + } + } + + _prev_p = {x:rr.x, y:rr.y, lon:j, lat:i}; + } + ctx.stroke(); + } + else { + var __lon_right = _lon_r; + var rr = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_right)); + if (curvedLon) { + __lon_right = map.containerPointToLatLng(L.point(0, rr.y)); + __lon_right = __lon_right.lng; + rr = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_right)); + + var __lon_left = map.containerPointToLatLng(L.point(ww, rr.y)); + __lon_left = __lon_left.lng; + ll = self._latLngToCanvasPoint(L.latLng(lat_tick, __lon_left)); + } + + ctx.beginPath(); + ctx.moveTo(ll.x+1, ll.y); + ctx.lineTo(rr.x-1, rr.y); + ctx.stroke(); + if (self.options.showLabel && label) { + var _yy = ll.y + (txtHeight/2)-2; + ctx.fillText(latstr, 0, _yy); + ctx.fillText(latstr, ww-txtWidth, _yy); + } + } + }; + + if (latInterval > 0) { + for (var i=latInterval; i<=_lat_t; i+=latInterval) { + if (i >= _lat_b) { + __draw_lat_line(this, i); + } + } + for (var i=0; i>=_lat_b; i-=latInterval) { + if (i <= _lat_t) { + __draw_lat_line(this, i); + } + } + } + + function __draw_lon_line(self, lon_tick) { + lngstr = self.__format_lng(lon_tick); + txtWidth = ctx.measureText(lngstr).width; + var bb = self._latLngToCanvasPoint(L.latLng(_lat_b, lon_tick)); + + if (curvedLon) { + if (typeof(curvedLon) == 'number') { + _lat_delta = curvedLon; + } + + ctx.beginPath(); + ctx.moveTo(bb.x, bb.y); + var _prev_p = null; + for (var j=_lat_b; j<_lat_t; j+=_lat_delta) { + var tt = self._latLngToCanvasPoint(L.latLng(j, lon_tick)); + ctx.lineTo(tt.x, tt.y); + + if (self.options.showLabel && label && _prev_p != null) { + if (_prev_p.y > 8 && tt.y <= 8) { + ctx.fillText(lngstr, tt.x - (txtWidth/2), txtHeight); + } + else if (_prev_p.y >= hh && tt.y < hh) { + ctx.fillText(lngstr, tt.x - (txtWidth/2), hh-2); + } + } + + _prev_p = {x:tt.x, y:tt.y, lon:lon_tick, lat:j}; + } + ctx.stroke(); + } + else { + var __lat_top = _lat_t; + var tt = self._latLngToCanvasPoint(L.latLng(__lat_top, lon_tick)); + if (curvedLat) { + __lat_top = map.containerPointToLatLng(L.point(tt.x, 0)); + __lat_top = __lat_top.lat; + if (__lat_top > 90) { __lat_top = 90; } + tt = self._latLngToCanvasPoint(L.latLng(__lat_top, lon_tick)); + + var __lat_bottom = map.containerPointToLatLng(L.point(bb.x, hh)); + __lat_bottom = __lat_bottom.lat; + if (__lat_bottom < -90) { __lat_bottom = -90; } + bb = self._latLngToCanvasPoint(L.latLng(__lat_bottom, lon_tick)); + } + + ctx.beginPath(); + ctx.moveTo(tt.x, tt.y+1); + ctx.lineTo(bb.x, bb.y-1); + ctx.stroke(); + + if (self.options.showLabel && label) { + ctx.fillText(lngstr, tt.x - (txtWidth/2), txtHeight+1); + ctx.fillText(lngstr, bb.x - (txtWidth/2), hh-3); + } + } + }; + + if (lngInterval > 0) { + for (var i=lngInterval; i<=_lon_r; i+=lngInterval) { + if (i >= _lon_l) { + __draw_lon_line(this, i); + } + } + for (var i=0; i>=_lon_l; i-=lngInterval) { + if (i <= _lon_r) { + __draw_lon_line(this, i); + } + } + } + } + }, + + _latLngToCanvasPoint: function(latlng) { + map = this._map; + var projectedPoint = map.project(L.latLng(latlng)); + projectedPoint._subtract(map.getPixelOrigin()); + return L.point(projectedPoint).add(map._getMapPanePos()); + } + +}); + +L.latlngGraticule = function (options) { + return new L.LatLngGraticule(options); +}; + + +}(this, document)); + diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet.markercluster-src.js b/themes/bootstrap3/js/vendor/leaflet/leaflet.markercluster-src.js new file mode 100644 index 0000000000000000000000000000000000000000..92f152511ef8d98de7cb7d396d470257846f45bf --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet.markercluster-src.js @@ -0,0 +1,2706 @@ +/* + * Leaflet.markercluster 1.3.0+master.a4cf31f, + * Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps. + * https://github.com/Leaflet/Leaflet.markercluster + * (c) 2012-2017, Dave Leaver, smartrak + * + * Copyright 2012 David Leaver + * + * 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. + * + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.Leaflet = global.Leaflet || {}, global.Leaflet.markercluster = global.Leaflet.markercluster || {}))); +}(this, (function (exports) { 'use strict'; + +/* + * L.MarkerClusterGroup extends L.FeatureGroup by clustering the markers contained within + */ + +var MarkerClusterGroup = L.MarkerClusterGroup = L.FeatureGroup.extend({ + + options: { + maxClusterRadius: 80, //A cluster will cover at most this many pixels from its center + iconCreateFunction: null, + clusterPane: L.Marker.prototype.options.pane, + + spiderfyOnMaxZoom: true, + showCoverageOnHover: true, + zoomToBoundsOnClick: true, + singleMarkerMode: false, + + disableClusteringAtZoom: null, + + // Setting this to false prevents the removal of any clusters outside of the viewpoint, which + // is the default behaviour for performance reasons. + removeOutsideVisibleBounds: true, + + // Set to false to disable all animations (zoom and spiderfy). + // If false, option animateAddingMarkers below has no effect. + // If L.DomUtil.TRANSITION is falsy, this option has no effect. + animate: true, + + //Whether to animate adding markers after adding the MarkerClusterGroup to the map + // If you are adding individual markers set to true, if adding bulk markers leave false for massive performance gains. + animateAddingMarkers: false, + + //Increase to increase the distance away that spiderfied markers appear from the center + spiderfyDistanceMultiplier: 1, + + // Make it possible to specify a polyline options on a spider leg + spiderLegPolylineOptions: { weight: 1.5, color: '#222', opacity: 0.5 }, + + // When bulk adding layers, adds markers in chunks. Means addLayers may not add all the layers in the call, others will be loaded during setTimeouts + chunkedLoading: false, + chunkInterval: 200, // process markers for a maximum of ~ n milliseconds (then trigger the chunkProgress callback) + chunkDelay: 50, // at the end of each interval, give n milliseconds back to system/browser + chunkProgress: null, // progress callback: function(processed, total, elapsed) (e.g. for a progress indicator) + + //Options to pass to the L.Polygon constructor + polygonOptions: {} + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + if (!this.options.iconCreateFunction) { + this.options.iconCreateFunction = this._defaultIconCreateFunction; + } + + this._featureGroup = L.featureGroup(); + this._featureGroup.addEventParent(this); + + this._nonPointGroup = L.featureGroup(); + this._nonPointGroup.addEventParent(this); + + this._inZoomAnimation = 0; + this._needsClustering = []; + this._needsRemoving = []; //Markers removed while we aren't on the map need to be kept track of + //The bounds of the currently shown area (from _getExpandedVisibleBounds) Updated on zoom/move + this._currentShownBounds = null; + + this._queue = []; + + this._childMarkerEventHandlers = { + 'dragstart': this._childMarkerDragStart, + 'move': this._childMarkerMoved, + 'dragend': this._childMarkerDragEnd, + }; + + // Hook the appropriate animation methods. + var animate = L.DomUtil.TRANSITION && this.options.animate; + L.extend(this, animate ? this._withAnimation : this._noAnimation); + // Remember which MarkerCluster class to instantiate (animated or not). + this._markerCluster = animate ? L.MarkerCluster : L.MarkerClusterNonAnimated; + }, + + addLayer: function (layer) { + + if (layer instanceof L.LayerGroup) { + return this.addLayers([layer]); + } + + //Don't cluster non point data + if (!layer.getLatLng) { + this._nonPointGroup.addLayer(layer); + this.fire('layeradd', { layer: layer }); + return this; + } + + if (!this._map) { + this._needsClustering.push(layer); + this.fire('layeradd', { layer: layer }); + return this; + } + + if (this.hasLayer(layer)) { + return this; + } + + + //If we have already clustered we'll need to add this one to a cluster + + if (this._unspiderfy) { + this._unspiderfy(); + } + + this._addLayer(layer, this._maxZoom); + this.fire('layeradd', { layer: layer }); + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + //Work out what is visible + var visibleLayer = layer, + currentZoom = this._zoom; + if (layer.__parent) { + while (visibleLayer.__parent._zoom >= currentZoom) { + visibleLayer = visibleLayer.__parent; + } + } + + if (this._currentShownBounds.contains(visibleLayer.getLatLng())) { + if (this.options.animateAddingMarkers) { + this._animationAddLayer(layer, visibleLayer); + } else { + this._animationAddLayerNonAnimated(layer, visibleLayer); + } + } + return this; + }, + + removeLayer: function (layer) { + + if (layer instanceof L.LayerGroup) { + return this.removeLayers([layer]); + } + + //Non point layers + if (!layer.getLatLng) { + this._nonPointGroup.removeLayer(layer); + this.fire('layerremove', { layer: layer }); + return this; + } + + if (!this._map) { + if (!this._arraySplice(this._needsClustering, layer) && this.hasLayer(layer)) { + this._needsRemoving.push({ layer: layer, latlng: layer._latlng }); + } + this.fire('layerremove', { layer: layer }); + return this; + } + + if (!layer.__parent) { + return this; + } + + if (this._unspiderfy) { + this._unspiderfy(); + this._unspiderfyLayer(layer); + } + + //Remove the marker from clusters + this._removeLayer(layer, true); + this.fire('layerremove', { layer: layer }); + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + layer.off(this._childMarkerEventHandlers, this); + + if (this._featureGroup.hasLayer(layer)) { + this._featureGroup.removeLayer(layer); + if (layer.clusterShow) { + layer.clusterShow(); + } + } + + return this; + }, + + //Takes an array of markers and adds them in bulk + addLayers: function (layersArray, skipLayerAddEvent) { + if (!L.Util.isArray(layersArray)) { + return this.addLayer(layersArray); + } + + var fg = this._featureGroup, + npg = this._nonPointGroup, + chunked = this.options.chunkedLoading, + chunkInterval = this.options.chunkInterval, + chunkProgress = this.options.chunkProgress, + l = layersArray.length, + offset = 0, + originalArray = true, + m; + + if (this._map) { + var started = (new Date()).getTime(); + var process = L.bind(function () { + var start = (new Date()).getTime(); + for (; offset < l; offset++) { + if (chunked && offset % 200 === 0) { + // every couple hundred markers, instrument the time elapsed since processing started: + var elapsed = (new Date()).getTime() - start; + if (elapsed > chunkInterval) { + break; // been working too hard, time to take a break :-) + } + } + + m = layersArray[offset]; + + // Group of layers, append children to layersArray and skip. + // Side effects: + // - Total increases, so chunkProgress ratio jumps backward. + // - Groups are not included in this group, only their non-group child layers (hasLayer). + // Changing array length while looping does not affect performance in current browsers: + // http://jsperf.com/for-loop-changing-length/6 + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + //Not point data, can't be clustered + if (!m.getLatLng) { + npg.addLayer(m); + if (!skipLayerAddEvent) { + this.fire('layeradd', { layer: m }); + } + continue; + } + + if (this.hasLayer(m)) { + continue; + } + + this._addLayer(m, this._maxZoom); + if (!skipLayerAddEvent) { + this.fire('layeradd', { layer: m }); + } + + //If we just made a cluster of size 2 then we need to remove the other marker from the map (if it is) or we never will + if (m.__parent) { + if (m.__parent.getChildCount() === 2) { + var markers = m.__parent.getAllChildMarkers(), + otherMarker = markers[0] === m ? markers[1] : markers[0]; + fg.removeLayer(otherMarker); + } + } + } + + if (chunkProgress) { + // report progress and time elapsed: + chunkProgress(offset, l, (new Date()).getTime() - started); + } + + // Completed processing all markers. + if (offset === l) { + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); + } else { + setTimeout(process, this.options.chunkDelay); + } + }, this); + + process(); + } else { + var needsClustering = this._needsClustering; + + for (; offset < l; offset++) { + m = layersArray[offset]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + //Not point data, can't be clustered + if (!m.getLatLng) { + npg.addLayer(m); + continue; + } + + if (this.hasLayer(m)) { + continue; + } + + needsClustering.push(m); + } + } + return this; + }, + + //Takes an array of markers and removes them in bulk + removeLayers: function (layersArray) { + var i, m, + l = layersArray.length, + fg = this._featureGroup, + npg = this._nonPointGroup, + originalArray = true; + + if (!this._map) { + for (i = 0; i < l; i++) { + m = layersArray[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + this._arraySplice(this._needsClustering, m); + npg.removeLayer(m); + if (this.hasLayer(m)) { + this._needsRemoving.push({ layer: m, latlng: m._latlng }); + } + this.fire('layerremove', { layer: m }); + } + return this; + } + + if (this._unspiderfy) { + this._unspiderfy(); + + // Work on a copy of the array, so that next loop is not affected. + var layersArray2 = layersArray.slice(), + l2 = l; + for (i = 0; i < l2; i++) { + m = layersArray2[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + this._extractNonGroupLayers(m, layersArray2); + l2 = layersArray2.length; + continue; + } + + this._unspiderfyLayer(m); + } + } + + for (i = 0; i < l; i++) { + m = layersArray[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + if (!m.__parent) { + npg.removeLayer(m); + this.fire('layerremove', { layer: m }); + continue; + } + + this._removeLayer(m, true, true); + this.fire('layerremove', { layer: m }); + + if (fg.hasLayer(m)) { + fg.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + //Fix up the clusters and markers on the map + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); + + return this; + }, + + //Removes all layers from the MarkerClusterGroup + clearLayers: function () { + //Need our own special implementation as the LayerGroup one doesn't work for us + + //If we aren't on the map (yet), blow away the markers we know of + if (!this._map) { + this._needsClustering = []; + delete this._gridClusters; + delete this._gridUnclustered; + } + + if (this._noanimationUnspiderfy) { + this._noanimationUnspiderfy(); + } + + //Remove all the visible layers + this._featureGroup.clearLayers(); + this._nonPointGroup.clearLayers(); + + this.eachLayer(function (marker) { + marker.off(this._childMarkerEventHandlers, this); + delete marker.__parent; + }, this); + + if (this._map) { + //Reset _topClusterLevel and the DistanceGrids + this._generateInitialClusters(); + } + + return this; + }, + + //Override FeatureGroup.getBounds as it doesn't work + getBounds: function () { + var bounds = new L.LatLngBounds(); + + if (this._topClusterLevel) { + bounds.extend(this._topClusterLevel._bounds); + } + + for (var i = this._needsClustering.length - 1; i >= 0; i--) { + bounds.extend(this._needsClustering[i].getLatLng()); + } + + bounds.extend(this._nonPointGroup.getBounds()); + + return bounds; + }, + + //Overrides LayerGroup.eachLayer + eachLayer: function (method, context) { + var markers = this._needsClustering.slice(), + needsRemoving = this._needsRemoving, + thisNeedsRemoving, i, j; + + if (this._topClusterLevel) { + this._topClusterLevel.getAllChildMarkers(markers); + } + + for (i = markers.length - 1; i >= 0; i--) { + thisNeedsRemoving = true; + + for (j = needsRemoving.length - 1; j >= 0; j--) { + if (needsRemoving[j].layer === markers[i]) { + thisNeedsRemoving = false; + break; + } + } + + if (thisNeedsRemoving) { + method.call(context, markers[i]); + } + } + + this._nonPointGroup.eachLayer(method, context); + }, + + //Overrides LayerGroup.getLayers + getLayers: function () { + var layers = []; + this.eachLayer(function (l) { + layers.push(l); + }); + return layers; + }, + + //Overrides LayerGroup.getLayer, WARNING: Really bad performance + getLayer: function (id) { + var result = null; + + id = parseInt(id, 10); + + this.eachLayer(function (l) { + if (L.stamp(l) === id) { + result = l; + } + }); + + return result; + }, + + //Returns true if the given layer is in this MarkerClusterGroup + hasLayer: function (layer) { + if (!layer) { + return false; + } + + var i, anArray = this._needsClustering; + + for (i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === layer) { + return true; + } + } + + anArray = this._needsRemoving; + for (i = anArray.length - 1; i >= 0; i--) { + if (anArray[i].layer === layer) { + return false; + } + } + + return !!(layer.__parent && layer.__parent._group === this) || this._nonPointGroup.hasLayer(layer); + }, + + //Zoom down to show the given layer (spiderfying if necessary) then calls the callback + zoomToShowLayer: function (layer, callback) { + + if (typeof callback !== 'function') { + callback = function () {}; + } + + var showMarker = function () { + if ((layer._icon || layer.__parent._icon) && !this._inZoomAnimation) { + this._map.off('moveend', showMarker, this); + this.off('animationend', showMarker, this); + + if (layer._icon) { + callback(); + } else if (layer.__parent._icon) { + this.once('spiderfied', callback, this); + layer.__parent.spiderfy(); + } + } + }; + + if (layer._icon && this._map.getBounds().contains(layer.getLatLng())) { + //Layer is visible ond on screen, immediate return + callback(); + } else if (layer.__parent._zoom < Math.round(this._map._zoom)) { + //Layer should be visible at this zoom level. It must not be on screen so just pan over to it + this._map.on('moveend', showMarker, this); + this._map.panTo(layer.getLatLng()); + } else { + this._map.on('moveend', showMarker, this); + this.on('animationend', showMarker, this); + layer.__parent.zoomToBounds(); + } + }, + + //Overrides FeatureGroup.onAdd + onAdd: function (map) { + this._map = map; + var i, l, layer; + + if (!isFinite(this._map.getMaxZoom())) { + throw "Map has no maxZoom specified"; + } + + this._featureGroup.addTo(map); + this._nonPointGroup.addTo(map); + + if (!this._gridClusters) { + this._generateInitialClusters(); + } + + this._maxLat = map.options.crs.projection.MAX_LATITUDE; + + //Restore all the positions as they are in the MCG before removing them + for (i = 0, l = this._needsRemoving.length; i < l; i++) { + layer = this._needsRemoving[i]; + layer.newlatlng = layer.layer._latlng; + layer.layer._latlng = layer.latlng; + } + //Remove them, then restore their new positions + for (i = 0, l = this._needsRemoving.length; i < l; i++) { + layer = this._needsRemoving[i]; + this._removeLayer(layer.layer, true); + layer.layer._latlng = layer.newlatlng; + } + this._needsRemoving = []; + + //Remember the current zoom level and bounds + this._zoom = Math.round(this._map._zoom); + this._currentShownBounds = this._getExpandedVisibleBounds(); + + this._map.on('zoomend', this._zoomEnd, this); + this._map.on('moveend', this._moveEnd, this); + + if (this._spiderfierOnAdd) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely + this._spiderfierOnAdd(); + } + + this._bindEvents(); + + //Actually add our markers to the map: + l = this._needsClustering; + this._needsClustering = []; + this.addLayers(l, true); + }, + + //Overrides FeatureGroup.onRemove + onRemove: function (map) { + map.off('zoomend', this._zoomEnd, this); + map.off('moveend', this._moveEnd, this); + + this._unbindEvents(); + + //In case we are in a cluster animation + this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', ''); + + if (this._spiderfierOnRemove) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely + this._spiderfierOnRemove(); + } + + delete this._maxLat; + + //Clean up all the layers we added to the map + this._hideCoverage(); + this._featureGroup.remove(); + this._nonPointGroup.remove(); + + this._featureGroup.clearLayers(); + + this._map = null; + }, + + getVisibleParent: function (marker) { + var vMarker = marker; + while (vMarker && !vMarker._icon) { + vMarker = vMarker.__parent; + } + return vMarker || null; + }, + + //Remove the given object from the given array + _arraySplice: function (anArray, obj) { + for (var i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === obj) { + anArray.splice(i, 1); + return true; + } + } + }, + + /** + * Removes a marker from all _gridUnclustered zoom levels, starting at the supplied zoom. + * @param marker to be removed from _gridUnclustered. + * @param z integer bottom start zoom level (included) + * @private + */ + _removeFromGridUnclustered: function (marker, z) { + var map = this._map, + gridUnclustered = this._gridUnclustered, + minZoom = Math.floor(this._map.getMinZoom()); + + for (; z >= minZoom; z--) { + if (!gridUnclustered[z].removeObject(marker, map.project(marker.getLatLng(), z))) { + break; + } + } + }, + + _childMarkerDragStart: function (e) { + e.target.__dragStart = e.target._latlng; + }, + + _childMarkerMoved: function (e) { + if (!this._ignoreMove && !e.target.__dragStart) { + var isPopupOpen = e.target._popup && e.target._popup.isOpen(); + + this._moveChild(e.target, e.oldLatLng, e.latlng); + + if (isPopupOpen) { + e.target.openPopup(); + } + } + }, + + _moveChild: function (layer, from, to) { + layer._latlng = from; + this.removeLayer(layer); + + layer._latlng = to; + this.addLayer(layer); + }, + + _childMarkerDragEnd: function (e) { + if (e.target.__dragStart) { + this._moveChild(e.target, e.target.__dragStart, e.target._latlng); + } + delete e.target.__dragStart; + }, + + + //Internal function for removing a marker from everything. + //dontUpdateMap: set to true if you will handle updating the map manually (for bulk functions) + _removeLayer: function (marker, removeFromDistanceGrid, dontUpdateMap) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + fg = this._featureGroup, + map = this._map, + minZoom = Math.floor(this._map.getMinZoom()); + + //Remove the marker from distance clusters it might be in + if (removeFromDistanceGrid) { + this._removeFromGridUnclustered(marker, this._maxZoom); + } + + //Work our way up the clusters removing them as we go if required + var cluster = marker.__parent, + markers = cluster._markers, + otherMarker; + + //Remove the marker from the immediate parents marker list + this._arraySplice(markers, marker); + + while (cluster) { + cluster._childCount--; + cluster._boundsNeedUpdate = true; + + if (cluster._zoom < minZoom) { + //Top level, do nothing + break; + } else if (removeFromDistanceGrid && cluster._childCount <= 1) { //Cluster no longer required + //We need to push the other marker up to the parent + otherMarker = cluster._markers[0] === marker ? cluster._markers[1] : cluster._markers[0]; + + //Update distance grid + gridClusters[cluster._zoom].removeObject(cluster, map.project(cluster._cLatLng, cluster._zoom)); + gridUnclustered[cluster._zoom].addObject(otherMarker, map.project(otherMarker.getLatLng(), cluster._zoom)); + + //Move otherMarker up to parent + this._arraySplice(cluster.__parent._childClusters, cluster); + cluster.__parent._markers.push(otherMarker); + otherMarker.__parent = cluster.__parent; + + if (cluster._icon) { + //Cluster is currently on the map, need to put the marker on the map instead + fg.removeLayer(cluster); + if (!dontUpdateMap) { + fg.addLayer(otherMarker); + } + } + } else { + cluster._iconNeedsUpdate = true; + } + + cluster = cluster.__parent; + } + + delete marker.__parent; + }, + + _isOrIsParent: function (el, oel) { + while (oel) { + if (el === oel) { + return true; + } + oel = oel.parentNode; + } + return false; + }, + + //Override L.Evented.fire + fire: function (type, data, propagate) { + if (data && data.layer instanceof L.MarkerCluster) { + //Prevent multiple clustermouseover/off events if the icon is made up of stacked divs (Doesn't work in ie <= 8, no relatedTarget) + if (data.originalEvent && this._isOrIsParent(data.layer._icon, data.originalEvent.relatedTarget)) { + return; + } + type = 'cluster' + type; + } + + L.FeatureGroup.prototype.fire.call(this, type, data, propagate); + }, + + //Override L.Evented.listens + listens: function (type, propagate) { + return L.FeatureGroup.prototype.listens.call(this, type, propagate) || L.FeatureGroup.prototype.listens.call(this, 'cluster' + type, propagate); + }, + + //Default functionality + _defaultIconCreateFunction: function (cluster) { + var childCount = cluster.getChildCount(); + + var c = ' marker-cluster-'; + if (childCount < 10) { + c += 'small'; + } else if (childCount < 100) { + c += 'medium'; + } else { + c += 'large'; + } + + return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) }); + }, + + _bindEvents: function () { + var map = this._map, + spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom, + showCoverageOnHover = this.options.showCoverageOnHover, + zoomToBoundsOnClick = this.options.zoomToBoundsOnClick; + + //Zoom on cluster click or spiderfy if we are at the lowest level + if (spiderfyOnMaxZoom || zoomToBoundsOnClick) { + this.on('clusterclick', this._zoomOrSpiderfy, this); + } + + //Show convex hull (boundary) polygon on mouse over + if (showCoverageOnHover) { + this.on('clustermouseover', this._showCoverage, this); + this.on('clustermouseout', this._hideCoverage, this); + map.on('zoomend', this._hideCoverage, this); + } + }, + + _zoomOrSpiderfy: function (e) { + var cluster = e.layer, + bottomCluster = cluster; + + while (bottomCluster._childClusters.length === 1) { + bottomCluster = bottomCluster._childClusters[0]; + } + + if (bottomCluster._zoom === this._maxZoom && + bottomCluster._childCount === cluster._childCount && + this.options.spiderfyOnMaxZoom) { + + // All child markers are contained in a single cluster from this._maxZoom to this cluster. + cluster.spiderfy(); + } else if (this.options.zoomToBoundsOnClick) { + cluster.zoomToBounds(); + } + + // Focus the map again for keyboard users. + if (e.originalEvent && e.originalEvent.keyCode === 13) { + this._map._container.focus(); + } + }, + + _showCoverage: function (e) { + var map = this._map; + if (this._inZoomAnimation) { + return; + } + if (this._shownPolygon) { + map.removeLayer(this._shownPolygon); + } + if (e.layer.getChildCount() > 2 && e.layer !== this._spiderfied) { + this._shownPolygon = new L.Polygon(e.layer.getConvexHull(), this.options.polygonOptions); + map.addLayer(this._shownPolygon); + } + }, + + _hideCoverage: function () { + if (this._shownPolygon) { + this._map.removeLayer(this._shownPolygon); + this._shownPolygon = null; + } + }, + + _unbindEvents: function () { + var spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom, + showCoverageOnHover = this.options.showCoverageOnHover, + zoomToBoundsOnClick = this.options.zoomToBoundsOnClick, + map = this._map; + + if (spiderfyOnMaxZoom || zoomToBoundsOnClick) { + this.off('clusterclick', this._zoomOrSpiderfy, this); + } + if (showCoverageOnHover) { + this.off('clustermouseover', this._showCoverage, this); + this.off('clustermouseout', this._hideCoverage, this); + map.off('zoomend', this._hideCoverage, this); + } + }, + + _zoomEnd: function () { + if (!this._map) { //May have been removed from the map by a zoomEnd handler + return; + } + this._mergeSplitClusters(); + + this._zoom = Math.round(this._map._zoom); + this._currentShownBounds = this._getExpandedVisibleBounds(); + }, + + _moveEnd: function () { + if (this._inZoomAnimation) { + return; + } + + var newBounds = this._getExpandedVisibleBounds(); + + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), this._zoom, newBounds); + this._topClusterLevel._recursivelyAddChildrenToMap(null, Math.round(this._map._zoom), newBounds); + + this._currentShownBounds = newBounds; + return; + }, + + _generateInitialClusters: function () { + var maxZoom = Math.ceil(this._map.getMaxZoom()), + minZoom = Math.floor(this._map.getMinZoom()), + radius = this.options.maxClusterRadius, + radiusFn = radius; + + //If we just set maxClusterRadius to a single number, we need to create + //a simple function to return that number. Otherwise, we just have to + //use the function we've passed in. + if (typeof radius !== "function") { + radiusFn = function () { return radius; }; + } + + if (this.options.disableClusteringAtZoom !== null) { + maxZoom = this.options.disableClusteringAtZoom - 1; + } + this._maxZoom = maxZoom; + this._gridClusters = {}; + this._gridUnclustered = {}; + + //Set up DistanceGrids for each zoom + for (var zoom = maxZoom; zoom >= minZoom; zoom--) { + this._gridClusters[zoom] = new L.DistanceGrid(radiusFn(zoom)); + this._gridUnclustered[zoom] = new L.DistanceGrid(radiusFn(zoom)); + } + + // Instantiate the appropriate L.MarkerCluster class (animated or not). + this._topClusterLevel = new this._markerCluster(this, minZoom - 1); + }, + + //Zoom: Zoom to start adding at (Pass this._maxZoom to start at the bottom) + _addLayer: function (layer, zoom) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + minZoom = Math.floor(this._map.getMinZoom()), + markerPoint, z; + + if (this.options.singleMarkerMode) { + this._overrideMarkerIcon(layer); + } + + layer.on(this._childMarkerEventHandlers, this); + + //Find the lowest zoom level to slot this one in + for (; zoom >= minZoom; zoom--) { + markerPoint = this._map.project(layer.getLatLng(), zoom); // calculate pixel position + + //Try find a cluster close by + var closest = gridClusters[zoom].getNearObject(markerPoint); + if (closest) { + closest._addChild(layer); + layer.__parent = closest; + return; + } + + //Try find a marker close by to form a new cluster with + closest = gridUnclustered[zoom].getNearObject(markerPoint); + if (closest) { + var parent = closest.__parent; + if (parent) { + this._removeLayer(closest, false); + } + + //Create new cluster with these 2 in it + + var newCluster = new this._markerCluster(this, zoom, closest, layer); + gridClusters[zoom].addObject(newCluster, this._map.project(newCluster._cLatLng, zoom)); + closest.__parent = newCluster; + layer.__parent = newCluster; + + //First create any new intermediate parent clusters that don't exist + var lastParent = newCluster; + for (z = zoom - 1; z > parent._zoom; z--) { + lastParent = new this._markerCluster(this, z, lastParent); + gridClusters[z].addObject(lastParent, this._map.project(closest.getLatLng(), z)); + } + parent._addChild(lastParent); + + //Remove closest from this zoom level and any above that it is in, replace with newCluster + this._removeFromGridUnclustered(closest, zoom); + + return; + } + + //Didn't manage to cluster in at this zoom, record us as a marker here and continue upwards + gridUnclustered[zoom].addObject(layer, markerPoint); + } + + //Didn't get in anything, add us to the top + this._topClusterLevel._addChild(layer); + layer.__parent = this._topClusterLevel; + return; + }, + + /** + * Refreshes the icon of all "dirty" visible clusters. + * Non-visible "dirty" clusters will be updated when they are added to the map. + * @private + */ + _refreshClustersIcons: function () { + this._featureGroup.eachLayer(function (c) { + if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) { + c._updateIcon(); + } + }); + }, + + //Enqueue code to fire after the marker expand/contract has happened + _enqueue: function (fn) { + this._queue.push(fn); + if (!this._queueTimeout) { + this._queueTimeout = setTimeout(L.bind(this._processQueue, this), 300); + } + }, + _processQueue: function () { + for (var i = 0; i < this._queue.length; i++) { + this._queue[i].call(this); + } + this._queue.length = 0; + clearTimeout(this._queueTimeout); + this._queueTimeout = null; + }, + + //Merge and split any existing clusters that are too big or small + _mergeSplitClusters: function () { + var mapZoom = Math.round(this._map._zoom); + + //In case we are starting to split before the animation finished + this._processQueue(); + + if (this._zoom < mapZoom && this._currentShownBounds.intersects(this._getExpandedVisibleBounds())) { //Zoom in, split + this._animationStart(); + //Remove clusters now off screen + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), this._zoom, this._getExpandedVisibleBounds()); + + this._animationZoomIn(this._zoom, mapZoom); + + } else if (this._zoom > mapZoom) { //Zoom out, merge + this._animationStart(); + + this._animationZoomOut(this._zoom, mapZoom); + } else { + this._moveEnd(); + } + }, + + //Gets the maps visible bounds expanded in each direction by the size of the screen (so the user cannot see an area we do not cover in one pan) + _getExpandedVisibleBounds: function () { + if (!this.options.removeOutsideVisibleBounds) { + return this._mapBoundsInfinite; + } else if (L.Browser.mobile) { + return this._checkBoundsMaxLat(this._map.getBounds()); + } + + return this._checkBoundsMaxLat(this._map.getBounds().pad(1)); // Padding expands the bounds by its own dimensions but scaled with the given factor. + }, + + /** + * Expands the latitude to Infinity (or -Infinity) if the input bounds reach the map projection maximum defined latitude + * (in the case of Web/Spherical Mercator, it is 85.0511287798 / see https://en.wikipedia.org/wiki/Web_Mercator#Formulas). + * Otherwise, the removeOutsideVisibleBounds option will remove markers beyond that limit, whereas the same markers without + * this option (or outside MCG) will have their position floored (ceiled) by the projection and rendered at that limit, + * making the user think that MCG "eats" them and never displays them again. + * @param bounds L.LatLngBounds + * @returns {L.LatLngBounds} + * @private + */ + _checkBoundsMaxLat: function (bounds) { + var maxLat = this._maxLat; + + if (maxLat !== undefined) { + if (bounds.getNorth() >= maxLat) { + bounds._northEast.lat = Infinity; + } + if (bounds.getSouth() <= -maxLat) { + bounds._southWest.lat = -Infinity; + } + } + + return bounds; + }, + + //Shared animation code + _animationAddLayerNonAnimated: function (layer, newCluster) { + if (newCluster === layer) { + this._featureGroup.addLayer(layer); + } else if (newCluster._childCount === 2) { + newCluster._addToMap(); + + var markers = newCluster.getAllChildMarkers(); + this._featureGroup.removeLayer(markers[0]); + this._featureGroup.removeLayer(markers[1]); + } else { + newCluster._updateIcon(); + } + }, + + /** + * Extracts individual (i.e. non-group) layers from a Layer Group. + * @param group to extract layers from. + * @param output {Array} in which to store the extracted layers. + * @returns {*|Array} + * @private + */ + _extractNonGroupLayers: function (group, output) { + var layers = group.getLayers(), + i = 0, + layer; + + output = output || []; + + for (; i < layers.length; i++) { + layer = layers[i]; + + if (layer instanceof L.LayerGroup) { + this._extractNonGroupLayers(layer, output); + continue; + } + + output.push(layer); + } + + return output; + }, + + /** + * Implements the singleMarkerMode option. + * @param layer Marker to re-style using the Clusters iconCreateFunction. + * @returns {L.Icon} The newly created icon. + * @private + */ + _overrideMarkerIcon: function (layer) { + var icon = layer.options.icon = this.options.iconCreateFunction({ + getChildCount: function () { + return 1; + }, + getAllChildMarkers: function () { + return [layer]; + } + }); + + return icon; + } +}); + +// Constant bounds used in case option "removeOutsideVisibleBounds" is set to false. +L.MarkerClusterGroup.include({ + _mapBoundsInfinite: new L.LatLngBounds(new L.LatLng(-Infinity, -Infinity), new L.LatLng(Infinity, Infinity)) +}); + +L.MarkerClusterGroup.include({ + _noAnimation: { + //Non Animated versions of everything + _animationStart: function () { + //Do nothing... + }, + _animationZoomIn: function (previousZoomLevel, newZoomLevel) { + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + + //We didn't actually animate, but we use this event to mean "clustering animations have finished" + this.fire('animationend'); + }, + _animationZoomOut: function (previousZoomLevel, newZoomLevel) { + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + + //We didn't actually animate, but we use this event to mean "clustering animations have finished" + this.fire('animationend'); + }, + _animationAddLayer: function (layer, newCluster) { + this._animationAddLayerNonAnimated(layer, newCluster); + } + }, + + _withAnimation: { + //Animated versions here + _animationStart: function () { + this._map._mapPane.className += ' leaflet-cluster-anim'; + this._inZoomAnimation++; + }, + + _animationZoomIn: function (previousZoomLevel, newZoomLevel) { + var bounds = this._getExpandedVisibleBounds(), + fg = this._featureGroup, + minZoom = Math.floor(this._map.getMinZoom()), + i; + + this._ignoreMove = true; + + //Add all children of current clusters to map and remove those clusters from map + this._topClusterLevel._recursively(bounds, previousZoomLevel, minZoom, function (c) { + var startPos = c._latlng, + markers = c._markers, + m; + + if (!bounds.contains(startPos)) { + startPos = null; + } + + if (c._isSingleParent() && previousZoomLevel + 1 === newZoomLevel) { //Immediately add the new child and remove us + fg.removeLayer(c); + c._recursivelyAddChildrenToMap(null, newZoomLevel, bounds); + } else { + //Fade out old cluster + c.clusterHide(); + c._recursivelyAddChildrenToMap(startPos, newZoomLevel, bounds); + } + + //Remove all markers that aren't visible any more + //TODO: Do we actually need to do this on the higher levels too? + for (i = markers.length - 1; i >= 0; i--) { + m = markers[i]; + if (!bounds.contains(m._latlng)) { + fg.removeLayer(m); + } + } + + }); + + this._forceLayout(); + + //Update opacities + this._topClusterLevel._recursivelyBecomeVisible(bounds, newZoomLevel); + //TODO Maybe? Update markers in _recursivelyBecomeVisible + fg.eachLayer(function (n) { + if (!(n instanceof L.MarkerCluster) && n._icon) { + n.clusterShow(); + } + }); + + //update the positions of the just added clusters/markers + this._topClusterLevel._recursively(bounds, previousZoomLevel, newZoomLevel, function (c) { + c._recursivelyRestoreChildPositions(newZoomLevel); + }); + + this._ignoreMove = false; + + //Remove the old clusters and close the zoom animation + this._enqueue(function () { + //update the positions of the just added clusters/markers + this._topClusterLevel._recursively(bounds, previousZoomLevel, minZoom, function (c) { + fg.removeLayer(c); + c.clusterShow(); + }); + + this._animationEnd(); + }); + }, + + _animationZoomOut: function (previousZoomLevel, newZoomLevel) { + this._animationZoomOutSingle(this._topClusterLevel, previousZoomLevel - 1, newZoomLevel); + + //Need to add markers for those that weren't on the map before but are now + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + //Remove markers that were on the map before but won't be now + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), previousZoomLevel, this._getExpandedVisibleBounds()); + }, + + _animationAddLayer: function (layer, newCluster) { + var me = this, + fg = this._featureGroup; + + fg.addLayer(layer); + if (newCluster !== layer) { + if (newCluster._childCount > 2) { //Was already a cluster + + newCluster._updateIcon(); + this._forceLayout(); + this._animationStart(); + + layer._setPos(this._map.latLngToLayerPoint(newCluster.getLatLng())); + layer.clusterHide(); + + this._enqueue(function () { + fg.removeLayer(layer); + layer.clusterShow(); + + me._animationEnd(); + }); + + } else { //Just became a cluster + this._forceLayout(); + + me._animationStart(); + me._animationZoomOutSingle(newCluster, this._map.getMaxZoom(), this._zoom); + } + } + } + }, + + // Private methods for animated versions. + _animationZoomOutSingle: function (cluster, previousZoomLevel, newZoomLevel) { + var bounds = this._getExpandedVisibleBounds(), + minZoom = Math.floor(this._map.getMinZoom()); + + //Animate all of the markers in the clusters to move to their cluster center point + cluster._recursivelyAnimateChildrenInAndAddSelfToMap(bounds, minZoom, previousZoomLevel + 1, newZoomLevel); + + var me = this; + + //Update the opacity (If we immediately set it they won't animate) + this._forceLayout(); + cluster._recursivelyBecomeVisible(bounds, newZoomLevel); + + //TODO: Maybe use the transition timing stuff to make this more reliable + //When the animations are done, tidy up + this._enqueue(function () { + + //This cluster stopped being a cluster before the timeout fired + if (cluster._childCount === 1) { + var m = cluster._markers[0]; + //If we were in a cluster animation at the time then the opacity and position of our child could be wrong now, so fix it + this._ignoreMove = true; + m.setLatLng(m.getLatLng()); + this._ignoreMove = false; + if (m.clusterShow) { + m.clusterShow(); + } + } else { + cluster._recursively(bounds, newZoomLevel, minZoom, function (c) { + c._recursivelyRemoveChildrenFromMap(bounds, minZoom, previousZoomLevel + 1); + }); + } + me._animationEnd(); + }); + }, + + _animationEnd: function () { + if (this._map) { + this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', ''); + } + this._inZoomAnimation--; + this.fire('animationend'); + }, + + //Force a browser layout of stuff in the map + // Should apply the current opacity and location to all elements so we can update them again for an animation + _forceLayout: function () { + //In my testing this works, infact offsetWidth of any element seems to work. + //Could loop all this._layers and do this for each _icon if it stops working + + L.Util.falseFn(document.body.offsetWidth); + } +}); + +L.markerClusterGroup = function (options) { + return new L.MarkerClusterGroup(options); +}; + +var MarkerCluster = L.MarkerCluster = L.Marker.extend({ + options: L.Icon.prototype.options, + + initialize: function (group, zoom, a, b) { + + L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0), + { icon: this, pane: group.options.clusterPane }); + + this._group = group; + this._zoom = zoom; + + this._markers = []; + this._childClusters = []; + this._childCount = 0; + this._iconNeedsUpdate = true; + this._boundsNeedUpdate = true; + + this._bounds = new L.LatLngBounds(); + + if (a) { + this._addChild(a); + } + if (b) { + this._addChild(b); + } + }, + + //Recursively retrieve all child markers of this cluster + getAllChildMarkers: function (storageArray) { + storageArray = storageArray || []; + + for (var i = this._childClusters.length - 1; i >= 0; i--) { + this._childClusters[i].getAllChildMarkers(storageArray); + } + + for (var j = this._markers.length - 1; j >= 0; j--) { + storageArray.push(this._markers[j]); + } + + return storageArray; + }, + + //Returns the count of how many child markers we have + getChildCount: function () { + return this._childCount; + }, + + //Zoom to the minimum of showing all of the child markers, or the extents of this cluster + zoomToBounds: function (fitBoundsOptions) { + var childClusters = this._childClusters.slice(), + map = this._group._map, + boundsZoom = map.getBoundsZoom(this._bounds), + zoom = this._zoom + 1, + mapZoom = map.getZoom(), + i; + + //calculate how far we need to zoom down to see all of the markers + while (childClusters.length > 0 && boundsZoom > zoom) { + zoom++; + var newClusters = []; + for (i = 0; i < childClusters.length; i++) { + newClusters = newClusters.concat(childClusters[i]._childClusters); + } + childClusters = newClusters; + } + + if (boundsZoom > zoom) { + this._group._map.setView(this._latlng, zoom); + } else if (boundsZoom <= mapZoom) { //If fitBounds wouldn't zoom us down, zoom us down instead + this._group._map.setView(this._latlng, mapZoom + 1); + } else { + this._group._map.fitBounds(this._bounds, fitBoundsOptions); + } + }, + + getBounds: function () { + var bounds = new L.LatLngBounds(); + bounds.extend(this._bounds); + return bounds; + }, + + _updateIcon: function () { + this._iconNeedsUpdate = true; + if (this._icon) { + this.setIcon(this); + } + }, + + //Cludge for Icon, we pretend to be an icon for performance + createIcon: function () { + if (this._iconNeedsUpdate) { + this._iconObj = this._group.options.iconCreateFunction(this); + this._iconNeedsUpdate = false; + } + return this._iconObj.createIcon(); + }, + createShadow: function () { + return this._iconObj.createShadow(); + }, + + + _addChild: function (new1, isNotificationFromChild) { + + this._iconNeedsUpdate = true; + + this._boundsNeedUpdate = true; + this._setClusterCenter(new1); + + if (new1 instanceof L.MarkerCluster) { + if (!isNotificationFromChild) { + this._childClusters.push(new1); + new1.__parent = this; + } + this._childCount += new1._childCount; + } else { + if (!isNotificationFromChild) { + this._markers.push(new1); + } + this._childCount++; + } + + if (this.__parent) { + this.__parent._addChild(new1, true); + } + }, + + /** + * Makes sure the cluster center is set. If not, uses the child center if it is a cluster, or the marker position. + * @param child L.MarkerCluster|L.Marker that will be used as cluster center if not defined yet. + * @private + */ + _setClusterCenter: function (child) { + if (!this._cLatLng) { + // when clustering, take position of the first point as the cluster center + this._cLatLng = child._cLatLng || child._latlng; + } + }, + + /** + * Assigns impossible bounding values so that the next extend entirely determines the new bounds. + * This method avoids having to trash the previous L.LatLngBounds object and to create a new one, which is much slower for this class. + * As long as the bounds are not extended, most other methods would probably fail, as they would with bounds initialized but not extended. + * @private + */ + _resetBounds: function () { + var bounds = this._bounds; + + if (bounds._southWest) { + bounds._southWest.lat = Infinity; + bounds._southWest.lng = Infinity; + } + if (bounds._northEast) { + bounds._northEast.lat = -Infinity; + bounds._northEast.lng = -Infinity; + } + }, + + _recalculateBounds: function () { + var markers = this._markers, + childClusters = this._childClusters, + latSum = 0, + lngSum = 0, + totalCount = this._childCount, + i, child, childLatLng, childCount; + + // Case where all markers are removed from the map and we are left with just an empty _topClusterLevel. + if (totalCount === 0) { + return; + } + + // Reset rather than creating a new object, for performance. + this._resetBounds(); + + // Child markers. + for (i = 0; i < markers.length; i++) { + childLatLng = markers[i]._latlng; + + this._bounds.extend(childLatLng); + + latSum += childLatLng.lat; + lngSum += childLatLng.lng; + } + + // Child clusters. + for (i = 0; i < childClusters.length; i++) { + child = childClusters[i]; + + // Re-compute child bounds and weighted position first if necessary. + if (child._boundsNeedUpdate) { + child._recalculateBounds(); + } + + this._bounds.extend(child._bounds); + + childLatLng = child._wLatLng; + childCount = child._childCount; + + latSum += childLatLng.lat * childCount; + lngSum += childLatLng.lng * childCount; + } + + this._latlng = this._wLatLng = new L.LatLng(latSum / totalCount, lngSum / totalCount); + + // Reset dirty flag. + this._boundsNeedUpdate = false; + }, + + //Set our markers position as given and add it to the map + _addToMap: function (startPos) { + if (startPos) { + this._backupLatlng = this._latlng; + this.setLatLng(startPos); + } + this._group._featureGroup.addLayer(this); + }, + + _recursivelyAnimateChildrenIn: function (bounds, center, maxZoom) { + this._recursively(bounds, this._group._map.getMinZoom(), maxZoom - 1, + function (c) { + var markers = c._markers, + i, m; + for (i = markers.length - 1; i >= 0; i--) { + m = markers[i]; + + //Only do it if the icon is still on the map + if (m._icon) { + m._setPos(center); + m.clusterHide(); + } + } + }, + function (c) { + var childClusters = c._childClusters, + j, cm; + for (j = childClusters.length - 1; j >= 0; j--) { + cm = childClusters[j]; + if (cm._icon) { + cm._setPos(center); + cm.clusterHide(); + } + } + } + ); + }, + + _recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, mapMinZoom, previousZoomLevel, newZoomLevel) { + this._recursively(bounds, newZoomLevel, mapMinZoom, + function (c) { + c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), previousZoomLevel); + + //TODO: depthToAnimateIn affects _isSingleParent, if there is a multizoom we may/may not be. + //As a hack we only do a animation free zoom on a single level zoom, if someone does multiple levels then we always animate + if (c._isSingleParent() && previousZoomLevel - 1 === newZoomLevel) { + c.clusterShow(); + c._recursivelyRemoveChildrenFromMap(bounds, mapMinZoom, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds + } else { + c.clusterHide(); + } + + c._addToMap(); + } + ); + }, + + _recursivelyBecomeVisible: function (bounds, zoomLevel) { + this._recursively(bounds, this._group._map.getMinZoom(), zoomLevel, null, function (c) { + c.clusterShow(); + }); + }, + + _recursivelyAddChildrenToMap: function (startPos, zoomLevel, bounds) { + this._recursively(bounds, this._group._map.getMinZoom() - 1, zoomLevel, + function (c) { + if (zoomLevel === c._zoom) { + return; + } + + //Add our child markers at startPos (so they can be animated out) + for (var i = c._markers.length - 1; i >= 0; i--) { + var nm = c._markers[i]; + + if (!bounds.contains(nm._latlng)) { + continue; + } + + if (startPos) { + nm._backupLatlng = nm.getLatLng(); + + nm.setLatLng(startPos); + if (nm.clusterHide) { + nm.clusterHide(); + } + } + + c._group._featureGroup.addLayer(nm); + } + }, + function (c) { + c._addToMap(startPos); + } + ); + }, + + _recursivelyRestoreChildPositions: function (zoomLevel) { + //Fix positions of child markers + for (var i = this._markers.length - 1; i >= 0; i--) { + var nm = this._markers[i]; + if (nm._backupLatlng) { + nm.setLatLng(nm._backupLatlng); + delete nm._backupLatlng; + } + } + + if (zoomLevel - 1 === this._zoom) { + //Reposition child clusters + for (var j = this._childClusters.length - 1; j >= 0; j--) { + this._childClusters[j]._restorePosition(); + } + } else { + for (var k = this._childClusters.length - 1; k >= 0; k--) { + this._childClusters[k]._recursivelyRestoreChildPositions(zoomLevel); + } + } + }, + + _restorePosition: function () { + if (this._backupLatlng) { + this.setLatLng(this._backupLatlng); + delete this._backupLatlng; + } + }, + + //exceptBounds: If set, don't remove any markers/clusters in it + _recursivelyRemoveChildrenFromMap: function (previousBounds, mapMinZoom, zoomLevel, exceptBounds) { + var m, i; + this._recursively(previousBounds, mapMinZoom - 1, zoomLevel - 1, + function (c) { + //Remove markers at every level + for (i = c._markers.length - 1; i >= 0; i--) { + m = c._markers[i]; + if (!exceptBounds || !exceptBounds.contains(m._latlng)) { + c._group._featureGroup.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + }, + function (c) { + //Remove child clusters at just the bottom level + for (i = c._childClusters.length - 1; i >= 0; i--) { + m = c._childClusters[i]; + if (!exceptBounds || !exceptBounds.contains(m._latlng)) { + c._group._featureGroup.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + } + ); + }, + + //Run the given functions recursively to this and child clusters + // boundsToApplyTo: a L.LatLngBounds representing the bounds of what clusters to recurse in to + // zoomLevelToStart: zoom level to start running functions (inclusive) + // zoomLevelToStop: zoom level to stop running functions (inclusive) + // runAtEveryLevel: function that takes an L.MarkerCluster as an argument that should be applied on every level + // runAtBottomLevel: function that takes an L.MarkerCluster as an argument that should be applied at only the bottom level + _recursively: function (boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel) { + var childClusters = this._childClusters, + zoom = this._zoom, + i, c; + + if (zoomLevelToStart <= zoom) { + if (runAtEveryLevel) { + runAtEveryLevel(this); + } + if (runAtBottomLevel && zoom === zoomLevelToStop) { + runAtBottomLevel(this); + } + } + + if (zoom < zoomLevelToStart || zoom < zoomLevelToStop) { + for (i = childClusters.length - 1; i >= 0; i--) { + c = childClusters[i]; + if (boundsToApplyTo.intersects(c._bounds)) { + c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel); + } + } + } + }, + + //Returns true if we are the parent of only one cluster and that cluster is the same as us + _isSingleParent: function () { + //Don't need to check this._markers as the rest won't work if there are any + return this._childClusters.length > 0 && this._childClusters[0]._childCount === this._childCount; + } +}); + +/* +* Extends L.Marker to include two extra methods: clusterHide and clusterShow. +* +* They work as setOpacity(0) and setOpacity(1) respectively, but +* they will remember the marker's opacity when hiding and showing it again. +* +*/ + + +L.Marker.include({ + + clusterHide: function () { + this.options.opacityWhenUnclustered = this.options.opacity || 1; + return this.setOpacity(0); + }, + + clusterShow: function () { + var ret = this.setOpacity(this.options.opacity || this.options.opacityWhenUnclustered); + delete this.options.opacityWhenUnclustered; + return ret; + } + +}); + +L.DistanceGrid = function (cellSize) { + this._cellSize = cellSize; + this._sqCellSize = cellSize * cellSize; + this._grid = {}; + this._objectPoint = { }; +}; + +L.DistanceGrid.prototype = { + + addObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + stamp = L.Util.stamp(obj); + + this._objectPoint[stamp] = point; + + cell.push(obj); + }, + + updateObject: function (obj, point) { + this.removeObject(obj); + this.addObject(obj, point); + }, + + //Returns true if the object was found + removeObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + i, len; + + delete this._objectPoint[L.Util.stamp(obj)]; + + for (i = 0, len = cell.length; i < len; i++) { + if (cell[i] === obj) { + + cell.splice(i, 1); + + if (len === 1) { + delete row[x]; + } + + return true; + } + } + + }, + + eachObject: function (fn, context) { + var i, j, k, len, row, cell, removed, + grid = this._grid; + + for (i in grid) { + row = grid[i]; + + for (j in row) { + cell = row[j]; + + for (k = 0, len = cell.length; k < len; k++) { + removed = fn.call(context, cell[k]); + if (removed) { + k--; + len--; + } + } + } + } + }, + + getNearObject: function (point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + i, j, k, row, cell, len, obj, dist, + objectPoint = this._objectPoint, + closestDistSq = this._sqCellSize, + closest = null; + + for (i = y - 1; i <= y + 1; i++) { + row = this._grid[i]; + if (row) { + + for (j = x - 1; j <= x + 1; j++) { + cell = row[j]; + if (cell) { + + for (k = 0, len = cell.length; k < len; k++) { + obj = cell[k]; + dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point); + if (dist < closestDistSq || + dist <= closestDistSq && closest === null) { + closestDistSq = dist; + closest = obj; + } + } + } + } + } + } + return closest; + }, + + _getCoord: function (x) { + var coord = Math.floor(x / this._cellSize); + return isFinite(coord) ? coord : x; + }, + + _sqDist: function (p, p2) { + var dx = p2.x - p.x, + dy = p2.y - p.y; + return dx * dx + dy * dy; + } +}; + +/* Copyright (c) 2012 the authors listed at the following URL, and/or +the authors of referenced articles or incorporated external code: +http://en.literateprograms.org/Quickhull_(Javascript)?action=history&offset=20120410175256 + +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. + +Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=18434 +*/ + +(function () { + L.QuickHull = { + + /* + * @param {Object} cpt a point to be measured from the baseline + * @param {Array} bl the baseline, as represented by a two-element + * array of latlng objects. + * @returns {Number} an approximate distance measure + */ + getDistant: function (cpt, bl) { + var vY = bl[1].lat - bl[0].lat, + vX = bl[0].lng - bl[1].lng; + return (vX * (cpt.lat - bl[0].lat) + vY * (cpt.lng - bl[0].lng)); + }, + + /* + * @param {Array} baseLine a two-element array of latlng objects + * representing the baseline to project from + * @param {Array} latLngs an array of latlng objects + * @returns {Object} the maximum point and all new points to stay + * in consideration for the hull. + */ + findMostDistantPointFromBaseLine: function (baseLine, latLngs) { + var maxD = 0, + maxPt = null, + newPoints = [], + i, pt, d; + + for (i = latLngs.length - 1; i >= 0; i--) { + pt = latLngs[i]; + d = this.getDistant(pt, baseLine); + + if (d > 0) { + newPoints.push(pt); + } else { + continue; + } + + if (d > maxD) { + maxD = d; + maxPt = pt; + } + } + + return { maxPoint: maxPt, newPoints: newPoints }; + }, + + + /* + * Given a baseline, compute the convex hull of latLngs as an array + * of latLngs. + * + * @param {Array} latLngs + * @returns {Array} + */ + buildConvexHull: function (baseLine, latLngs) { + var convexHullBaseLines = [], + t = this.findMostDistantPointFromBaseLine(baseLine, latLngs); + + if (t.maxPoint) { // if there is still a point "outside" the base line + convexHullBaseLines = + convexHullBaseLines.concat( + this.buildConvexHull([baseLine[0], t.maxPoint], t.newPoints) + ); + convexHullBaseLines = + convexHullBaseLines.concat( + this.buildConvexHull([t.maxPoint, baseLine[1]], t.newPoints) + ); + return convexHullBaseLines; + } else { // if there is no more point "outside" the base line, the current base line is part of the convex hull + return [baseLine[0]]; + } + }, + + /* + * Given an array of latlngs, compute a convex hull as an array + * of latlngs + * + * @param {Array} latLngs + * @returns {Array} + */ + getConvexHull: function (latLngs) { + // find first baseline + var maxLat = false, minLat = false, + maxLng = false, minLng = false, + maxLatPt = null, minLatPt = null, + maxLngPt = null, minLngPt = null, + maxPt = null, minPt = null, + i; + + for (i = latLngs.length - 1; i >= 0; i--) { + var pt = latLngs[i]; + if (maxLat === false || pt.lat > maxLat) { + maxLatPt = pt; + maxLat = pt.lat; + } + if (minLat === false || pt.lat < minLat) { + minLatPt = pt; + minLat = pt.lat; + } + if (maxLng === false || pt.lng > maxLng) { + maxLngPt = pt; + maxLng = pt.lng; + } + if (minLng === false || pt.lng < minLng) { + minLngPt = pt; + minLng = pt.lng; + } + } + + if (minLat !== maxLat) { + minPt = minLatPt; + maxPt = maxLatPt; + } else { + minPt = minLngPt; + maxPt = maxLngPt; + } + + var ch = [].concat(this.buildConvexHull([minPt, maxPt], latLngs), + this.buildConvexHull([maxPt, minPt], latLngs)); + return ch; + } + }; +}()); + +L.MarkerCluster.include({ + getConvexHull: function () { + var childMarkers = this.getAllChildMarkers(), + points = [], + p, i; + + for (i = childMarkers.length - 1; i >= 0; i--) { + p = childMarkers[i].getLatLng(); + points.push(p); + } + + return L.QuickHull.getConvexHull(points); + } +}); + +//This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet +//Huge thanks to jawj for implementing it first to make my job easy :-) + +L.MarkerCluster.include({ + + _2PI: Math.PI * 2, + _circleFootSeparation: 25, //related to circumference of circle + _circleStartAngle: 0, + + _spiralFootSeparation: 28, //related to size of spiral (experiment!) + _spiralLengthStart: 11, + _spiralLengthFactor: 5, + + _circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards. + // 0 -> always spiral; Infinity -> always circle + + spiderfy: function () { + if (this._group._spiderfied === this || this._group._inZoomAnimation) { + return; + } + + var childMarkers = this.getAllChildMarkers(), + group = this._group, + map = group._map, + center = map.latLngToLayerPoint(this._latlng), + positions; + + this._group._unspiderfy(); + this._group._spiderfied = this; + + //TODO Maybe: childMarkers order by distance to center + + if (childMarkers.length >= this._circleSpiralSwitchover) { + positions = this._generatePointsSpiral(childMarkers.length, center); + } else { + center.y += 10; // Otherwise circles look wrong => hack for standard blue icon, renders differently for other icons. + positions = this._generatePointsCircle(childMarkers.length, center); + } + + this._animationSpiderfy(childMarkers, positions); + }, + + unspiderfy: function (zoomDetails) { + /// <param Name="zoomDetails">Argument from zoomanim if being called in a zoom animation or null otherwise</param> + if (this._group._inZoomAnimation) { + return; + } + this._animationUnspiderfy(zoomDetails); + + this._group._spiderfied = null; + }, + + _generatePointsCircle: function (count, centerPt) { + var circumference = this._group.options.spiderfyDistanceMultiplier * this._circleFootSeparation * (2 + count), + legLength = circumference / this._2PI, //radius from circumference + angleStep = this._2PI / count, + res = [], + i, angle; + + legLength = Math.max(legLength, 35); // Minimum distance to get outside the cluster icon. + + res.length = count; + + for (i = 0; i < count; i++) { // Clockwise, like spiral. + angle = this._circleStartAngle + i * angleStep; + res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round(); + } + + return res; + }, + + _generatePointsSpiral: function (count, centerPt) { + var spiderfyDistanceMultiplier = this._group.options.spiderfyDistanceMultiplier, + legLength = spiderfyDistanceMultiplier * this._spiralLengthStart, + separation = spiderfyDistanceMultiplier * this._spiralFootSeparation, + lengthFactor = spiderfyDistanceMultiplier * this._spiralLengthFactor * this._2PI, + angle = 0, + res = [], + i; + + res.length = count; + + // Higher index, closer position to cluster center. + for (i = count; i >= 0; i--) { + // Skip the first position, so that we are already farther from center and we avoid + // being under the default cluster icon (especially important for Circle Markers). + if (i < count) { + res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round(); + } + angle += separation / legLength + i * 0.0005; + legLength += lengthFactor / angle; + } + return res; + }, + + _noanimationUnspiderfy: function () { + var group = this._group, + map = group._map, + fg = group._featureGroup, + childMarkers = this.getAllChildMarkers(), + m, i; + + group._ignoreMove = true; + + this.setOpacity(1); + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + fg.removeLayer(m); + + if (m._preSpiderfyLatlng) { + m.setLatLng(m._preSpiderfyLatlng); + delete m._preSpiderfyLatlng; + } + if (m.setZIndexOffset) { + m.setZIndexOffset(0); + } + + if (m._spiderLeg) { + map.removeLayer(m._spiderLeg); + delete m._spiderLeg; + } + } + + group.fire('unspiderfied', { + cluster: this, + markers: childMarkers + }); + group._ignoreMove = false; + group._spiderfied = null; + } +}); + +//Non Animated versions of everything +L.MarkerClusterNonAnimated = L.MarkerCluster.extend({ + _animationSpiderfy: function (childMarkers, positions) { + var group = this._group, + map = group._map, + fg = group._featureGroup, + legOptions = this._group.options.spiderLegPolylineOptions, + i, m, leg, newPos; + + group._ignoreMove = true; + + // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition. + // The reverse order trick no longer improves performance on modern browsers. + for (i = 0; i < childMarkers.length; i++) { + newPos = map.layerPointToLatLng(positions[i]); + m = childMarkers[i]; + + // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it. + leg = new L.Polyline([this._latlng, newPos], legOptions); + map.addLayer(leg); + m._spiderLeg = leg; + + // Now add the marker. + m._preSpiderfyLatlng = m._latlng; + m.setLatLng(newPos); + if (m.setZIndexOffset) { + m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING + } + + fg.addLayer(m); + } + this.setOpacity(0.3); + + group._ignoreMove = false; + group.fire('spiderfied', { + cluster: this, + markers: childMarkers + }); + }, + + _animationUnspiderfy: function () { + this._noanimationUnspiderfy(); + } +}); + +//Animated versions here +L.MarkerCluster.include({ + + _animationSpiderfy: function (childMarkers, positions) { + var me = this, + group = this._group, + map = group._map, + fg = group._featureGroup, + thisLayerLatLng = this._latlng, + thisLayerPos = map.latLngToLayerPoint(thisLayerLatLng), + svg = L.Path.SVG, + legOptions = L.extend({}, this._group.options.spiderLegPolylineOptions), // Copy the options so that we can modify them for animation. + finalLegOpacity = legOptions.opacity, + i, m, leg, legPath, legLength, newPos; + + if (finalLegOpacity === undefined) { + finalLegOpacity = L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity; + } + + if (svg) { + // If the initial opacity of the spider leg is not 0 then it appears before the animation starts. + legOptions.opacity = 0; + + // Add the class for CSS transitions. + legOptions.className = (legOptions.className || '') + ' leaflet-cluster-spider-leg'; + } else { + // Make sure we have a defined opacity. + legOptions.opacity = finalLegOpacity; + } + + group._ignoreMove = true; + + // Add markers and spider legs to map, hidden at our center point. + // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition. + // The reverse order trick no longer improves performance on modern browsers. + for (i = 0; i < childMarkers.length; i++) { + m = childMarkers[i]; + + newPos = map.layerPointToLatLng(positions[i]); + + // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it. + leg = new L.Polyline([thisLayerLatLng, newPos], legOptions); + map.addLayer(leg); + m._spiderLeg = leg; + + // Explanations: https://jakearchibald.com/2013/animated-line-drawing-svg/ + // In our case the transition property is declared in the CSS file. + if (svg) { + legPath = leg._path; + legLength = legPath.getTotalLength() + 0.1; // Need a small extra length to avoid remaining dot in Firefox. + legPath.style.strokeDasharray = legLength; // Just 1 length is enough, it will be duplicated. + legPath.style.strokeDashoffset = legLength; + } + + // If it is a marker, add it now and we'll animate it out + if (m.setZIndexOffset) { + m.setZIndexOffset(1000000); // Make normal markers appear on top of EVERYTHING + } + if (m.clusterHide) { + m.clusterHide(); + } + + // Vectors just get immediately added + fg.addLayer(m); + + if (m._setPos) { + m._setPos(thisLayerPos); + } + } + + group._forceLayout(); + group._animationStart(); + + // Reveal markers and spider legs. + for (i = childMarkers.length - 1; i >= 0; i--) { + newPos = map.layerPointToLatLng(positions[i]); + m = childMarkers[i]; + + //Move marker to new position + m._preSpiderfyLatlng = m._latlng; + m.setLatLng(newPos); + + if (m.clusterShow) { + m.clusterShow(); + } + + // Animate leg (animation is actually delegated to CSS transition). + if (svg) { + leg = m._spiderLeg; + legPath = leg._path; + legPath.style.strokeDashoffset = 0; + //legPath.style.strokeOpacity = finalLegOpacity; + leg.setStyle({opacity: finalLegOpacity}); + } + } + this.setOpacity(0.3); + + group._ignoreMove = false; + + setTimeout(function () { + group._animationEnd(); + group.fire('spiderfied', { + cluster: me, + markers: childMarkers + }); + }, 200); + }, + + _animationUnspiderfy: function (zoomDetails) { + var me = this, + group = this._group, + map = group._map, + fg = group._featureGroup, + thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng), + childMarkers = this.getAllChildMarkers(), + svg = L.Path.SVG, + m, i, leg, legPath, legLength, nonAnimatable; + + group._ignoreMove = true; + group._animationStart(); + + //Make us visible and bring the child markers back in + this.setOpacity(1); + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + //Marker was added to us after we were spiderfied + if (!m._preSpiderfyLatlng) { + continue; + } + + //Close any popup on the marker first, otherwise setting the location of the marker will make the map scroll + m.closePopup(); + + //Fix up the location to the real one + m.setLatLng(m._preSpiderfyLatlng); + delete m._preSpiderfyLatlng; + + //Hack override the location to be our center + nonAnimatable = true; + if (m._setPos) { + m._setPos(thisLayerPos); + nonAnimatable = false; + } + if (m.clusterHide) { + m.clusterHide(); + nonAnimatable = false; + } + if (nonAnimatable) { + fg.removeLayer(m); + } + + // Animate the spider leg back in (animation is actually delegated to CSS transition). + if (svg) { + leg = m._spiderLeg; + legPath = leg._path; + legLength = legPath.getTotalLength() + 0.1; + legPath.style.strokeDashoffset = legLength; + leg.setStyle({opacity: 0}); + } + } + + group._ignoreMove = false; + + setTimeout(function () { + //If we have only <= one child left then that marker will be shown on the map so don't remove it! + var stillThereChildCount = 0; + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + if (m._spiderLeg) { + stillThereChildCount++; + } + } + + + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + if (!m._spiderLeg) { //Has already been unspiderfied + continue; + } + + if (m.clusterShow) { + m.clusterShow(); + } + if (m.setZIndexOffset) { + m.setZIndexOffset(0); + } + + if (stillThereChildCount > 1) { + fg.removeLayer(m); + } + + map.removeLayer(m._spiderLeg); + delete m._spiderLeg; + } + group._animationEnd(); + group.fire('unspiderfied', { + cluster: me, + markers: childMarkers + }); + }, 200); + } +}); + + +L.MarkerClusterGroup.include({ + //The MarkerCluster currently spiderfied (if any) + _spiderfied: null, + + unspiderfy: function () { + this._unspiderfy.apply(this, arguments); + }, + + _spiderfierOnAdd: function () { + this._map.on('click', this._unspiderfyWrapper, this); + + if (this._map.options.zoomAnimation) { + this._map.on('zoomstart', this._unspiderfyZoomStart, this); + } + //Browsers without zoomAnimation or a big zoom don't fire zoomstart + this._map.on('zoomend', this._noanimationUnspiderfy, this); + + if (!L.Browser.touch) { + this._map.getRenderer(this); + //Needs to happen in the pageload, not after, or animations don't work in webkit + // http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements + //Disable on touch browsers as the animation messes up on a touch zoom and isn't very noticable + } + }, + + _spiderfierOnRemove: function () { + this._map.off('click', this._unspiderfyWrapper, this); + this._map.off('zoomstart', this._unspiderfyZoomStart, this); + this._map.off('zoomanim', this._unspiderfyZoomAnim, this); + this._map.off('zoomend', this._noanimationUnspiderfy, this); + + //Ensure that markers are back where they should be + // Use no animation to avoid a sticky leaflet-cluster-anim class on mapPane + this._noanimationUnspiderfy(); + }, + + //On zoom start we add a zoomanim handler so that we are guaranteed to be last (after markers are animated) + //This means we can define the animation they do rather than Markers doing an animation to their actual location + _unspiderfyZoomStart: function () { + if (!this._map) { //May have been removed from the map by a zoomEnd handler + return; + } + + this._map.on('zoomanim', this._unspiderfyZoomAnim, this); + }, + + _unspiderfyZoomAnim: function (zoomDetails) { + //Wait until the first zoomanim after the user has finished touch-zooming before running the animation + if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) { + return; + } + + this._map.off('zoomanim', this._unspiderfyZoomAnim, this); + this._unspiderfy(zoomDetails); + }, + + _unspiderfyWrapper: function () { + /// <summary>_unspiderfy but passes no arguments</summary> + this._unspiderfy(); + }, + + _unspiderfy: function (zoomDetails) { + if (this._spiderfied) { + this._spiderfied.unspiderfy(zoomDetails); + } + }, + + _noanimationUnspiderfy: function () { + if (this._spiderfied) { + this._spiderfied._noanimationUnspiderfy(); + } + }, + + //If the given layer is currently being spiderfied then we unspiderfy it so it isn't on the map anymore etc + _unspiderfyLayer: function (layer) { + if (layer._spiderLeg) { + this._featureGroup.removeLayer(layer); + + if (layer.clusterShow) { + layer.clusterShow(); + } + //Position will be fixed up immediately in _animationUnspiderfy + if (layer.setZIndexOffset) { + layer.setZIndexOffset(0); + } + + this._map.removeLayer(layer._spiderLeg); + delete layer._spiderLeg; + } + } +}); + +/** + * Adds 1 public method to MCG and 1 to L.Marker to facilitate changing + * markers' icon options and refreshing their icon and their parent clusters + * accordingly (case where their iconCreateFunction uses data of childMarkers + * to make up the cluster icon). + */ + + +L.MarkerClusterGroup.include({ + /** + * Updates the icon of all clusters which are parents of the given marker(s). + * In singleMarkerMode, also updates the given marker(s) icon. + * @param layers L.MarkerClusterGroup|L.LayerGroup|Array(L.Marker)|Map(L.Marker)| + * L.MarkerCluster|L.Marker (optional) list of markers (or single marker) whose parent + * clusters need to be updated. If not provided, retrieves all child markers of this. + * @returns {L.MarkerClusterGroup} + */ + refreshClusters: function (layers) { + if (!layers) { + layers = this._topClusterLevel.getAllChildMarkers(); + } else if (layers instanceof L.MarkerClusterGroup) { + layers = layers._topClusterLevel.getAllChildMarkers(); + } else if (layers instanceof L.LayerGroup) { + layers = layers._layers; + } else if (layers instanceof L.MarkerCluster) { + layers = layers.getAllChildMarkers(); + } else if (layers instanceof L.Marker) { + layers = [layers]; + } // else: must be an Array(L.Marker)|Map(L.Marker) + this._flagParentsIconsNeedUpdate(layers); + this._refreshClustersIcons(); + + // In case of singleMarkerMode, also re-draw the markers. + if (this.options.singleMarkerMode) { + this._refreshSingleMarkerModeMarkers(layers); + } + + return this; + }, + + /** + * Simply flags all parent clusters of the given markers as having a "dirty" icon. + * @param layers Array(L.Marker)|Map(L.Marker) list of markers. + * @private + */ + _flagParentsIconsNeedUpdate: function (layers) { + var id, parent; + + // Assumes layers is an Array or an Object whose prototype is non-enumerable. + for (id in layers) { + // Flag parent clusters' icon as "dirty", all the way up. + // Dumb process that flags multiple times upper parents, but still + // much more efficient than trying to be smart and make short lists, + // at least in the case of a hierarchy following a power law: + // http://jsperf.com/flag-nodes-in-power-hierarchy/2 + parent = layers[id].__parent; + while (parent) { + parent._iconNeedsUpdate = true; + parent = parent.__parent; + } + } + }, + + /** + * Re-draws the icon of the supplied markers. + * To be used in singleMarkerMode only. + * @param layers Array(L.Marker)|Map(L.Marker) list of markers. + * @private + */ + _refreshSingleMarkerModeMarkers: function (layers) { + var id, layer; + + for (id in layers) { + layer = layers[id]; + + // Make sure we do not override markers that do not belong to THIS group. + if (this.hasLayer(layer)) { + // Need to re-create the icon first, then re-draw the marker. + layer.setIcon(this._overrideMarkerIcon(layer)); + } + } + } +}); + +L.Marker.include({ + /** + * Updates the given options in the marker's icon and refreshes the marker. + * @param options map object of icon options. + * @param directlyRefreshClusters boolean (optional) true to trigger + * MCG.refreshClustersOf() right away with this single marker. + * @returns {L.Marker} + */ + refreshIconOptions: function (options, directlyRefreshClusters) { + var icon = this.options.icon; + + L.setOptions(icon, options); + + this.setIcon(icon); + + // Shortcut to refresh the associated MCG clusters right away. + // To be used when refreshing a single marker. + // Otherwise, better use MCG.refreshClusters() once at the end with + // the list of modified markers. + if (directlyRefreshClusters && this.__parent) { + this.__parent._group.refreshClusters(this); + } + + return this; + } +}); + +exports.MarkerClusterGroup = MarkerClusterGroup; +exports.MarkerCluster = MarkerCluster; + +}))); diff --git a/themes/bootstrap3/js/vendor/leaflet/leaflet.markercluster.js b/themes/bootstrap3/js/vendor/leaflet/leaflet.markercluster.js new file mode 100644 index 0000000000000000000000000000000000000000..92f152511ef8d98de7cb7d396d470257846f45bf --- /dev/null +++ b/themes/bootstrap3/js/vendor/leaflet/leaflet.markercluster.js @@ -0,0 +1,2706 @@ +/* + * Leaflet.markercluster 1.3.0+master.a4cf31f, + * Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps. + * https://github.com/Leaflet/Leaflet.markercluster + * (c) 2012-2017, Dave Leaver, smartrak + * + * Copyright 2012 David Leaver + * + * 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. + * + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.Leaflet = global.Leaflet || {}, global.Leaflet.markercluster = global.Leaflet.markercluster || {}))); +}(this, (function (exports) { 'use strict'; + +/* + * L.MarkerClusterGroup extends L.FeatureGroup by clustering the markers contained within + */ + +var MarkerClusterGroup = L.MarkerClusterGroup = L.FeatureGroup.extend({ + + options: { + maxClusterRadius: 80, //A cluster will cover at most this many pixels from its center + iconCreateFunction: null, + clusterPane: L.Marker.prototype.options.pane, + + spiderfyOnMaxZoom: true, + showCoverageOnHover: true, + zoomToBoundsOnClick: true, + singleMarkerMode: false, + + disableClusteringAtZoom: null, + + // Setting this to false prevents the removal of any clusters outside of the viewpoint, which + // is the default behaviour for performance reasons. + removeOutsideVisibleBounds: true, + + // Set to false to disable all animations (zoom and spiderfy). + // If false, option animateAddingMarkers below has no effect. + // If L.DomUtil.TRANSITION is falsy, this option has no effect. + animate: true, + + //Whether to animate adding markers after adding the MarkerClusterGroup to the map + // If you are adding individual markers set to true, if adding bulk markers leave false for massive performance gains. + animateAddingMarkers: false, + + //Increase to increase the distance away that spiderfied markers appear from the center + spiderfyDistanceMultiplier: 1, + + // Make it possible to specify a polyline options on a spider leg + spiderLegPolylineOptions: { weight: 1.5, color: '#222', opacity: 0.5 }, + + // When bulk adding layers, adds markers in chunks. Means addLayers may not add all the layers in the call, others will be loaded during setTimeouts + chunkedLoading: false, + chunkInterval: 200, // process markers for a maximum of ~ n milliseconds (then trigger the chunkProgress callback) + chunkDelay: 50, // at the end of each interval, give n milliseconds back to system/browser + chunkProgress: null, // progress callback: function(processed, total, elapsed) (e.g. for a progress indicator) + + //Options to pass to the L.Polygon constructor + polygonOptions: {} + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + if (!this.options.iconCreateFunction) { + this.options.iconCreateFunction = this._defaultIconCreateFunction; + } + + this._featureGroup = L.featureGroup(); + this._featureGroup.addEventParent(this); + + this._nonPointGroup = L.featureGroup(); + this._nonPointGroup.addEventParent(this); + + this._inZoomAnimation = 0; + this._needsClustering = []; + this._needsRemoving = []; //Markers removed while we aren't on the map need to be kept track of + //The bounds of the currently shown area (from _getExpandedVisibleBounds) Updated on zoom/move + this._currentShownBounds = null; + + this._queue = []; + + this._childMarkerEventHandlers = { + 'dragstart': this._childMarkerDragStart, + 'move': this._childMarkerMoved, + 'dragend': this._childMarkerDragEnd, + }; + + // Hook the appropriate animation methods. + var animate = L.DomUtil.TRANSITION && this.options.animate; + L.extend(this, animate ? this._withAnimation : this._noAnimation); + // Remember which MarkerCluster class to instantiate (animated or not). + this._markerCluster = animate ? L.MarkerCluster : L.MarkerClusterNonAnimated; + }, + + addLayer: function (layer) { + + if (layer instanceof L.LayerGroup) { + return this.addLayers([layer]); + } + + //Don't cluster non point data + if (!layer.getLatLng) { + this._nonPointGroup.addLayer(layer); + this.fire('layeradd', { layer: layer }); + return this; + } + + if (!this._map) { + this._needsClustering.push(layer); + this.fire('layeradd', { layer: layer }); + return this; + } + + if (this.hasLayer(layer)) { + return this; + } + + + //If we have already clustered we'll need to add this one to a cluster + + if (this._unspiderfy) { + this._unspiderfy(); + } + + this._addLayer(layer, this._maxZoom); + this.fire('layeradd', { layer: layer }); + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + //Work out what is visible + var visibleLayer = layer, + currentZoom = this._zoom; + if (layer.__parent) { + while (visibleLayer.__parent._zoom >= currentZoom) { + visibleLayer = visibleLayer.__parent; + } + } + + if (this._currentShownBounds.contains(visibleLayer.getLatLng())) { + if (this.options.animateAddingMarkers) { + this._animationAddLayer(layer, visibleLayer); + } else { + this._animationAddLayerNonAnimated(layer, visibleLayer); + } + } + return this; + }, + + removeLayer: function (layer) { + + if (layer instanceof L.LayerGroup) { + return this.removeLayers([layer]); + } + + //Non point layers + if (!layer.getLatLng) { + this._nonPointGroup.removeLayer(layer); + this.fire('layerremove', { layer: layer }); + return this; + } + + if (!this._map) { + if (!this._arraySplice(this._needsClustering, layer) && this.hasLayer(layer)) { + this._needsRemoving.push({ layer: layer, latlng: layer._latlng }); + } + this.fire('layerremove', { layer: layer }); + return this; + } + + if (!layer.__parent) { + return this; + } + + if (this._unspiderfy) { + this._unspiderfy(); + this._unspiderfyLayer(layer); + } + + //Remove the marker from clusters + this._removeLayer(layer, true); + this.fire('layerremove', { layer: layer }); + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + layer.off(this._childMarkerEventHandlers, this); + + if (this._featureGroup.hasLayer(layer)) { + this._featureGroup.removeLayer(layer); + if (layer.clusterShow) { + layer.clusterShow(); + } + } + + return this; + }, + + //Takes an array of markers and adds them in bulk + addLayers: function (layersArray, skipLayerAddEvent) { + if (!L.Util.isArray(layersArray)) { + return this.addLayer(layersArray); + } + + var fg = this._featureGroup, + npg = this._nonPointGroup, + chunked = this.options.chunkedLoading, + chunkInterval = this.options.chunkInterval, + chunkProgress = this.options.chunkProgress, + l = layersArray.length, + offset = 0, + originalArray = true, + m; + + if (this._map) { + var started = (new Date()).getTime(); + var process = L.bind(function () { + var start = (new Date()).getTime(); + for (; offset < l; offset++) { + if (chunked && offset % 200 === 0) { + // every couple hundred markers, instrument the time elapsed since processing started: + var elapsed = (new Date()).getTime() - start; + if (elapsed > chunkInterval) { + break; // been working too hard, time to take a break :-) + } + } + + m = layersArray[offset]; + + // Group of layers, append children to layersArray and skip. + // Side effects: + // - Total increases, so chunkProgress ratio jumps backward. + // - Groups are not included in this group, only their non-group child layers (hasLayer). + // Changing array length while looping does not affect performance in current browsers: + // http://jsperf.com/for-loop-changing-length/6 + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + //Not point data, can't be clustered + if (!m.getLatLng) { + npg.addLayer(m); + if (!skipLayerAddEvent) { + this.fire('layeradd', { layer: m }); + } + continue; + } + + if (this.hasLayer(m)) { + continue; + } + + this._addLayer(m, this._maxZoom); + if (!skipLayerAddEvent) { + this.fire('layeradd', { layer: m }); + } + + //If we just made a cluster of size 2 then we need to remove the other marker from the map (if it is) or we never will + if (m.__parent) { + if (m.__parent.getChildCount() === 2) { + var markers = m.__parent.getAllChildMarkers(), + otherMarker = markers[0] === m ? markers[1] : markers[0]; + fg.removeLayer(otherMarker); + } + } + } + + if (chunkProgress) { + // report progress and time elapsed: + chunkProgress(offset, l, (new Date()).getTime() - started); + } + + // Completed processing all markers. + if (offset === l) { + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); + } else { + setTimeout(process, this.options.chunkDelay); + } + }, this); + + process(); + } else { + var needsClustering = this._needsClustering; + + for (; offset < l; offset++) { + m = layersArray[offset]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + //Not point data, can't be clustered + if (!m.getLatLng) { + npg.addLayer(m); + continue; + } + + if (this.hasLayer(m)) { + continue; + } + + needsClustering.push(m); + } + } + return this; + }, + + //Takes an array of markers and removes them in bulk + removeLayers: function (layersArray) { + var i, m, + l = layersArray.length, + fg = this._featureGroup, + npg = this._nonPointGroup, + originalArray = true; + + if (!this._map) { + for (i = 0; i < l; i++) { + m = layersArray[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + this._arraySplice(this._needsClustering, m); + npg.removeLayer(m); + if (this.hasLayer(m)) { + this._needsRemoving.push({ layer: m, latlng: m._latlng }); + } + this.fire('layerremove', { layer: m }); + } + return this; + } + + if (this._unspiderfy) { + this._unspiderfy(); + + // Work on a copy of the array, so that next loop is not affected. + var layersArray2 = layersArray.slice(), + l2 = l; + for (i = 0; i < l2; i++) { + m = layersArray2[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + this._extractNonGroupLayers(m, layersArray2); + l2 = layersArray2.length; + continue; + } + + this._unspiderfyLayer(m); + } + } + + for (i = 0; i < l; i++) { + m = layersArray[i]; + + // Group of layers, append children to layersArray and skip. + if (m instanceof L.LayerGroup) { + if (originalArray) { + layersArray = layersArray.slice(); + originalArray = false; + } + this._extractNonGroupLayers(m, layersArray); + l = layersArray.length; + continue; + } + + if (!m.__parent) { + npg.removeLayer(m); + this.fire('layerremove', { layer: m }); + continue; + } + + this._removeLayer(m, true, true); + this.fire('layerremove', { layer: m }); + + if (fg.hasLayer(m)) { + fg.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + + // Refresh bounds and weighted positions. + this._topClusterLevel._recalculateBounds(); + + this._refreshClustersIcons(); + + //Fix up the clusters and markers on the map + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); + + return this; + }, + + //Removes all layers from the MarkerClusterGroup + clearLayers: function () { + //Need our own special implementation as the LayerGroup one doesn't work for us + + //If we aren't on the map (yet), blow away the markers we know of + if (!this._map) { + this._needsClustering = []; + delete this._gridClusters; + delete this._gridUnclustered; + } + + if (this._noanimationUnspiderfy) { + this._noanimationUnspiderfy(); + } + + //Remove all the visible layers + this._featureGroup.clearLayers(); + this._nonPointGroup.clearLayers(); + + this.eachLayer(function (marker) { + marker.off(this._childMarkerEventHandlers, this); + delete marker.__parent; + }, this); + + if (this._map) { + //Reset _topClusterLevel and the DistanceGrids + this._generateInitialClusters(); + } + + return this; + }, + + //Override FeatureGroup.getBounds as it doesn't work + getBounds: function () { + var bounds = new L.LatLngBounds(); + + if (this._topClusterLevel) { + bounds.extend(this._topClusterLevel._bounds); + } + + for (var i = this._needsClustering.length - 1; i >= 0; i--) { + bounds.extend(this._needsClustering[i].getLatLng()); + } + + bounds.extend(this._nonPointGroup.getBounds()); + + return bounds; + }, + + //Overrides LayerGroup.eachLayer + eachLayer: function (method, context) { + var markers = this._needsClustering.slice(), + needsRemoving = this._needsRemoving, + thisNeedsRemoving, i, j; + + if (this._topClusterLevel) { + this._topClusterLevel.getAllChildMarkers(markers); + } + + for (i = markers.length - 1; i >= 0; i--) { + thisNeedsRemoving = true; + + for (j = needsRemoving.length - 1; j >= 0; j--) { + if (needsRemoving[j].layer === markers[i]) { + thisNeedsRemoving = false; + break; + } + } + + if (thisNeedsRemoving) { + method.call(context, markers[i]); + } + } + + this._nonPointGroup.eachLayer(method, context); + }, + + //Overrides LayerGroup.getLayers + getLayers: function () { + var layers = []; + this.eachLayer(function (l) { + layers.push(l); + }); + return layers; + }, + + //Overrides LayerGroup.getLayer, WARNING: Really bad performance + getLayer: function (id) { + var result = null; + + id = parseInt(id, 10); + + this.eachLayer(function (l) { + if (L.stamp(l) === id) { + result = l; + } + }); + + return result; + }, + + //Returns true if the given layer is in this MarkerClusterGroup + hasLayer: function (layer) { + if (!layer) { + return false; + } + + var i, anArray = this._needsClustering; + + for (i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === layer) { + return true; + } + } + + anArray = this._needsRemoving; + for (i = anArray.length - 1; i >= 0; i--) { + if (anArray[i].layer === layer) { + return false; + } + } + + return !!(layer.__parent && layer.__parent._group === this) || this._nonPointGroup.hasLayer(layer); + }, + + //Zoom down to show the given layer (spiderfying if necessary) then calls the callback + zoomToShowLayer: function (layer, callback) { + + if (typeof callback !== 'function') { + callback = function () {}; + } + + var showMarker = function () { + if ((layer._icon || layer.__parent._icon) && !this._inZoomAnimation) { + this._map.off('moveend', showMarker, this); + this.off('animationend', showMarker, this); + + if (layer._icon) { + callback(); + } else if (layer.__parent._icon) { + this.once('spiderfied', callback, this); + layer.__parent.spiderfy(); + } + } + }; + + if (layer._icon && this._map.getBounds().contains(layer.getLatLng())) { + //Layer is visible ond on screen, immediate return + callback(); + } else if (layer.__parent._zoom < Math.round(this._map._zoom)) { + //Layer should be visible at this zoom level. It must not be on screen so just pan over to it + this._map.on('moveend', showMarker, this); + this._map.panTo(layer.getLatLng()); + } else { + this._map.on('moveend', showMarker, this); + this.on('animationend', showMarker, this); + layer.__parent.zoomToBounds(); + } + }, + + //Overrides FeatureGroup.onAdd + onAdd: function (map) { + this._map = map; + var i, l, layer; + + if (!isFinite(this._map.getMaxZoom())) { + throw "Map has no maxZoom specified"; + } + + this._featureGroup.addTo(map); + this._nonPointGroup.addTo(map); + + if (!this._gridClusters) { + this._generateInitialClusters(); + } + + this._maxLat = map.options.crs.projection.MAX_LATITUDE; + + //Restore all the positions as they are in the MCG before removing them + for (i = 0, l = this._needsRemoving.length; i < l; i++) { + layer = this._needsRemoving[i]; + layer.newlatlng = layer.layer._latlng; + layer.layer._latlng = layer.latlng; + } + //Remove them, then restore their new positions + for (i = 0, l = this._needsRemoving.length; i < l; i++) { + layer = this._needsRemoving[i]; + this._removeLayer(layer.layer, true); + layer.layer._latlng = layer.newlatlng; + } + this._needsRemoving = []; + + //Remember the current zoom level and bounds + this._zoom = Math.round(this._map._zoom); + this._currentShownBounds = this._getExpandedVisibleBounds(); + + this._map.on('zoomend', this._zoomEnd, this); + this._map.on('moveend', this._moveEnd, this); + + if (this._spiderfierOnAdd) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely + this._spiderfierOnAdd(); + } + + this._bindEvents(); + + //Actually add our markers to the map: + l = this._needsClustering; + this._needsClustering = []; + this.addLayers(l, true); + }, + + //Overrides FeatureGroup.onRemove + onRemove: function (map) { + map.off('zoomend', this._zoomEnd, this); + map.off('moveend', this._moveEnd, this); + + this._unbindEvents(); + + //In case we are in a cluster animation + this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', ''); + + if (this._spiderfierOnRemove) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely + this._spiderfierOnRemove(); + } + + delete this._maxLat; + + //Clean up all the layers we added to the map + this._hideCoverage(); + this._featureGroup.remove(); + this._nonPointGroup.remove(); + + this._featureGroup.clearLayers(); + + this._map = null; + }, + + getVisibleParent: function (marker) { + var vMarker = marker; + while (vMarker && !vMarker._icon) { + vMarker = vMarker.__parent; + } + return vMarker || null; + }, + + //Remove the given object from the given array + _arraySplice: function (anArray, obj) { + for (var i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === obj) { + anArray.splice(i, 1); + return true; + } + } + }, + + /** + * Removes a marker from all _gridUnclustered zoom levels, starting at the supplied zoom. + * @param marker to be removed from _gridUnclustered. + * @param z integer bottom start zoom level (included) + * @private + */ + _removeFromGridUnclustered: function (marker, z) { + var map = this._map, + gridUnclustered = this._gridUnclustered, + minZoom = Math.floor(this._map.getMinZoom()); + + for (; z >= minZoom; z--) { + if (!gridUnclustered[z].removeObject(marker, map.project(marker.getLatLng(), z))) { + break; + } + } + }, + + _childMarkerDragStart: function (e) { + e.target.__dragStart = e.target._latlng; + }, + + _childMarkerMoved: function (e) { + if (!this._ignoreMove && !e.target.__dragStart) { + var isPopupOpen = e.target._popup && e.target._popup.isOpen(); + + this._moveChild(e.target, e.oldLatLng, e.latlng); + + if (isPopupOpen) { + e.target.openPopup(); + } + } + }, + + _moveChild: function (layer, from, to) { + layer._latlng = from; + this.removeLayer(layer); + + layer._latlng = to; + this.addLayer(layer); + }, + + _childMarkerDragEnd: function (e) { + if (e.target.__dragStart) { + this._moveChild(e.target, e.target.__dragStart, e.target._latlng); + } + delete e.target.__dragStart; + }, + + + //Internal function for removing a marker from everything. + //dontUpdateMap: set to true if you will handle updating the map manually (for bulk functions) + _removeLayer: function (marker, removeFromDistanceGrid, dontUpdateMap) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + fg = this._featureGroup, + map = this._map, + minZoom = Math.floor(this._map.getMinZoom()); + + //Remove the marker from distance clusters it might be in + if (removeFromDistanceGrid) { + this._removeFromGridUnclustered(marker, this._maxZoom); + } + + //Work our way up the clusters removing them as we go if required + var cluster = marker.__parent, + markers = cluster._markers, + otherMarker; + + //Remove the marker from the immediate parents marker list + this._arraySplice(markers, marker); + + while (cluster) { + cluster._childCount--; + cluster._boundsNeedUpdate = true; + + if (cluster._zoom < minZoom) { + //Top level, do nothing + break; + } else if (removeFromDistanceGrid && cluster._childCount <= 1) { //Cluster no longer required + //We need to push the other marker up to the parent + otherMarker = cluster._markers[0] === marker ? cluster._markers[1] : cluster._markers[0]; + + //Update distance grid + gridClusters[cluster._zoom].removeObject(cluster, map.project(cluster._cLatLng, cluster._zoom)); + gridUnclustered[cluster._zoom].addObject(otherMarker, map.project(otherMarker.getLatLng(), cluster._zoom)); + + //Move otherMarker up to parent + this._arraySplice(cluster.__parent._childClusters, cluster); + cluster.__parent._markers.push(otherMarker); + otherMarker.__parent = cluster.__parent; + + if (cluster._icon) { + //Cluster is currently on the map, need to put the marker on the map instead + fg.removeLayer(cluster); + if (!dontUpdateMap) { + fg.addLayer(otherMarker); + } + } + } else { + cluster._iconNeedsUpdate = true; + } + + cluster = cluster.__parent; + } + + delete marker.__parent; + }, + + _isOrIsParent: function (el, oel) { + while (oel) { + if (el === oel) { + return true; + } + oel = oel.parentNode; + } + return false; + }, + + //Override L.Evented.fire + fire: function (type, data, propagate) { + if (data && data.layer instanceof L.MarkerCluster) { + //Prevent multiple clustermouseover/off events if the icon is made up of stacked divs (Doesn't work in ie <= 8, no relatedTarget) + if (data.originalEvent && this._isOrIsParent(data.layer._icon, data.originalEvent.relatedTarget)) { + return; + } + type = 'cluster' + type; + } + + L.FeatureGroup.prototype.fire.call(this, type, data, propagate); + }, + + //Override L.Evented.listens + listens: function (type, propagate) { + return L.FeatureGroup.prototype.listens.call(this, type, propagate) || L.FeatureGroup.prototype.listens.call(this, 'cluster' + type, propagate); + }, + + //Default functionality + _defaultIconCreateFunction: function (cluster) { + var childCount = cluster.getChildCount(); + + var c = ' marker-cluster-'; + if (childCount < 10) { + c += 'small'; + } else if (childCount < 100) { + c += 'medium'; + } else { + c += 'large'; + } + + return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) }); + }, + + _bindEvents: function () { + var map = this._map, + spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom, + showCoverageOnHover = this.options.showCoverageOnHover, + zoomToBoundsOnClick = this.options.zoomToBoundsOnClick; + + //Zoom on cluster click or spiderfy if we are at the lowest level + if (spiderfyOnMaxZoom || zoomToBoundsOnClick) { + this.on('clusterclick', this._zoomOrSpiderfy, this); + } + + //Show convex hull (boundary) polygon on mouse over + if (showCoverageOnHover) { + this.on('clustermouseover', this._showCoverage, this); + this.on('clustermouseout', this._hideCoverage, this); + map.on('zoomend', this._hideCoverage, this); + } + }, + + _zoomOrSpiderfy: function (e) { + var cluster = e.layer, + bottomCluster = cluster; + + while (bottomCluster._childClusters.length === 1) { + bottomCluster = bottomCluster._childClusters[0]; + } + + if (bottomCluster._zoom === this._maxZoom && + bottomCluster._childCount === cluster._childCount && + this.options.spiderfyOnMaxZoom) { + + // All child markers are contained in a single cluster from this._maxZoom to this cluster. + cluster.spiderfy(); + } else if (this.options.zoomToBoundsOnClick) { + cluster.zoomToBounds(); + } + + // Focus the map again for keyboard users. + if (e.originalEvent && e.originalEvent.keyCode === 13) { + this._map._container.focus(); + } + }, + + _showCoverage: function (e) { + var map = this._map; + if (this._inZoomAnimation) { + return; + } + if (this._shownPolygon) { + map.removeLayer(this._shownPolygon); + } + if (e.layer.getChildCount() > 2 && e.layer !== this._spiderfied) { + this._shownPolygon = new L.Polygon(e.layer.getConvexHull(), this.options.polygonOptions); + map.addLayer(this._shownPolygon); + } + }, + + _hideCoverage: function () { + if (this._shownPolygon) { + this._map.removeLayer(this._shownPolygon); + this._shownPolygon = null; + } + }, + + _unbindEvents: function () { + var spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom, + showCoverageOnHover = this.options.showCoverageOnHover, + zoomToBoundsOnClick = this.options.zoomToBoundsOnClick, + map = this._map; + + if (spiderfyOnMaxZoom || zoomToBoundsOnClick) { + this.off('clusterclick', this._zoomOrSpiderfy, this); + } + if (showCoverageOnHover) { + this.off('clustermouseover', this._showCoverage, this); + this.off('clustermouseout', this._hideCoverage, this); + map.off('zoomend', this._hideCoverage, this); + } + }, + + _zoomEnd: function () { + if (!this._map) { //May have been removed from the map by a zoomEnd handler + return; + } + this._mergeSplitClusters(); + + this._zoom = Math.round(this._map._zoom); + this._currentShownBounds = this._getExpandedVisibleBounds(); + }, + + _moveEnd: function () { + if (this._inZoomAnimation) { + return; + } + + var newBounds = this._getExpandedVisibleBounds(); + + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), this._zoom, newBounds); + this._topClusterLevel._recursivelyAddChildrenToMap(null, Math.round(this._map._zoom), newBounds); + + this._currentShownBounds = newBounds; + return; + }, + + _generateInitialClusters: function () { + var maxZoom = Math.ceil(this._map.getMaxZoom()), + minZoom = Math.floor(this._map.getMinZoom()), + radius = this.options.maxClusterRadius, + radiusFn = radius; + + //If we just set maxClusterRadius to a single number, we need to create + //a simple function to return that number. Otherwise, we just have to + //use the function we've passed in. + if (typeof radius !== "function") { + radiusFn = function () { return radius; }; + } + + if (this.options.disableClusteringAtZoom !== null) { + maxZoom = this.options.disableClusteringAtZoom - 1; + } + this._maxZoom = maxZoom; + this._gridClusters = {}; + this._gridUnclustered = {}; + + //Set up DistanceGrids for each zoom + for (var zoom = maxZoom; zoom >= minZoom; zoom--) { + this._gridClusters[zoom] = new L.DistanceGrid(radiusFn(zoom)); + this._gridUnclustered[zoom] = new L.DistanceGrid(radiusFn(zoom)); + } + + // Instantiate the appropriate L.MarkerCluster class (animated or not). + this._topClusterLevel = new this._markerCluster(this, minZoom - 1); + }, + + //Zoom: Zoom to start adding at (Pass this._maxZoom to start at the bottom) + _addLayer: function (layer, zoom) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + minZoom = Math.floor(this._map.getMinZoom()), + markerPoint, z; + + if (this.options.singleMarkerMode) { + this._overrideMarkerIcon(layer); + } + + layer.on(this._childMarkerEventHandlers, this); + + //Find the lowest zoom level to slot this one in + for (; zoom >= minZoom; zoom--) { + markerPoint = this._map.project(layer.getLatLng(), zoom); // calculate pixel position + + //Try find a cluster close by + var closest = gridClusters[zoom].getNearObject(markerPoint); + if (closest) { + closest._addChild(layer); + layer.__parent = closest; + return; + } + + //Try find a marker close by to form a new cluster with + closest = gridUnclustered[zoom].getNearObject(markerPoint); + if (closest) { + var parent = closest.__parent; + if (parent) { + this._removeLayer(closest, false); + } + + //Create new cluster with these 2 in it + + var newCluster = new this._markerCluster(this, zoom, closest, layer); + gridClusters[zoom].addObject(newCluster, this._map.project(newCluster._cLatLng, zoom)); + closest.__parent = newCluster; + layer.__parent = newCluster; + + //First create any new intermediate parent clusters that don't exist + var lastParent = newCluster; + for (z = zoom - 1; z > parent._zoom; z--) { + lastParent = new this._markerCluster(this, z, lastParent); + gridClusters[z].addObject(lastParent, this._map.project(closest.getLatLng(), z)); + } + parent._addChild(lastParent); + + //Remove closest from this zoom level and any above that it is in, replace with newCluster + this._removeFromGridUnclustered(closest, zoom); + + return; + } + + //Didn't manage to cluster in at this zoom, record us as a marker here and continue upwards + gridUnclustered[zoom].addObject(layer, markerPoint); + } + + //Didn't get in anything, add us to the top + this._topClusterLevel._addChild(layer); + layer.__parent = this._topClusterLevel; + return; + }, + + /** + * Refreshes the icon of all "dirty" visible clusters. + * Non-visible "dirty" clusters will be updated when they are added to the map. + * @private + */ + _refreshClustersIcons: function () { + this._featureGroup.eachLayer(function (c) { + if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) { + c._updateIcon(); + } + }); + }, + + //Enqueue code to fire after the marker expand/contract has happened + _enqueue: function (fn) { + this._queue.push(fn); + if (!this._queueTimeout) { + this._queueTimeout = setTimeout(L.bind(this._processQueue, this), 300); + } + }, + _processQueue: function () { + for (var i = 0; i < this._queue.length; i++) { + this._queue[i].call(this); + } + this._queue.length = 0; + clearTimeout(this._queueTimeout); + this._queueTimeout = null; + }, + + //Merge and split any existing clusters that are too big or small + _mergeSplitClusters: function () { + var mapZoom = Math.round(this._map._zoom); + + //In case we are starting to split before the animation finished + this._processQueue(); + + if (this._zoom < mapZoom && this._currentShownBounds.intersects(this._getExpandedVisibleBounds())) { //Zoom in, split + this._animationStart(); + //Remove clusters now off screen + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), this._zoom, this._getExpandedVisibleBounds()); + + this._animationZoomIn(this._zoom, mapZoom); + + } else if (this._zoom > mapZoom) { //Zoom out, merge + this._animationStart(); + + this._animationZoomOut(this._zoom, mapZoom); + } else { + this._moveEnd(); + } + }, + + //Gets the maps visible bounds expanded in each direction by the size of the screen (so the user cannot see an area we do not cover in one pan) + _getExpandedVisibleBounds: function () { + if (!this.options.removeOutsideVisibleBounds) { + return this._mapBoundsInfinite; + } else if (L.Browser.mobile) { + return this._checkBoundsMaxLat(this._map.getBounds()); + } + + return this._checkBoundsMaxLat(this._map.getBounds().pad(1)); // Padding expands the bounds by its own dimensions but scaled with the given factor. + }, + + /** + * Expands the latitude to Infinity (or -Infinity) if the input bounds reach the map projection maximum defined latitude + * (in the case of Web/Spherical Mercator, it is 85.0511287798 / see https://en.wikipedia.org/wiki/Web_Mercator#Formulas). + * Otherwise, the removeOutsideVisibleBounds option will remove markers beyond that limit, whereas the same markers without + * this option (or outside MCG) will have their position floored (ceiled) by the projection and rendered at that limit, + * making the user think that MCG "eats" them and never displays them again. + * @param bounds L.LatLngBounds + * @returns {L.LatLngBounds} + * @private + */ + _checkBoundsMaxLat: function (bounds) { + var maxLat = this._maxLat; + + if (maxLat !== undefined) { + if (bounds.getNorth() >= maxLat) { + bounds._northEast.lat = Infinity; + } + if (bounds.getSouth() <= -maxLat) { + bounds._southWest.lat = -Infinity; + } + } + + return bounds; + }, + + //Shared animation code + _animationAddLayerNonAnimated: function (layer, newCluster) { + if (newCluster === layer) { + this._featureGroup.addLayer(layer); + } else if (newCluster._childCount === 2) { + newCluster._addToMap(); + + var markers = newCluster.getAllChildMarkers(); + this._featureGroup.removeLayer(markers[0]); + this._featureGroup.removeLayer(markers[1]); + } else { + newCluster._updateIcon(); + } + }, + + /** + * Extracts individual (i.e. non-group) layers from a Layer Group. + * @param group to extract layers from. + * @param output {Array} in which to store the extracted layers. + * @returns {*|Array} + * @private + */ + _extractNonGroupLayers: function (group, output) { + var layers = group.getLayers(), + i = 0, + layer; + + output = output || []; + + for (; i < layers.length; i++) { + layer = layers[i]; + + if (layer instanceof L.LayerGroup) { + this._extractNonGroupLayers(layer, output); + continue; + } + + output.push(layer); + } + + return output; + }, + + /** + * Implements the singleMarkerMode option. + * @param layer Marker to re-style using the Clusters iconCreateFunction. + * @returns {L.Icon} The newly created icon. + * @private + */ + _overrideMarkerIcon: function (layer) { + var icon = layer.options.icon = this.options.iconCreateFunction({ + getChildCount: function () { + return 1; + }, + getAllChildMarkers: function () { + return [layer]; + } + }); + + return icon; + } +}); + +// Constant bounds used in case option "removeOutsideVisibleBounds" is set to false. +L.MarkerClusterGroup.include({ + _mapBoundsInfinite: new L.LatLngBounds(new L.LatLng(-Infinity, -Infinity), new L.LatLng(Infinity, Infinity)) +}); + +L.MarkerClusterGroup.include({ + _noAnimation: { + //Non Animated versions of everything + _animationStart: function () { + //Do nothing... + }, + _animationZoomIn: function (previousZoomLevel, newZoomLevel) { + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + + //We didn't actually animate, but we use this event to mean "clustering animations have finished" + this.fire('animationend'); + }, + _animationZoomOut: function (previousZoomLevel, newZoomLevel) { + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + + //We didn't actually animate, but we use this event to mean "clustering animations have finished" + this.fire('animationend'); + }, + _animationAddLayer: function (layer, newCluster) { + this._animationAddLayerNonAnimated(layer, newCluster); + } + }, + + _withAnimation: { + //Animated versions here + _animationStart: function () { + this._map._mapPane.className += ' leaflet-cluster-anim'; + this._inZoomAnimation++; + }, + + _animationZoomIn: function (previousZoomLevel, newZoomLevel) { + var bounds = this._getExpandedVisibleBounds(), + fg = this._featureGroup, + minZoom = Math.floor(this._map.getMinZoom()), + i; + + this._ignoreMove = true; + + //Add all children of current clusters to map and remove those clusters from map + this._topClusterLevel._recursively(bounds, previousZoomLevel, minZoom, function (c) { + var startPos = c._latlng, + markers = c._markers, + m; + + if (!bounds.contains(startPos)) { + startPos = null; + } + + if (c._isSingleParent() && previousZoomLevel + 1 === newZoomLevel) { //Immediately add the new child and remove us + fg.removeLayer(c); + c._recursivelyAddChildrenToMap(null, newZoomLevel, bounds); + } else { + //Fade out old cluster + c.clusterHide(); + c._recursivelyAddChildrenToMap(startPos, newZoomLevel, bounds); + } + + //Remove all markers that aren't visible any more + //TODO: Do we actually need to do this on the higher levels too? + for (i = markers.length - 1; i >= 0; i--) { + m = markers[i]; + if (!bounds.contains(m._latlng)) { + fg.removeLayer(m); + } + } + + }); + + this._forceLayout(); + + //Update opacities + this._topClusterLevel._recursivelyBecomeVisible(bounds, newZoomLevel); + //TODO Maybe? Update markers in _recursivelyBecomeVisible + fg.eachLayer(function (n) { + if (!(n instanceof L.MarkerCluster) && n._icon) { + n.clusterShow(); + } + }); + + //update the positions of the just added clusters/markers + this._topClusterLevel._recursively(bounds, previousZoomLevel, newZoomLevel, function (c) { + c._recursivelyRestoreChildPositions(newZoomLevel); + }); + + this._ignoreMove = false; + + //Remove the old clusters and close the zoom animation + this._enqueue(function () { + //update the positions of the just added clusters/markers + this._topClusterLevel._recursively(bounds, previousZoomLevel, minZoom, function (c) { + fg.removeLayer(c); + c.clusterShow(); + }); + + this._animationEnd(); + }); + }, + + _animationZoomOut: function (previousZoomLevel, newZoomLevel) { + this._animationZoomOutSingle(this._topClusterLevel, previousZoomLevel - 1, newZoomLevel); + + //Need to add markers for those that weren't on the map before but are now + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); + //Remove markers that were on the map before but won't be now + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, Math.floor(this._map.getMinZoom()), previousZoomLevel, this._getExpandedVisibleBounds()); + }, + + _animationAddLayer: function (layer, newCluster) { + var me = this, + fg = this._featureGroup; + + fg.addLayer(layer); + if (newCluster !== layer) { + if (newCluster._childCount > 2) { //Was already a cluster + + newCluster._updateIcon(); + this._forceLayout(); + this._animationStart(); + + layer._setPos(this._map.latLngToLayerPoint(newCluster.getLatLng())); + layer.clusterHide(); + + this._enqueue(function () { + fg.removeLayer(layer); + layer.clusterShow(); + + me._animationEnd(); + }); + + } else { //Just became a cluster + this._forceLayout(); + + me._animationStart(); + me._animationZoomOutSingle(newCluster, this._map.getMaxZoom(), this._zoom); + } + } + } + }, + + // Private methods for animated versions. + _animationZoomOutSingle: function (cluster, previousZoomLevel, newZoomLevel) { + var bounds = this._getExpandedVisibleBounds(), + minZoom = Math.floor(this._map.getMinZoom()); + + //Animate all of the markers in the clusters to move to their cluster center point + cluster._recursivelyAnimateChildrenInAndAddSelfToMap(bounds, minZoom, previousZoomLevel + 1, newZoomLevel); + + var me = this; + + //Update the opacity (If we immediately set it they won't animate) + this._forceLayout(); + cluster._recursivelyBecomeVisible(bounds, newZoomLevel); + + //TODO: Maybe use the transition timing stuff to make this more reliable + //When the animations are done, tidy up + this._enqueue(function () { + + //This cluster stopped being a cluster before the timeout fired + if (cluster._childCount === 1) { + var m = cluster._markers[0]; + //If we were in a cluster animation at the time then the opacity and position of our child could be wrong now, so fix it + this._ignoreMove = true; + m.setLatLng(m.getLatLng()); + this._ignoreMove = false; + if (m.clusterShow) { + m.clusterShow(); + } + } else { + cluster._recursively(bounds, newZoomLevel, minZoom, function (c) { + c._recursivelyRemoveChildrenFromMap(bounds, minZoom, previousZoomLevel + 1); + }); + } + me._animationEnd(); + }); + }, + + _animationEnd: function () { + if (this._map) { + this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', ''); + } + this._inZoomAnimation--; + this.fire('animationend'); + }, + + //Force a browser layout of stuff in the map + // Should apply the current opacity and location to all elements so we can update them again for an animation + _forceLayout: function () { + //In my testing this works, infact offsetWidth of any element seems to work. + //Could loop all this._layers and do this for each _icon if it stops working + + L.Util.falseFn(document.body.offsetWidth); + } +}); + +L.markerClusterGroup = function (options) { + return new L.MarkerClusterGroup(options); +}; + +var MarkerCluster = L.MarkerCluster = L.Marker.extend({ + options: L.Icon.prototype.options, + + initialize: function (group, zoom, a, b) { + + L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0), + { icon: this, pane: group.options.clusterPane }); + + this._group = group; + this._zoom = zoom; + + this._markers = []; + this._childClusters = []; + this._childCount = 0; + this._iconNeedsUpdate = true; + this._boundsNeedUpdate = true; + + this._bounds = new L.LatLngBounds(); + + if (a) { + this._addChild(a); + } + if (b) { + this._addChild(b); + } + }, + + //Recursively retrieve all child markers of this cluster + getAllChildMarkers: function (storageArray) { + storageArray = storageArray || []; + + for (var i = this._childClusters.length - 1; i >= 0; i--) { + this._childClusters[i].getAllChildMarkers(storageArray); + } + + for (var j = this._markers.length - 1; j >= 0; j--) { + storageArray.push(this._markers[j]); + } + + return storageArray; + }, + + //Returns the count of how many child markers we have + getChildCount: function () { + return this._childCount; + }, + + //Zoom to the minimum of showing all of the child markers, or the extents of this cluster + zoomToBounds: function (fitBoundsOptions) { + var childClusters = this._childClusters.slice(), + map = this._group._map, + boundsZoom = map.getBoundsZoom(this._bounds), + zoom = this._zoom + 1, + mapZoom = map.getZoom(), + i; + + //calculate how far we need to zoom down to see all of the markers + while (childClusters.length > 0 && boundsZoom > zoom) { + zoom++; + var newClusters = []; + for (i = 0; i < childClusters.length; i++) { + newClusters = newClusters.concat(childClusters[i]._childClusters); + } + childClusters = newClusters; + } + + if (boundsZoom > zoom) { + this._group._map.setView(this._latlng, zoom); + } else if (boundsZoom <= mapZoom) { //If fitBounds wouldn't zoom us down, zoom us down instead + this._group._map.setView(this._latlng, mapZoom + 1); + } else { + this._group._map.fitBounds(this._bounds, fitBoundsOptions); + } + }, + + getBounds: function () { + var bounds = new L.LatLngBounds(); + bounds.extend(this._bounds); + return bounds; + }, + + _updateIcon: function () { + this._iconNeedsUpdate = true; + if (this._icon) { + this.setIcon(this); + } + }, + + //Cludge for Icon, we pretend to be an icon for performance + createIcon: function () { + if (this._iconNeedsUpdate) { + this._iconObj = this._group.options.iconCreateFunction(this); + this._iconNeedsUpdate = false; + } + return this._iconObj.createIcon(); + }, + createShadow: function () { + return this._iconObj.createShadow(); + }, + + + _addChild: function (new1, isNotificationFromChild) { + + this._iconNeedsUpdate = true; + + this._boundsNeedUpdate = true; + this._setClusterCenter(new1); + + if (new1 instanceof L.MarkerCluster) { + if (!isNotificationFromChild) { + this._childClusters.push(new1); + new1.__parent = this; + } + this._childCount += new1._childCount; + } else { + if (!isNotificationFromChild) { + this._markers.push(new1); + } + this._childCount++; + } + + if (this.__parent) { + this.__parent._addChild(new1, true); + } + }, + + /** + * Makes sure the cluster center is set. If not, uses the child center if it is a cluster, or the marker position. + * @param child L.MarkerCluster|L.Marker that will be used as cluster center if not defined yet. + * @private + */ + _setClusterCenter: function (child) { + if (!this._cLatLng) { + // when clustering, take position of the first point as the cluster center + this._cLatLng = child._cLatLng || child._latlng; + } + }, + + /** + * Assigns impossible bounding values so that the next extend entirely determines the new bounds. + * This method avoids having to trash the previous L.LatLngBounds object and to create a new one, which is much slower for this class. + * As long as the bounds are not extended, most other methods would probably fail, as they would with bounds initialized but not extended. + * @private + */ + _resetBounds: function () { + var bounds = this._bounds; + + if (bounds._southWest) { + bounds._southWest.lat = Infinity; + bounds._southWest.lng = Infinity; + } + if (bounds._northEast) { + bounds._northEast.lat = -Infinity; + bounds._northEast.lng = -Infinity; + } + }, + + _recalculateBounds: function () { + var markers = this._markers, + childClusters = this._childClusters, + latSum = 0, + lngSum = 0, + totalCount = this._childCount, + i, child, childLatLng, childCount; + + // Case where all markers are removed from the map and we are left with just an empty _topClusterLevel. + if (totalCount === 0) { + return; + } + + // Reset rather than creating a new object, for performance. + this._resetBounds(); + + // Child markers. + for (i = 0; i < markers.length; i++) { + childLatLng = markers[i]._latlng; + + this._bounds.extend(childLatLng); + + latSum += childLatLng.lat; + lngSum += childLatLng.lng; + } + + // Child clusters. + for (i = 0; i < childClusters.length; i++) { + child = childClusters[i]; + + // Re-compute child bounds and weighted position first if necessary. + if (child._boundsNeedUpdate) { + child._recalculateBounds(); + } + + this._bounds.extend(child._bounds); + + childLatLng = child._wLatLng; + childCount = child._childCount; + + latSum += childLatLng.lat * childCount; + lngSum += childLatLng.lng * childCount; + } + + this._latlng = this._wLatLng = new L.LatLng(latSum / totalCount, lngSum / totalCount); + + // Reset dirty flag. + this._boundsNeedUpdate = false; + }, + + //Set our markers position as given and add it to the map + _addToMap: function (startPos) { + if (startPos) { + this._backupLatlng = this._latlng; + this.setLatLng(startPos); + } + this._group._featureGroup.addLayer(this); + }, + + _recursivelyAnimateChildrenIn: function (bounds, center, maxZoom) { + this._recursively(bounds, this._group._map.getMinZoom(), maxZoom - 1, + function (c) { + var markers = c._markers, + i, m; + for (i = markers.length - 1; i >= 0; i--) { + m = markers[i]; + + //Only do it if the icon is still on the map + if (m._icon) { + m._setPos(center); + m.clusterHide(); + } + } + }, + function (c) { + var childClusters = c._childClusters, + j, cm; + for (j = childClusters.length - 1; j >= 0; j--) { + cm = childClusters[j]; + if (cm._icon) { + cm._setPos(center); + cm.clusterHide(); + } + } + } + ); + }, + + _recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, mapMinZoom, previousZoomLevel, newZoomLevel) { + this._recursively(bounds, newZoomLevel, mapMinZoom, + function (c) { + c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), previousZoomLevel); + + //TODO: depthToAnimateIn affects _isSingleParent, if there is a multizoom we may/may not be. + //As a hack we only do a animation free zoom on a single level zoom, if someone does multiple levels then we always animate + if (c._isSingleParent() && previousZoomLevel - 1 === newZoomLevel) { + c.clusterShow(); + c._recursivelyRemoveChildrenFromMap(bounds, mapMinZoom, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds + } else { + c.clusterHide(); + } + + c._addToMap(); + } + ); + }, + + _recursivelyBecomeVisible: function (bounds, zoomLevel) { + this._recursively(bounds, this._group._map.getMinZoom(), zoomLevel, null, function (c) { + c.clusterShow(); + }); + }, + + _recursivelyAddChildrenToMap: function (startPos, zoomLevel, bounds) { + this._recursively(bounds, this._group._map.getMinZoom() - 1, zoomLevel, + function (c) { + if (zoomLevel === c._zoom) { + return; + } + + //Add our child markers at startPos (so they can be animated out) + for (var i = c._markers.length - 1; i >= 0; i--) { + var nm = c._markers[i]; + + if (!bounds.contains(nm._latlng)) { + continue; + } + + if (startPos) { + nm._backupLatlng = nm.getLatLng(); + + nm.setLatLng(startPos); + if (nm.clusterHide) { + nm.clusterHide(); + } + } + + c._group._featureGroup.addLayer(nm); + } + }, + function (c) { + c._addToMap(startPos); + } + ); + }, + + _recursivelyRestoreChildPositions: function (zoomLevel) { + //Fix positions of child markers + for (var i = this._markers.length - 1; i >= 0; i--) { + var nm = this._markers[i]; + if (nm._backupLatlng) { + nm.setLatLng(nm._backupLatlng); + delete nm._backupLatlng; + } + } + + if (zoomLevel - 1 === this._zoom) { + //Reposition child clusters + for (var j = this._childClusters.length - 1; j >= 0; j--) { + this._childClusters[j]._restorePosition(); + } + } else { + for (var k = this._childClusters.length - 1; k >= 0; k--) { + this._childClusters[k]._recursivelyRestoreChildPositions(zoomLevel); + } + } + }, + + _restorePosition: function () { + if (this._backupLatlng) { + this.setLatLng(this._backupLatlng); + delete this._backupLatlng; + } + }, + + //exceptBounds: If set, don't remove any markers/clusters in it + _recursivelyRemoveChildrenFromMap: function (previousBounds, mapMinZoom, zoomLevel, exceptBounds) { + var m, i; + this._recursively(previousBounds, mapMinZoom - 1, zoomLevel - 1, + function (c) { + //Remove markers at every level + for (i = c._markers.length - 1; i >= 0; i--) { + m = c._markers[i]; + if (!exceptBounds || !exceptBounds.contains(m._latlng)) { + c._group._featureGroup.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + }, + function (c) { + //Remove child clusters at just the bottom level + for (i = c._childClusters.length - 1; i >= 0; i--) { + m = c._childClusters[i]; + if (!exceptBounds || !exceptBounds.contains(m._latlng)) { + c._group._featureGroup.removeLayer(m); + if (m.clusterShow) { + m.clusterShow(); + } + } + } + } + ); + }, + + //Run the given functions recursively to this and child clusters + // boundsToApplyTo: a L.LatLngBounds representing the bounds of what clusters to recurse in to + // zoomLevelToStart: zoom level to start running functions (inclusive) + // zoomLevelToStop: zoom level to stop running functions (inclusive) + // runAtEveryLevel: function that takes an L.MarkerCluster as an argument that should be applied on every level + // runAtBottomLevel: function that takes an L.MarkerCluster as an argument that should be applied at only the bottom level + _recursively: function (boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel) { + var childClusters = this._childClusters, + zoom = this._zoom, + i, c; + + if (zoomLevelToStart <= zoom) { + if (runAtEveryLevel) { + runAtEveryLevel(this); + } + if (runAtBottomLevel && zoom === zoomLevelToStop) { + runAtBottomLevel(this); + } + } + + if (zoom < zoomLevelToStart || zoom < zoomLevelToStop) { + for (i = childClusters.length - 1; i >= 0; i--) { + c = childClusters[i]; + if (boundsToApplyTo.intersects(c._bounds)) { + c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel); + } + } + } + }, + + //Returns true if we are the parent of only one cluster and that cluster is the same as us + _isSingleParent: function () { + //Don't need to check this._markers as the rest won't work if there are any + return this._childClusters.length > 0 && this._childClusters[0]._childCount === this._childCount; + } +}); + +/* +* Extends L.Marker to include two extra methods: clusterHide and clusterShow. +* +* They work as setOpacity(0) and setOpacity(1) respectively, but +* they will remember the marker's opacity when hiding and showing it again. +* +*/ + + +L.Marker.include({ + + clusterHide: function () { + this.options.opacityWhenUnclustered = this.options.opacity || 1; + return this.setOpacity(0); + }, + + clusterShow: function () { + var ret = this.setOpacity(this.options.opacity || this.options.opacityWhenUnclustered); + delete this.options.opacityWhenUnclustered; + return ret; + } + +}); + +L.DistanceGrid = function (cellSize) { + this._cellSize = cellSize; + this._sqCellSize = cellSize * cellSize; + this._grid = {}; + this._objectPoint = { }; +}; + +L.DistanceGrid.prototype = { + + addObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + stamp = L.Util.stamp(obj); + + this._objectPoint[stamp] = point; + + cell.push(obj); + }, + + updateObject: function (obj, point) { + this.removeObject(obj); + this.addObject(obj, point); + }, + + //Returns true if the object was found + removeObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + i, len; + + delete this._objectPoint[L.Util.stamp(obj)]; + + for (i = 0, len = cell.length; i < len; i++) { + if (cell[i] === obj) { + + cell.splice(i, 1); + + if (len === 1) { + delete row[x]; + } + + return true; + } + } + + }, + + eachObject: function (fn, context) { + var i, j, k, len, row, cell, removed, + grid = this._grid; + + for (i in grid) { + row = grid[i]; + + for (j in row) { + cell = row[j]; + + for (k = 0, len = cell.length; k < len; k++) { + removed = fn.call(context, cell[k]); + if (removed) { + k--; + len--; + } + } + } + } + }, + + getNearObject: function (point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + i, j, k, row, cell, len, obj, dist, + objectPoint = this._objectPoint, + closestDistSq = this._sqCellSize, + closest = null; + + for (i = y - 1; i <= y + 1; i++) { + row = this._grid[i]; + if (row) { + + for (j = x - 1; j <= x + 1; j++) { + cell = row[j]; + if (cell) { + + for (k = 0, len = cell.length; k < len; k++) { + obj = cell[k]; + dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point); + if (dist < closestDistSq || + dist <= closestDistSq && closest === null) { + closestDistSq = dist; + closest = obj; + } + } + } + } + } + } + return closest; + }, + + _getCoord: function (x) { + var coord = Math.floor(x / this._cellSize); + return isFinite(coord) ? coord : x; + }, + + _sqDist: function (p, p2) { + var dx = p2.x - p.x, + dy = p2.y - p.y; + return dx * dx + dy * dy; + } +}; + +/* Copyright (c) 2012 the authors listed at the following URL, and/or +the authors of referenced articles or incorporated external code: +http://en.literateprograms.org/Quickhull_(Javascript)?action=history&offset=20120410175256 + +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. + +Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=18434 +*/ + +(function () { + L.QuickHull = { + + /* + * @param {Object} cpt a point to be measured from the baseline + * @param {Array} bl the baseline, as represented by a two-element + * array of latlng objects. + * @returns {Number} an approximate distance measure + */ + getDistant: function (cpt, bl) { + var vY = bl[1].lat - bl[0].lat, + vX = bl[0].lng - bl[1].lng; + return (vX * (cpt.lat - bl[0].lat) + vY * (cpt.lng - bl[0].lng)); + }, + + /* + * @param {Array} baseLine a two-element array of latlng objects + * representing the baseline to project from + * @param {Array} latLngs an array of latlng objects + * @returns {Object} the maximum point and all new points to stay + * in consideration for the hull. + */ + findMostDistantPointFromBaseLine: function (baseLine, latLngs) { + var maxD = 0, + maxPt = null, + newPoints = [], + i, pt, d; + + for (i = latLngs.length - 1; i >= 0; i--) { + pt = latLngs[i]; + d = this.getDistant(pt, baseLine); + + if (d > 0) { + newPoints.push(pt); + } else { + continue; + } + + if (d > maxD) { + maxD = d; + maxPt = pt; + } + } + + return { maxPoint: maxPt, newPoints: newPoints }; + }, + + + /* + * Given a baseline, compute the convex hull of latLngs as an array + * of latLngs. + * + * @param {Array} latLngs + * @returns {Array} + */ + buildConvexHull: function (baseLine, latLngs) { + var convexHullBaseLines = [], + t = this.findMostDistantPointFromBaseLine(baseLine, latLngs); + + if (t.maxPoint) { // if there is still a point "outside" the base line + convexHullBaseLines = + convexHullBaseLines.concat( + this.buildConvexHull([baseLine[0], t.maxPoint], t.newPoints) + ); + convexHullBaseLines = + convexHullBaseLines.concat( + this.buildConvexHull([t.maxPoint, baseLine[1]], t.newPoints) + ); + return convexHullBaseLines; + } else { // if there is no more point "outside" the base line, the current base line is part of the convex hull + return [baseLine[0]]; + } + }, + + /* + * Given an array of latlngs, compute a convex hull as an array + * of latlngs + * + * @param {Array} latLngs + * @returns {Array} + */ + getConvexHull: function (latLngs) { + // find first baseline + var maxLat = false, minLat = false, + maxLng = false, minLng = false, + maxLatPt = null, minLatPt = null, + maxLngPt = null, minLngPt = null, + maxPt = null, minPt = null, + i; + + for (i = latLngs.length - 1; i >= 0; i--) { + var pt = latLngs[i]; + if (maxLat === false || pt.lat > maxLat) { + maxLatPt = pt; + maxLat = pt.lat; + } + if (minLat === false || pt.lat < minLat) { + minLatPt = pt; + minLat = pt.lat; + } + if (maxLng === false || pt.lng > maxLng) { + maxLngPt = pt; + maxLng = pt.lng; + } + if (minLng === false || pt.lng < minLng) { + minLngPt = pt; + minLng = pt.lng; + } + } + + if (minLat !== maxLat) { + minPt = minLatPt; + maxPt = maxLatPt; + } else { + minPt = minLngPt; + maxPt = maxLngPt; + } + + var ch = [].concat(this.buildConvexHull([minPt, maxPt], latLngs), + this.buildConvexHull([maxPt, minPt], latLngs)); + return ch; + } + }; +}()); + +L.MarkerCluster.include({ + getConvexHull: function () { + var childMarkers = this.getAllChildMarkers(), + points = [], + p, i; + + for (i = childMarkers.length - 1; i >= 0; i--) { + p = childMarkers[i].getLatLng(); + points.push(p); + } + + return L.QuickHull.getConvexHull(points); + } +}); + +//This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet +//Huge thanks to jawj for implementing it first to make my job easy :-) + +L.MarkerCluster.include({ + + _2PI: Math.PI * 2, + _circleFootSeparation: 25, //related to circumference of circle + _circleStartAngle: 0, + + _spiralFootSeparation: 28, //related to size of spiral (experiment!) + _spiralLengthStart: 11, + _spiralLengthFactor: 5, + + _circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards. + // 0 -> always spiral; Infinity -> always circle + + spiderfy: function () { + if (this._group._spiderfied === this || this._group._inZoomAnimation) { + return; + } + + var childMarkers = this.getAllChildMarkers(), + group = this._group, + map = group._map, + center = map.latLngToLayerPoint(this._latlng), + positions; + + this._group._unspiderfy(); + this._group._spiderfied = this; + + //TODO Maybe: childMarkers order by distance to center + + if (childMarkers.length >= this._circleSpiralSwitchover) { + positions = this._generatePointsSpiral(childMarkers.length, center); + } else { + center.y += 10; // Otherwise circles look wrong => hack for standard blue icon, renders differently for other icons. + positions = this._generatePointsCircle(childMarkers.length, center); + } + + this._animationSpiderfy(childMarkers, positions); + }, + + unspiderfy: function (zoomDetails) { + /// <param Name="zoomDetails">Argument from zoomanim if being called in a zoom animation or null otherwise</param> + if (this._group._inZoomAnimation) { + return; + } + this._animationUnspiderfy(zoomDetails); + + this._group._spiderfied = null; + }, + + _generatePointsCircle: function (count, centerPt) { + var circumference = this._group.options.spiderfyDistanceMultiplier * this._circleFootSeparation * (2 + count), + legLength = circumference / this._2PI, //radius from circumference + angleStep = this._2PI / count, + res = [], + i, angle; + + legLength = Math.max(legLength, 35); // Minimum distance to get outside the cluster icon. + + res.length = count; + + for (i = 0; i < count; i++) { // Clockwise, like spiral. + angle = this._circleStartAngle + i * angleStep; + res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round(); + } + + return res; + }, + + _generatePointsSpiral: function (count, centerPt) { + var spiderfyDistanceMultiplier = this._group.options.spiderfyDistanceMultiplier, + legLength = spiderfyDistanceMultiplier * this._spiralLengthStart, + separation = spiderfyDistanceMultiplier * this._spiralFootSeparation, + lengthFactor = spiderfyDistanceMultiplier * this._spiralLengthFactor * this._2PI, + angle = 0, + res = [], + i; + + res.length = count; + + // Higher index, closer position to cluster center. + for (i = count; i >= 0; i--) { + // Skip the first position, so that we are already farther from center and we avoid + // being under the default cluster icon (especially important for Circle Markers). + if (i < count) { + res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round(); + } + angle += separation / legLength + i * 0.0005; + legLength += lengthFactor / angle; + } + return res; + }, + + _noanimationUnspiderfy: function () { + var group = this._group, + map = group._map, + fg = group._featureGroup, + childMarkers = this.getAllChildMarkers(), + m, i; + + group._ignoreMove = true; + + this.setOpacity(1); + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + fg.removeLayer(m); + + if (m._preSpiderfyLatlng) { + m.setLatLng(m._preSpiderfyLatlng); + delete m._preSpiderfyLatlng; + } + if (m.setZIndexOffset) { + m.setZIndexOffset(0); + } + + if (m._spiderLeg) { + map.removeLayer(m._spiderLeg); + delete m._spiderLeg; + } + } + + group.fire('unspiderfied', { + cluster: this, + markers: childMarkers + }); + group._ignoreMove = false; + group._spiderfied = null; + } +}); + +//Non Animated versions of everything +L.MarkerClusterNonAnimated = L.MarkerCluster.extend({ + _animationSpiderfy: function (childMarkers, positions) { + var group = this._group, + map = group._map, + fg = group._featureGroup, + legOptions = this._group.options.spiderLegPolylineOptions, + i, m, leg, newPos; + + group._ignoreMove = true; + + // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition. + // The reverse order trick no longer improves performance on modern browsers. + for (i = 0; i < childMarkers.length; i++) { + newPos = map.layerPointToLatLng(positions[i]); + m = childMarkers[i]; + + // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it. + leg = new L.Polyline([this._latlng, newPos], legOptions); + map.addLayer(leg); + m._spiderLeg = leg; + + // Now add the marker. + m._preSpiderfyLatlng = m._latlng; + m.setLatLng(newPos); + if (m.setZIndexOffset) { + m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING + } + + fg.addLayer(m); + } + this.setOpacity(0.3); + + group._ignoreMove = false; + group.fire('spiderfied', { + cluster: this, + markers: childMarkers + }); + }, + + _animationUnspiderfy: function () { + this._noanimationUnspiderfy(); + } +}); + +//Animated versions here +L.MarkerCluster.include({ + + _animationSpiderfy: function (childMarkers, positions) { + var me = this, + group = this._group, + map = group._map, + fg = group._featureGroup, + thisLayerLatLng = this._latlng, + thisLayerPos = map.latLngToLayerPoint(thisLayerLatLng), + svg = L.Path.SVG, + legOptions = L.extend({}, this._group.options.spiderLegPolylineOptions), // Copy the options so that we can modify them for animation. + finalLegOpacity = legOptions.opacity, + i, m, leg, legPath, legLength, newPos; + + if (finalLegOpacity === undefined) { + finalLegOpacity = L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity; + } + + if (svg) { + // If the initial opacity of the spider leg is not 0 then it appears before the animation starts. + legOptions.opacity = 0; + + // Add the class for CSS transitions. + legOptions.className = (legOptions.className || '') + ' leaflet-cluster-spider-leg'; + } else { + // Make sure we have a defined opacity. + legOptions.opacity = finalLegOpacity; + } + + group._ignoreMove = true; + + // Add markers and spider legs to map, hidden at our center point. + // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition. + // The reverse order trick no longer improves performance on modern browsers. + for (i = 0; i < childMarkers.length; i++) { + m = childMarkers[i]; + + newPos = map.layerPointToLatLng(positions[i]); + + // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it. + leg = new L.Polyline([thisLayerLatLng, newPos], legOptions); + map.addLayer(leg); + m._spiderLeg = leg; + + // Explanations: https://jakearchibald.com/2013/animated-line-drawing-svg/ + // In our case the transition property is declared in the CSS file. + if (svg) { + legPath = leg._path; + legLength = legPath.getTotalLength() + 0.1; // Need a small extra length to avoid remaining dot in Firefox. + legPath.style.strokeDasharray = legLength; // Just 1 length is enough, it will be duplicated. + legPath.style.strokeDashoffset = legLength; + } + + // If it is a marker, add it now and we'll animate it out + if (m.setZIndexOffset) { + m.setZIndexOffset(1000000); // Make normal markers appear on top of EVERYTHING + } + if (m.clusterHide) { + m.clusterHide(); + } + + // Vectors just get immediately added + fg.addLayer(m); + + if (m._setPos) { + m._setPos(thisLayerPos); + } + } + + group._forceLayout(); + group._animationStart(); + + // Reveal markers and spider legs. + for (i = childMarkers.length - 1; i >= 0; i--) { + newPos = map.layerPointToLatLng(positions[i]); + m = childMarkers[i]; + + //Move marker to new position + m._preSpiderfyLatlng = m._latlng; + m.setLatLng(newPos); + + if (m.clusterShow) { + m.clusterShow(); + } + + // Animate leg (animation is actually delegated to CSS transition). + if (svg) { + leg = m._spiderLeg; + legPath = leg._path; + legPath.style.strokeDashoffset = 0; + //legPath.style.strokeOpacity = finalLegOpacity; + leg.setStyle({opacity: finalLegOpacity}); + } + } + this.setOpacity(0.3); + + group._ignoreMove = false; + + setTimeout(function () { + group._animationEnd(); + group.fire('spiderfied', { + cluster: me, + markers: childMarkers + }); + }, 200); + }, + + _animationUnspiderfy: function (zoomDetails) { + var me = this, + group = this._group, + map = group._map, + fg = group._featureGroup, + thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng), + childMarkers = this.getAllChildMarkers(), + svg = L.Path.SVG, + m, i, leg, legPath, legLength, nonAnimatable; + + group._ignoreMove = true; + group._animationStart(); + + //Make us visible and bring the child markers back in + this.setOpacity(1); + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + //Marker was added to us after we were spiderfied + if (!m._preSpiderfyLatlng) { + continue; + } + + //Close any popup on the marker first, otherwise setting the location of the marker will make the map scroll + m.closePopup(); + + //Fix up the location to the real one + m.setLatLng(m._preSpiderfyLatlng); + delete m._preSpiderfyLatlng; + + //Hack override the location to be our center + nonAnimatable = true; + if (m._setPos) { + m._setPos(thisLayerPos); + nonAnimatable = false; + } + if (m.clusterHide) { + m.clusterHide(); + nonAnimatable = false; + } + if (nonAnimatable) { + fg.removeLayer(m); + } + + // Animate the spider leg back in (animation is actually delegated to CSS transition). + if (svg) { + leg = m._spiderLeg; + legPath = leg._path; + legLength = legPath.getTotalLength() + 0.1; + legPath.style.strokeDashoffset = legLength; + leg.setStyle({opacity: 0}); + } + } + + group._ignoreMove = false; + + setTimeout(function () { + //If we have only <= one child left then that marker will be shown on the map so don't remove it! + var stillThereChildCount = 0; + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + if (m._spiderLeg) { + stillThereChildCount++; + } + } + + + for (i = childMarkers.length - 1; i >= 0; i--) { + m = childMarkers[i]; + + if (!m._spiderLeg) { //Has already been unspiderfied + continue; + } + + if (m.clusterShow) { + m.clusterShow(); + } + if (m.setZIndexOffset) { + m.setZIndexOffset(0); + } + + if (stillThereChildCount > 1) { + fg.removeLayer(m); + } + + map.removeLayer(m._spiderLeg); + delete m._spiderLeg; + } + group._animationEnd(); + group.fire('unspiderfied', { + cluster: me, + markers: childMarkers + }); + }, 200); + } +}); + + +L.MarkerClusterGroup.include({ + //The MarkerCluster currently spiderfied (if any) + _spiderfied: null, + + unspiderfy: function () { + this._unspiderfy.apply(this, arguments); + }, + + _spiderfierOnAdd: function () { + this._map.on('click', this._unspiderfyWrapper, this); + + if (this._map.options.zoomAnimation) { + this._map.on('zoomstart', this._unspiderfyZoomStart, this); + } + //Browsers without zoomAnimation or a big zoom don't fire zoomstart + this._map.on('zoomend', this._noanimationUnspiderfy, this); + + if (!L.Browser.touch) { + this._map.getRenderer(this); + //Needs to happen in the pageload, not after, or animations don't work in webkit + // http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements + //Disable on touch browsers as the animation messes up on a touch zoom and isn't very noticable + } + }, + + _spiderfierOnRemove: function () { + this._map.off('click', this._unspiderfyWrapper, this); + this._map.off('zoomstart', this._unspiderfyZoomStart, this); + this._map.off('zoomanim', this._unspiderfyZoomAnim, this); + this._map.off('zoomend', this._noanimationUnspiderfy, this); + + //Ensure that markers are back where they should be + // Use no animation to avoid a sticky leaflet-cluster-anim class on mapPane + this._noanimationUnspiderfy(); + }, + + //On zoom start we add a zoomanim handler so that we are guaranteed to be last (after markers are animated) + //This means we can define the animation they do rather than Markers doing an animation to their actual location + _unspiderfyZoomStart: function () { + if (!this._map) { //May have been removed from the map by a zoomEnd handler + return; + } + + this._map.on('zoomanim', this._unspiderfyZoomAnim, this); + }, + + _unspiderfyZoomAnim: function (zoomDetails) { + //Wait until the first zoomanim after the user has finished touch-zooming before running the animation + if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) { + return; + } + + this._map.off('zoomanim', this._unspiderfyZoomAnim, this); + this._unspiderfy(zoomDetails); + }, + + _unspiderfyWrapper: function () { + /// <summary>_unspiderfy but passes no arguments</summary> + this._unspiderfy(); + }, + + _unspiderfy: function (zoomDetails) { + if (this._spiderfied) { + this._spiderfied.unspiderfy(zoomDetails); + } + }, + + _noanimationUnspiderfy: function () { + if (this._spiderfied) { + this._spiderfied._noanimationUnspiderfy(); + } + }, + + //If the given layer is currently being spiderfied then we unspiderfy it so it isn't on the map anymore etc + _unspiderfyLayer: function (layer) { + if (layer._spiderLeg) { + this._featureGroup.removeLayer(layer); + + if (layer.clusterShow) { + layer.clusterShow(); + } + //Position will be fixed up immediately in _animationUnspiderfy + if (layer.setZIndexOffset) { + layer.setZIndexOffset(0); + } + + this._map.removeLayer(layer._spiderLeg); + delete layer._spiderLeg; + } + } +}); + +/** + * Adds 1 public method to MCG and 1 to L.Marker to facilitate changing + * markers' icon options and refreshing their icon and their parent clusters + * accordingly (case where their iconCreateFunction uses data of childMarkers + * to make up the cluster icon). + */ + + +L.MarkerClusterGroup.include({ + /** + * Updates the icon of all clusters which are parents of the given marker(s). + * In singleMarkerMode, also updates the given marker(s) icon. + * @param layers L.MarkerClusterGroup|L.LayerGroup|Array(L.Marker)|Map(L.Marker)| + * L.MarkerCluster|L.Marker (optional) list of markers (or single marker) whose parent + * clusters need to be updated. If not provided, retrieves all child markers of this. + * @returns {L.MarkerClusterGroup} + */ + refreshClusters: function (layers) { + if (!layers) { + layers = this._topClusterLevel.getAllChildMarkers(); + } else if (layers instanceof L.MarkerClusterGroup) { + layers = layers._topClusterLevel.getAllChildMarkers(); + } else if (layers instanceof L.LayerGroup) { + layers = layers._layers; + } else if (layers instanceof L.MarkerCluster) { + layers = layers.getAllChildMarkers(); + } else if (layers instanceof L.Marker) { + layers = [layers]; + } // else: must be an Array(L.Marker)|Map(L.Marker) + this._flagParentsIconsNeedUpdate(layers); + this._refreshClustersIcons(); + + // In case of singleMarkerMode, also re-draw the markers. + if (this.options.singleMarkerMode) { + this._refreshSingleMarkerModeMarkers(layers); + } + + return this; + }, + + /** + * Simply flags all parent clusters of the given markers as having a "dirty" icon. + * @param layers Array(L.Marker)|Map(L.Marker) list of markers. + * @private + */ + _flagParentsIconsNeedUpdate: function (layers) { + var id, parent; + + // Assumes layers is an Array or an Object whose prototype is non-enumerable. + for (id in layers) { + // Flag parent clusters' icon as "dirty", all the way up. + // Dumb process that flags multiple times upper parents, but still + // much more efficient than trying to be smart and make short lists, + // at least in the case of a hierarchy following a power law: + // http://jsperf.com/flag-nodes-in-power-hierarchy/2 + parent = layers[id].__parent; + while (parent) { + parent._iconNeedsUpdate = true; + parent = parent.__parent; + } + } + }, + + /** + * Re-draws the icon of the supplied markers. + * To be used in singleMarkerMode only. + * @param layers Array(L.Marker)|Map(L.Marker) list of markers. + * @private + */ + _refreshSingleMarkerModeMarkers: function (layers) { + var id, layer; + + for (id in layers) { + layer = layers[id]; + + // Make sure we do not override markers that do not belong to THIS group. + if (this.hasLayer(layer)) { + // Need to re-create the icon first, then re-draw the marker. + layer.setIcon(this._overrideMarkerIcon(layer)); + } + } + } +}); + +L.Marker.include({ + /** + * Updates the given options in the marker's icon and refreshes the marker. + * @param options map object of icon options. + * @param directlyRefreshClusters boolean (optional) true to trigger + * MCG.refreshClustersOf() right away with this single marker. + * @returns {L.Marker} + */ + refreshIconOptions: function (options, directlyRefreshClusters) { + var icon = this.options.icon; + + L.setOptions(icon, options); + + this.setIcon(icon); + + // Shortcut to refresh the associated MCG clusters right away. + // To be used when refreshing a single marker. + // Otherwise, better use MCG.refreshClusters() once at the end with + // the list of modified markers. + if (directlyRefreshClusters && this.__parent) { + this.__parent._group.refreshClusters(this); + } + + return this; + } +}); + +exports.MarkerClusterGroup = MarkerClusterGroup; +exports.MarkerCluster = MarkerCluster; + +}))); diff --git a/themes/bootstrap3/js/vendor/ol/ol-debug.js b/themes/bootstrap3/js/vendor/ol/ol-debug.js deleted file mode 100644 index e7c7f9eaceadf89b22bcefbfaac1a55d7285c925..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/js/vendor/ol/ol-debug.js +++ /dev/null @@ -1,92598 +0,0 @@ -// OpenLayers. See https://openlayers.org/ -// License: https://raw.githubusercontent.com/openlayers/openlayers/master/LICENSE.md -// Version: v4.2.0 -;(function (root, factory) { - if (typeof exports === "object") { - module.exports = factory(); - } else if (typeof define === "function" && define.amd) { - define([], factory); - } else { - root.ol = factory(); - } -}(this, function () { - var OPENLAYERS = {}; - var goog = this.goog = {}; -this.CLOSURE_NO_DEPS = true; -// Copyright 2006 The Closure Library Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview Bootstrap for the Google JS Library (Closure). - * - * In uncompiled mode base.js will write out Closure's deps file, unless the - * global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects to - * include their own deps file(s) from different locations. - * - * Avoid including base.js more than once. This is strictly discouraged and not - * supported. goog.require(...) won't work properly in that case. - * - * @author arv@google.com (Erik Arvidsson) - * - * @provideGoog - */ - - -/** - * @define {boolean} Overridden to true by the compiler when - * --process_closure_primitives is specified. - */ -var COMPILED = false; - - -/** - * Base namespace for the Closure library. Checks to see goog is already - * defined in the current scope before assigning to prevent clobbering if - * base.js is loaded more than once. - * - * @const - */ -var goog = goog || {}; - - -/** - * Reference to the global context. In most cases this will be 'window'. - */ -goog.global = this; - - -/** - * A hook for overriding the define values in uncompiled mode. - * - * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before - * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, - * {@code goog.define} will use the value instead of the default value. This - * allows flags to be overwritten without compilation (this is normally - * accomplished with the compiler's "define" flag). - * - * Example: - * <pre> - * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false}; - * </pre> - * - * @type {Object<string, (string|number|boolean)>|undefined} - */ -goog.global.CLOSURE_UNCOMPILED_DEFINES; - - -/** - * A hook for overriding the define values in uncompiled or compiled mode, - * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In - * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. - * - * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or - * string literals or the compiler will emit an error. - * - * While any @define value may be set, only those set with goog.define will be - * effective for uncompiled code. - * - * Example: - * <pre> - * var CLOSURE_DEFINES = {'goog.DEBUG': false} ; - * </pre> - * - * @type {Object<string, (string|number|boolean)>|undefined} - */ -goog.global.CLOSURE_DEFINES; - - -/** - * Returns true if the specified value is not undefined. - * WARNING: Do not use this to test if an object has a property. Use the in - * operator instead. - * - * @param {?} val Variable to test. - * @return {boolean} Whether variable is defined. - */ -goog.isDef = function(val) { - // void 0 always evaluates to undefined and hence we do not need to depend on - // the definition of the global variable named 'undefined'. - return val !== void 0; -}; - - -/** - * Builds an object structure for the provided namespace path, ensuring that - * names that already exist are not overwritten. For example: - * "a.b.c" -> a = {};a.b={};a.b.c={}; - * Used by goog.provide and goog.exportSymbol. - * @param {string} name name of the object that this file defines. - * @param {*=} opt_object the object to expose at the end of the path. - * @param {Object=} opt_objectToExportTo The object to add the path to; default - * is |goog.global|. - * @private - */ -goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { - var parts = name.split('.'); - var cur = opt_objectToExportTo || goog.global; - - // Internet Explorer exhibits strange behavior when throwing errors from - // methods externed in this manner. See the testExportSymbolExceptions in - // base_test.html for an example. - if (!(parts[0] in cur) && cur.execScript) { - cur.execScript('var ' + parts[0]); - } - - // Certain browsers cannot parse code in the form for((a in b); c;); - // This pattern is produced by the JSCompiler when it collapses the - // statement above into the conditional loop below. To prevent this from - // happening, use a for-loop and reserve the init logic as below. - - // Parentheses added to eliminate strict JS warning in Firefox. - for (var part; parts.length && (part = parts.shift());) { - if (!parts.length && goog.isDef(opt_object)) { - // last part and we have an object; use it - cur[part] = opt_object; - } else if (cur[part] && cur[part] !== Object.prototype[part]) { - cur = cur[part]; - } else { - cur = cur[part] = {}; - } - } -}; - - -/** - * Defines a named value. In uncompiled mode, the value is retrieved from - * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and - * has the property specified, and otherwise used the defined defaultValue. - * When compiled the default can be overridden using the compiler - * options or the value set in the CLOSURE_DEFINES object. - * - * @param {string} name The distinguished name to provide. - * @param {string|number|boolean} defaultValue - */ -goog.define = function(name, defaultValue) { - var value = defaultValue; - if (!COMPILED) { - if (goog.global.CLOSURE_UNCOMPILED_DEFINES && - Object.prototype.hasOwnProperty.call( - goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { - value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; - } else if ( - goog.global.CLOSURE_DEFINES && - Object.prototype.hasOwnProperty.call( - goog.global.CLOSURE_DEFINES, name)) { - value = goog.global.CLOSURE_DEFINES[name]; - } - } - goog.exportPath_(name, value); -}; - - -/** - * @define {boolean} DEBUG is provided as a convenience so that debugging code - * that should not be included in a production js_binary can be easily stripped - * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most - * toString() methods should be declared inside an "if (goog.DEBUG)" conditional - * because they are generally used for debugging purposes and it is difficult - * for the JSCompiler to statically determine whether they are used. - */ -goog.define('goog.DEBUG', true); - - -/** - * @define {string} LOCALE defines the locale being used for compilation. It is - * used to select locale specific data to be compiled in js binary. BUILD rule - * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler - * option. - * - * Take into account that the locale code format is important. You should use - * the canonical Unicode format with hyphen as a delimiter. Language must be - * lowercase, Language Script - Capitalized, Region - UPPERCASE. - * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. - * - * See more info about locale codes here: - * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers - * - * For language codes you should use values defined by ISO 693-1. See it here - * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from - * this rule: the Hebrew language. For legacy reasons the old code (iw) should - * be used instead of the new code (he), see http://wiki/Main/IIISynonyms. - */ -goog.define('goog.LOCALE', 'en'); // default to en - - -/** - * @define {boolean} Whether this code is running on trusted sites. - * - * On untrusted sites, several native functions can be defined or overridden by - * external libraries like Prototype, Datejs, and JQuery and setting this flag - * to false forces closure to use its own implementations when possible. - * - * If your JavaScript can be loaded by a third party site and you are wary about - * relying on non-standard implementations, specify - * "--define goog.TRUSTED_SITE=false" to the JSCompiler. - */ -goog.define('goog.TRUSTED_SITE', true); - - -/** - * @define {boolean} Whether a project is expected to be running in strict mode. - * - * This define can be used to trigger alternate implementations compatible with - * running in EcmaScript Strict mode or warn about unavailable functionality. - * @see https://goo.gl/PudQ4y - * - */ -goog.define('goog.STRICT_MODE_COMPATIBLE', false); - - -/** - * @define {boolean} Whether code that calls {@link goog.setTestOnly} should - * be disallowed in the compilation unit. - */ -goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); - - -/** - * @define {boolean} Whether to use a Chrome app CSP-compliant method for - * loading scripts via goog.require. @see appendScriptSrcNode_. - */ -goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); - - -/** - * Defines a namespace in Closure. - * - * A namespace may only be defined once in a codebase. It may be defined using - * goog.provide() or goog.module(). - * - * The presence of one or more goog.provide() calls in a file indicates - * that the file defines the given objects/namespaces. - * Provided symbols must not be null or undefined. - * - * In addition, goog.provide() creates the object stubs for a namespace - * (for example, goog.provide("goog.foo.bar") will create the object - * goog.foo.bar if it does not already exist). - * - * Build tools also scan for provide/require/module statements - * to discern dependencies, build dependency files (see deps.js), etc. - * - * @see goog.require - * @see goog.module - * @param {string} name Namespace provided by this file in the form - * "goog.package.part". - */ -goog.provide = function(name) { - if (goog.isInModuleLoader_()) { - throw Error('goog.provide can not be used within a goog.module.'); - } - if (!COMPILED) { - // Ensure that the same namespace isn't provided twice. - // A goog.module/goog.provide maps a goog.require to a specific file - if (goog.isProvided_(name)) { - throw Error('Namespace "' + name + '" already declared.'); - } - } - - goog.constructNamespace_(name); -}; - - -/** - * @param {string} name Namespace provided by this file in the form - * "goog.package.part". - * @param {Object=} opt_obj The object to embed in the namespace. - * @private - */ -goog.constructNamespace_ = function(name, opt_obj) { - if (!COMPILED) { - delete goog.implicitNamespaces_[name]; - - var namespace = name; - while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { - if (goog.getObjectByName(namespace)) { - break; - } - goog.implicitNamespaces_[namespace] = true; - } - } - - goog.exportPath_(name, opt_obj); -}; - - -/** - * Module identifier validation regexp. - * Note: This is a conservative check, it is very possible to be more lenient, - * the primary exclusion here is "/" and "\" and a leading ".", these - * restrictions are intended to leave the door open for using goog.require - * with relative file paths rather than module identifiers. - * @private - */ -goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; - - -/** - * Defines a module in Closure. - * - * Marks that this file must be loaded as a module and claims the namespace. - * - * A namespace may only be defined once in a codebase. It may be defined using - * goog.provide() or goog.module(). - * - * goog.module() has three requirements: - * - goog.module may not be used in the same file as goog.provide. - * - goog.module must be the first statement in the file. - * - only one goog.module is allowed per file. - * - * When a goog.module annotated file is loaded, it is enclosed in - * a strict function closure. This means that: - * - any variables declared in a goog.module file are private to the file - * (not global), though the compiler is expected to inline the module. - * - The code must obey all the rules of "strict" JavaScript. - * - the file will be marked as "use strict" - * - * NOTE: unlike goog.provide, goog.module does not declare any symbols by - * itself. If declared symbols are desired, use - * goog.module.declareLegacyNamespace(). - * - * - * See the public goog.module proposal: http://goo.gl/Va1hin - * - * @param {string} name Namespace provided by this file in the form - * "goog.package.part", is expected but not required. - * @return {void} - */ -goog.module = function(name) { - if (!goog.isString(name) || !name || - name.search(goog.VALID_MODULE_RE_) == -1) { - throw Error('Invalid module identifier'); - } - if (!goog.isInModuleLoader_()) { - throw Error( - 'Module ' + name + ' has been loaded incorrectly. Note, ' + - 'modules cannot be loaded as normal scripts. They require some kind of ' + - 'pre-processing step. You\'re likely trying to load a module via a ' + - 'script tag or as a part of a concatenated bundle without rewriting the ' + - 'module. For more info see: ' + - 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); - } - if (goog.moduleLoaderState_.moduleName) { - throw Error('goog.module may only be called once per module.'); - } - - // Store the module name for the loader. - goog.moduleLoaderState_.moduleName = name; - if (!COMPILED) { - // Ensure that the same namespace isn't provided twice. - // A goog.module/goog.provide maps a goog.require to a specific file - if (goog.isProvided_(name)) { - throw Error('Namespace "' + name + '" already declared.'); - } - delete goog.implicitNamespaces_[name]; - } -}; - - -/** - * @param {string} name The module identifier. - * @return {?} The module exports for an already loaded module or null. - * - * Note: This is not an alternative to goog.require, it does not - * indicate a hard dependency, instead it is used to indicate - * an optional dependency or to access the exports of a module - * that has already been loaded. - * @suppress {missingProvide} - */ -goog.module.get = function(name) { - return goog.module.getInternal_(name); -}; - - -/** - * @param {string} name The module identifier. - * @return {?} The module exports for an already loaded module or null. - * @private - */ -goog.module.getInternal_ = function(name) { - if (!COMPILED) { - if (name in goog.loadedModules_) { - return goog.loadedModules_[name]; - } else if (!goog.implicitNamespaces_[name]) { - var ns = goog.getObjectByName(name); - return ns != null ? ns : null; - } - } - return null; -}; - - -/** - * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}} - */ -goog.moduleLoaderState_ = null; - - -/** - * @private - * @return {boolean} Whether a goog.module is currently being initialized. - */ -goog.isInModuleLoader_ = function() { - return goog.moduleLoaderState_ != null; -}; - - -/** - * Provide the module's exports as a globally accessible object under the - * module's declared name. This is intended to ease migration to goog.module - * for files that have existing usages. - * @suppress {missingProvide} - */ -goog.module.declareLegacyNamespace = function() { - if (!COMPILED && !goog.isInModuleLoader_()) { - throw new Error( - 'goog.module.declareLegacyNamespace must be called from ' + - 'within a goog.module'); - } - if (!COMPILED && !goog.moduleLoaderState_.moduleName) { - throw Error( - 'goog.module must be called prior to ' + - 'goog.module.declareLegacyNamespace.'); - } - goog.moduleLoaderState_.declareLegacyNamespace = true; -}; - - -/** - * Marks that the current file should only be used for testing, and never for - * live code in production. - * - * In the case of unit tests, the message may optionally be an exact namespace - * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra - * provide (if not explicitly defined in the code). - * - * @param {string=} opt_message Optional message to add to the error that's - * raised when used in production code. - */ -goog.setTestOnly = function(opt_message) { - if (goog.DISALLOW_TEST_ONLY_CODE) { - opt_message = opt_message || ''; - throw Error( - 'Importing test-only code into non-debug environment' + - (opt_message ? ': ' + opt_message : '.')); - } -}; - - -/** - * Forward declares a symbol. This is an indication to the compiler that the - * symbol may be used in the source yet is not required and may not be provided - * in compilation. - * - * The most common usage of forward declaration is code that takes a type as a - * function parameter but does not need to require it. By forward declaring - * instead of requiring, no hard dependency is made, and (if not required - * elsewhere) the namespace may never be required and thus, not be pulled - * into the JavaScript binary. If it is required elsewhere, it will be type - * checked as normal. - * - * Before using goog.forwardDeclare, please read the documentation at - * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to - * understand the options and tradeoffs when working with forward declarations. - * - * @param {string} name The namespace to forward declare in the form of - * "goog.package.part". - */ -goog.forwardDeclare = function(name) {}; - - -/** - * Forward declare type information. Used to assign types to goog.global - * referenced object that would otherwise result in unknown type references - * and thus block property disambiguation. - */ -goog.forwardDeclare('Document'); -goog.forwardDeclare('HTMLScriptElement'); -goog.forwardDeclare('XMLHttpRequest'); - - -if (!COMPILED) { - /** - * Check if the given name has been goog.provided. This will return false for - * names that are available only as implicit namespaces. - * @param {string} name name of the object to look for. - * @return {boolean} Whether the name has been provided. - * @private - */ - goog.isProvided_ = function(name) { - return (name in goog.loadedModules_) || - (!goog.implicitNamespaces_[name] && - goog.isDefAndNotNull(goog.getObjectByName(name))); - }; - - /** - * Namespaces implicitly defined by goog.provide. For example, - * goog.provide('goog.events.Event') implicitly declares that 'goog' and - * 'goog.events' must be namespaces. - * - * @type {!Object<string, (boolean|undefined)>} - * @private - */ - goog.implicitNamespaces_ = {'goog.module': true}; - - // NOTE: We add goog.module as an implicit namespace as goog.module is defined - // here and because the existing module package has not been moved yet out of - // the goog.module namespace. This satisifies both the debug loader and - // ahead-of-time dependency management. -} - - -/** - * Returns an object based on its fully qualified external name. The object - * is not found if null or undefined. If you are using a compilation pass that - * renames property names beware that using this function will not find renamed - * properties. - * - * @param {string} name The fully qualified name. - * @param {Object=} opt_obj The object within which to look; default is - * |goog.global|. - * @return {?} The value (object or primitive) or, if not found, null. - */ -goog.getObjectByName = function(name, opt_obj) { - var parts = name.split('.'); - var cur = opt_obj || goog.global; - for (var part; part = parts.shift();) { - if (goog.isDefAndNotNull(cur[part])) { - cur = cur[part]; - } else { - return null; - } - } - return cur; -}; - - -/** - * Globalizes a whole namespace, such as goog or goog.lang. - * - * @param {!Object} obj The namespace to globalize. - * @param {Object=} opt_global The object to add the properties to. - * @deprecated Properties may be explicitly exported to the global scope, but - * this should no longer be done in bulk. - */ -goog.globalize = function(obj, opt_global) { - var global = opt_global || goog.global; - for (var x in obj) { - global[x] = obj[x]; - } -}; - - -/** - * Adds a dependency from a file to the files it requires. - * @param {string} relPath The path to the js file. - * @param {!Array<string>} provides An array of strings with - * the names of the objects this file provides. - * @param {!Array<string>} requires An array of strings with - * the names of the objects this file requires. - * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating - * how the file must be loaded. The boolean 'true' is equivalent - * to {'module': 'goog'} for backwards-compatibility. Valid properties - * and values include {'module': 'goog'} and {'lang': 'es6'}. - */ -goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { - if (goog.DEPENDENCIES_ENABLED) { - var provide, require; - var path = relPath.replace(/\\/g, '/'); - var deps = goog.dependencies_; - if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') { - opt_loadFlags = opt_loadFlags ? {'module': 'goog'} : {}; - } - for (var i = 0; provide = provides[i]; i++) { - deps.nameToPath[provide] = path; - deps.loadFlags[path] = opt_loadFlags; - } - for (var j = 0; require = requires[j]; j++) { - if (!(path in deps.requires)) { - deps.requires[path] = {}; - } - deps.requires[path][require] = true; - } - } -}; - - - - -// NOTE(nnaze): The debug DOM loader was included in base.js as an original way -// to do "debug-mode" development. The dependency system can sometimes be -// confusing, as can the debug DOM loader's asynchronous nature. -// -// With the DOM loader, a call to goog.require() is not blocking -- the script -// will not load until some point after the current script. If a namespace is -// needed at runtime, it needs to be defined in a previous script, or loaded via -// require() with its registered dependencies. -// -// User-defined namespaces may need their own deps file. For a reference on -// creating a deps file, see: -// Externally: https://developers.google.com/closure/library/docs/depswriter -// -// Because of legacy clients, the DOM loader can't be easily removed from -// base.js. Work is being done to make it disableable or replaceable for -// different environments (DOM-less JavaScript interpreters like Rhino or V8, -// for example). See bootstrap/ for more information. - - -/** - * @define {boolean} Whether to enable the debug loader. - * - * If enabled, a call to goog.require() will attempt to load the namespace by - * appending a script tag to the DOM (if the namespace has been registered). - * - * If disabled, goog.require() will simply assert that the namespace has been - * provided (and depend on the fact that some outside tool correctly ordered - * the script). - */ -goog.define('goog.ENABLE_DEBUG_LOADER', true); - - -/** - * @param {string} msg - * @private - */ -goog.logToConsole_ = function(msg) { - if (goog.global.console) { - goog.global.console['error'](msg); - } -}; - - -/** - * Implements a system for the dynamic resolution of dependencies that works in - * parallel with the BUILD system. Note that all calls to goog.require will be - * stripped by the JSCompiler when the --process_closure_primitives option is - * used. - * @see goog.provide - * @param {string} name Namespace to include (as was given in goog.provide()) in - * the form "goog.package.part". - * @return {?} If called within a goog.module file, the associated namespace or - * module otherwise null. - */ -goog.require = function(name) { - // If the object already exists we do not need do do anything. - if (!COMPILED) { - if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) { - goog.maybeProcessDeferredDep_(name); - } - - if (goog.isProvided_(name)) { - if (goog.isInModuleLoader_()) { - return goog.module.getInternal_(name); - } - } else if (goog.ENABLE_DEBUG_LOADER) { - var path = goog.getPathFromDeps_(name); - if (path) { - goog.writeScripts_(path); - } else { - var errorMessage = 'goog.require could not find: ' + name; - goog.logToConsole_(errorMessage); - - throw Error(errorMessage); - } - } - - return null; - } -}; - - -/** - * Path for included scripts. - * @type {string} - */ -goog.basePath = ''; - - -/** - * A hook for overriding the base path. - * @type {string|undefined} - */ -goog.global.CLOSURE_BASE_PATH; - - -/** - * Whether to write out Closure's deps file. By default, the deps are written. - * @type {boolean|undefined} - */ -goog.global.CLOSURE_NO_DEPS; - - -/** - * A function to import a single script. This is meant to be overridden when - * Closure is being run in non-HTML contexts, such as web workers. It's defined - * in the global scope so that it can be set before base.js is loaded, which - * allows deps.js to be imported properly. - * - * The function is passed the script source, which is a relative URI. It should - * return true if the script was imported, false otherwise. - * @type {(function(string): boolean)|undefined} - */ -goog.global.CLOSURE_IMPORT_SCRIPT; - - -/** - * Null function used for default values of callbacks, etc. - * @return {void} Nothing. - */ -goog.nullFunction = function() {}; - - -/** - * When defining a class Foo with an abstract method bar(), you can do: - * Foo.prototype.bar = goog.abstractMethod - * - * Now if a subclass of Foo fails to override bar(), an error will be thrown - * when bar() is invoked. - * - * Note: This does not take the name of the function to override as an argument - * because that would make it more difficult to obfuscate our JavaScript code. - * - * @type {!Function} - * @throws {Error} when invoked to indicate the method should be overridden. - */ -goog.abstractMethod = function() { - throw Error('unimplemented abstract method'); -}; - - -/** - * Adds a {@code getInstance} static method that always returns the same - * instance object. - * @param {!Function} ctor The constructor for the class to add the static - * method to. - */ -goog.addSingletonGetter = function(ctor) { - // instance_ is immediately set to prevent issues with sealed constructors - // such as are encountered when a constructor is returned as the export object - // of a goog.module in unoptimized code. - ctor.instance_ = undefined; - ctor.getInstance = function() { - if (ctor.instance_) { - return ctor.instance_; - } - if (goog.DEBUG) { - // NOTE: JSCompiler can't optimize away Array#push. - goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; - } - return ctor.instance_ = new ctor; - }; -}; - - -/** - * All singleton classes that have been instantiated, for testing. Don't read - * it directly, use the {@code goog.testing.singleton} module. The compiler - * removes this variable if unused. - * @type {!Array<!Function>} - * @private - */ -goog.instantiatedSingletons_ = []; - - -/** - * @define {boolean} Whether to load goog.modules using {@code eval} when using - * the debug loader. This provides a better debugging experience as the - * source is unmodified and can be edited using Chrome Workspaces or similar. - * However in some environments the use of {@code eval} is banned - * so we provide an alternative. - */ -goog.define('goog.LOAD_MODULE_USING_EVAL', true); - - -/** - * @define {boolean} Whether the exports of goog.modules should be sealed when - * possible. - */ -goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); - - -/** - * The registry of initialized modules: - * the module identifier to module exports map. - * @private @const {!Object<string, ?>} - */ -goog.loadedModules_ = {}; - - -/** - * True if goog.dependencies_ is available. - * @const {boolean} - */ -goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; - - -/** - * @define {string} How to decide whether to transpile. Valid values - * are 'always', 'never', and 'detect'. The default ('detect') is to - * use feature detection to determine which language levels need - * transpilation. - */ -// NOTE(user): we could expand this to accept a language level to bypass -// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but -// would leave ES3 and ES5 files alone. -goog.define('goog.TRANSPILE', 'detect'); - - -/** - * @define {string} Path to the transpiler. Executing the script at this - * path (relative to base.js) should define a function $jscomp.transpile. - */ -goog.define('goog.TRANSPILER', 'transpile.js'); - - -if (goog.DEPENDENCIES_ENABLED) { - /** - * This object is used to keep track of dependencies and other data that is - * used for loading scripts. - * @private - * @type {{ - * loadFlags: !Object<string, !Object<string, string>>, - * nameToPath: !Object<string, string>, - * requires: !Object<string, !Object<string, boolean>>, - * visited: !Object<string, boolean>, - * written: !Object<string, boolean>, - * deferred: !Object<string, string> - * }} - */ - goog.dependencies_ = { - loadFlags: {}, // 1 to 1 - - nameToPath: {}, // 1 to 1 - - requires: {}, // 1 to many - - // Used when resolving dependencies to prevent us from visiting file twice. - visited: {}, - - written: {}, // Used to keep track of script files we have written. - - deferred: {} // Used to track deferred module evaluations in old IEs - }; - - - /** - * Tries to detect whether is in the context of an HTML document. - * @return {boolean} True if it looks like HTML document. - * @private - */ - goog.inHtmlDocument_ = function() { - /** @type {Document} */ - var doc = goog.global.document; - return doc != null && 'write' in doc; // XULDocument misses write. - }; - - - /** - * Tries to detect the base path of base.js script that bootstraps Closure. - * @private - */ - goog.findBasePath_ = function() { - if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) { - goog.basePath = goog.global.CLOSURE_BASE_PATH; - return; - } else if (!goog.inHtmlDocument_()) { - return; - } - /** @type {Document} */ - var doc = goog.global.document; - var scripts = doc.getElementsByTagName('SCRIPT'); - // Search backwards since the current script is in almost all cases the one - // that has base.js. - for (var i = scripts.length - 1; i >= 0; --i) { - var script = /** @type {!HTMLScriptElement} */ (scripts[i]); - var src = script.src; - var qmark = src.lastIndexOf('?'); - var l = qmark == -1 ? src.length : qmark; - if (src.substr(l - 7, 7) == 'base.js') { - goog.basePath = src.substr(0, l - 7); - return; - } - } - }; - - - /** - * Imports a script if, and only if, that script hasn't already been imported. - * (Must be called at execution time) - * @param {string} src Script source. - * @param {string=} opt_sourceText The optionally source text to evaluate - * @private - */ - goog.importScript_ = function(src, opt_sourceText) { - var importScript = - goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; - if (importScript(src, opt_sourceText)) { - goog.dependencies_.written[src] = true; - } - }; - - - /** - * Whether the browser is IE9 or earlier, which needs special handling - * for deferred modules. - * @const @private {boolean} - */ - goog.IS_OLD_IE_ = - !!(!goog.global.atob && goog.global.document && goog.global.document.all); - - - /** - * Whether IE9 or earlier is waiting on a dependency. This ensures that - * deferred modules that have no non-deferred dependencies actually get - * loaded, since if we defer them and then never pull in a non-deferred - * script, then `goog.loadQueuedModules_` will never be called. Instead, - * if not waiting on anything we simply don't defer in the first place. - * @private {boolean} - */ - goog.oldIeWaiting_ = false; - - - /** - * Given a URL initiate retrieval and execution of a script that needs - * pre-processing. - * @param {string} src Script source URL. - * @param {boolean} isModule Whether this is a goog.module. - * @param {boolean} needsTranspile Whether this source needs transpilation. - * @private - */ - goog.importProcessedScript_ = function(src, isModule, needsTranspile) { - // In an attempt to keep browsers from timing out loading scripts using - // synchronous XHRs, put each load in its own script block. - var bootstrap = 'goog.retrieveAndExec_("' + src + '", ' + isModule + ', ' + - needsTranspile + ');'; - - goog.importScript_('', bootstrap); - }; - - - /** @private {!Array<string>} */ - goog.queuedModules_ = []; - - - /** - * Return an appropriate module text. Suitable to insert into - * a script tag (that is unescaped). - * @param {string} srcUrl - * @param {string} scriptText - * @return {string} - * @private - */ - goog.wrapModule_ = function(srcUrl, scriptText) { - if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) { - return '' + - 'goog.loadModule(function(exports) {' + - '"use strict";' + scriptText + - '\n' + // terminate any trailing single line comment. - ';return exports' + - '});' + - '\n//# sourceURL=' + srcUrl + '\n'; - } else { - return '' + - 'goog.loadModule(' + - goog.global.JSON.stringify( - scriptText + '\n//# sourceURL=' + srcUrl + '\n') + - ');'; - } - }; - - // On IE9 and earlier, it is necessary to handle - // deferred module loads. In later browsers, the - // code to be evaluated is simply inserted as a script - // block in the correct order. To eval deferred - // code at the right time, we piggy back on goog.require to call - // goog.maybeProcessDeferredDep_. - // - // The goog.requires are used both to bootstrap - // the loading process (when no deps are available) and - // declare that they should be available. - // - // Here we eval the sources, if all the deps are available - // either already eval'd or goog.require'd. This will - // be the case when all the dependencies have already - // been loaded, and the dependent module is loaded. - // - // But this alone isn't sufficient because it is also - // necessary to handle the case where there is no root - // that is not deferred. For that there we register for an event - // and trigger goog.loadQueuedModules_ handle any remaining deferred - // evaluations. - - /** - * Handle any remaining deferred goog.module evals. - * @private - */ - goog.loadQueuedModules_ = function() { - var count = goog.queuedModules_.length; - if (count > 0) { - var queue = goog.queuedModules_; - goog.queuedModules_ = []; - for (var i = 0; i < count; i++) { - var path = queue[i]; - goog.maybeProcessDeferredPath_(path); - } - } - goog.oldIeWaiting_ = false; - }; - - - /** - * Eval the named module if its dependencies are - * available. - * @param {string} name The module to load. - * @private - */ - goog.maybeProcessDeferredDep_ = function(name) { - if (goog.isDeferredModule_(name) && goog.allDepsAreAvailable_(name)) { - var path = goog.getPathFromDeps_(name); - goog.maybeProcessDeferredPath_(goog.basePath + path); - } - }; - - /** - * @param {string} name The module to check. - * @return {boolean} Whether the name represents a - * module whose evaluation has been deferred. - * @private - */ - goog.isDeferredModule_ = function(name) { - var path = goog.getPathFromDeps_(name); - var loadFlags = path && goog.dependencies_.loadFlags[path] || {}; - var languageLevel = loadFlags['lang'] || 'es3'; - if (path && (loadFlags['module'] == 'goog' || - goog.needsTranspile_(languageLevel))) { - var abspath = goog.basePath + path; - return (abspath) in goog.dependencies_.deferred; - } - return false; - }; - - /** - * @param {string} name The module to check. - * @return {boolean} Whether the name represents a - * module whose declared dependencies have all been loaded - * (eval'd or a deferred module load) - * @private - */ - goog.allDepsAreAvailable_ = function(name) { - var path = goog.getPathFromDeps_(name); - if (path && (path in goog.dependencies_.requires)) { - for (var requireName in goog.dependencies_.requires[path]) { - if (!goog.isProvided_(requireName) && - !goog.isDeferredModule_(requireName)) { - return false; - } - } - } - return true; - }; - - - /** - * @param {string} abspath - * @private - */ - goog.maybeProcessDeferredPath_ = function(abspath) { - if (abspath in goog.dependencies_.deferred) { - var src = goog.dependencies_.deferred[abspath]; - delete goog.dependencies_.deferred[abspath]; - goog.globalEval(src); - } - }; - - - /** - * Load a goog.module from the provided URL. This is not a general purpose - * code loader and does not support late loading code, that is it should only - * be used during page load. This method exists to support unit tests and - * "debug" loaders that would otherwise have inserted script tags. Under the - * hood this needs to use a synchronous XHR and is not recommeneded for - * production code. - * - * The module's goog.requires must have already been satisified; an exception - * will be thrown if this is not the case. This assumption is that no - * "deps.js" file exists, so there is no way to discover and locate the - * module-to-be-loaded's dependencies and no attempt is made to do so. - * - * There should only be one attempt to load a module. If - * "goog.loadModuleFromUrl" is called for an already loaded module, an - * exception will be throw. - * - * @param {string} url The URL from which to attempt to load the goog.module. - */ - goog.loadModuleFromUrl = function(url) { - // Because this executes synchronously, we don't need to do any additional - // bookkeeping. When "goog.loadModule" the namespace will be marked as - // having been provided which is sufficient. - goog.retrieveAndExec_(url, true, false); - }; - - - /** - * Writes a new script pointing to {@code src} directly into the DOM. - * - * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for - * the fallback mechanism. - * - * @param {string} src The script URL. - * @private - */ - goog.writeScriptSrcNode_ = function(src) { - goog.global.document.write( - '<script type="text/javascript" src="' + src + '"></' + - 'script>'); - }; - - - /** - * Appends a new script node to the DOM using a CSP-compliant mechanism. This - * method exists as a fallback for document.write (which is not allowed in a - * strict CSP context, e.g., Chrome apps). - * - * NOTE: This method is not analogous to using document.write to insert a - * <script> tag; specifically, the user agent will execute a script added by - * document.write immediately after the current script block finishes - * executing, whereas the DOM-appended script node will not be executed until - * the entire document is parsed and executed. That is to say, this script is - * added to the end of the script execution queue. - * - * The page must not attempt to call goog.required entities until after the - * document has loaded, e.g., in or after the window.onload callback. - * - * @param {string} src The script URL. - * @private - */ - goog.appendScriptSrcNode_ = function(src) { - /** @type {Document} */ - var doc = goog.global.document; - var scriptEl = - /** @type {HTMLScriptElement} */ (doc.createElement('script')); - scriptEl.type = 'text/javascript'; - scriptEl.src = src; - scriptEl.defer = false; - scriptEl.async = false; - doc.head.appendChild(scriptEl); - }; - - - /** - * The default implementation of the import function. Writes a script tag to - * import the script. - * - * @param {string} src The script url. - * @param {string=} opt_sourceText The optionally source text to evaluate - * @return {boolean} True if the script was imported, false otherwise. - * @private - */ - goog.writeScriptTag_ = function(src, opt_sourceText) { - if (goog.inHtmlDocument_()) { - /** @type {!HTMLDocument} */ - var doc = goog.global.document; - - // If the user tries to require a new symbol after document load, - // something has gone terribly wrong. Doing a document.write would - // wipe out the page. This does not apply to the CSP-compliant method - // of writing script tags. - if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING && - doc.readyState == 'complete') { - // Certain test frameworks load base.js multiple times, which tries - // to write deps.js each time. If that happens, just fail silently. - // These frameworks wipe the page between each load of base.js, so this - // is OK. - var isDeps = /\bdeps.js$/.test(src); - if (isDeps) { - return false; - } else { - throw Error('Cannot write "' + src + '" after document load'); - } - } - - if (opt_sourceText === undefined) { - if (!goog.IS_OLD_IE_) { - if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) { - goog.appendScriptSrcNode_(src); - } else { - goog.writeScriptSrcNode_(src); - } - } else { - goog.oldIeWaiting_ = true; - var state = ' onreadystatechange=\'goog.onScriptLoad_(this, ' + - ++goog.lastNonModuleScriptIndex_ + ')\' '; - doc.write( - '<script type="text/javascript" src="' + src + '"' + state + - '></' + - 'script>'); - } - } else { - doc.write( - '<script type="text/javascript">' + - goog.protectScriptTag_(opt_sourceText) + '</' + - 'script>'); - } - return true; - } else { - return false; - } - }; - - /** - * Rewrites closing script tags in input to avoid ending an enclosing script - * tag. - * - * @param {string} str - * @return {string} - * @private - */ - goog.protectScriptTag_ = function(str) { - return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1'); - }; - - /** - * Determines whether the given language needs to be transpiled. - * @param {string} lang - * @return {boolean} - * @private - */ - goog.needsTranspile_ = function(lang) { - if (goog.TRANSPILE == 'always') { - return true; - } else if (goog.TRANSPILE == 'never') { - return false; - } else if (!goog.requiresTranspilation_) { - goog.requiresTranspilation_ = goog.createRequiresTranspilation_(); - } - if (lang in goog.requiresTranspilation_) { - return goog.requiresTranspilation_[lang]; - } else { - throw new Error('Unknown language mode: ' + lang); - } - }; - - /** @private {?Object<string, boolean>} */ - goog.requiresTranspilation_ = null; - - - /** @private {number} */ - goog.lastNonModuleScriptIndex_ = 0; - - - /** - * A readystatechange handler for legacy IE - * @param {!HTMLScriptElement} script - * @param {number} scriptIndex - * @return {boolean} - * @private - */ - goog.onScriptLoad_ = function(script, scriptIndex) { - // for now load the modules when we reach the last script, - // later allow more inter-mingling. - if (script.readyState == 'complete' && - goog.lastNonModuleScriptIndex_ == scriptIndex) { - goog.loadQueuedModules_(); - } - return true; - }; - - /** - * Resolves dependencies based on the dependencies added using addDependency - * and calls importScript_ in the correct order. - * @param {string} pathToLoad The path from which to start discovering - * dependencies. - * @private - */ - goog.writeScripts_ = function(pathToLoad) { - /** @type {!Array<string>} The scripts we need to write this time. */ - var scripts = []; - var seenScript = {}; - var deps = goog.dependencies_; - - /** @param {string} path */ - function visitNode(path) { - if (path in deps.written) { - return; - } - - // We have already visited this one. We can get here if we have cyclic - // dependencies. - if (path in deps.visited) { - return; - } - - deps.visited[path] = true; - - if (path in deps.requires) { - for (var requireName in deps.requires[path]) { - // If the required name is defined, we assume that it was already - // bootstrapped by other means. - if (!goog.isProvided_(requireName)) { - if (requireName in deps.nameToPath) { - visitNode(deps.nameToPath[requireName]); - } else { - throw Error('Undefined nameToPath for ' + requireName); - } - } - } - } - - if (!(path in seenScript)) { - seenScript[path] = true; - scripts.push(path); - } - } - - visitNode(pathToLoad); - - // record that we are going to load all these scripts. - for (var i = 0; i < scripts.length; i++) { - var path = scripts[i]; - goog.dependencies_.written[path] = true; - } - - // If a module is loaded synchronously then we need to - // clear the current inModuleLoader value, and restore it when we are - // done loading the current "requires". - var moduleState = goog.moduleLoaderState_; - goog.moduleLoaderState_ = null; - - for (var i = 0; i < scripts.length; i++) { - var path = scripts[i]; - if (path) { - var loadFlags = deps.loadFlags[path] || {}; - var languageLevel = loadFlags['lang'] || 'es3'; - var needsTranspile = goog.needsTranspile_(languageLevel); - if (loadFlags['module'] == 'goog' || needsTranspile) { - goog.importProcessedScript_( - goog.basePath + path, loadFlags['module'] == 'goog', - needsTranspile); - } else { - goog.importScript_(goog.basePath + path); - } - } else { - goog.moduleLoaderState_ = moduleState; - throw Error('Undefined script input'); - } - } - - // restore the current "module loading state" - goog.moduleLoaderState_ = moduleState; - }; - - - /** - * Looks at the dependency rules and tries to determine the script file that - * fulfills a particular rule. - * @param {string} rule In the form goog.namespace.Class or project.script. - * @return {?string} Url corresponding to the rule, or null. - * @private - */ - goog.getPathFromDeps_ = function(rule) { - if (rule in goog.dependencies_.nameToPath) { - return goog.dependencies_.nameToPath[rule]; - } else { - return null; - } - }; - - goog.findBasePath_(); - - // Allow projects to manage the deps files themselves. - if (!goog.global.CLOSURE_NO_DEPS) { - goog.importScript_(goog.basePath + 'deps.js'); - } -} - - -/** - * @package {?boolean} - * Visible for testing. - */ -goog.hasBadLetScoping = null; - - -/** - * @return {boolean} - * @package Visible for testing. - */ -goog.useSafari10Workaround = function() { - if (goog.hasBadLetScoping == null) { - var hasBadLetScoping; - try { - hasBadLetScoping = !eval( - '"use strict";' + - 'let x = 1; function f() { return typeof x; };' + - 'f() == "number";'); - } catch (e) { - // Assume that ES6 syntax isn't supported. - hasBadLetScoping = false; - } - goog.hasBadLetScoping = hasBadLetScoping; - } - return goog.hasBadLetScoping; -}; - - -/** - * @param {string} moduleDef - * @return {string} - * @package Visible for testing. - */ -goog.workaroundSafari10EvalBug = function(moduleDef) { - return '(function(){' + moduleDef + - '\n' + // Terminate any trailing single line comment. - ';' + // Terminate any trailing expression. - '})();\n'; -}; - - -/** - * @param {function(?):?|string} moduleDef The module definition. - */ -goog.loadModule = function(moduleDef) { - // NOTE: we allow function definitions to be either in the from - // of a string to eval (which keeps the original source intact) or - // in a eval forbidden environment (CSP) we allow a function definition - // which in its body must call {@code goog.module}, and return the exports - // of the module. - var previousState = goog.moduleLoaderState_; - try { - goog.moduleLoaderState_ = { - moduleName: undefined, - declareLegacyNamespace: false - }; - var exports; - if (goog.isFunction(moduleDef)) { - exports = moduleDef.call(undefined, {}); - } else if (goog.isString(moduleDef)) { - if (goog.useSafari10Workaround()) { - moduleDef = goog.workaroundSafari10EvalBug(moduleDef); - } - - exports = goog.loadModuleFromSource_.call(undefined, moduleDef); - } else { - throw Error('Invalid module definition'); - } - - var moduleName = goog.moduleLoaderState_.moduleName; - if (!goog.isString(moduleName) || !moduleName) { - throw Error('Invalid module name \"' + moduleName + '\"'); - } - - // Don't seal legacy namespaces as they may be uses as a parent of - // another namespace - if (goog.moduleLoaderState_.declareLegacyNamespace) { - goog.constructNamespace_(moduleName, exports); - } else if ( - goog.SEAL_MODULE_EXPORTS && Object.seal && typeof exports == 'object' && - exports != null) { - Object.seal(exports); - } - - goog.loadedModules_[moduleName] = exports; - } finally { - goog.moduleLoaderState_ = previousState; - } -}; - - -/** - * @private @const - */ -goog.loadModuleFromSource_ = /** @type {function(string):?} */ (function() { - // NOTE: we avoid declaring parameters or local variables here to avoid - // masking globals or leaking values into the module definition. - 'use strict'; - var exports = {}; - eval(arguments[0]); - return exports; -}); - - -/** - * Normalize a file path by removing redundant ".." and extraneous "." file - * path components. - * @param {string} path - * @return {string} - * @private - */ -goog.normalizePath_ = function(path) { - var components = path.split('/'); - var i = 0; - while (i < components.length) { - if (components[i] == '.') { - components.splice(i, 1); - } else if ( - i && components[i] == '..' && components[i - 1] && - components[i - 1] != '..') { - components.splice(--i, 2); - } else { - i++; - } - } - return components.join('/'); -}; - - -/** - * Provides a hook for loading a file when using Closure's goog.require() API - * with goog.modules. In particular this hook is provided to support Node.js. - * - * @type {(function(string):string)|undefined} - */ -goog.global.CLOSURE_LOAD_FILE_SYNC; - - -/** - * Loads file by synchronous XHR. Should not be used in production environments. - * @param {string} src Source URL. - * @return {?string} File contents, or null if load failed. - * @private - */ -goog.loadFileSync_ = function(src) { - if (goog.global.CLOSURE_LOAD_FILE_SYNC) { - return goog.global.CLOSURE_LOAD_FILE_SYNC(src); - } else { - try { - /** @type {XMLHttpRequest} */ - var xhr = new goog.global['XMLHttpRequest'](); - xhr.open('get', src, false); - xhr.send(); - // NOTE: Successful http: requests have a status of 200, but successful - // file: requests may have a status of zero. Any other status, or a - // thrown exception (particularly in case of file: requests) indicates - // some sort of error, which we treat as a missing or unavailable file. - return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null; - } catch (err) { - // No need to rethrow or log, since errors should show up on their own. - return null; - } - } -}; - - -/** - * Retrieve and execute a script that needs some sort of wrapping. - * @param {string} src Script source URL. - * @param {boolean} isModule Whether to load as a module. - * @param {boolean} needsTranspile Whether to transpile down to ES3. - * @private - */ -goog.retrieveAndExec_ = function(src, isModule, needsTranspile) { - if (!COMPILED) { - // The full but non-canonicalized URL for later use. - var originalPath = src; - // Canonicalize the path, removing any /./ or /../ since Chrome's debugging - // console doesn't auto-canonicalize XHR loads as it does <script> srcs. - src = goog.normalizePath_(src); - - var importScript = - goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; - - var scriptText = goog.loadFileSync_(src); - if (scriptText == null) { - throw new Error('Load of "' + src + '" failed'); - } - - if (needsTranspile) { - scriptText = goog.transpile_.call(goog.global, scriptText, src); - } - - if (isModule) { - scriptText = goog.wrapModule_(src, scriptText); - } else { - scriptText += '\n//# sourceURL=' + src; - } - var isOldIE = goog.IS_OLD_IE_; - if (isOldIE && goog.oldIeWaiting_) { - goog.dependencies_.deferred[originalPath] = scriptText; - goog.queuedModules_.push(originalPath); - } else { - importScript(src, scriptText); - } - } -}; - - -/** - * Lazily retrieves the transpiler and applies it to the source. - * @param {string} code JS code. - * @param {string} path Path to the code. - * @return {string} The transpiled code. - * @private - */ -goog.transpile_ = function(code, path) { - var jscomp = goog.global['$jscomp']; - if (!jscomp) { - goog.global['$jscomp'] = jscomp = {}; - } - var transpile = jscomp.transpile; - if (!transpile) { - var transpilerPath = goog.basePath + goog.TRANSPILER; - var transpilerCode = goog.loadFileSync_(transpilerPath); - if (transpilerCode) { - // This must be executed synchronously, since by the time we know we - // need it, we're about to load and write the ES6 code synchronously, - // so a normal script-tag load will be too slow. - eval(transpilerCode + '\n//# sourceURL=' + transpilerPath); - // Even though the transpiler is optional, if $gwtExport is found, it's - // a sign the transpiler was loaded and the $jscomp.transpile *should* - // be there. - if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] && - !goog.global['$gwtExport']['$jscomp']['transpile']) { - throw new Error( - 'The transpiler did not properly export the "transpile" ' + - 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport'])); - } - // transpile.js only exports a single $jscomp function, transpile. We - // grab just that and add it to the existing definition of $jscomp which - // contains the polyfills. - goog.global['$jscomp'].transpile = - goog.global['$gwtExport']['$jscomp']['transpile']; - jscomp = goog.global['$jscomp']; - transpile = jscomp.transpile; - } - } - if (!transpile) { - // The transpiler is an optional component. If it's not available then - // replace it with a pass-through function that simply logs. - var suffix = ' requires transpilation but no transpiler was found.'; - transpile = jscomp.transpile = function(code, path) { - // TODO(user): figure out some way to get this error to show up - // in test results, noting that the failure may occur in many - // different ways, including in loadModule() before the test - // runner even comes up. - goog.logToConsole_(path + suffix); - return code; - }; - } - // Note: any transpilation errors/warnings will be logged to the console. - return transpile(code, path); -}; - - -//============================================================================== -// Language Enhancements -//============================================================================== - - -/** - * This is a "fixed" version of the typeof operator. It differs from the typeof - * operator in such a way that null returns 'null' and arrays return 'array'. - * @param {?} value The value to get the type of. - * @return {string} The name of the type. - */ -goog.typeOf = function(value) { - var s = typeof value; - if (s == 'object') { - if (value) { - // Check these first, so we can avoid calling Object.prototype.toString if - // possible. - // - // IE improperly marshals typeof across execution contexts, but a - // cross-context object will still return false for "instanceof Object". - if (value instanceof Array) { - return 'array'; - } else if (value instanceof Object) { - return s; - } - - // HACK: In order to use an Object prototype method on the arbitrary - // value, the compiler requires the value be cast to type Object, - // even though the ECMA spec explicitly allows it. - var className = Object.prototype.toString.call( - /** @type {!Object} */ (value)); - // In Firefox 3.6, attempting to access iframe window objects' length - // property throws an NS_ERROR_FAILURE, so we need to special-case it - // here. - if (className == '[object Window]') { - return 'object'; - } - - // We cannot always use constructor == Array or instanceof Array because - // different frames have different Array objects. In IE6, if the iframe - // where the array was created is destroyed, the array loses its - // prototype. Then dereferencing val.splice here throws an exception, so - // we can't use goog.isFunction. Calling typeof directly returns 'unknown' - // so that will work. In this case, this function will return false and - // most array functions will still work because the array is still - // array-like (supports length and []) even though it has lost its - // prototype. - // Mark Miller noticed that Object.prototype.toString - // allows access to the unforgeable [[Class]] property. - // 15.2.4.2 Object.prototype.toString ( ) - // When the toString method is called, the following steps are taken: - // 1. Get the [[Class]] property of this object. - // 2. Compute a string value by concatenating the three strings - // "[object ", Result(1), and "]". - // 3. Return Result(2). - // and this behavior survives the destruction of the execution context. - if ((className == '[object Array]' || - // In IE all non value types are wrapped as objects across window - // boundaries (not iframe though) so we have to do object detection - // for this edge case. - typeof value.length == 'number' && - typeof value.splice != 'undefined' && - typeof value.propertyIsEnumerable != 'undefined' && - !value.propertyIsEnumerable('splice') - - )) { - return 'array'; - } - // HACK: There is still an array case that fails. - // function ArrayImpostor() {} - // ArrayImpostor.prototype = []; - // var impostor = new ArrayImpostor; - // this can be fixed by getting rid of the fast path - // (value instanceof Array) and solely relying on - // (value && Object.prototype.toString.vall(value) === '[object Array]') - // but that would require many more function calls and is not warranted - // unless closure code is receiving objects from untrusted sources. - - // IE in cross-window calls does not correctly marshal the function type - // (it appears just as an object) so we cannot use just typeof val == - // 'function'. However, if the object has a call property, it is a - // function. - if ((className == '[object Function]' || - typeof value.call != 'undefined' && - typeof value.propertyIsEnumerable != 'undefined' && - !value.propertyIsEnumerable('call'))) { - return 'function'; - } - - } else { - return 'null'; - } - - } else if (s == 'function' && typeof value.call == 'undefined') { - // In Safari typeof nodeList returns 'function', and on Firefox typeof - // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We - // would like to return object for those and we can detect an invalid - // function by making sure that the function object has a call method. - return 'object'; - } - return s; -}; - - -/** - * Returns true if the specified value is null. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is null. - */ -goog.isNull = function(val) { - return val === null; -}; - - -/** - * Returns true if the specified value is defined and not null. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is defined and not null. - */ -goog.isDefAndNotNull = function(val) { - // Note that undefined == null. - return val != null; -}; - - -/** - * Returns true if the specified value is an array. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is an array. - */ -goog.isArray = function(val) { - return goog.typeOf(val) == 'array'; -}; - - -/** - * Returns true if the object looks like an array. To qualify as array like - * the value needs to be either a NodeList or an object with a Number length - * property. As a special case, a function value is not array like, because its - * length property is fixed to correspond to the number of expected arguments. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is an array. - */ -goog.isArrayLike = function(val) { - var type = goog.typeOf(val); - // We do not use goog.isObject here in order to exclude function values. - return type == 'array' || type == 'object' && typeof val.length == 'number'; -}; - - -/** - * Returns true if the object looks like a Date. To qualify as Date-like the - * value needs to be an object and have a getFullYear() function. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is a like a Date. - */ -goog.isDateLike = function(val) { - return goog.isObject(val) && typeof val.getFullYear == 'function'; -}; - - -/** - * Returns true if the specified value is a string. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is a string. - */ -goog.isString = function(val) { - return typeof val == 'string'; -}; - - -/** - * Returns true if the specified value is a boolean. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is boolean. - */ -goog.isBoolean = function(val) { - return typeof val == 'boolean'; -}; - - -/** - * Returns true if the specified value is a number. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is a number. - */ -goog.isNumber = function(val) { - return typeof val == 'number'; -}; - - -/** - * Returns true if the specified value is a function. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is a function. - */ -goog.isFunction = function(val) { - return goog.typeOf(val) == 'function'; -}; - - -/** - * Returns true if the specified value is an object. This includes arrays and - * functions. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is an object. - */ -goog.isObject = function(val) { - var type = typeof val; - return type == 'object' && val != null || type == 'function'; - // return Object(val) === val also works, but is slower, especially if val is - // not an object. -}; - - -/** - * Gets a unique ID for an object. This mutates the object so that further calls - * with the same object as a parameter returns the same value. The unique ID is - * guaranteed to be unique across the current session amongst objects that are - * passed into {@code getUid}. There is no guarantee that the ID is unique or - * consistent across sessions. It is unsafe to generate unique ID for function - * prototypes. - * - * @param {Object} obj The object to get the unique ID for. - * @return {number} The unique ID for the object. - */ -goog.getUid = function(obj) { - // TODO(arv): Make the type stricter, do not accept null. - - // In Opera window.hasOwnProperty exists but always returns false so we avoid - // using it. As a consequence the unique ID generated for BaseClass.prototype - // and SubClass.prototype will be the same. - return obj[goog.UID_PROPERTY_] || - (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); -}; - - -/** - * Whether the given object is already assigned a unique ID. - * - * This does not modify the object. - * - * @param {!Object} obj The object to check. - * @return {boolean} Whether there is an assigned unique id for the object. - */ -goog.hasUid = function(obj) { - return !!obj[goog.UID_PROPERTY_]; -}; - - -/** - * Removes the unique ID from an object. This is useful if the object was - * previously mutated using {@code goog.getUid} in which case the mutation is - * undone. - * @param {Object} obj The object to remove the unique ID field from. - */ -goog.removeUid = function(obj) { - // TODO(arv): Make the type stricter, do not accept null. - - // In IE, DOM nodes are not instances of Object and throw an exception if we - // try to delete. Instead we try to use removeAttribute. - if (obj !== null && 'removeAttribute' in obj) { - obj.removeAttribute(goog.UID_PROPERTY_); - } - - try { - delete obj[goog.UID_PROPERTY_]; - } catch (ex) { - } -}; - - -/** - * Name for unique ID property. Initialized in a way to help avoid collisions - * with other closure JavaScript on the same page. - * @type {string} - * @private - */ -goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); - - -/** - * Counter for UID. - * @type {number} - * @private - */ -goog.uidCounter_ = 0; - - -/** - * Adds a hash code field to an object. The hash code is unique for the - * given object. - * @param {Object} obj The object to get the hash code for. - * @return {number} The hash code for the object. - * @deprecated Use goog.getUid instead. - */ -goog.getHashCode = goog.getUid; - - -/** - * Removes the hash code field from an object. - * @param {Object} obj The object to remove the field from. - * @deprecated Use goog.removeUid instead. - */ -goog.removeHashCode = goog.removeUid; - - -/** - * Clones a value. The input may be an Object, Array, or basic type. Objects and - * arrays will be cloned recursively. - * - * WARNINGS: - * <code>goog.cloneObject</code> does not detect reference loops. Objects that - * refer to themselves will cause infinite recursion. - * - * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies - * UIDs created by <code>getUid</code> into cloned results. - * - * @param {*} obj The value to clone. - * @return {*} A clone of the input value. - * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. - */ -goog.cloneObject = function(obj) { - var type = goog.typeOf(obj); - if (type == 'object' || type == 'array') { - if (obj.clone) { - return obj.clone(); - } - var clone = type == 'array' ? [] : {}; - for (var key in obj) { - clone[key] = goog.cloneObject(obj[key]); - } - return clone; - } - - return obj; -}; - - -/** - * A native implementation of goog.bind. - * @param {?function(this:T, ...)} fn A function to partially apply. - * @param {T} selfObj Specifies the object which this should point to when the - * function is run. - * @param {...*} var_args Additional arguments that are partially applied to the - * function. - * @return {!Function} A partially-applied form of the function goog.bind() was - * invoked as a method of. - * @template T - * @private - */ -goog.bindNative_ = function(fn, selfObj, var_args) { - return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); -}; - - -/** - * A pure-JS implementation of goog.bind. - * @param {?function(this:T, ...)} fn A function to partially apply. - * @param {T} selfObj Specifies the object which this should point to when the - * function is run. - * @param {...*} var_args Additional arguments that are partially applied to the - * function. - * @return {!Function} A partially-applied form of the function goog.bind() was - * invoked as a method of. - * @template T - * @private - */ -goog.bindJs_ = function(fn, selfObj, var_args) { - if (!fn) { - throw new Error(); - } - - if (arguments.length > 2) { - var boundArgs = Array.prototype.slice.call(arguments, 2); - return function() { - // Prepend the bound arguments to the current arguments. - var newArgs = Array.prototype.slice.call(arguments); - Array.prototype.unshift.apply(newArgs, boundArgs); - return fn.apply(selfObj, newArgs); - }; - - } else { - return function() { - return fn.apply(selfObj, arguments); - }; - } -}; - - -/** - * Partially applies this function to a particular 'this object' and zero or - * more arguments. The result is a new function with some arguments of the first - * function pre-filled and the value of this 'pre-specified'. - * - * Remaining arguments specified at call-time are appended to the pre-specified - * ones. - * - * Also see: {@link #partial}. - * - * Usage: - * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2'); - * barMethBound('arg3', 'arg4');</pre> - * - * @param {?function(this:T, ...)} fn A function to partially apply. - * @param {T} selfObj Specifies the object which this should point to when the - * function is run. - * @param {...*} var_args Additional arguments that are partially applied to the - * function. - * @return {!Function} A partially-applied form of the function goog.bind() was - * invoked as a method of. - * @template T - * @suppress {deprecated} See above. - */ -goog.bind = function(fn, selfObj, var_args) { - // TODO(nicksantos): narrow the type signature. - if (Function.prototype.bind && - // NOTE(nicksantos): Somebody pulled base.js into the default Chrome - // extension environment. This means that for Chrome extensions, they get - // the implementation of Function.prototype.bind that calls goog.bind - // instead of the native one. Even worse, we don't want to introduce a - // circular dependency between goog.bind and Function.prototype.bind, so - // we have to hack this to make sure it works correctly. - Function.prototype.bind.toString().indexOf('native code') != -1) { - goog.bind = goog.bindNative_; - } else { - goog.bind = goog.bindJs_; - } - return goog.bind.apply(null, arguments); -}; - - -/** - * Like goog.bind(), except that a 'this object' is not required. Useful when - * the target function is already bound. - * - * Usage: - * var g = goog.partial(f, arg1, arg2); - * g(arg3, arg4); - * - * @param {Function} fn A function to partially apply. - * @param {...*} var_args Additional arguments that are partially applied to fn. - * @return {!Function} A partially-applied form of the function goog.partial() - * was invoked as a method of. - */ -goog.partial = function(fn, var_args) { - var args = Array.prototype.slice.call(arguments, 1); - return function() { - // Clone the array (with slice()) and append additional arguments - // to the existing arguments. - var newArgs = args.slice(); - newArgs.push.apply(newArgs, arguments); - return fn.apply(this, newArgs); - }; -}; - - -/** - * Copies all the members of a source object to a target object. This method - * does not work on all browsers for all objects that contain keys such as - * toString or hasOwnProperty. Use goog.object.extend for this purpose. - * @param {Object} target Target. - * @param {Object} source Source. - */ -goog.mixin = function(target, source) { - for (var x in source) { - target[x] = source[x]; - } - - // For IE7 or lower, the for-in-loop does not contain any properties that are - // not enumerable on the prototype object (for example, isPrototypeOf from - // Object.prototype) but also it will not include 'replace' on objects that - // extend String and change 'replace' (not that it is common for anyone to - // extend anything except Object). -}; - - -/** - * @return {number} An integer value representing the number of milliseconds - * between midnight, January 1, 1970 and the current time. - */ -goog.now = (goog.TRUSTED_SITE && Date.now) || (function() { - // Unary plus operator converts its operand to a number which in - // the case of - // a date is done by calling getTime(). - return +new Date(); - }); - - -/** - * Evals JavaScript in the global scope. In IE this uses execScript, other - * browsers use goog.global.eval. If goog.global.eval does not evaluate in the - * global scope (for example, in Safari), appends a script tag instead. - * Throws an exception if neither execScript or eval is defined. - * @param {string} script JavaScript string. - */ -goog.globalEval = function(script) { - if (goog.global.execScript) { - goog.global.execScript(script, 'JavaScript'); - } else if (goog.global.eval) { - // Test to see if eval works - if (goog.evalWorksForGlobals_ == null) { - goog.global.eval('var _evalTest_ = 1;'); - if (typeof goog.global['_evalTest_'] != 'undefined') { - try { - delete goog.global['_evalTest_']; - } catch (ignore) { - // Microsoft edge fails the deletion above in strict mode. - } - goog.evalWorksForGlobals_ = true; - } else { - goog.evalWorksForGlobals_ = false; - } - } - - if (goog.evalWorksForGlobals_) { - goog.global.eval(script); - } else { - /** @type {Document} */ - var doc = goog.global.document; - var scriptElt = - /** @type {!HTMLScriptElement} */ (doc.createElement('SCRIPT')); - scriptElt.type = 'text/javascript'; - scriptElt.defer = false; - // Note(user): can't use .innerHTML since "t('<test>')" will fail and - // .text doesn't work in Safari 2. Therefore we append a text node. - scriptElt.appendChild(doc.createTextNode(script)); - doc.body.appendChild(scriptElt); - doc.body.removeChild(scriptElt); - } - } else { - throw Error('goog.globalEval not available'); - } -}; - - -/** - * Indicates whether or not we can call 'eval' directly to eval code in the - * global scope. Set to a Boolean by the first call to goog.globalEval (which - * empirically tests whether eval works for globals). @see goog.globalEval - * @type {?boolean} - * @private - */ -goog.evalWorksForGlobals_ = null; - - -/** - * Optional map of CSS class names to obfuscated names used with - * goog.getCssName(). - * @private {!Object<string, string>|undefined} - * @see goog.setCssNameMapping - */ -goog.cssNameMapping_; - - -/** - * Optional obfuscation style for CSS class names. Should be set to either - * 'BY_WHOLE' or 'BY_PART' if defined. - * @type {string|undefined} - * @private - * @see goog.setCssNameMapping - */ -goog.cssNameMappingStyle_; - - - -/** - * A hook for modifying the default behavior goog.getCssName. The function - * if present, will recieve the standard output of the goog.getCssName as - * its input. - * - * @type {(function(string):string)|undefined} - */ -goog.global.CLOSURE_CSS_NAME_MAP_FN; - - -/** - * Handles strings that are intended to be used as CSS class names. - * - * This function works in tandem with @see goog.setCssNameMapping. - * - * Without any mapping set, the arguments are simple joined with a hyphen and - * passed through unaltered. - * - * When there is a mapping, there are two possible styles in which these - * mappings are used. In the BY_PART style, each part (i.e. in between hyphens) - * of the passed in css name is rewritten according to the map. In the BY_WHOLE - * style, the full css name is looked up in the map directly. If a rewrite is - * not specified by the map, the compiler will output a warning. - * - * When the mapping is passed to the compiler, it will replace calls to - * goog.getCssName with the strings from the mapping, e.g. - * var x = goog.getCssName('foo'); - * var y = goog.getCssName(this.baseClass, 'active'); - * becomes: - * var x = 'foo'; - * var y = this.baseClass + '-active'; - * - * If one argument is passed it will be processed, if two are passed only the - * modifier will be processed, as it is assumed the first argument was generated - * as a result of calling goog.getCssName. - * - * @param {string} className The class name. - * @param {string=} opt_modifier A modifier to be appended to the class name. - * @return {string} The class name or the concatenation of the class name and - * the modifier. - */ -goog.getCssName = function(className, opt_modifier) { - // String() is used for compatibility with compiled soy where the passed - // className can be non-string objects. - if (String(className).charAt(0) == '.') { - throw new Error( - 'className passed in goog.getCssName must not start with ".".' + - ' You passed: ' + className); - } - - var getMapping = function(cssName) { - return goog.cssNameMapping_[cssName] || cssName; - }; - - var renameByParts = function(cssName) { - // Remap all the parts individually. - var parts = cssName.split('-'); - var mapped = []; - for (var i = 0; i < parts.length; i++) { - mapped.push(getMapping(parts[i])); - } - return mapped.join('-'); - }; - - var rename; - if (goog.cssNameMapping_) { - rename = - goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts; - } else { - rename = function(a) { - return a; - }; - } - - var result = - opt_modifier ? className + '-' + rename(opt_modifier) : rename(className); - - // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further - // processing of the class name. - if (goog.global.CLOSURE_CSS_NAME_MAP_FN) { - return goog.global.CLOSURE_CSS_NAME_MAP_FN(result); - } - - return result; -}; - - -/** - * Sets the map to check when returning a value from goog.getCssName(). Example: - * <pre> - * goog.setCssNameMapping({ - * "goog": "a", - * "disabled": "b", - * }); - * - * var x = goog.getCssName('goog'); - * // The following evaluates to: "a a-b". - * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled') - * </pre> - * When declared as a map of string literals to string literals, the JSCompiler - * will replace all calls to goog.getCssName() using the supplied map if the - * --process_closure_primitives flag is set. - * - * @param {!Object} mapping A map of strings to strings where keys are possible - * arguments to goog.getCssName() and values are the corresponding values - * that should be returned. - * @param {string=} opt_style The style of css name mapping. There are two valid - * options: 'BY_PART', and 'BY_WHOLE'. - * @see goog.getCssName for a description. - */ -goog.setCssNameMapping = function(mapping, opt_style) { - goog.cssNameMapping_ = mapping; - goog.cssNameMappingStyle_ = opt_style; -}; - - -/** - * To use CSS renaming in compiled mode, one of the input files should have a - * call to goog.setCssNameMapping() with an object literal that the JSCompiler - * can extract and use to replace all calls to goog.getCssName(). In uncompiled - * mode, JavaScript code should be loaded before this base.js file that declares - * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is - * to ensure that the mapping is loaded before any calls to goog.getCssName() - * are made in uncompiled mode. - * - * A hook for overriding the CSS name mapping. - * @type {!Object<string, string>|undefined} - */ -goog.global.CLOSURE_CSS_NAME_MAPPING; - - -if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) { - // This does not call goog.setCssNameMapping() because the JSCompiler - // requires that goog.setCssNameMapping() be called with an object literal. - goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING; -} - - -/** - * Gets a localized message. - * - * This function is a compiler primitive. If you give the compiler a localized - * message bundle, it will replace the string at compile-time with a localized - * version, and expand goog.getMsg call to a concatenated string. - * - * Messages must be initialized in the form: - * <code> - * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); - * </code> - * - * This function produces a string which should be treated as plain text. Use - * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to - * produce SafeHtml. - * - * @param {string} str Translatable string, places holders in the form {$foo}. - * @param {Object<string, string>=} opt_values Maps place holder name to value. - * @return {string} message with placeholders filled. - */ -goog.getMsg = function(str, opt_values) { - if (opt_values) { - str = str.replace(/\{\$([^}]+)}/g, function(match, key) { - return (opt_values != null && key in opt_values) ? opt_values[key] : - match; - }); - } - return str; -}; - - -/** - * Gets a localized message. If the message does not have a translation, gives a - * fallback message. - * - * This is useful when introducing a new message that has not yet been - * translated into all languages. - * - * This function is a compiler primitive. Must be used in the form: - * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code> - * where MSG_A and MSG_B were initialized with goog.getMsg. - * - * @param {string} a The preferred message. - * @param {string} b The fallback message. - * @return {string} The best translated message. - */ -goog.getMsgWithFallback = function(a, b) { - return a; -}; - - -/** - * Exposes an unobfuscated global namespace path for the given object. - * Note that fields of the exported object *will* be obfuscated, unless they are - * exported in turn via this function or goog.exportProperty. - * - * Also handy for making public items that are defined in anonymous closures. - * - * ex. goog.exportSymbol('public.path.Foo', Foo); - * - * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction); - * public.path.Foo.staticFunction(); - * - * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod', - * Foo.prototype.myMethod); - * new public.path.Foo().myMethod(); - * - * @param {string} publicPath Unobfuscated name to export. - * @param {*} object Object the name should point to. - * @param {Object=} opt_objectToExportTo The object to add the path to; default - * is goog.global. - */ -goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) { - goog.exportPath_(publicPath, object, opt_objectToExportTo); -}; - - -/** - * Exports a property unobfuscated into the object's namespace. - * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); - * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); - * @param {Object} object Object whose static property is being exported. - * @param {string} publicName Unobfuscated name to export. - * @param {*} symbol Object the name should point to. - */ -goog.exportProperty = function(object, publicName, symbol) { - object[publicName] = symbol; -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * Usage: - * <pre> - * function ParentClass(a, b) { } - * ParentClass.prototype.foo = function(a) { }; - * - * function ChildClass(a, b, c) { - * ChildClass.base(this, 'constructor', a, b); - * } - * goog.inherits(ChildClass, ParentClass); - * - * var child = new ChildClass('a', 'b', 'see'); - * child.foo(); // This works. - * </pre> - * - * @param {!Function} childCtor Child class. - * @param {!Function} parentCtor Parent class. - */ -goog.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {} - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - /** @override */ - childCtor.prototype.constructor = childCtor; - - /** - * Calls superclass constructor/method. - * - * This function is only available if you use goog.inherits to - * express inheritance relationships between classes. - * - * NOTE: This is a replacement for goog.base and for superClass_ - * property defined in childCtor. - * - * @param {!Object} me Should always be "this". - * @param {string} methodName The method name to call. Calling - * superclass constructor can be done with the special string - * 'constructor'. - * @param {...*} var_args The arguments to pass to superclass - * method/constructor. - * @return {*} The return value of the superclass method/constructor. - */ - childCtor.base = function(me, methodName, var_args) { - // Copying using loop to avoid deop due to passing arguments object to - // function. This is faster in many JS engines as of late 2014. - var args = new Array(arguments.length - 2); - for (var i = 2; i < arguments.length; i++) { - args[i - 2] = arguments[i]; - } - return parentCtor.prototype[methodName].apply(me, args); - }; -}; - - -/** - * Call up to the superclass. - * - * If this is called from a constructor, then this calls the superclass - * constructor with arguments 1-N. - * - * If this is called from a prototype method, then you must pass the name of the - * method as the second argument to this function. If you do not, you will get a - * runtime error. This calls the superclass' method with arguments 2-N. - * - * This function only works if you use goog.inherits to express inheritance - * relationships between your classes. - * - * This function is a compiler primitive. At compile-time, the compiler will do - * macro expansion to remove a lot of the extra overhead that this function - * introduces. The compiler will also enforce a lot of the assumptions that this - * function makes, and treat it as a compiler error if you break them. - * - * @param {!Object} me Should always be "this". - * @param {*=} opt_methodName The method name if calling a super method. - * @param {...*} var_args The rest of the arguments. - * @return {*} The return value of the superclass method. - * @suppress {es5Strict} This method can not be used in strict mode, but - * all Closure Library consumers must depend on this file. - */ -goog.base = function(me, opt_methodName, var_args) { - var caller = arguments.callee.caller; - - if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) { - throw Error( - 'arguments.caller not defined. goog.base() cannot be used ' + - 'with strict mode code. See ' + - 'http://www.ecma-international.org/ecma-262/5.1/#sec-C'); - } - - if (caller.superClass_) { - // Copying using loop to avoid deop due to passing arguments object to - // function. This is faster in many JS engines as of late 2014. - var ctorArgs = new Array(arguments.length - 1); - for (var i = 1; i < arguments.length; i++) { - ctorArgs[i - 1] = arguments[i]; - } - // This is a constructor. Call the superclass constructor. - return caller.superClass_.constructor.apply(me, ctorArgs); - } - - // Copying using loop to avoid deop due to passing arguments object to - // function. This is faster in many JS engines as of late 2014. - var args = new Array(arguments.length - 2); - for (var i = 2; i < arguments.length; i++) { - args[i - 2] = arguments[i]; - } - var foundCaller = false; - for (var ctor = me.constructor; ctor; - ctor = ctor.superClass_ && ctor.superClass_.constructor) { - if (ctor.prototype[opt_methodName] === caller) { - foundCaller = true; - } else if (foundCaller) { - return ctor.prototype[opt_methodName].apply(me, args); - } - } - - // If we did not find the caller in the prototype chain, then one of two - // things happened: - // 1) The caller is an instance method. - // 2) This method was not called by the right caller. - if (me[opt_methodName] === caller) { - return me.constructor.prototype[opt_methodName].apply(me, args); - } else { - throw Error( - 'goog.base called from a method of one name ' + - 'to a method of a different name'); - } -}; - - -/** - * Allow for aliasing within scope functions. This function exists for - * uncompiled code - in compiled code the calls will be inlined and the aliases - * applied. In uncompiled code the function is simply run since the aliases as - * written are valid JavaScript. - * - * - * @param {function()} fn Function to call. This function can contain aliases - * to namespaces (e.g. "var dom = goog.dom") or classes - * (e.g. "var Timer = goog.Timer"). - */ -goog.scope = function(fn) { - if (goog.isInModuleLoader_()) { - throw Error('goog.scope is not supported within a goog.module.'); - } - fn.call(goog.global); -}; - - -/* - * To support uncompiled, strict mode bundles that use eval to divide source - * like so: - * eval('someSource;//# sourceUrl sourcefile.js'); - * We need to export the globally defined symbols "goog" and "COMPILED". - * Exporting "goog" breaks the compiler optimizations, so we required that - * be defined externally. - * NOTE: We don't use goog.exportSymbol here because we don't want to trigger - * extern generation when that compiler option is enabled. - */ -if (!COMPILED) { - goog.global['COMPILED'] = COMPILED; -} - - -//============================================================================== -// goog.defineClass implementation -//============================================================================== - - -/** - * Creates a restricted form of a Closure "class": - * - from the compiler's perspective, the instance returned from the - * constructor is sealed (no new properties may be added). This enables - * better checks. - * - the compiler will rewrite this definition to a form that is optimal - * for type checking and optimization (initially this will be a more - * traditional form). - * - * @param {Function} superClass The superclass, Object or null. - * @param {goog.defineClass.ClassDescriptor} def - * An object literal describing - * the class. It may have the following properties: - * "constructor": the constructor function - * "statics": an object literal containing methods to add to the constructor - * as "static" methods or a function that will receive the constructor - * function as its only parameter to which static properties can - * be added. - * all other properties are added to the prototype. - * @return {!Function} The class constructor. - */ -goog.defineClass = function(superClass, def) { - // TODO(johnlenz): consider making the superClass an optional parameter. - var constructor = def.constructor; - var statics = def.statics; - // Wrap the constructor prior to setting up the prototype and static methods. - if (!constructor || constructor == Object.prototype.constructor) { - constructor = function() { - throw Error('cannot instantiate an interface (no constructor defined).'); - }; - } - - var cls = goog.defineClass.createSealingConstructor_(constructor, superClass); - if (superClass) { - goog.inherits(cls, superClass); - } - - // Remove all the properties that should not be copied to the prototype. - delete def.constructor; - delete def.statics; - - goog.defineClass.applyProperties_(cls.prototype, def); - if (statics != null) { - if (statics instanceof Function) { - statics(cls); - } else { - goog.defineClass.applyProperties_(cls, statics); - } - } - - return cls; -}; - - -/** - * @typedef {{ - * constructor: (!Function|undefined), - * statics: (Object|undefined|function(Function):void) - * }} - */ -goog.defineClass.ClassDescriptor; - - -/** - * @define {boolean} Whether the instances returned by goog.defineClass should - * be sealed when possible. - * - * When sealing is disabled the constructor function will not be wrapped by - * goog.defineClass, making it incompatible with ES6 class methods. - */ -goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG); - - -/** - * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is - * defined, this function will wrap the constructor in a function that seals the - * results of the provided constructor function. - * - * @param {!Function} ctr The constructor whose results maybe be sealed. - * @param {Function} superClass The superclass constructor. - * @return {!Function} The replacement constructor. - * @private - */ -goog.defineClass.createSealingConstructor_ = function(ctr, superClass) { - if (!goog.defineClass.SEAL_CLASS_INSTANCES) { - // Do now wrap the constructor when sealing is disabled. Angular code - // depends on this for injection to work properly. - return ctr; - } - - // Compute whether the constructor is sealable at definition time, rather - // than when the instance is being constructed. - var superclassSealable = !goog.defineClass.isUnsealable_(superClass); - - /** - * @this {Object} - * @return {?} - */ - var wrappedCtr = function() { - // Don't seal an instance of a subclass when it calls the constructor of - // its super class as there is most likely still setup to do. - var instance = ctr.apply(this, arguments) || this; - instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_]; - - if (this.constructor === wrappedCtr && superclassSealable && - Object.seal instanceof Function) { - Object.seal(instance); - } - return instance; - }; - - return wrappedCtr; -}; - - -/** - * @param {Function} ctr The constructor to test. - * @return {boolean} Whether the constructor has been tagged as unsealable - * using goog.tagUnsealableClass. - * @private - */ -goog.defineClass.isUnsealable_ = function(ctr) { - return ctr && ctr.prototype && - ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]; -}; - - -// TODO(johnlenz): share these values with the goog.object -/** - * The names of the fields that are defined on Object.prototype. - * @type {!Array<string>} - * @private - * @const - */ -goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [ - 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', - 'toLocaleString', 'toString', 'valueOf' -]; - - -// TODO(johnlenz): share this function with the goog.object -/** - * @param {!Object} target The object to add properties to. - * @param {!Object} source The object to copy properties from. - * @private - */ -goog.defineClass.applyProperties_ = function(target, source) { - // TODO(johnlenz): update this to support ES5 getters/setters - - var key; - for (key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - - // For IE the for-in-loop does not contain any properties that are not - // enumerable on the prototype object (for example isPrototypeOf from - // Object.prototype) and it will also not include 'replace' on objects that - // extend String and change 'replace' (not that it is common for anyone to - // extend anything except Object). - for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) { - key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i]; - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } -}; - - -/** - * Sealing classes breaks the older idiom of assigning properties on the - * prototype rather than in the constructor. As such, goog.defineClass - * must not seal subclasses of these old-style classes until they are fixed. - * Until then, this marks a class as "broken", instructing defineClass - * not to seal subclasses. - * @param {!Function} ctr The legacy constructor to tag as unsealable. - */ -goog.tagUnsealableClass = function(ctr) { - if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) { - ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true; - } -}; - - -/** - * Name for unsealable tag property. - * @const @private {string} - */ -goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable'; - - -/** - * Returns a newly created map from language mode string to a boolean - * indicating whether transpilation should be done for that mode. - * - * Guaranteed invariant: - * For any two modes, l1 and l2 where l2 is a newer mode than l1, - * `map[l1] == true` implies that `map[l2] == true`. - * @private - * @return {!Object<string, boolean>} - */ -goog.createRequiresTranspilation_ = function() { - var /** !Object<string, boolean> */ requiresTranspilation = {'es3': false}; - var transpilationRequiredForAllLaterModes = false; - - /** - * Adds an entry to requiresTranspliation for the given language mode. - * - * IMPORTANT: Calls must be made in order from oldest to newest language - * mode. - * @param {string} modeName - * @param {function(): boolean} isSupported Returns true if the JS engine - * supports the given mode. - */ - function addNewerLanguageTranspilationCheck(modeName, isSupported) { - if (transpilationRequiredForAllLaterModes) { - requiresTranspilation[modeName] = true; - } else if (isSupported()) { - requiresTranspilation[modeName] = false; - } else { - requiresTranspilation[modeName] = true; - transpilationRequiredForAllLaterModes = true; - } - } - - /** - * Does the given code evaluate without syntax errors and return a truthy - * result? - */ - function /** boolean */ evalCheck(/** string */ code) { - try { - return !!eval(code); - } catch (ignored) { - return false; - } - } - - var userAgent = goog.global.navigator && goog.global.navigator.userAgent ? - goog.global.navigator.userAgent : - ''; - - // Identify ES3-only browsers by their incorrect treatment of commas. - addNewerLanguageTranspilationCheck('es5', function() { - return evalCheck('[1,].length==1'); - }); - addNewerLanguageTranspilationCheck('es6', function() { - // Edge has a non-deterministic (i.e., not reproducible) bug with ES6: - // https://github.com/Microsoft/ChakraCore/issues/1496. - var re = /Edge\/(\d+)(\.\d)*/i; - var edgeUserAgent = userAgent.match(re); - if (edgeUserAgent && Number(edgeUserAgent[1]) < 15) { - return false; - } - // Test es6: [FF50 (?), Edge 14 (?), Chrome 50] - // (a) default params (specifically shadowing locals), - // (b) destructuring, (c) block-scoped functions, - // (d) for-of (const), (e) new.target/Reflect.construct - var es6fullTest = - 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' + - 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' + - 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' + - 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' + - '==3}'; - - return evalCheck('(()=>{"use strict";' + es6fullTest + '})()'); - }); - // TODO(joeltine): Remove es6-impl references for b/31340605. - // Consider es6-impl (widely-implemented es6 features) to be supported - // whenever es6 is supported. Technically es6-impl is a lower level of - // support than es6, but we don't have tests specifically for it. - addNewerLanguageTranspilationCheck('es6-impl', function() { - return true; - }); - // ** and **= are the only new features in 'es7' - addNewerLanguageTranspilationCheck('es7', function() { - return evalCheck('2 ** 2 == 4'); - }); - // async functions are the only new features in 'es8' - addNewerLanguageTranspilationCheck('es8', function() { - return evalCheck('async () => 1, true'); - }); - return requiresTranspilation; -}; - -goog.provide('ol.array'); - - -/** - * Performs a binary search on the provided sorted list and returns the index of the item if found. If it can't be found it'll return -1. - * https://github.com/darkskyapp/binary-search - * - * @param {Array.<*>} haystack Items to search through. - * @param {*} needle The item to look for. - * @param {Function=} opt_comparator Comparator function. - * @return {number} The index of the item if found, -1 if not. - */ -ol.array.binarySearch = function(haystack, needle, opt_comparator) { - var mid, cmp; - var comparator = opt_comparator || ol.array.numberSafeCompareFunction; - var low = 0; - var high = haystack.length; - var found = false; - - while (low < high) { - /* Note that "(low + high) >>> 1" may overflow, and results in a typecast - * to double (which gives the wrong results). */ - mid = low + (high - low >> 1); - cmp = +comparator(haystack[mid], needle); - - if (cmp < 0.0) { /* Too low. */ - low = mid + 1; - - } else { /* Key found or too high */ - high = mid; - found = !cmp; - } - } - - /* Key not found. */ - return found ? low : ~low; -}; - - -/** - * Compare function for array sort that is safe for numbers. - * @param {*} a The first object to be compared. - * @param {*} b The second object to be compared. - * @return {number} A negative number, zero, or a positive number as the first - * argument is less than, equal to, or greater than the second. - */ -ol.array.numberSafeCompareFunction = function(a, b) { - return a > b ? 1 : a < b ? -1 : 0; -}; - - -/** - * Whether the array contains the given object. - * @param {Array.<*>} arr The array to test for the presence of the element. - * @param {*} obj The object for which to test. - * @return {boolean} The object is in the array. - */ -ol.array.includes = function(arr, obj) { - return arr.indexOf(obj) >= 0; -}; - - -/** - * @param {Array.<number>} arr Array. - * @param {number} target Target. - * @param {number} direction 0 means return the nearest, > 0 - * means return the largest nearest, < 0 means return the - * smallest nearest. - * @return {number} Index. - */ -ol.array.linearFindNearest = function(arr, target, direction) { - var n = arr.length; - if (arr[0] <= target) { - return 0; - } else if (target <= arr[n - 1]) { - return n - 1; - } else { - var i; - if (direction > 0) { - for (i = 1; i < n; ++i) { - if (arr[i] < target) { - return i - 1; - } - } - } else if (direction < 0) { - for (i = 1; i < n; ++i) { - if (arr[i] <= target) { - return i; - } - } - } else { - for (i = 1; i < n; ++i) { - if (arr[i] == target) { - return i; - } else if (arr[i] < target) { - if (arr[i - 1] - target < target - arr[i]) { - return i - 1; - } else { - return i; - } - } - } - } - return n - 1; - } -}; - - -/** - * @param {Array.<*>} arr Array. - * @param {number} begin Begin index. - * @param {number} end End index. - */ -ol.array.reverseSubArray = function(arr, begin, end) { - while (begin < end) { - var tmp = arr[begin]; - arr[begin] = arr[end]; - arr[end] = tmp; - ++begin; - --end; - } -}; - - -/** - * @param {Array.<VALUE>} arr The array to modify. - * @param {Array.<VALUE>|VALUE} data The elements or arrays of elements - * to add to arr. - * @template VALUE - */ -ol.array.extend = function(arr, data) { - var i; - var extension = Array.isArray(data) ? data : [data]; - var length = extension.length; - for (i = 0; i < length; i++) { - arr[arr.length] = extension[i]; - } -}; - - -/** - * @param {Array.<VALUE>} arr The array to modify. - * @param {VALUE} obj The element to remove. - * @template VALUE - * @return {boolean} If the element was removed. - */ -ol.array.remove = function(arr, obj) { - var i = arr.indexOf(obj); - var found = i > -1; - if (found) { - arr.splice(i, 1); - } - return found; -}; - - -/** - * @param {Array.<VALUE>} arr The array to search in. - * @param {function(VALUE, number, ?) : boolean} func The function to compare. - * @template VALUE - * @return {VALUE} The element found. - */ -ol.array.find = function(arr, func) { - var length = arr.length >>> 0; - var value; - - for (var i = 0; i < length; i++) { - value = arr[i]; - if (func(value, i, arr)) { - return value; - } - } - return null; -}; - - -/** - * @param {Array|Uint8ClampedArray} arr1 The first array to compare. - * @param {Array|Uint8ClampedArray} arr2 The second array to compare. - * @return {boolean} Whether the two arrays are equal. - */ -ol.array.equals = function(arr1, arr2) { - var len1 = arr1.length; - if (len1 !== arr2.length) { - return false; - } - for (var i = 0; i < len1; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } - } - return true; -}; - - -/** - * @param {Array.<*>} arr The array to sort (modifies original). - * @param {Function} compareFnc Comparison function. - */ -ol.array.stableSort = function(arr, compareFnc) { - var length = arr.length; - var tmp = Array(arr.length); - var i; - for (i = 0; i < length; i++) { - tmp[i] = {index: i, value: arr[i]}; - } - tmp.sort(function(a, b) { - return compareFnc(a.value, b.value) || a.index - b.index; - }); - for (i = 0; i < arr.length; i++) { - arr[i] = tmp[i].value; - } -}; - - -/** - * @param {Array.<*>} arr The array to search in. - * @param {Function} func Comparison function. - * @return {number} Return index. - */ -ol.array.findIndex = function(arr, func) { - var index; - var found = !arr.every(function(el, idx) { - index = idx; - return !func(el, idx, arr); - }); - return found ? index : -1; -}; - - -/** - * @param {Array.<*>} arr The array to test. - * @param {Function=} opt_func Comparison function. - * @param {boolean=} opt_strict Strictly sorted (default false). - * @return {boolean} Return index. - */ -ol.array.isSorted = function(arr, opt_func, opt_strict) { - var compare = opt_func || ol.array.numberSafeCompareFunction; - return arr.every(function(currentVal, index) { - if (index === 0) { - return true; - } - var res = compare(arr[index - 1], currentVal); - return !(res > 0 || opt_strict && res === 0); - }); -}; - -goog.provide('ol'); - - -/** - * Constants defined with the define tag cannot be changed in application - * code, but can be set at compile time. - * Some reduce the size of the build in advanced compile mode. - */ - - -/** - * @define {boolean} Assume touch. Default is `false`. - */ -ol.ASSUME_TOUCH = false; - - -/** - * TODO: rename this to something having to do with tile grids - * see https://github.com/openlayers/openlayers/issues/2076 - * @define {number} Default maximum zoom for default tile grids. - */ -ol.DEFAULT_MAX_ZOOM = 42; - - -/** - * @define {number} Default min zoom level for the map view. Default is `0`. - */ -ol.DEFAULT_MIN_ZOOM = 0; - - -/** - * @define {number} Default maximum allowed threshold (in pixels) for - * reprojection triangulation. Default is `0.5`. - */ -ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD = 0.5; - - -/** - * @define {number} Default tile size. - */ -ol.DEFAULT_TILE_SIZE = 256; - - -/** - * @define {string} Default WMS version. - */ -ol.DEFAULT_WMS_VERSION = '1.3.0'; - - -/** - * @define {boolean} Enable the Canvas renderer. Default is `true`. Setting - * this to false at compile time in advanced mode removes all code - * supporting the Canvas renderer from the build. - */ -ol.ENABLE_CANVAS = true; - - -/** - * @define {boolean} Enable integration with the Proj4js library. Default is - * `true`. - */ -ol.ENABLE_PROJ4JS = true; - - -/** - * @define {boolean} Enable automatic reprojection of raster sources. Default is - * `true`. - */ -ol.ENABLE_RASTER_REPROJECTION = true; - - -/** - * @define {boolean} Enable the WebGL renderer. Default is `true`. Setting - * this to false at compile time in advanced mode removes all code - * supporting the WebGL renderer from the build. - */ -ol.ENABLE_WEBGL = true; - - -/** - * @define {boolean} Include debuggable shader sources. Default is `true`. - * This should be set to `false` for production builds (if `ol.ENABLE_WEBGL` - * is `true`). - */ -ol.DEBUG_WEBGL = true; - - -/** - * @define {number} The size in pixels of the first atlas image. Default is - * `256`. - */ -ol.INITIAL_ATLAS_SIZE = 256; - - -/** - * @define {number} The maximum size in pixels of atlas images. Default is - * `-1`, meaning it is not used (and `ol.WEBGL_MAX_TEXTURE_SIZE` is - * used instead). - */ -ol.MAX_ATLAS_SIZE = -1; - - -/** - * @define {number} Maximum mouse wheel delta. - */ -ol.MOUSEWHEELZOOM_MAXDELTA = 1; - - -/** - * @define {number} Maximum width and/or height extent ratio that determines - * when the overview map should be zoomed out. - */ -ol.OVERVIEWMAP_MAX_RATIO = 0.75; - - -/** - * @define {number} Minimum width and/or height extent ratio that determines - * when the overview map should be zoomed in. - */ -ol.OVERVIEWMAP_MIN_RATIO = 0.1; - - -/** - * @define {number} Maximum number of source tiles for raster reprojection of - * a single tile. - * If too many source tiles are determined to be loaded to create a single - * reprojected tile the browser can become unresponsive or even crash. - * This can happen if the developer defines projections improperly and/or - * with unlimited extents. - * If too many tiles are required, no tiles are loaded and - * `ol.TileState.ERROR` state is set. Default is `100`. - */ -ol.RASTER_REPROJECTION_MAX_SOURCE_TILES = 100; - - -/** - * @define {number} Maximum number of subdivision steps during raster - * reprojection triangulation. Prevents high memory usage and large - * number of proj4 calls (for certain transformations and areas). - * At most `2*(2^this)` triangles are created for each triangulated - * extent (tile/image). Default is `10`. - */ -ol.RASTER_REPROJECTION_MAX_SUBDIVISION = 10; - - -/** - * @define {number} Maximum allowed size of triangle relative to world width. - * When transforming corners of world extent between certain projections, - * the resulting triangulation seems to have zero error and no subdivision - * is performed. - * If the triangle width is more than this (relative to world width; 0-1), - * subdivison is forced (up to `ol.RASTER_REPROJECTION_MAX_SUBDIVISION`). - * Default is `0.25`. - */ -ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH = 0.25; - - -/** - * @define {number} Tolerance for geometry simplification in device pixels. - */ -ol.SIMPLIFY_TOLERANCE = 0.5; - - -/** - * @define {number} Texture cache high water mark. - */ -ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024; - - -/** - * @define {string} OpenLayers version. - */ -ol.VERSION = ''; - - -/** - * The maximum supported WebGL texture size in pixels. If WebGL is not - * supported, the value is set to `undefined`. - * @const - * @type {number|undefined} - */ -ol.WEBGL_MAX_TEXTURE_SIZE; // value is set in `ol.has` - - -/** - * List of supported WebGL extensions. - * @const - * @type {Array.<string>} - */ -ol.WEBGL_EXTENSIONS; // value is set in `ol.has` - - -/** - * Inherit the prototype methods from one constructor into another. - * - * Usage: - * - * function ParentClass(a, b) { } - * ParentClass.prototype.foo = function(a) { } - * - * function ChildClass(a, b, c) { - * // Call parent constructor - * ParentClass.call(this, a, b); - * } - * ol.inherits(ChildClass, ParentClass); - * - * var child = new ChildClass('a', 'b', 'see'); - * child.foo(); // This works. - * - * @param {!Function} childCtor Child constructor. - * @param {!Function} parentCtor Parent constructor. - * @function - * @api - */ -ol.inherits = function(childCtor, parentCtor) { - childCtor.prototype = Object.create(parentCtor.prototype); - childCtor.prototype.constructor = childCtor; -}; - - -/** - * A reusable function, used e.g. as a default for callbacks. - * - * @return {undefined} Nothing. - */ -ol.nullFunction = function() {}; - - -/** - * Gets a unique ID for an object. This mutates the object so that further calls - * with the same object as a parameter returns the same value. Unique IDs are generated - * as a strictly increasing sequence. Adapted from goog.getUid. - * - * @param {Object} obj The object to get the unique ID for. - * @return {number} The unique ID for the object. - */ -ol.getUid = function(obj) { - return obj.ol_uid || - (obj.ol_uid = ++ol.uidCounter_); -}; - - -/** - * Counter for getUid. - * @type {number} - * @private - */ -ol.uidCounter_ = 0; - -goog.provide('ol.AssertionError'); - -goog.require('ol'); - -/** - * Error object thrown when an assertion failed. This is an ECMA-262 Error, - * extended with a `code` property. - * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error} - * @constructor - * @extends {Error} - * @implements {oli.AssertionError} - * @param {number} code Error code. - */ -ol.AssertionError = function(code) { - - var path = ol.VERSION ? ol.VERSION.split('-')[0] : 'latest'; - - /** - * @type {string} - */ - this.message = 'Assertion failed. See https://openlayers.org/en/' + path + - '/doc/errors/#' + code + ' for details.'; - - /** - * Error code. The meaning of the code can be found on - * {@link https://openlayers.org/en/latest/doc/errors/} (replace `latest` with - * the version found in the OpenLayers script's header comment if a version - * other than the latest is used). - * @type {number} - * @api - */ - this.code = code; - - this.name = 'AssertionError'; - -}; -ol.inherits(ol.AssertionError, Error); - -goog.provide('ol.asserts'); - -goog.require('ol.AssertionError'); - - -/** - * @param {*} assertion Assertion we expected to be truthy. - * @param {number} errorCode Error code. - */ -ol.asserts.assert = function(assertion, errorCode) { - if (!assertion) { - throw new ol.AssertionError(errorCode); - } -}; - -goog.provide('ol.TileRange'); - - -/** - * A representation of a contiguous block of tiles. A tile range is specified - * by its min/max tile coordinates and is inclusive of coordinates. - * - * @constructor - * @param {number} minX Minimum X. - * @param {number} maxX Maximum X. - * @param {number} minY Minimum Y. - * @param {number} maxY Maximum Y. - * @struct - */ -ol.TileRange = function(minX, maxX, minY, maxY) { - - /** - * @type {number} - */ - this.minX = minX; - - /** - * @type {number} - */ - this.maxX = maxX; - - /** - * @type {number} - */ - this.minY = minY; - - /** - * @type {number} - */ - this.maxY = maxY; - -}; - - -/** - * @param {number} minX Minimum X. - * @param {number} maxX Maximum X. - * @param {number} minY Minimum Y. - * @param {number} maxY Maximum Y. - * @param {ol.TileRange|undefined} tileRange TileRange. - * @return {ol.TileRange} Tile range. - */ -ol.TileRange.createOrUpdate = function(minX, maxX, minY, maxY, tileRange) { - if (tileRange !== undefined) { - tileRange.minX = minX; - tileRange.maxX = maxX; - tileRange.minY = minY; - tileRange.maxY = maxY; - return tileRange; - } else { - return new ol.TileRange(minX, maxX, minY, maxY); - } -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @return {boolean} Contains tile coordinate. - */ -ol.TileRange.prototype.contains = function(tileCoord) { - return this.containsXY(tileCoord[1], tileCoord[2]); -}; - - -/** - * @param {ol.TileRange} tileRange Tile range. - * @return {boolean} Contains. - */ -ol.TileRange.prototype.containsTileRange = function(tileRange) { - return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && - this.minY <= tileRange.minY && tileRange.maxY <= this.maxY; -}; - - -/** - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @return {boolean} Contains coordinate. - */ -ol.TileRange.prototype.containsXY = function(x, y) { - return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; -}; - - -/** - * @param {ol.TileRange} tileRange Tile range. - * @return {boolean} Equals. - */ -ol.TileRange.prototype.equals = function(tileRange) { - return this.minX == tileRange.minX && this.minY == tileRange.minY && - this.maxX == tileRange.maxX && this.maxY == tileRange.maxY; -}; - - -/** - * @param {ol.TileRange} tileRange Tile range. - */ -ol.TileRange.prototype.extend = function(tileRange) { - if (tileRange.minX < this.minX) { - this.minX = tileRange.minX; - } - if (tileRange.maxX > this.maxX) { - this.maxX = tileRange.maxX; - } - if (tileRange.minY < this.minY) { - this.minY = tileRange.minY; - } - if (tileRange.maxY > this.maxY) { - this.maxY = tileRange.maxY; - } -}; - - -/** - * @return {number} Height. - */ -ol.TileRange.prototype.getHeight = function() { - return this.maxY - this.minY + 1; -}; - - -/** - * @return {ol.Size} Size. - */ -ol.TileRange.prototype.getSize = function() { - return [this.getWidth(), this.getHeight()]; -}; - - -/** - * @return {number} Width. - */ -ol.TileRange.prototype.getWidth = function() { - return this.maxX - this.minX + 1; -}; - - -/** - * @param {ol.TileRange} tileRange Tile range. - * @return {boolean} Intersects. - */ -ol.TileRange.prototype.intersects = function(tileRange) { - return this.minX <= tileRange.maxX && - this.maxX >= tileRange.minX && - this.minY <= tileRange.maxY && - this.maxY >= tileRange.minY; -}; - -goog.provide('ol.math'); - -goog.require('ol.asserts'); - - -/** - * Takes a number and clamps it to within the provided bounds. - * @param {number} value The input number. - * @param {number} min The minimum value to return. - * @param {number} max The maximum value to return. - * @return {number} The input number if it is within bounds, or the nearest - * number within the bounds. - */ -ol.math.clamp = function(value, min, max) { - return Math.min(Math.max(value, min), max); -}; - - -/** - * Return the hyperbolic cosine of a given number. The method will use the - * native `Math.cosh` function if it is available, otherwise the hyperbolic - * cosine will be calculated via the reference implementation of the Mozilla - * developer network. - * - * @param {number} x X. - * @return {number} Hyperbolic cosine of x. - */ -ol.math.cosh = (function() { - // Wrapped in a iife, to save the overhead of checking for the native - // implementation on every invocation. - var cosh; - if ('cosh' in Math) { - // The environment supports the native Math.cosh function, use it… - cosh = Math.cosh; - } else { - // … else, use the reference implementation of MDN: - cosh = function(x) { - var y = Math.exp(x); - return (y + 1 / y) / 2; - }; - } - return cosh; -}()); - - -/** - * @param {number} x X. - * @return {number} The smallest power of two greater than or equal to x. - */ -ol.math.roundUpToPowerOfTwo = function(x) { - ol.asserts.assert(0 < x, 29); // `x` must be greater than `0` - return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2)); -}; - - -/** - * Returns the square of the closest distance between the point (x, y) and the - * line segment (x1, y1) to (x2, y2). - * @param {number} x X. - * @param {number} y Y. - * @param {number} x1 X1. - * @param {number} y1 Y1. - * @param {number} x2 X2. - * @param {number} y2 Y2. - * @return {number} Squared distance. - */ -ol.math.squaredSegmentDistance = function(x, y, x1, y1, x2, y2) { - var dx = x2 - x1; - var dy = y2 - y1; - if (dx !== 0 || dy !== 0) { - var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); - if (t > 1) { - x1 = x2; - y1 = y2; - } else if (t > 0) { - x1 += dx * t; - y1 += dy * t; - } - } - return ol.math.squaredDistance(x, y, x1, y1); -}; - - -/** - * Returns the square of the distance between the points (x1, y1) and (x2, y2). - * @param {number} x1 X1. - * @param {number} y1 Y1. - * @param {number} x2 X2. - * @param {number} y2 Y2. - * @return {number} Squared distance. - */ -ol.math.squaredDistance = function(x1, y1, x2, y2) { - var dx = x2 - x1; - var dy = y2 - y1; - return dx * dx + dy * dy; -}; - - -/** - * Solves system of linear equations using Gaussian elimination method. - * - * @param {Array.<Array.<number>>} mat Augmented matrix (n x n + 1 column) - * in row-major order. - * @return {Array.<number>} The resulting vector. - */ -ol.math.solveLinearSystem = function(mat) { - var n = mat.length; - - for (var i = 0; i < n; i++) { - // Find max in the i-th column (ignoring i - 1 first rows) - var maxRow = i; - var maxEl = Math.abs(mat[i][i]); - for (var r = i + 1; r < n; r++) { - var absValue = Math.abs(mat[r][i]); - if (absValue > maxEl) { - maxEl = absValue; - maxRow = r; - } - } - - if (maxEl === 0) { - return null; // matrix is singular - } - - // Swap max row with i-th (current) row - var tmp = mat[maxRow]; - mat[maxRow] = mat[i]; - mat[i] = tmp; - - // Subtract the i-th row to make all the remaining rows 0 in the i-th column - for (var j = i + 1; j < n; j++) { - var coef = -mat[j][i] / mat[i][i]; - for (var k = i; k < n + 1; k++) { - if (i == k) { - mat[j][k] = 0; - } else { - mat[j][k] += coef * mat[i][k]; - } - } - } - } - - // Solve Ax=b for upper triangular matrix A (mat) - var x = new Array(n); - for (var l = n - 1; l >= 0; l--) { - x[l] = mat[l][n] / mat[l][l]; - for (var m = l - 1; m >= 0; m--) { - mat[m][n] -= mat[m][l] * x[l]; - } - } - return x; -}; - - -/** - * Converts radians to to degrees. - * - * @param {number} angleInRadians Angle in radians. - * @return {number} Angle in degrees. - */ -ol.math.toDegrees = function(angleInRadians) { - return angleInRadians * 180 / Math.PI; -}; - - -/** - * Converts degrees to radians. - * - * @param {number} angleInDegrees Angle in degrees. - * @return {number} Angle in radians. - */ -ol.math.toRadians = function(angleInDegrees) { - return angleInDegrees * Math.PI / 180; -}; - -/** - * Returns the modulo of a / b, depending on the sign of b. - * - * @param {number} a Dividend. - * @param {number} b Divisor. - * @return {number} Modulo. - */ -ol.math.modulo = function(a, b) { - var r = a % b; - return r * b < 0 ? r + b : r; -}; - -/** - * Calculates the linearly interpolated value of x between a and b. - * - * @param {number} a Number - * @param {number} b Number - * @param {number} x Value to be interpolated. - * @return {number} Interpolated value. - */ -ol.math.lerp = function(a, b, x) { - return a + x * (b - a); -}; - -goog.provide('ol.size'); - - -/** - * Returns a buffered size. - * @param {ol.Size} size Size. - * @param {number} buffer Buffer. - * @param {ol.Size=} opt_size Optional reusable size array. - * @return {ol.Size} The buffered size. - */ -ol.size.buffer = function(size, buffer, opt_size) { - if (opt_size === undefined) { - opt_size = [0, 0]; - } - opt_size[0] = size[0] + 2 * buffer; - opt_size[1] = size[1] + 2 * buffer; - return opt_size; -}; - - -/** - * Determines if a size has a positive area. - * @param {ol.Size} size The size to test. - * @return {boolean} The size has a positive area. - */ -ol.size.hasArea = function(size) { - return size[0] > 0 && size[1] > 0; -}; - - -/** - * Returns a size scaled by a ratio. The result will be an array of integers. - * @param {ol.Size} size Size. - * @param {number} ratio Ratio. - * @param {ol.Size=} opt_size Optional reusable size array. - * @return {ol.Size} The scaled size. - */ -ol.size.scale = function(size, ratio, opt_size) { - if (opt_size === undefined) { - opt_size = [0, 0]; - } - opt_size[0] = (size[0] * ratio + 0.5) | 0; - opt_size[1] = (size[1] * ratio + 0.5) | 0; - return opt_size; -}; - - -/** - * Returns an `ol.Size` array for the passed in number (meaning: square) or - * `ol.Size` array. - * (meaning: non-square), - * @param {number|ol.Size} size Width and height. - * @param {ol.Size=} opt_size Optional reusable size array. - * @return {ol.Size} Size. - * @api - */ -ol.size.toSize = function(size, opt_size) { - if (Array.isArray(size)) { - return size; - } else { - if (opt_size === undefined) { - opt_size = [size, size]; - } else { - opt_size[0] = opt_size[1] = /** @type {number} */ (size); - } - return opt_size; - } -}; - -goog.provide('ol.extent.Corner'); - -/** - * Extent corner. - * @enum {string} - */ -ol.extent.Corner = { - BOTTOM_LEFT: 'bottom-left', - BOTTOM_RIGHT: 'bottom-right', - TOP_LEFT: 'top-left', - TOP_RIGHT: 'top-right' -}; - -goog.provide('ol.extent.Relationship'); - - -/** - * Relationship to an extent. - * @enum {number} - */ -ol.extent.Relationship = { - UNKNOWN: 0, - INTERSECTING: 1, - ABOVE: 2, - RIGHT: 4, - BELOW: 8, - LEFT: 16 -}; - -goog.provide('ol.extent'); - -goog.require('ol.asserts'); -goog.require('ol.extent.Corner'); -goog.require('ol.extent.Relationship'); - - -/** - * Build an extent that includes all given coordinates. - * - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @return {ol.Extent} Bounding extent. - * @api - */ -ol.extent.boundingExtent = function(coordinates) { - var extent = ol.extent.createEmpty(); - for (var i = 0, ii = coordinates.length; i < ii; ++i) { - ol.extent.extendCoordinate(extent, coordinates[i]); - } - return extent; -}; - - -/** - * @param {Array.<number>} xs Xs. - * @param {Array.<number>} ys Ys. - * @param {ol.Extent=} opt_extent Destination extent. - * @private - * @return {ol.Extent} Extent. - */ -ol.extent.boundingExtentXYs_ = function(xs, ys, opt_extent) { - var minX = Math.min.apply(null, xs); - var minY = Math.min.apply(null, ys); - var maxX = Math.max.apply(null, xs); - var maxY = Math.max.apply(null, ys); - return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent); -}; - - -/** - * Return extent increased by the provided value. - * @param {ol.Extent} extent Extent. - * @param {number} value The amount by which the extent should be buffered. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - * @api - */ -ol.extent.buffer = function(extent, value, opt_extent) { - if (opt_extent) { - opt_extent[0] = extent[0] - value; - opt_extent[1] = extent[1] - value; - opt_extent[2] = extent[2] + value; - opt_extent[3] = extent[3] + value; - return opt_extent; - } else { - return [ - extent[0] - value, - extent[1] - value, - extent[2] + value, - extent[3] + value - ]; - } -}; - - -/** - * Creates a clone of an extent. - * - * @param {ol.Extent} extent Extent to clone. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} The clone. - */ -ol.extent.clone = function(extent, opt_extent) { - if (opt_extent) { - opt_extent[0] = extent[0]; - opt_extent[1] = extent[1]; - opt_extent[2] = extent[2]; - opt_extent[3] = extent[3]; - return opt_extent; - } else { - return extent.slice(); - } -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} x X. - * @param {number} y Y. - * @return {number} Closest squared distance. - */ -ol.extent.closestSquaredDistanceXY = function(extent, x, y) { - var dx, dy; - if (x < extent[0]) { - dx = extent[0] - x; - } else if (extent[2] < x) { - dx = x - extent[2]; - } else { - dx = 0; - } - if (y < extent[1]) { - dy = extent[1] - y; - } else if (extent[3] < y) { - dy = y - extent[3]; - } else { - dy = 0; - } - return dx * dx + dy * dy; -}; - - -/** - * Check if the passed coordinate is contained or on the edge of the extent. - * - * @param {ol.Extent} extent Extent. - * @param {ol.Coordinate} coordinate Coordinate. - * @return {boolean} The coordinate is contained in the extent. - * @api - */ -ol.extent.containsCoordinate = function(extent, coordinate) { - return ol.extent.containsXY(extent, coordinate[0], coordinate[1]); -}; - - -/** - * Check if one extent contains another. - * - * An extent is deemed contained if it lies completely within the other extent, - * including if they share one or more edges. - * - * @param {ol.Extent} extent1 Extent 1. - * @param {ol.Extent} extent2 Extent 2. - * @return {boolean} The second extent is contained by or on the edge of the - * first. - * @api - */ -ol.extent.containsExtent = function(extent1, extent2) { - return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] && - extent1[1] <= extent2[1] && extent2[3] <= extent1[3]; -}; - - -/** - * Check if the passed coordinate is contained or on the edge of the extent. - * - * @param {ol.Extent} extent Extent. - * @param {number} x X coordinate. - * @param {number} y Y coordinate. - * @return {boolean} The x, y values are contained in the extent. - * @api - */ -ol.extent.containsXY = function(extent, x, y) { - return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3]; -}; - - -/** - * Get the relationship between a coordinate and extent. - * @param {ol.Extent} extent The extent. - * @param {ol.Coordinate} coordinate The coordinate. - * @return {number} The relationship (bitwise compare with - * ol.extent.Relationship). - */ -ol.extent.coordinateRelationship = function(extent, coordinate) { - var minX = extent[0]; - var minY = extent[1]; - var maxX = extent[2]; - var maxY = extent[3]; - var x = coordinate[0]; - var y = coordinate[1]; - var relationship = ol.extent.Relationship.UNKNOWN; - if (x < minX) { - relationship = relationship | ol.extent.Relationship.LEFT; - } else if (x > maxX) { - relationship = relationship | ol.extent.Relationship.RIGHT; - } - if (y < minY) { - relationship = relationship | ol.extent.Relationship.BELOW; - } else if (y > maxY) { - relationship = relationship | ol.extent.Relationship.ABOVE; - } - if (relationship === ol.extent.Relationship.UNKNOWN) { - relationship = ol.extent.Relationship.INTERSECTING; - } - return relationship; -}; - - -/** - * Create an empty extent. - * @return {ol.Extent} Empty extent. - * @api - */ -ol.extent.createEmpty = function() { - return [Infinity, Infinity, -Infinity, -Infinity]; -}; - - -/** - * Create a new extent or update the provided extent. - * @param {number} minX Minimum X. - * @param {number} minY Minimum Y. - * @param {number} maxX Maximum X. - * @param {number} maxY Maximum Y. - * @param {ol.Extent=} opt_extent Destination extent. - * @return {ol.Extent} Extent. - */ -ol.extent.createOrUpdate = function(minX, minY, maxX, maxY, opt_extent) { - if (opt_extent) { - opt_extent[0] = minX; - opt_extent[1] = minY; - opt_extent[2] = maxX; - opt_extent[3] = maxY; - return opt_extent; - } else { - return [minX, minY, maxX, maxY]; - } -}; - - -/** - * Create a new empty extent or make the provided one empty. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.extent.createOrUpdateEmpty = function(opt_extent) { - return ol.extent.createOrUpdate( - Infinity, Infinity, -Infinity, -Infinity, opt_extent); -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.extent.createOrUpdateFromCoordinate = function(coordinate, opt_extent) { - var x = coordinate[0]; - var y = coordinate[1]; - return ol.extent.createOrUpdate(x, y, x, y, opt_extent); -}; - - -/** - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.extent.createOrUpdateFromCoordinates = function(coordinates, opt_extent) { - var extent = ol.extent.createOrUpdateEmpty(opt_extent); - return ol.extent.extendCoordinates(extent, coordinates); -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.extent.createOrUpdateFromFlatCoordinates = function(flatCoordinates, offset, end, stride, opt_extent) { - var extent = ol.extent.createOrUpdateEmpty(opt_extent); - return ol.extent.extendFlatCoordinates( - extent, flatCoordinates, offset, end, stride); -}; - - -/** - * @param {Array.<Array.<ol.Coordinate>>} rings Rings. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.extent.createOrUpdateFromRings = function(rings, opt_extent) { - var extent = ol.extent.createOrUpdateEmpty(opt_extent); - return ol.extent.extendRings(extent, rings); -}; - - -/** - * Determine if two extents are equivalent. - * @param {ol.Extent} extent1 Extent 1. - * @param {ol.Extent} extent2 Extent 2. - * @return {boolean} The two extents are equivalent. - * @api - */ -ol.extent.equals = function(extent1, extent2) { - return extent1[0] == extent2[0] && extent1[2] == extent2[2] && - extent1[1] == extent2[1] && extent1[3] == extent2[3]; -}; - - -/** - * Modify an extent to include another extent. - * @param {ol.Extent} extent1 The extent to be modified. - * @param {ol.Extent} extent2 The extent that will be included in the first. - * @return {ol.Extent} A reference to the first (extended) extent. - * @api - */ -ol.extent.extend = function(extent1, extent2) { - if (extent2[0] < extent1[0]) { - extent1[0] = extent2[0]; - } - if (extent2[2] > extent1[2]) { - extent1[2] = extent2[2]; - } - if (extent2[1] < extent1[1]) { - extent1[1] = extent2[1]; - } - if (extent2[3] > extent1[3]) { - extent1[3] = extent2[3]; - } - return extent1; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {ol.Coordinate} coordinate Coordinate. - */ -ol.extent.extendCoordinate = function(extent, coordinate) { - if (coordinate[0] < extent[0]) { - extent[0] = coordinate[0]; - } - if (coordinate[0] > extent[2]) { - extent[2] = coordinate[0]; - } - if (coordinate[1] < extent[1]) { - extent[1] = coordinate[1]; - } - if (coordinate[1] > extent[3]) { - extent[3] = coordinate[1]; - } -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @return {ol.Extent} Extent. - */ -ol.extent.extendCoordinates = function(extent, coordinates) { - var i, ii; - for (i = 0, ii = coordinates.length; i < ii; ++i) { - ol.extent.extendCoordinate(extent, coordinates[i]); - } - return extent; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {ol.Extent} Extent. - */ -ol.extent.extendFlatCoordinates = function(extent, flatCoordinates, offset, end, stride) { - for (; offset < end; offset += stride) { - ol.extent.extendXY( - extent, flatCoordinates[offset], flatCoordinates[offset + 1]); - } - return extent; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {Array.<Array.<ol.Coordinate>>} rings Rings. - * @return {ol.Extent} Extent. - */ -ol.extent.extendRings = function(extent, rings) { - var i, ii; - for (i = 0, ii = rings.length; i < ii; ++i) { - ol.extent.extendCoordinates(extent, rings[i]); - } - return extent; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} x X. - * @param {number} y Y. - */ -ol.extent.extendXY = function(extent, x, y) { - extent[0] = Math.min(extent[0], x); - extent[1] = Math.min(extent[1], y); - extent[2] = Math.max(extent[2], x); - extent[3] = Math.max(extent[3], y); -}; - - -/** - * This function calls `callback` for each corner of the extent. If the - * callback returns a truthy value the function returns that value - * immediately. Otherwise the function returns `false`. - * @param {ol.Extent} extent Extent. - * @param {function(this:T, ol.Coordinate): S} callback Callback. - * @param {T=} opt_this Value to use as `this` when executing `callback`. - * @return {S|boolean} Value. - * @template S, T - */ -ol.extent.forEachCorner = function(extent, callback, opt_this) { - var val; - val = callback.call(opt_this, ol.extent.getBottomLeft(extent)); - if (val) { - return val; - } - val = callback.call(opt_this, ol.extent.getBottomRight(extent)); - if (val) { - return val; - } - val = callback.call(opt_this, ol.extent.getTopRight(extent)); - if (val) { - return val; - } - val = callback.call(opt_this, ol.extent.getTopLeft(extent)); - if (val) { - return val; - } - return false; -}; - - -/** - * Get the size of an extent. - * @param {ol.Extent} extent Extent. - * @return {number} Area. - * @api - */ -ol.extent.getArea = function(extent) { - var area = 0; - if (!ol.extent.isEmpty(extent)) { - area = ol.extent.getWidth(extent) * ol.extent.getHeight(extent); - } - return area; -}; - - -/** - * Get the bottom left coordinate of an extent. - * @param {ol.Extent} extent Extent. - * @return {ol.Coordinate} Bottom left coordinate. - * @api - */ -ol.extent.getBottomLeft = function(extent) { - return [extent[0], extent[1]]; -}; - - -/** - * Get the bottom right coordinate of an extent. - * @param {ol.Extent} extent Extent. - * @return {ol.Coordinate} Bottom right coordinate. - * @api - */ -ol.extent.getBottomRight = function(extent) { - return [extent[2], extent[1]]; -}; - - -/** - * Get the center coordinate of an extent. - * @param {ol.Extent} extent Extent. - * @return {ol.Coordinate} Center. - * @api - */ -ol.extent.getCenter = function(extent) { - return [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2]; -}; - - -/** - * Get a corner coordinate of an extent. - * @param {ol.Extent} extent Extent. - * @param {ol.extent.Corner} corner Corner. - * @return {ol.Coordinate} Corner coordinate. - */ -ol.extent.getCorner = function(extent, corner) { - var coordinate; - if (corner === ol.extent.Corner.BOTTOM_LEFT) { - coordinate = ol.extent.getBottomLeft(extent); - } else if (corner === ol.extent.Corner.BOTTOM_RIGHT) { - coordinate = ol.extent.getBottomRight(extent); - } else if (corner === ol.extent.Corner.TOP_LEFT) { - coordinate = ol.extent.getTopLeft(extent); - } else if (corner === ol.extent.Corner.TOP_RIGHT) { - coordinate = ol.extent.getTopRight(extent); - } else { - ol.asserts.assert(false, 13); // Invalid corner - } - return /** @type {!ol.Coordinate} */ (coordinate); -}; - - -/** - * @param {ol.Extent} extent1 Extent 1. - * @param {ol.Extent} extent2 Extent 2. - * @return {number} Enlarged area. - */ -ol.extent.getEnlargedArea = function(extent1, extent2) { - var minX = Math.min(extent1[0], extent2[0]); - var minY = Math.min(extent1[1], extent2[1]); - var maxX = Math.max(extent1[2], extent2[2]); - var maxY = Math.max(extent1[3], extent2[3]); - return (maxX - minX) * (maxY - minY); -}; - - -/** - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {ol.Extent=} opt_extent Destination extent. - * @return {ol.Extent} Extent. - */ -ol.extent.getForViewAndSize = function(center, resolution, rotation, size, opt_extent) { - var dx = resolution * size[0] / 2; - var dy = resolution * size[1] / 2; - var cosRotation = Math.cos(rotation); - var sinRotation = Math.sin(rotation); - var xCos = dx * cosRotation; - var xSin = dx * sinRotation; - var yCos = dy * cosRotation; - var ySin = dy * sinRotation; - var x = center[0]; - var y = center[1]; - var x0 = x - xCos + ySin; - var x1 = x - xCos - ySin; - var x2 = x + xCos - ySin; - var x3 = x + xCos + ySin; - var y0 = y - xSin - yCos; - var y1 = y - xSin + yCos; - var y2 = y + xSin + yCos; - var y3 = y + xSin - yCos; - return ol.extent.createOrUpdate( - Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), - Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), - opt_extent); -}; - - -/** - * Get the height of an extent. - * @param {ol.Extent} extent Extent. - * @return {number} Height. - * @api - */ -ol.extent.getHeight = function(extent) { - return extent[3] - extent[1]; -}; - - -/** - * @param {ol.Extent} extent1 Extent 1. - * @param {ol.Extent} extent2 Extent 2. - * @return {number} Intersection area. - */ -ol.extent.getIntersectionArea = function(extent1, extent2) { - var intersection = ol.extent.getIntersection(extent1, extent2); - return ol.extent.getArea(intersection); -}; - - -/** - * Get the intersection of two extents. - * @param {ol.Extent} extent1 Extent 1. - * @param {ol.Extent} extent2 Extent 2. - * @param {ol.Extent=} opt_extent Optional extent to populate with intersection. - * @return {ol.Extent} Intersecting extent. - * @api - */ -ol.extent.getIntersection = function(extent1, extent2, opt_extent) { - var intersection = opt_extent ? opt_extent : ol.extent.createEmpty(); - if (ol.extent.intersects(extent1, extent2)) { - if (extent1[0] > extent2[0]) { - intersection[0] = extent1[0]; - } else { - intersection[0] = extent2[0]; - } - if (extent1[1] > extent2[1]) { - intersection[1] = extent1[1]; - } else { - intersection[1] = extent2[1]; - } - if (extent1[2] < extent2[2]) { - intersection[2] = extent1[2]; - } else { - intersection[2] = extent2[2]; - } - if (extent1[3] < extent2[3]) { - intersection[3] = extent1[3]; - } else { - intersection[3] = extent2[3]; - } - } - return intersection; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @return {number} Margin. - */ -ol.extent.getMargin = function(extent) { - return ol.extent.getWidth(extent) + ol.extent.getHeight(extent); -}; - - -/** - * Get the size (width, height) of an extent. - * @param {ol.Extent} extent The extent. - * @return {ol.Size} The extent size. - * @api - */ -ol.extent.getSize = function(extent) { - return [extent[2] - extent[0], extent[3] - extent[1]]; -}; - - -/** - * Get the top left coordinate of an extent. - * @param {ol.Extent} extent Extent. - * @return {ol.Coordinate} Top left coordinate. - * @api - */ -ol.extent.getTopLeft = function(extent) { - return [extent[0], extent[3]]; -}; - - -/** - * Get the top right coordinate of an extent. - * @param {ol.Extent} extent Extent. - * @return {ol.Coordinate} Top right coordinate. - * @api - */ -ol.extent.getTopRight = function(extent) { - return [extent[2], extent[3]]; -}; - - -/** - * Get the width of an extent. - * @param {ol.Extent} extent Extent. - * @return {number} Width. - * @api - */ -ol.extent.getWidth = function(extent) { - return extent[2] - extent[0]; -}; - - -/** - * Determine if one extent intersects another. - * @param {ol.Extent} extent1 Extent 1. - * @param {ol.Extent} extent2 Extent. - * @return {boolean} The two extents intersect. - * @api - */ -ol.extent.intersects = function(extent1, extent2) { - return extent1[0] <= extent2[2] && - extent1[2] >= extent2[0] && - extent1[1] <= extent2[3] && - extent1[3] >= extent2[1]; -}; - - -/** - * Determine if an extent is empty. - * @param {ol.Extent} extent Extent. - * @return {boolean} Is empty. - * @api - */ -ol.extent.isEmpty = function(extent) { - return extent[2] < extent[0] || extent[3] < extent[1]; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.extent.returnOrUpdate = function(extent, opt_extent) { - if (opt_extent) { - opt_extent[0] = extent[0]; - opt_extent[1] = extent[1]; - opt_extent[2] = extent[2]; - opt_extent[3] = extent[3]; - return opt_extent; - } else { - return extent; - } -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} value Value. - */ -ol.extent.scaleFromCenter = function(extent, value) { - var deltaX = ((extent[2] - extent[0]) / 2) * (value - 1); - var deltaY = ((extent[3] - extent[1]) / 2) * (value - 1); - extent[0] -= deltaX; - extent[2] += deltaX; - extent[1] -= deltaY; - extent[3] += deltaY; -}; - - -/** - * Determine if the segment between two coordinates intersects (crosses, - * touches, or is contained by) the provided extent. - * @param {ol.Extent} extent The extent. - * @param {ol.Coordinate} start Segment start coordinate. - * @param {ol.Coordinate} end Segment end coordinate. - * @return {boolean} The segment intersects the extent. - */ -ol.extent.intersectsSegment = function(extent, start, end) { - var intersects = false; - var startRel = ol.extent.coordinateRelationship(extent, start); - var endRel = ol.extent.coordinateRelationship(extent, end); - if (startRel === ol.extent.Relationship.INTERSECTING || - endRel === ol.extent.Relationship.INTERSECTING) { - intersects = true; - } else { - var minX = extent[0]; - var minY = extent[1]; - var maxX = extent[2]; - var maxY = extent[3]; - var startX = start[0]; - var startY = start[1]; - var endX = end[0]; - var endY = end[1]; - var slope = (endY - startY) / (endX - startX); - var x, y; - if (!!(endRel & ol.extent.Relationship.ABOVE) && - !(startRel & ol.extent.Relationship.ABOVE)) { - // potentially intersects top - x = endX - ((endY - maxY) / slope); - intersects = x >= minX && x <= maxX; - } - if (!intersects && !!(endRel & ol.extent.Relationship.RIGHT) && - !(startRel & ol.extent.Relationship.RIGHT)) { - // potentially intersects right - y = endY - ((endX - maxX) * slope); - intersects = y >= minY && y <= maxY; - } - if (!intersects && !!(endRel & ol.extent.Relationship.BELOW) && - !(startRel & ol.extent.Relationship.BELOW)) { - // potentially intersects bottom - x = endX - ((endY - minY) / slope); - intersects = x >= minX && x <= maxX; - } - if (!intersects && !!(endRel & ol.extent.Relationship.LEFT) && - !(startRel & ol.extent.Relationship.LEFT)) { - // potentially intersects left - y = endY - ((endX - minX) * slope); - intersects = y >= minY && y <= maxY; - } - - } - return intersects; -}; - - -/** - * Apply a transform function to the extent. - * @param {ol.Extent} extent Extent. - * @param {ol.TransformFunction} transformFn Transform function. Called with - * [minX, minY, maxX, maxY] extent coordinates. - * @param {ol.Extent=} opt_extent Destination extent. - * @return {ol.Extent} Extent. - * @api - */ -ol.extent.applyTransform = function(extent, transformFn, opt_extent) { - var coordinates = [ - extent[0], extent[1], - extent[0], extent[3], - extent[2], extent[1], - extent[2], extent[3] - ]; - transformFn(coordinates, coordinates, 2); - var xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]]; - var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]]; - return ol.extent.boundingExtentXYs_(xs, ys, opt_extent); -}; - -goog.provide('ol.obj'); - - -/** - * Polyfill for Object.assign(). Assigns enumerable and own properties from - * one or more source objects to a target object. - * - * @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - * @param {!Object} target The target object. - * @param {...Object} var_sources The source object(s). - * @return {!Object} The modified target object. - */ -ol.obj.assign = (typeof Object.assign === 'function') ? Object.assign : function(target, var_sources) { - if (target === undefined || target === null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - - var output = Object(target); - for (var i = 1, ii = arguments.length; i < ii; ++i) { - var source = arguments[i]; - if (source !== undefined && source !== null) { - for (var key in source) { - if (source.hasOwnProperty(key)) { - output[key] = source[key]; - } - } - } - } - return output; -}; - - -/** - * Removes all properties from an object. - * @param {Object} object The object to clear. - */ -ol.obj.clear = function(object) { - for (var property in object) { - delete object[property]; - } -}; - - -/** - * Get an array of property values from an object. - * @param {Object<K,V>} object The object from which to get the values. - * @return {!Array<V>} The property values. - * @template K,V - */ -ol.obj.getValues = function(object) { - var values = []; - for (var property in object) { - values.push(object[property]); - } - return values; -}; - - -/** - * Determine if an object has any properties. - * @param {Object} object The object to check. - * @return {boolean} The object is empty. - */ -ol.obj.isEmpty = function(object) { - var property; - for (property in object) { - return false; - } - return !property; -}; - -/** - * @license - * Latitude/longitude spherical geodesy formulae taken from - * http://www.movable-type.co.uk/scripts/latlong.html - * Licensed under CC-BY-3.0. - */ - -goog.provide('ol.Sphere'); - -goog.require('ol.math'); - - -/** - * @classdesc - * Class to create objects that can be used with {@link - * ol.geom.Polygon.circular}. - * - * For example to create a sphere whose radius is equal to the semi-major - * axis of the WGS84 ellipsoid: - * - * ```js - * var wgs84Sphere= new ol.Sphere(6378137); - * ``` - * - * @constructor - * @param {number} radius Radius. - * @api - */ -ol.Sphere = function(radius) { - - /** - * @type {number} - */ - this.radius = radius; - -}; - - -/** - * Returns the geodesic area for a list of coordinates. - * - * [Reference](https://trs-new.jpl.nasa.gov/handle/2014/40409) - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion - * Laboratory, Pasadena, CA, June 2007 - * - * @param {Array.<ol.Coordinate>} coordinates List of coordinates of a linear - * ring. If the ring is oriented clockwise, the area will be positive, - * otherwise it will be negative. - * @return {number} Area. - * @api - */ -ol.Sphere.prototype.geodesicArea = function(coordinates) { - var area = 0, len = coordinates.length; - var x1 = coordinates[len - 1][0]; - var y1 = coordinates[len - 1][1]; - for (var i = 0; i < len; i++) { - var x2 = coordinates[i][0], y2 = coordinates[i][1]; - area += ol.math.toRadians(x2 - x1) * - (2 + Math.sin(ol.math.toRadians(y1)) + - Math.sin(ol.math.toRadians(y2))); - x1 = x2; - y1 = y2; - } - return area * this.radius * this.radius / 2.0; -}; - - -/** - * Returns the distance from c1 to c2 using the haversine formula. - * - * @param {ol.Coordinate} c1 Coordinate 1. - * @param {ol.Coordinate} c2 Coordinate 2. - * @return {number} Haversine distance. - * @api - */ -ol.Sphere.prototype.haversineDistance = function(c1, c2) { - var lat1 = ol.math.toRadians(c1[1]); - var lat2 = ol.math.toRadians(c2[1]); - var deltaLatBy2 = (lat2 - lat1) / 2; - var deltaLonBy2 = ol.math.toRadians(c2[0] - c1[0]) / 2; - var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) + - Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) * - Math.cos(lat1) * Math.cos(lat2); - return 2 * this.radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); -}; - - -/** - * Returns the coordinate at the given distance and bearing from `c1`. - * - * @param {ol.Coordinate} c1 The origin point (`[lon, lat]` in degrees). - * @param {number} distance The great-circle distance between the origin - * point and the target point. - * @param {number} bearing The bearing (in radians). - * @return {ol.Coordinate} The target point. - */ -ol.Sphere.prototype.offset = function(c1, distance, bearing) { - var lat1 = ol.math.toRadians(c1[1]); - var lon1 = ol.math.toRadians(c1[0]); - var dByR = distance / this.radius; - var lat = Math.asin( - Math.sin(lat1) * Math.cos(dByR) + - Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)); - var lon = lon1 + Math.atan2( - Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), - Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)); - return [ol.math.toDegrees(lon), ol.math.toDegrees(lat)]; -}; - -goog.provide('ol.sphere.NORMAL'); - -goog.require('ol.Sphere'); - - -/** - * The normal sphere. - * @const - * @type {ol.Sphere} - */ -ol.sphere.NORMAL = new ol.Sphere(6370997); - -goog.provide('ol.proj.Units'); - -goog.require('ol.sphere.NORMAL'); - - -/** - * Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or - * `'us-ft'`. - * @enum {string} - */ -ol.proj.Units = { - DEGREES: 'degrees', - FEET: 'ft', - METERS: 'm', - PIXELS: 'pixels', - TILE_PIXELS: 'tile-pixels', - USFEET: 'us-ft' -}; - - -/** - * Meters per unit lookup table. - * @const - * @type {Object.<ol.proj.Units, number>} - * @api - */ -ol.proj.Units.METERS_PER_UNIT = {}; -ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.DEGREES] = - 2 * Math.PI * ol.sphere.NORMAL.radius / 360; -ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.FEET] = 0.3048; -ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.METERS] = 1; -ol.proj.Units.METERS_PER_UNIT[ol.proj.Units.USFEET] = 1200 / 3937; - -goog.provide('ol.proj.proj4'); - - -/** - * @private - * @type {Proj4} - */ -ol.proj.proj4.cache_ = null; - - -/** - * Store the proj4 function. - * @param {Proj4} proj4 The proj4 function. - */ -ol.proj.proj4.set = function(proj4) { - ol.proj.proj4.cache_ = proj4; -}; - - -/** - * Get proj4. - * @return {Proj4} The proj4 function set above or available globally. - */ -ol.proj.proj4.get = function() { - return ol.proj.proj4.cache_ || window['proj4']; -}; - -goog.provide('ol.proj.Projection'); - -goog.require('ol'); -goog.require('ol.proj.Units'); -goog.require('ol.proj.proj4'); - - -/** - * @classdesc - * Projection definition class. One of these is created for each projection - * supported in the application and stored in the {@link ol.proj} namespace. - * You can use these in applications, but this is not required, as API params - * and options use {@link ol.ProjectionLike} which means the simple string - * code will suffice. - * - * You can use {@link ol.proj.get} to retrieve the object for a particular - * projection. - * - * The library includes definitions for `EPSG:4326` and `EPSG:3857`, together - * with the following aliases: - * * `EPSG:4326`: CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, - * urn:ogc:def:crs:OGC:1.3:CRS84, urn:ogc:def:crs:OGC:2:84, - * http://www.opengis.net/gml/srs/epsg.xml#4326, - * urn:x-ogc:def:crs:EPSG:4326 - * * `EPSG:3857`: EPSG:102100, EPSG:102113, EPSG:900913, - * urn:ogc:def:crs:EPSG:6.18:3:3857, - * http://www.opengis.net/gml/srs/epsg.xml#3857 - * - * If you use proj4js, aliases can be added using `proj4.defs()`; see - * [documentation](https://github.com/proj4js/proj4js). To set an alternative - * namespace for proj4, use {@link ol.proj.setProj4}. - * - * @constructor - * @param {olx.ProjectionOptions} options Projection options. - * @struct - * @api - */ -ol.proj.Projection = function(options) { - /** - * @private - * @type {string} - */ - this.code_ = options.code; - - /** - * @private - * @type {ol.proj.Units} - */ - this.units_ = /** @type {ol.proj.Units} */ (options.units); - - /** - * @private - * @type {ol.Extent} - */ - this.extent_ = options.extent !== undefined ? options.extent : null; - - /** - * @private - * @type {ol.Extent} - */ - this.worldExtent_ = options.worldExtent !== undefined ? - options.worldExtent : null; - - /** - * @private - * @type {string} - */ - this.axisOrientation_ = options.axisOrientation !== undefined ? - options.axisOrientation : 'enu'; - - /** - * @private - * @type {boolean} - */ - this.global_ = options.global !== undefined ? options.global : false; - - /** - * @private - * @type {boolean} - */ - this.canWrapX_ = !!(this.global_ && this.extent_); - - /** - * @private - * @type {function(number, ol.Coordinate):number|undefined} - */ - this.getPointResolutionFunc_ = options.getPointResolution; - - /** - * @private - * @type {ol.tilegrid.TileGrid} - */ - this.defaultTileGrid_ = null; - - /** - * @private - * @type {number|undefined} - */ - this.metersPerUnit_ = options.metersPerUnit; - - var code = options.code; - if (ol.ENABLE_PROJ4JS) { - var proj4js = ol.proj.proj4.get(); - if (typeof proj4js == 'function') { - var def = proj4js.defs(code); - if (def !== undefined) { - if (def.axis !== undefined && options.axisOrientation === undefined) { - this.axisOrientation_ = def.axis; - } - if (options.metersPerUnit === undefined) { - this.metersPerUnit_ = def.to_meter; - } - if (options.units === undefined) { - this.units_ = def.units; - } - } - } - } -}; - - -/** - * @return {boolean} The projection is suitable for wrapping the x-axis - */ -ol.proj.Projection.prototype.canWrapX = function() { - return this.canWrapX_; -}; - - -/** - * Get the code for this projection, e.g. 'EPSG:4326'. - * @return {string} Code. - * @api - */ -ol.proj.Projection.prototype.getCode = function() { - return this.code_; -}; - - -/** - * Get the validity extent for this projection. - * @return {ol.Extent} Extent. - * @api - */ -ol.proj.Projection.prototype.getExtent = function() { - return this.extent_; -}; - - -/** - * Get the units of this projection. - * @return {ol.proj.Units} Units. - * @api - */ -ol.proj.Projection.prototype.getUnits = function() { - return this.units_; -}; - - -/** - * Get the amount of meters per unit of this projection. If the projection is - * not configured with `metersPerUnit` or a units identifier, the return is - * `undefined`. - * @return {number|undefined} Meters. - * @api - */ -ol.proj.Projection.prototype.getMetersPerUnit = function() { - return this.metersPerUnit_ || ol.proj.Units.METERS_PER_UNIT[this.units_]; -}; - - -/** - * Get the world extent for this projection. - * @return {ol.Extent} Extent. - * @api - */ -ol.proj.Projection.prototype.getWorldExtent = function() { - return this.worldExtent_; -}; - - -/** - * Get the axis orientation of this projection. - * Example values are: - * enu - the default easting, northing, elevation. - * neu - northing, easting, up - useful for "lat/long" geographic coordinates, - * or south orientated transverse mercator. - * wnu - westing, northing, up - some planetary coordinate systems have - * "west positive" coordinate systems - * @return {string} Axis orientation. - */ -ol.proj.Projection.prototype.getAxisOrientation = function() { - return this.axisOrientation_; -}; - - -/** - * Is this projection a global projection which spans the whole world? - * @return {boolean} Whether the projection is global. - * @api - */ -ol.proj.Projection.prototype.isGlobal = function() { - return this.global_; -}; - - -/** -* Set if the projection is a global projection which spans the whole world -* @param {boolean} global Whether the projection is global. -* @api -*/ -ol.proj.Projection.prototype.setGlobal = function(global) { - this.global_ = global; - this.canWrapX_ = !!(global && this.extent_); -}; - - -/** - * @return {ol.tilegrid.TileGrid} The default tile grid. - */ -ol.proj.Projection.prototype.getDefaultTileGrid = function() { - return this.defaultTileGrid_; -}; - - -/** - * @param {ol.tilegrid.TileGrid} tileGrid The default tile grid. - */ -ol.proj.Projection.prototype.setDefaultTileGrid = function(tileGrid) { - this.defaultTileGrid_ = tileGrid; -}; - - -/** - * Set the validity extent for this projection. - * @param {ol.Extent} extent Extent. - * @api - */ -ol.proj.Projection.prototype.setExtent = function(extent) { - this.extent_ = extent; - this.canWrapX_ = !!(this.global_ && extent); -}; - - -/** - * Set the world extent for this projection. - * @param {ol.Extent} worldExtent World extent - * [minlon, minlat, maxlon, maxlat]. - * @api - */ -ol.proj.Projection.prototype.setWorldExtent = function(worldExtent) { - this.worldExtent_ = worldExtent; -}; - - -/** - * Set the getPointResolution function (see {@link ol.proj#getPointResolution} - * for this projection. - * @param {function(number, ol.Coordinate):number} func Function - * @api - */ -ol.proj.Projection.prototype.setGetPointResolution = function(func) { - this.getPointResolutionFunc_ = func; -}; - - -/** - * Get the custom point resolution function for this projection (if set). - * @return {function(number, ol.Coordinate):number|undefined} The custom point - * resolution function (if set). - */ -ol.proj.Projection.prototype.getPointResolutionFunc = function() { - return this.getPointResolutionFunc_; -}; - -goog.provide('ol.proj.EPSG3857'); - -goog.require('ol'); -goog.require('ol.math'); -goog.require('ol.proj.Projection'); -goog.require('ol.proj.Units'); - - -/** - * @classdesc - * Projection object for web/spherical Mercator (EPSG:3857). - * - * @constructor - * @extends {ol.proj.Projection} - * @param {string} code Code. - * @private - */ -ol.proj.EPSG3857.Projection_ = function(code) { - ol.proj.Projection.call(this, { - code: code, - units: ol.proj.Units.METERS, - extent: ol.proj.EPSG3857.EXTENT, - global: true, - worldExtent: ol.proj.EPSG3857.WORLD_EXTENT, - getPointResolution: function(resolution, point) { - return resolution / ol.math.cosh(point[1] / ol.proj.EPSG3857.RADIUS); - } - }); -}; -ol.inherits(ol.proj.EPSG3857.Projection_, ol.proj.Projection); - - -/** - * @const - * @type {number} - */ -ol.proj.EPSG3857.RADIUS = 6378137; - - -/** - * @const - * @type {number} - */ -ol.proj.EPSG3857.HALF_SIZE = Math.PI * ol.proj.EPSG3857.RADIUS; - - -/** - * @const - * @type {ol.Extent} - */ -ol.proj.EPSG3857.EXTENT = [ - -ol.proj.EPSG3857.HALF_SIZE, -ol.proj.EPSG3857.HALF_SIZE, - ol.proj.EPSG3857.HALF_SIZE, ol.proj.EPSG3857.HALF_SIZE -]; - - -/** - * @const - * @type {ol.Extent} - */ -ol.proj.EPSG3857.WORLD_EXTENT = [-180, -85, 180, 85]; - - -/** - * Lists several projection codes with the same meaning as EPSG:3857. - * - * @type {Array.<string>} - */ -ol.proj.EPSG3857.CODES = [ - 'EPSG:3857', - 'EPSG:102100', - 'EPSG:102113', - 'EPSG:900913', - 'urn:ogc:def:crs:EPSG:6.18:3:3857', - 'urn:ogc:def:crs:EPSG::3857', - 'http://www.opengis.net/gml/srs/epsg.xml#3857' -]; - - -/** - * Projections equal to EPSG:3857. - * - * @const - * @type {Array.<ol.proj.Projection>} - */ -ol.proj.EPSG3857.PROJECTIONS = ol.proj.EPSG3857.CODES.map(function(code) { - return new ol.proj.EPSG3857.Projection_(code); -}); - - -/** - * Transformation from EPSG:4326 to EPSG:3857. - * - * @param {Array.<number>} input Input array of coordinate values. - * @param {Array.<number>=} opt_output Output array of coordinate values. - * @param {number=} opt_dimension Dimension (default is `2`). - * @return {Array.<number>} Output array of coordinate values. - */ -ol.proj.EPSG3857.fromEPSG4326 = function(input, opt_output, opt_dimension) { - var length = input.length, - dimension = opt_dimension > 1 ? opt_dimension : 2, - output = opt_output; - if (output === undefined) { - if (dimension > 2) { - // preserve values beyond second dimension - output = input.slice(); - } else { - output = new Array(length); - } - } - var halfSize = ol.proj.EPSG3857.HALF_SIZE; - for (var i = 0; i < length; i += dimension) { - output[i] = halfSize * input[i] / 180; - var y = ol.proj.EPSG3857.RADIUS * - Math.log(Math.tan(Math.PI * (input[i + 1] + 90) / 360)); - if (y > halfSize) { - y = halfSize; - } else if (y < -halfSize) { - y = -halfSize; - } - output[i + 1] = y; - } - return output; -}; - - -/** - * Transformation from EPSG:3857 to EPSG:4326. - * - * @param {Array.<number>} input Input array of coordinate values. - * @param {Array.<number>=} opt_output Output array of coordinate values. - * @param {number=} opt_dimension Dimension (default is `2`). - * @return {Array.<number>} Output array of coordinate values. - */ -ol.proj.EPSG3857.toEPSG4326 = function(input, opt_output, opt_dimension) { - var length = input.length, - dimension = opt_dimension > 1 ? opt_dimension : 2, - output = opt_output; - if (output === undefined) { - if (dimension > 2) { - // preserve values beyond second dimension - output = input.slice(); - } else { - output = new Array(length); - } - } - for (var i = 0; i < length; i += dimension) { - output[i] = 180 * input[i] / ol.proj.EPSG3857.HALF_SIZE; - output[i + 1] = 360 * Math.atan( - Math.exp(input[i + 1] / ol.proj.EPSG3857.RADIUS)) / Math.PI - 90; - } - return output; -}; - -goog.provide('ol.sphere.WGS84'); - -goog.require('ol.Sphere'); - - -/** - * A sphere with radius equal to the semi-major axis of the WGS84 ellipsoid. - * @const - * @type {ol.Sphere} - */ -ol.sphere.WGS84 = new ol.Sphere(6378137); - -goog.provide('ol.proj.EPSG4326'); - -goog.require('ol'); -goog.require('ol.proj.Projection'); -goog.require('ol.proj.Units'); -goog.require('ol.sphere.WGS84'); - - -/** - * @classdesc - * Projection object for WGS84 geographic coordinates (EPSG:4326). - * - * Note that OpenLayers does not strictly comply with the EPSG definition. - * The EPSG registry defines 4326 as a CRS for Latitude,Longitude (y,x). - * OpenLayers treats EPSG:4326 as a pseudo-projection, with x,y coordinates. - * - * @constructor - * @extends {ol.proj.Projection} - * @param {string} code Code. - * @param {string=} opt_axisOrientation Axis orientation. - * @private - */ -ol.proj.EPSG4326.Projection_ = function(code, opt_axisOrientation) { - ol.proj.Projection.call(this, { - code: code, - units: ol.proj.Units.DEGREES, - extent: ol.proj.EPSG4326.EXTENT, - axisOrientation: opt_axisOrientation, - global: true, - metersPerUnit: ol.proj.EPSG4326.METERS_PER_UNIT, - worldExtent: ol.proj.EPSG4326.EXTENT - }); -}; -ol.inherits(ol.proj.EPSG4326.Projection_, ol.proj.Projection); - - -/** - * Extent of the EPSG:4326 projection which is the whole world. - * - * @const - * @type {ol.Extent} - */ -ol.proj.EPSG4326.EXTENT = [-180, -90, 180, 90]; - - -/** - * @const - * @type {number} - */ -ol.proj.EPSG4326.METERS_PER_UNIT = Math.PI * ol.sphere.WGS84.radius / 180; - - -/** - * Projections equal to EPSG:4326. - * - * @const - * @type {Array.<ol.proj.Projection>} - */ -ol.proj.EPSG4326.PROJECTIONS = [ - new ol.proj.EPSG4326.Projection_('CRS:84'), - new ol.proj.EPSG4326.Projection_('EPSG:4326', 'neu'), - new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:EPSG::4326', 'neu'), - new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'), - new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:OGC:1.3:CRS84'), - new ol.proj.EPSG4326.Projection_('urn:ogc:def:crs:OGC:2:84'), - new ol.proj.EPSG4326.Projection_('http://www.opengis.net/gml/srs/epsg.xml#4326', 'neu'), - new ol.proj.EPSG4326.Projection_('urn:x-ogc:def:crs:EPSG:4326', 'neu') -]; - -goog.provide('ol.proj.projections'); - - -/** - * @private - * @type {Object.<string, ol.proj.Projection>} - */ -ol.proj.projections.cache_ = {}; - - -/** - * Clear the projections cache. - */ -ol.proj.projections.clear = function() { - ol.proj.projections.cache_ = {}; -}; - - -/** - * Get a cached projection by code. - * @param {string} code The code for the projection. - * @return {ol.proj.Projection} The projection (if cached). - */ -ol.proj.projections.get = function(code) { - var projections = ol.proj.projections.cache_; - return projections[code] || null; -}; - - -/** - * Add a projection to the cache. - * @param {string} code The projection code. - * @param {ol.proj.Projection} projection The projection to cache. - */ -ol.proj.projections.add = function(code, projection) { - var projections = ol.proj.projections.cache_; - projections[code] = projection; -}; - -goog.provide('ol.proj.transforms'); - -goog.require('ol.obj'); - - -/** - * @private - * @type {Object.<string, Object.<string, ol.TransformFunction>>} - */ -ol.proj.transforms.cache_ = {}; - - -/** - * Clear the transform cache. - */ -ol.proj.transforms.clear = function() { - ol.proj.transforms.cache_ = {}; -}; - - -/** - * Registers a conversion function to convert coordinates from the source - * projection to the destination projection. - * - * @param {ol.proj.Projection} source Source. - * @param {ol.proj.Projection} destination Destination. - * @param {ol.TransformFunction} transformFn Transform. - */ -ol.proj.transforms.add = function(source, destination, transformFn) { - var sourceCode = source.getCode(); - var destinationCode = destination.getCode(); - var transforms = ol.proj.transforms.cache_; - if (!(sourceCode in transforms)) { - transforms[sourceCode] = {}; - } - transforms[sourceCode][destinationCode] = transformFn; -}; - - -/** - * Unregisters the conversion function to convert coordinates from the source - * projection to the destination projection. This method is used to clean up - * cached transforms during testing. - * - * @param {ol.proj.Projection} source Source projection. - * @param {ol.proj.Projection} destination Destination projection. - * @return {ol.TransformFunction} transformFn The unregistered transform. - */ -ol.proj.transforms.remove = function(source, destination) { - var sourceCode = source.getCode(); - var destinationCode = destination.getCode(); - var transforms = ol.proj.transforms.cache_; - var transform = transforms[sourceCode][destinationCode]; - delete transforms[sourceCode][destinationCode]; - if (ol.obj.isEmpty(transforms[sourceCode])) { - delete transforms[sourceCode]; - } - return transform; -}; - - -/** - * Get a transform given a source code and a destination code. - * @param {string} sourceCode The code for the source projection. - * @param {string} destinationCode The code for the destination projection. - * @return {ol.TransformFunction|undefined} The transform function (if found). - */ -ol.proj.transforms.get = function(sourceCode, destinationCode) { - var transform; - var transforms = ol.proj.transforms.cache_; - if (sourceCode in transforms && destinationCode in transforms[sourceCode]) { - transform = transforms[sourceCode][destinationCode]; - } - return transform; -}; - -goog.provide('ol.proj'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.proj.EPSG3857'); -goog.require('ol.proj.EPSG4326'); -goog.require('ol.proj.Projection'); -goog.require('ol.proj.Units'); -goog.require('ol.proj.proj4'); -goog.require('ol.proj.projections'); -goog.require('ol.proj.transforms'); -goog.require('ol.sphere.NORMAL'); - - -/** - * Meters per unit lookup table. - * @const - * @type {Object.<ol.proj.Units, number>} - * @api - */ -ol.proj.METERS_PER_UNIT = ol.proj.Units.METERS_PER_UNIT; - - -if (ol.ENABLE_PROJ4JS) { - /** - * Register proj4. If not explicitly registered, it will be assumed that - * proj4js will be loaded in the global namespace. For example in a - * browserify ES6 environment you could use: - * - * import ol from 'openlayers'; - * import proj4 from 'proj4'; - * ol.proj.setProj4(proj4); - * - * @param {Proj4} proj4 Proj4. - * @api - */ - ol.proj.setProj4 = function(proj4) { - ol.proj.proj4.set(proj4); - }; -} - - -/** - * Get the resolution of the point in degrees or distance units. - * For projections with degrees as the unit this will simply return the - * provided resolution. For other projections the point resolution is - * by default estimated by transforming the 'point' pixel to EPSG:4326, - * measuring its width and height on the normal sphere, - * and taking the average of the width and height. - * A custom function can be provided for a specific projection, either - * by setting the `getPointResolution` option in the - * {@link ol.proj.Projection} constructor or by using - * {@link ol.proj.Projection#setGetPointResolution} to change an existing - * projection object. - * @param {ol.ProjectionLike} projection The projection. - * @param {number} resolution Nominal resolution in projection units. - * @param {ol.Coordinate} point Point to find adjusted resolution at. - * @return {number} Point resolution at point in projection units. - * @api - */ -ol.proj.getPointResolution = function(projection, resolution, point) { - projection = ol.proj.get(projection); - var pointResolution; - var getter = projection.getPointResolutionFunc(); - if (getter) { - pointResolution = getter(resolution, point); - } else { - var units = projection.getUnits(); - if (units == ol.proj.Units.DEGREES) { - pointResolution = resolution; - } else { - // Estimate point resolution by transforming the center pixel to EPSG:4326, - // measuring its width and height on the normal sphere, and taking the - // average of the width and height. - var toEPSG4326 = ol.proj.getTransformFromProjections(projection, ol.proj.get('EPSG:4326')); - var vertices = [ - point[0] - resolution / 2, point[1], - point[0] + resolution / 2, point[1], - point[0], point[1] - resolution / 2, - point[0], point[1] + resolution / 2 - ]; - vertices = toEPSG4326(vertices, vertices, 2); - var width = ol.sphere.NORMAL.haversineDistance( - vertices.slice(0, 2), vertices.slice(2, 4)); - var height = ol.sphere.NORMAL.haversineDistance( - vertices.slice(4, 6), vertices.slice(6, 8)); - pointResolution = (width + height) / 2; - var metersPerUnit = projection.getMetersPerUnit(); - if (metersPerUnit !== undefined) { - pointResolution /= metersPerUnit; - } - } - } - return pointResolution; -}; - - -/** - * Registers transformation functions that don't alter coordinates. Those allow - * to transform between projections with equal meaning. - * - * @param {Array.<ol.proj.Projection>} projections Projections. - * @api - */ -ol.proj.addEquivalentProjections = function(projections) { - ol.proj.addProjections(projections); - projections.forEach(function(source) { - projections.forEach(function(destination) { - if (source !== destination) { - ol.proj.transforms.add(source, destination, ol.proj.cloneTransform); - } - }); - }); -}; - - -/** - * Registers transformation functions to convert coordinates in any projection - * in projection1 to any projection in projection2. - * - * @param {Array.<ol.proj.Projection>} projections1 Projections with equal - * meaning. - * @param {Array.<ol.proj.Projection>} projections2 Projections with equal - * meaning. - * @param {ol.TransformFunction} forwardTransform Transformation from any - * projection in projection1 to any projection in projection2. - * @param {ol.TransformFunction} inverseTransform Transform from any projection - * in projection2 to any projection in projection1.. - */ -ol.proj.addEquivalentTransforms = function(projections1, projections2, forwardTransform, inverseTransform) { - projections1.forEach(function(projection1) { - projections2.forEach(function(projection2) { - ol.proj.transforms.add(projection1, projection2, forwardTransform); - ol.proj.transforms.add(projection2, projection1, inverseTransform); - }); - }); -}; - - -/** - * Add a Projection object to the list of supported projections that can be - * looked up by their code. - * - * @param {ol.proj.Projection} projection Projection instance. - * @api - */ -ol.proj.addProjection = function(projection) { - ol.proj.projections.add(projection.getCode(), projection); - ol.proj.transforms.add(projection, projection, ol.proj.cloneTransform); -}; - - -/** - * @param {Array.<ol.proj.Projection>} projections Projections. - */ -ol.proj.addProjections = function(projections) { - projections.forEach(ol.proj.addProjection); -}; - - -/** - * Clear all cached projections and transforms. - */ -ol.proj.clearAllProjections = function() { - ol.proj.projections.clear(); - ol.proj.transforms.clear(); -}; - - -/** - * @param {ol.proj.Projection|string|undefined} projection Projection. - * @param {string} defaultCode Default code. - * @return {ol.proj.Projection} Projection. - */ -ol.proj.createProjection = function(projection, defaultCode) { - if (!projection) { - return ol.proj.get(defaultCode); - } else if (typeof projection === 'string') { - return ol.proj.get(projection); - } else { - return /** @type {ol.proj.Projection} */ (projection); - } -}; - - -/** - * Registers coordinate transform functions to convert coordinates between the - * source projection and the destination projection. - * The forward and inverse functions convert coordinate pairs; this function - * converts these into the functions used internally which also handle - * extents and coordinate arrays. - * - * @param {ol.ProjectionLike} source Source projection. - * @param {ol.ProjectionLike} destination Destination projection. - * @param {function(ol.Coordinate): ol.Coordinate} forward The forward transform - * function (that is, from the source projection to the destination - * projection) that takes a {@link ol.Coordinate} as argument and returns - * the transformed {@link ol.Coordinate}. - * @param {function(ol.Coordinate): ol.Coordinate} inverse The inverse transform - * function (that is, from the destination projection to the source - * projection) that takes a {@link ol.Coordinate} as argument and returns - * the transformed {@link ol.Coordinate}. - * @api - */ -ol.proj.addCoordinateTransforms = function(source, destination, forward, inverse) { - var sourceProj = ol.proj.get(source); - var destProj = ol.proj.get(destination); - ol.proj.transforms.add(sourceProj, destProj, - ol.proj.createTransformFromCoordinateTransform(forward)); - ol.proj.transforms.add(destProj, sourceProj, - ol.proj.createTransformFromCoordinateTransform(inverse)); -}; - - -/** - * Creates a {@link ol.TransformFunction} from a simple 2D coordinate transform - * function. - * @param {function(ol.Coordinate): ol.Coordinate} transform Coordinate - * transform. - * @return {ol.TransformFunction} Transform function. - */ -ol.proj.createTransformFromCoordinateTransform = function(transform) { - return ( - /** - * @param {Array.<number>} input Input. - * @param {Array.<number>=} opt_output Output. - * @param {number=} opt_dimension Dimension. - * @return {Array.<number>} Output. - */ - function(input, opt_output, opt_dimension) { - var length = input.length; - var dimension = opt_dimension !== undefined ? opt_dimension : 2; - var output = opt_output !== undefined ? opt_output : new Array(length); - var point, i, j; - for (i = 0; i < length; i += dimension) { - point = transform([input[i], input[i + 1]]); - output[i] = point[0]; - output[i + 1] = point[1]; - for (j = dimension - 1; j >= 2; --j) { - output[i + j] = input[i + j]; - } - } - return output; - }); -}; - - -/** - * Transforms a coordinate from longitude/latitude to a different projection. - * @param {ol.Coordinate} coordinate Coordinate as longitude and latitude, i.e. - * an array with longitude as 1st and latitude as 2nd element. - * @param {ol.ProjectionLike=} opt_projection Target projection. The - * default is Web Mercator, i.e. 'EPSG:3857'. - * @return {ol.Coordinate} Coordinate projected to the target projection. - * @api - */ -ol.proj.fromLonLat = function(coordinate, opt_projection) { - return ol.proj.transform(coordinate, 'EPSG:4326', - opt_projection !== undefined ? opt_projection : 'EPSG:3857'); -}; - - -/** - * Transforms a coordinate to longitude/latitude. - * @param {ol.Coordinate} coordinate Projected coordinate. - * @param {ol.ProjectionLike=} opt_projection Projection of the coordinate. - * The default is Web Mercator, i.e. 'EPSG:3857'. - * @return {ol.Coordinate} Coordinate as longitude and latitude, i.e. an array - * with longitude as 1st and latitude as 2nd element. - * @api - */ -ol.proj.toLonLat = function(coordinate, opt_projection) { - return ol.proj.transform(coordinate, - opt_projection !== undefined ? opt_projection : 'EPSG:3857', 'EPSG:4326'); -}; - - -/** - * Fetches a Projection object for the code specified. - * - * @param {ol.ProjectionLike} projectionLike Either a code string which is - * a combination of authority and identifier such as "EPSG:4326", or an - * existing projection object, or undefined. - * @return {ol.proj.Projection} Projection object, or null if not in list. - * @api - */ -ol.proj.get = function(projectionLike) { - var projection = null; - if (projectionLike instanceof ol.proj.Projection) { - projection = projectionLike; - } else if (typeof projectionLike === 'string') { - var code = projectionLike; - projection = ol.proj.projections.get(code); - if (ol.ENABLE_PROJ4JS) { - var proj4js = ol.proj.proj4.get(); - if (!projection && typeof proj4js == 'function' && - proj4js.defs(code) !== undefined) { - projection = new ol.proj.Projection({code: code}); - ol.proj.addProjection(projection); - } - } - } - return projection; -}; - - -/** - * Checks if two projections are the same, that is every coordinate in one - * projection does represent the same geographic point as the same coordinate in - * the other projection. - * - * @param {ol.proj.Projection} projection1 Projection 1. - * @param {ol.proj.Projection} projection2 Projection 2. - * @return {boolean} Equivalent. - * @api - */ -ol.proj.equivalent = function(projection1, projection2) { - if (projection1 === projection2) { - return true; - } - var equalUnits = projection1.getUnits() === projection2.getUnits(); - if (projection1.getCode() === projection2.getCode()) { - return equalUnits; - } else { - var transformFn = ol.proj.getTransformFromProjections( - projection1, projection2); - return transformFn === ol.proj.cloneTransform && equalUnits; - } -}; - - -/** - * Given the projection-like objects, searches for a transformation - * function to convert a coordinates array from the source projection to the - * destination projection. - * - * @param {ol.ProjectionLike} source Source. - * @param {ol.ProjectionLike} destination Destination. - * @return {ol.TransformFunction} Transform function. - * @api - */ -ol.proj.getTransform = function(source, destination) { - var sourceProjection = ol.proj.get(source); - var destinationProjection = ol.proj.get(destination); - return ol.proj.getTransformFromProjections( - sourceProjection, destinationProjection); -}; - - -/** - * Searches in the list of transform functions for the function for converting - * coordinates from the source projection to the destination projection. - * - * @param {ol.proj.Projection} sourceProjection Source Projection object. - * @param {ol.proj.Projection} destinationProjection Destination Projection - * object. - * @return {ol.TransformFunction} Transform function. - */ -ol.proj.getTransformFromProjections = function(sourceProjection, destinationProjection) { - var sourceCode = sourceProjection.getCode(); - var destinationCode = destinationProjection.getCode(); - var transform = ol.proj.transforms.get(sourceCode, destinationCode); - if (ol.ENABLE_PROJ4JS && !transform) { - var proj4js = ol.proj.proj4.get(); - if (typeof proj4js == 'function') { - var sourceDef = proj4js.defs(sourceCode); - var destinationDef = proj4js.defs(destinationCode); - - if (sourceDef !== undefined && destinationDef !== undefined) { - if (sourceDef === destinationDef) { - ol.proj.addEquivalentProjections([destinationProjection, sourceProjection]); - } else { - var proj4Transform = proj4js(destinationCode, sourceCode); - ol.proj.addCoordinateTransforms(destinationProjection, sourceProjection, - proj4Transform.forward, proj4Transform.inverse); - } - transform = ol.proj.transforms.get(sourceCode, destinationCode); - } - } - } - if (!transform) { - transform = ol.proj.identityTransform; - } - return transform; -}; - - -/** - * @param {Array.<number>} input Input coordinate array. - * @param {Array.<number>=} opt_output Output array of coordinate values. - * @param {number=} opt_dimension Dimension. - * @return {Array.<number>} Input coordinate array (same array as input). - */ -ol.proj.identityTransform = function(input, opt_output, opt_dimension) { - if (opt_output !== undefined && input !== opt_output) { - for (var i = 0, ii = input.length; i < ii; ++i) { - opt_output[i] = input[i]; - } - input = opt_output; - } - return input; -}; - - -/** - * @param {Array.<number>} input Input coordinate array. - * @param {Array.<number>=} opt_output Output array of coordinate values. - * @param {number=} opt_dimension Dimension. - * @return {Array.<number>} Output coordinate array (new array, same coordinate - * values). - */ -ol.proj.cloneTransform = function(input, opt_output, opt_dimension) { - var output; - if (opt_output !== undefined) { - for (var i = 0, ii = input.length; i < ii; ++i) { - opt_output[i] = input[i]; - } - output = opt_output; - } else { - output = input.slice(); - } - return output; -}; - - -/** - * Transforms a coordinate from source projection to destination projection. - * This returns a new coordinate (and does not modify the original). - * - * See {@link ol.proj.transformExtent} for extent transformation. - * See the transform method of {@link ol.geom.Geometry} and its subclasses for - * geometry transforms. - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.ProjectionLike} source Source projection-like. - * @param {ol.ProjectionLike} destination Destination projection-like. - * @return {ol.Coordinate} Coordinate. - * @api - */ -ol.proj.transform = function(coordinate, source, destination) { - var transformFn = ol.proj.getTransform(source, destination); - return transformFn(coordinate, undefined, coordinate.length); -}; - - -/** - * Transforms an extent from source projection to destination projection. This - * returns a new extent (and does not modify the original). - * - * @param {ol.Extent} extent The extent to transform. - * @param {ol.ProjectionLike} source Source projection-like. - * @param {ol.ProjectionLike} destination Destination projection-like. - * @return {ol.Extent} The transformed extent. - * @api - */ -ol.proj.transformExtent = function(extent, source, destination) { - var transformFn = ol.proj.getTransform(source, destination); - return ol.extent.applyTransform(extent, transformFn); -}; - - -/** - * Transforms the given point to the destination projection. - * - * @param {ol.Coordinate} point Point. - * @param {ol.proj.Projection} sourceProjection Source projection. - * @param {ol.proj.Projection} destinationProjection Destination projection. - * @return {ol.Coordinate} Point. - */ -ol.proj.transformWithProjections = function(point, sourceProjection, destinationProjection) { - var transformFn = ol.proj.getTransformFromProjections( - sourceProjection, destinationProjection); - return transformFn(point); -}; - -/** - * Add transforms to and from EPSG:4326 and EPSG:3857. This function is called - * by when this module is executed and should only need to be called again after - * `ol.proj.clearAllProjections()` is called (e.g. in tests). - */ -ol.proj.addCommon = function() { - // Add transformations that don't alter coordinates to convert within set of - // projections with equal meaning. - ol.proj.addEquivalentProjections(ol.proj.EPSG3857.PROJECTIONS); - ol.proj.addEquivalentProjections(ol.proj.EPSG4326.PROJECTIONS); - // Add transformations to convert EPSG:4326 like coordinates to EPSG:3857 like - // coordinates and back. - ol.proj.addEquivalentTransforms( - ol.proj.EPSG4326.PROJECTIONS, - ol.proj.EPSG3857.PROJECTIONS, - ol.proj.EPSG3857.fromEPSG4326, - ol.proj.EPSG3857.toEPSG4326); -}; - -ol.proj.addCommon(); - -goog.provide('ol.tilecoord'); - - -/** - * @param {number} z Z. - * @param {number} x X. - * @param {number} y Y. - * @param {ol.TileCoord=} opt_tileCoord Tile coordinate. - * @return {ol.TileCoord} Tile coordinate. - */ -ol.tilecoord.createOrUpdate = function(z, x, y, opt_tileCoord) { - if (opt_tileCoord !== undefined) { - opt_tileCoord[0] = z; - opt_tileCoord[1] = x; - opt_tileCoord[2] = y; - return opt_tileCoord; - } else { - return [z, x, y]; - } -}; - - -/** - * @param {number} z Z. - * @param {number} x X. - * @param {number} y Y. - * @return {string} Key. - */ -ol.tilecoord.getKeyZXY = function(z, x, y) { - return z + '/' + x + '/' + y; -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coord. - * @return {number} Hash. - */ -ol.tilecoord.hash = function(tileCoord) { - return (tileCoord[1] << tileCoord[0]) + tileCoord[2]; -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coord. - * @return {string} Quad key. - */ -ol.tilecoord.quadKey = function(tileCoord) { - var z = tileCoord[0]; - var digits = new Array(z); - var mask = 1 << (z - 1); - var i, charCode; - for (i = 0; i < z; ++i) { - // 48 is charCode for 0 - '0'.charCodeAt(0) - charCode = 48; - if (tileCoord[1] & mask) { - charCode += 1; - } - if (tileCoord[2] & mask) { - charCode += 2; - } - digits[i] = String.fromCharCode(charCode); - mask >>= 1; - } - return digits.join(''); -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid. - * @return {boolean} Tile coordinate is within extent and zoom level range. - */ -ol.tilecoord.withinExtentAndZ = function(tileCoord, tileGrid) { - var z = tileCoord[0]; - var x = tileCoord[1]; - var y = tileCoord[2]; - - if (tileGrid.getMinZoom() > z || z > tileGrid.getMaxZoom()) { - return false; - } - var extent = tileGrid.getExtent(); - var tileRange; - if (!extent) { - tileRange = tileGrid.getFullTileRange(z); - } else { - tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - } - if (!tileRange) { - return true; - } else { - return tileRange.containsXY(x, y); - } -}; - -goog.provide('ol.tilegrid.TileGrid'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.TileRange'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.math'); -goog.require('ol.size'); -goog.require('ol.tilecoord'); - - -/** - * @classdesc - * Base class for setting the grid pattern for sources accessing tiled-image - * servers. - * - * @constructor - * @param {olx.tilegrid.TileGridOptions} options Tile grid options. - * @struct - * @api - */ -ol.tilegrid.TileGrid = function(options) { - - /** - * @protected - * @type {number} - */ - this.minZoom = options.minZoom !== undefined ? options.minZoom : 0; - - /** - * @private - * @type {!Array.<number>} - */ - this.resolutions_ = options.resolutions; - ol.asserts.assert(ol.array.isSorted(this.resolutions_, function(a, b) { - return b - a; - }, true), 17); // `resolutions` must be sorted in descending order - - /** - * @protected - * @type {number} - */ - this.maxZoom = this.resolutions_.length - 1; - - /** - * @private - * @type {ol.Coordinate} - */ - this.origin_ = options.origin !== undefined ? options.origin : null; - - /** - * @private - * @type {Array.<ol.Coordinate>} - */ - this.origins_ = null; - if (options.origins !== undefined) { - this.origins_ = options.origins; - ol.asserts.assert(this.origins_.length == this.resolutions_.length, - 20); // Number of `origins` and `resolutions` must be equal - } - - var extent = options.extent; - - if (extent !== undefined && - !this.origin_ && !this.origins_) { - this.origin_ = ol.extent.getTopLeft(extent); - } - - ol.asserts.assert( - (!this.origin_ && this.origins_) || (this.origin_ && !this.origins_), - 18); // Either `origin` or `origins` must be configured, never both - - /** - * @private - * @type {Array.<number|ol.Size>} - */ - this.tileSizes_ = null; - if (options.tileSizes !== undefined) { - this.tileSizes_ = options.tileSizes; - ol.asserts.assert(this.tileSizes_.length == this.resolutions_.length, - 19); // Number of `tileSizes` and `resolutions` must be equal - } - - /** - * @private - * @type {number|ol.Size} - */ - this.tileSize_ = options.tileSize !== undefined ? - options.tileSize : - !this.tileSizes_ ? ol.DEFAULT_TILE_SIZE : null; - ol.asserts.assert( - (!this.tileSize_ && this.tileSizes_) || - (this.tileSize_ && !this.tileSizes_), - 22); // Either `tileSize` or `tileSizes` must be configured, never both - - /** - * @private - * @type {ol.Extent} - */ - this.extent_ = extent !== undefined ? extent : null; - - - /** - * @private - * @type {Array.<ol.TileRange>} - */ - this.fullTileRanges_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.tmpSize_ = [0, 0]; - - if (options.sizes !== undefined) { - this.fullTileRanges_ = options.sizes.map(function(size, z) { - var tileRange = new ol.TileRange( - Math.min(0, size[0]), Math.max(size[0] - 1, -1), - Math.min(0, size[1]), Math.max(size[1] - 1, -1)); - return tileRange; - }, this); - } else if (extent) { - this.calculateTileRanges_(extent); - } - -}; - - -/** - * @private - * @type {ol.TileCoord} - */ -ol.tilegrid.TileGrid.tmpTileCoord_ = [0, 0, 0]; - - -/** - * Call a function with each tile coordinate for a given extent and zoom level. - * - * @param {ol.Extent} extent Extent. - * @param {number} zoom Zoom level. - * @param {function(ol.TileCoord)} callback Function called with each tile coordinate. - * @api - */ -ol.tilegrid.TileGrid.prototype.forEachTileCoord = function(extent, zoom, callback) { - var tileRange = this.getTileRangeForExtentAndZ(extent, zoom); - for (var i = tileRange.minX, ii = tileRange.maxX; i <= ii; ++i) { - for (var j = tileRange.minY, jj = tileRange.maxY; j <= jj; ++j) { - callback([zoom, i, j]); - } - } -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {function(this: T, number, ol.TileRange): boolean} callback Callback. - * @param {T=} opt_this The object to use as `this` in `callback`. - * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object. - * @param {ol.Extent=} opt_extent Temporary ol.Extent object. - * @return {boolean} Callback succeeded. - * @template T - */ -ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange = function(tileCoord, callback, opt_this, opt_tileRange, opt_extent) { - var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); - var z = tileCoord[0] - 1; - while (z >= this.minZoom) { - if (callback.call(opt_this, z, - this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange))) { - return true; - } - --z; - } - return false; -}; - - -/** - * Get the extent for this tile grid, if it was configured. - * @return {ol.Extent} Extent. - */ -ol.tilegrid.TileGrid.prototype.getExtent = function() { - return this.extent_; -}; - - -/** - * Get the maximum zoom level for the grid. - * @return {number} Max zoom. - * @api - */ -ol.tilegrid.TileGrid.prototype.getMaxZoom = function() { - return this.maxZoom; -}; - - -/** - * Get the minimum zoom level for the grid. - * @return {number} Min zoom. - * @api - */ -ol.tilegrid.TileGrid.prototype.getMinZoom = function() { - return this.minZoom; -}; - - -/** - * Get the origin for the grid at the given zoom level. - * @param {number} z Z. - * @return {ol.Coordinate} Origin. - * @api - */ -ol.tilegrid.TileGrid.prototype.getOrigin = function(z) { - if (this.origin_) { - return this.origin_; - } else { - return this.origins_[z]; - } -}; - - -/** - * Get the resolution for the given zoom level. - * @param {number} z Z. - * @return {number} Resolution. - * @api - */ -ol.tilegrid.TileGrid.prototype.getResolution = function(z) { - return this.resolutions_[z]; -}; - - -/** - * Get the list of resolutions for the tile grid. - * @return {Array.<number>} Resolutions. - * @api - */ -ol.tilegrid.TileGrid.prototype.getResolutions = function() { - return this.resolutions_; -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object. - * @param {ol.Extent=} opt_extent Temporary ol.Extent object. - * @return {ol.TileRange} Tile range. - */ -ol.tilegrid.TileGrid.prototype.getTileCoordChildTileRange = function(tileCoord, opt_tileRange, opt_extent) { - if (tileCoord[0] < this.maxZoom) { - var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); - return this.getTileRangeForExtentAndZ( - tileCoordExtent, tileCoord[0] + 1, opt_tileRange); - } else { - return null; - } -}; - - -/** - * @param {number} z Z. - * @param {ol.TileRange} tileRange Tile range. - * @param {ol.Extent=} opt_extent Temporary ol.Extent object. - * @return {ol.Extent} Extent. - */ -ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange, opt_extent) { - var origin = this.getOrigin(z); - var resolution = this.getResolution(z); - var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_); - var minX = origin[0] + tileRange.minX * tileSize[0] * resolution; - var maxX = origin[0] + (tileRange.maxX + 1) * tileSize[0] * resolution; - var minY = origin[1] + tileRange.minY * tileSize[1] * resolution; - var maxY = origin[1] + (tileRange.maxY + 1) * tileSize[1] * resolution; - return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {ol.TileRange=} opt_tileRange Temporary tile range object. - * @return {ol.TileRange} Tile range. - */ -ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function(extent, resolution, opt_tileRange) { - var tileCoord = ol.tilegrid.TileGrid.tmpTileCoord_; - this.getTileCoordForXYAndResolution_( - extent[0], extent[1], resolution, false, tileCoord); - var minX = tileCoord[1]; - var minY = tileCoord[2]; - this.getTileCoordForXYAndResolution_( - extent[2], extent[3], resolution, true, tileCoord); - return ol.TileRange.createOrUpdate( - minX, tileCoord[1], minY, tileCoord[2], opt_tileRange); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} z Z. - * @param {ol.TileRange=} opt_tileRange Temporary tile range object. - * @return {ol.TileRange} Tile range. - */ -ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndZ = function(extent, z, opt_tileRange) { - var resolution = this.getResolution(z); - return this.getTileRangeForExtentAndResolution( - extent, resolution, opt_tileRange); -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @return {ol.Coordinate} Tile center. - */ -ol.tilegrid.TileGrid.prototype.getTileCoordCenter = function(tileCoord) { - var origin = this.getOrigin(tileCoord[0]); - var resolution = this.getResolution(tileCoord[0]); - var tileSize = ol.size.toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); - return [ - origin[0] + (tileCoord[1] + 0.5) * tileSize[0] * resolution, - origin[1] + (tileCoord[2] + 0.5) * tileSize[1] * resolution - ]; -}; - - -/** - * Get the extent of a tile coordinate. - * - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.Extent=} opt_extent Temporary extent object. - * @return {ol.Extent} Extent. - * @api - */ -ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord, opt_extent) { - var origin = this.getOrigin(tileCoord[0]); - var resolution = this.getResolution(tileCoord[0]); - var tileSize = ol.size.toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); - var minX = origin[0] + tileCoord[1] * tileSize[0] * resolution; - var minY = origin[1] + tileCoord[2] * tileSize[1] * resolution; - var maxX = minX + tileSize[0] * resolution; - var maxY = minY + tileSize[1] * resolution; - return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent); -}; - - -/** - * Get the tile coordinate for the given map coordinate and resolution. This - * method considers that coordinates that intersect tile boundaries should be - * assigned the higher tile coordinate. - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object. - * @return {ol.TileCoord} Tile coordinate. - * @api - */ -ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function(coordinate, resolution, opt_tileCoord) { - return this.getTileCoordForXYAndResolution_( - coordinate[0], coordinate[1], resolution, false, opt_tileCoord); -}; - - -/** - * @param {number} x X. - * @param {number} y Y. - * @param {number} resolution Resolution. - * @param {boolean} reverseIntersectionPolicy Instead of letting edge - * intersections go to the higher tile coordinate, let edge intersections - * go to the lower tile coordinate. - * @param {ol.TileCoord=} opt_tileCoord Temporary ol.TileCoord object. - * @return {ol.TileCoord} Tile coordinate. - * @private - */ -ol.tilegrid.TileGrid.prototype.getTileCoordForXYAndResolution_ = function( - x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) { - var z = this.getZForResolution(resolution); - var scale = resolution / this.getResolution(z); - var origin = this.getOrigin(z); - var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_); - - var adjustX = reverseIntersectionPolicy ? 0.5 : 0; - var adjustY = reverseIntersectionPolicy ? 0 : 0.5; - var xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX); - var yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY); - var tileCoordX = scale * xFromOrigin / tileSize[0]; - var tileCoordY = scale * yFromOrigin / tileSize[1]; - - if (reverseIntersectionPolicy) { - tileCoordX = Math.ceil(tileCoordX) - 1; - tileCoordY = Math.ceil(tileCoordY) - 1; - } else { - tileCoordX = Math.floor(tileCoordX); - tileCoordY = Math.floor(tileCoordY); - } - - return ol.tilecoord.createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord); -}; - - -/** - * Get a tile coordinate given a map coordinate and zoom level. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} z Zoom level. - * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object. - * @return {ol.TileCoord} Tile coordinate. - * @api - */ -ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z, opt_tileCoord) { - var resolution = this.getResolution(z); - return this.getTileCoordForXYAndResolution_( - coordinate[0], coordinate[1], resolution, false, opt_tileCoord); -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @return {number} Tile resolution. - */ -ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) { - return this.resolutions_[tileCoord[0]]; -}; - - -/** - * Get the tile size for a zoom level. The type of the return value matches the - * `tileSize` or `tileSizes` that the tile grid was configured with. To always - * get an `ol.Size`, run the result through `ol.size.toSize()`. - * @param {number} z Z. - * @return {number|ol.Size} Tile size. - * @api - */ -ol.tilegrid.TileGrid.prototype.getTileSize = function(z) { - if (this.tileSize_) { - return this.tileSize_; - } else { - return this.tileSizes_[z]; - } -}; - - -/** - * @param {number} z Zoom level. - * @return {ol.TileRange} Extent tile range for the specified zoom level. - */ -ol.tilegrid.TileGrid.prototype.getFullTileRange = function(z) { - if (!this.fullTileRanges_) { - return null; - } else { - return this.fullTileRanges_[z]; - } -}; - - -/** - * @param {number} resolution Resolution. - * @param {number=} opt_direction If 0, the nearest resolution will be used. - * If 1, the nearest lower resolution will be used. If -1, the nearest - * higher resolution will be used. Default is 0. - * @return {number} Z. - * @api - */ -ol.tilegrid.TileGrid.prototype.getZForResolution = function( - resolution, opt_direction) { - var z = ol.array.linearFindNearest(this.resolutions_, resolution, - opt_direction || 0); - return ol.math.clamp(z, this.minZoom, this.maxZoom); -}; - - -/** - * @param {!ol.Extent} extent Extent for this tile grid. - * @private - */ -ol.tilegrid.TileGrid.prototype.calculateTileRanges_ = function(extent) { - var length = this.resolutions_.length; - var fullTileRanges = new Array(length); - for (var z = this.minZoom; z < length; ++z) { - fullTileRanges[z] = this.getTileRangeForExtentAndZ(extent, z); - } - this.fullTileRanges_ = fullTileRanges; -}; - -goog.provide('ol.tilegrid'); - -goog.require('ol'); -goog.require('ol.size'); -goog.require('ol.extent'); -goog.require('ol.extent.Corner'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.proj.Units'); -goog.require('ol.tilegrid.TileGrid'); - - -/** - * @param {ol.proj.Projection} projection Projection. - * @return {!ol.tilegrid.TileGrid} Default tile grid for the passed projection. - */ -ol.tilegrid.getForProjection = function(projection) { - var tileGrid = projection.getDefaultTileGrid(); - if (!tileGrid) { - tileGrid = ol.tilegrid.createForProjection(projection); - projection.setDefaultTileGrid(tileGrid); - } - return tileGrid; -}; - - -/** - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.proj.Projection} projection Projection. - * @return {ol.TileCoord} Tile coordinate. - */ -ol.tilegrid.wrapX = function(tileGrid, tileCoord, projection) { - var z = tileCoord[0]; - var center = tileGrid.getTileCoordCenter(tileCoord); - var projectionExtent = ol.tilegrid.extentFromProjection(projection); - if (!ol.extent.containsCoordinate(projectionExtent, center)) { - var worldWidth = ol.extent.getWidth(projectionExtent); - var worldsAway = Math.ceil((projectionExtent[0] - center[0]) / worldWidth); - center[0] += worldWidth * worldsAway; - return tileGrid.getTileCoordForCoordAndZ(center, z); - } else { - return tileCoord; - } -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number=} opt_maxZoom Maximum zoom level (default is - * ol.DEFAULT_MAX_ZOOM). - * @param {number|ol.Size=} opt_tileSize Tile size (default uses - * ol.DEFAULT_TILE_SIZE). - * @param {ol.extent.Corner=} opt_corner Extent corner (default is - * ol.extent.Corner.TOP_LEFT). - * @return {!ol.tilegrid.TileGrid} TileGrid instance. - */ -ol.tilegrid.createForExtent = function(extent, opt_maxZoom, opt_tileSize, opt_corner) { - var corner = opt_corner !== undefined ? - opt_corner : ol.extent.Corner.TOP_LEFT; - - var resolutions = ol.tilegrid.resolutionsFromExtent( - extent, opt_maxZoom, opt_tileSize); - - return new ol.tilegrid.TileGrid({ - extent: extent, - origin: ol.extent.getCorner(extent, corner), - resolutions: resolutions, - tileSize: opt_tileSize - }); -}; - - -/** - * Creates a tile grid with a standard XYZ tiling scheme. - * @param {olx.tilegrid.XYZOptions=} opt_options Tile grid options. - * @return {!ol.tilegrid.TileGrid} Tile grid instance. - * @api - */ -ol.tilegrid.createXYZ = function(opt_options) { - var options = /** @type {olx.tilegrid.TileGridOptions} */ ({}); - ol.obj.assign(options, opt_options !== undefined ? - opt_options : /** @type {olx.tilegrid.XYZOptions} */ ({})); - if (options.extent === undefined) { - options.extent = ol.proj.get('EPSG:3857').getExtent(); - } - options.resolutions = ol.tilegrid.resolutionsFromExtent( - options.extent, options.maxZoom, options.tileSize); - delete options.maxZoom; - - return new ol.tilegrid.TileGrid(options); -}; - - -/** - * Create a resolutions array from an extent. A zoom factor of 2 is assumed. - * @param {ol.Extent} extent Extent. - * @param {number=} opt_maxZoom Maximum zoom level (default is - * ol.DEFAULT_MAX_ZOOM). - * @param {number|ol.Size=} opt_tileSize Tile size (default uses - * ol.DEFAULT_TILE_SIZE). - * @return {!Array.<number>} Resolutions array. - */ -ol.tilegrid.resolutionsFromExtent = function(extent, opt_maxZoom, opt_tileSize) { - var maxZoom = opt_maxZoom !== undefined ? - opt_maxZoom : ol.DEFAULT_MAX_ZOOM; - - var height = ol.extent.getHeight(extent); - var width = ol.extent.getWidth(extent); - - var tileSize = ol.size.toSize(opt_tileSize !== undefined ? - opt_tileSize : ol.DEFAULT_TILE_SIZE); - var maxResolution = Math.max( - width / tileSize[0], height / tileSize[1]); - - var length = maxZoom + 1; - var resolutions = new Array(length); - for (var z = 0; z < length; ++z) { - resolutions[z] = maxResolution / Math.pow(2, z); - } - return resolutions; -}; - - -/** - * @param {ol.ProjectionLike} projection Projection. - * @param {number=} opt_maxZoom Maximum zoom level (default is - * ol.DEFAULT_MAX_ZOOM). - * @param {number|ol.Size=} opt_tileSize Tile size (default uses - * ol.DEFAULT_TILE_SIZE). - * @param {ol.extent.Corner=} opt_corner Extent corner (default is - * ol.extent.Corner.BOTTOM_LEFT). - * @return {!ol.tilegrid.TileGrid} TileGrid instance. - */ -ol.tilegrid.createForProjection = function(projection, opt_maxZoom, opt_tileSize, opt_corner) { - var extent = ol.tilegrid.extentFromProjection(projection); - return ol.tilegrid.createForExtent( - extent, opt_maxZoom, opt_tileSize, opt_corner); -}; - - -/** - * Generate a tile grid extent from a projection. If the projection has an - * extent, it is used. If not, a global extent is assumed. - * @param {ol.ProjectionLike} projection Projection. - * @return {ol.Extent} Extent. - */ -ol.tilegrid.extentFromProjection = function(projection) { - projection = ol.proj.get(projection); - var extent = projection.getExtent(); - if (!extent) { - var half = 180 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] / - projection.getMetersPerUnit(); - extent = ol.extent.createOrUpdate(-half, -half, half, half); - } - return extent; -}; - -goog.provide('ol.Attribution'); - -goog.require('ol.TileRange'); -goog.require('ol.math'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * An attribution for a layer source. - * - * Example: - * - * source: new ol.source.OSM({ - * attributions: [ - * new ol.Attribution({ - * html: 'All maps © ' + - * '<a href="https://www.opencyclemap.org/">OpenCycleMap</a>' - * }), - * ol.source.OSM.ATTRIBUTION - * ], - * .. - * - * @constructor - * @param {olx.AttributionOptions} options Attribution options. - * @struct - * @api - */ -ol.Attribution = function(options) { - - /** - * @private - * @type {string} - */ - this.html_ = options.html; - - /** - * @private - * @type {Object.<string, Array.<ol.TileRange>>} - */ - this.tileRanges_ = options.tileRanges ? options.tileRanges : null; - -}; - - -/** - * Get the attribution markup. - * @return {string} The attribution HTML. - * @api - */ -ol.Attribution.prototype.getHTML = function() { - return this.html_; -}; - - -/** - * @param {Object.<string, ol.TileRange>} tileRanges Tile ranges. - * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid. - * @param {!ol.proj.Projection} projection Projection. - * @return {boolean} Intersects any tile range. - */ -ol.Attribution.prototype.intersectsAnyTileRange = function(tileRanges, tileGrid, projection) { - if (!this.tileRanges_) { - return true; - } - var i, ii, tileRange, zKey; - for (zKey in tileRanges) { - if (!(zKey in this.tileRanges_)) { - continue; - } - tileRange = tileRanges[zKey]; - var testTileRange; - for (i = 0, ii = this.tileRanges_[zKey].length; i < ii; ++i) { - testTileRange = this.tileRanges_[zKey][i]; - if (testTileRange.intersects(tileRange)) { - return true; - } - var extentTileRange = tileGrid.getTileRangeForExtentAndZ( - ol.tilegrid.extentFromProjection(projection), parseInt(zKey, 10)); - var width = extentTileRange.getWidth(); - if (tileRange.minX < extentTileRange.minX || - tileRange.maxX > extentTileRange.maxX) { - if (testTileRange.intersects(new ol.TileRange( - ol.math.modulo(tileRange.minX, width), - ol.math.modulo(tileRange.maxX, width), - tileRange.minY, tileRange.maxY))) { - return true; - } - if (tileRange.getWidth() > width && - testTileRange.intersects(extentTileRange)) { - return true; - } - } - } - } - return false; -}; - -goog.provide('ol.CenterConstraint'); - -goog.require('ol.math'); - - -/** - * @param {ol.Extent} extent Extent. - * @return {ol.CenterConstraintType} The constraint. - */ -ol.CenterConstraint.createExtent = function(extent) { - return ( - /** - * @param {ol.Coordinate|undefined} center Center. - * @return {ol.Coordinate|undefined} Center. - */ - function(center) { - if (center) { - return [ - ol.math.clamp(center[0], extent[0], extent[2]), - ol.math.clamp(center[1], extent[1], extent[3]) - ]; - } else { - return undefined; - } - }); -}; - - -/** - * @param {ol.Coordinate|undefined} center Center. - * @return {ol.Coordinate|undefined} Center. - */ -ol.CenterConstraint.none = function(center) { - return center; -}; - -goog.provide('ol.CollectionEventType'); - -/** - * @enum {string} - */ -ol.CollectionEventType = { - /** - * Triggered when an item is added to the collection. - * @event ol.Collection.Event#add - * @api - */ - ADD: 'add', - /** - * Triggered when an item is removed from the collection. - * @event ol.Collection.Event#remove - * @api - */ - REMOVE: 'remove' -}; - -goog.provide('ol.ObjectEventType'); - -/** - * @enum {string} - */ -ol.ObjectEventType = { - /** - * Triggered when a property is changed. - * @event ol.Object.Event#propertychange - * @api - */ - PROPERTYCHANGE: 'propertychange' -}; - -goog.provide('ol.events'); - -goog.require('ol.obj'); - - -/** - * @param {ol.EventsKey} listenerObj Listener object. - * @return {ol.EventsListenerFunctionType} Bound listener. - */ -ol.events.bindListener_ = function(listenerObj) { - var boundListener = function(evt) { - var listener = listenerObj.listener; - var bindTo = listenerObj.bindTo || listenerObj.target; - if (listenerObj.callOnce) { - ol.events.unlistenByKey(listenerObj); - } - return listener.call(bindTo, evt); - }; - listenerObj.boundListener = boundListener; - return boundListener; -}; - - -/** - * Finds the matching {@link ol.EventsKey} in the given listener - * array. - * - * @param {!Array<!ol.EventsKey>} listeners Array of listeners. - * @param {!Function} listener The listener function. - * @param {Object=} opt_this The `this` value inside the listener. - * @param {boolean=} opt_setDeleteIndex Set the deleteIndex on the matching - * listener, for {@link ol.events.unlistenByKey}. - * @return {ol.EventsKey|undefined} The matching listener object. - * @private - */ -ol.events.findListener_ = function(listeners, listener, opt_this, - opt_setDeleteIndex) { - var listenerObj; - for (var i = 0, ii = listeners.length; i < ii; ++i) { - listenerObj = listeners[i]; - if (listenerObj.listener === listener && - listenerObj.bindTo === opt_this) { - if (opt_setDeleteIndex) { - listenerObj.deleteIndex = i; - } - return listenerObj; - } - } - return undefined; -}; - - -/** - * @param {ol.EventTargetLike} target Target. - * @param {string} type Type. - * @return {Array.<ol.EventsKey>|undefined} Listeners. - */ -ol.events.getListeners = function(target, type) { - var listenerMap = target.ol_lm; - return listenerMap ? listenerMap[type] : undefined; -}; - - -/** - * Get the lookup of listeners. If one does not exist on the target, it is - * created. - * @param {ol.EventTargetLike} target Target. - * @return {!Object.<string, Array.<ol.EventsKey>>} Map of - * listeners by event type. - * @private - */ -ol.events.getListenerMap_ = function(target) { - var listenerMap = target.ol_lm; - if (!listenerMap) { - listenerMap = target.ol_lm = {}; - } - return listenerMap; -}; - - -/** - * Clean up all listener objects of the given type. All properties on the - * listener objects will be removed, and if no listeners remain in the listener - * map, it will be removed from the target. - * @param {ol.EventTargetLike} target Target. - * @param {string} type Type. - * @private - */ -ol.events.removeListeners_ = function(target, type) { - var listeners = ol.events.getListeners(target, type); - if (listeners) { - for (var i = 0, ii = listeners.length; i < ii; ++i) { - target.removeEventListener(type, listeners[i].boundListener); - ol.obj.clear(listeners[i]); - } - listeners.length = 0; - var listenerMap = target.ol_lm; - if (listenerMap) { - delete listenerMap[type]; - if (Object.keys(listenerMap).length === 0) { - delete target.ol_lm; - } - } - } -}; - - -/** - * Registers an event listener on an event target. Inspired by - * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} - * - * This function efficiently binds a `listener` to a `this` object, and returns - * a key for use with {@link ol.events.unlistenByKey}. - * - * @param {ol.EventTargetLike} target Event target. - * @param {string} type Event type. - * @param {ol.EventsListenerFunctionType} listener Listener. - * @param {Object=} opt_this Object referenced by the `this` keyword in the - * listener. Default is the `target`. - * @param {boolean=} opt_once If true, add the listener as one-off listener. - * @return {ol.EventsKey} Unique key for the listener. - */ -ol.events.listen = function(target, type, listener, opt_this, opt_once) { - var listenerMap = ol.events.getListenerMap_(target); - var listeners = listenerMap[type]; - if (!listeners) { - listeners = listenerMap[type] = []; - } - var listenerObj = ol.events.findListener_(listeners, listener, opt_this, - false); - if (listenerObj) { - if (!opt_once) { - // Turn one-off listener into a permanent one. - listenerObj.callOnce = false; - } - } else { - listenerObj = /** @type {ol.EventsKey} */ ({ - bindTo: opt_this, - callOnce: !!opt_once, - listener: listener, - target: target, - type: type - }); - target.addEventListener(type, ol.events.bindListener_(listenerObj)); - listeners.push(listenerObj); - } - - return listenerObj; -}; - - -/** - * Registers a one-off event listener on an event target. Inspired by - * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} - * - * This function efficiently binds a `listener` as self-unregistering listener - * to a `this` object, and returns a key for use with - * {@link ol.events.unlistenByKey} in case the listener needs to be unregistered - * before it is called. - * - * When {@link ol.events.listen} is called with the same arguments after this - * function, the self-unregistering listener will be turned into a permanent - * listener. - * - * @param {ol.EventTargetLike} target Event target. - * @param {string} type Event type. - * @param {ol.EventsListenerFunctionType} listener Listener. - * @param {Object=} opt_this Object referenced by the `this` keyword in the - * listener. Default is the `target`. - * @return {ol.EventsKey} Key for unlistenByKey. - */ -ol.events.listenOnce = function(target, type, listener, opt_this) { - return ol.events.listen(target, type, listener, opt_this, true); -}; - - -/** - * Unregisters an event listener on an event target. Inspired by - * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} - * - * To return a listener, this function needs to be called with the exact same - * arguments that were used for a previous {@link ol.events.listen} call. - * - * @param {ol.EventTargetLike} target Event target. - * @param {string} type Event type. - * @param {ol.EventsListenerFunctionType} listener Listener. - * @param {Object=} opt_this Object referenced by the `this` keyword in the - * listener. Default is the `target`. - */ -ol.events.unlisten = function(target, type, listener, opt_this) { - var listeners = ol.events.getListeners(target, type); - if (listeners) { - var listenerObj = ol.events.findListener_(listeners, listener, opt_this, - true); - if (listenerObj) { - ol.events.unlistenByKey(listenerObj); - } - } -}; - - -/** - * Unregisters event listeners on an event target. Inspired by - * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} - * - * The argument passed to this function is the key returned from - * {@link ol.events.listen} or {@link ol.events.listenOnce}. - * - * @param {ol.EventsKey} key The key. - */ -ol.events.unlistenByKey = function(key) { - if (key && key.target) { - key.target.removeEventListener(key.type, key.boundListener); - var listeners = ol.events.getListeners(key.target, key.type); - if (listeners) { - var i = 'deleteIndex' in key ? key.deleteIndex : listeners.indexOf(key); - if (i !== -1) { - listeners.splice(i, 1); - } - if (listeners.length === 0) { - ol.events.removeListeners_(key.target, key.type); - } - } - ol.obj.clear(key); - } -}; - - -/** - * Unregisters all event listeners on an event target. Inspired by - * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} - * - * @param {ol.EventTargetLike} target Target. - */ -ol.events.unlistenAll = function(target) { - var listenerMap = ol.events.getListenerMap_(target); - for (var type in listenerMap) { - ol.events.removeListeners_(target, type); - } -}; - -goog.provide('ol.Disposable'); - -goog.require('ol'); - -/** - * Objects that need to clean up after themselves. - * @constructor - */ -ol.Disposable = function() {}; - -/** - * The object has already been disposed. - * @type {boolean} - * @private - */ -ol.Disposable.prototype.disposed_ = false; - -/** - * Clean up. - */ -ol.Disposable.prototype.dispose = function() { - if (!this.disposed_) { - this.disposed_ = true; - this.disposeInternal(); - } -}; - -/** - * Extension point for disposable objects. - * @protected - */ -ol.Disposable.prototype.disposeInternal = ol.nullFunction; - -goog.provide('ol.events.Event'); - - -/** - * @classdesc - * Stripped down implementation of the W3C DOM Level 2 Event interface. - * @see {@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface} - * - * This implementation only provides `type` and `target` properties, and - * `stopPropagation` and `preventDefault` methods. It is meant as base class - * for higher level events defined in the library, and works with - * {@link ol.events.EventTarget}. - * - * @constructor - * @implements {oli.events.Event} - * @param {string} type Type. - */ -ol.events.Event = function(type) { - - /** - * @type {boolean} - */ - this.propagationStopped; - - /** - * The event type. - * @type {string} - * @api - */ - this.type = type; - - /** - * The event target. - * @type {Object} - * @api - */ - this.target = null; - -}; - - -/** - * Stop event propagation. - * @function - * @override - * @api - */ -ol.events.Event.prototype.preventDefault = - -/** - * Stop event propagation. - * @function - * @override - * @api - */ -ol.events.Event.prototype.stopPropagation = function() { - this.propagationStopped = true; -}; - - -/** - * @param {Event|ol.events.Event} evt Event - */ -ol.events.Event.stopPropagation = function(evt) { - evt.stopPropagation(); -}; - - -/** - * @param {Event|ol.events.Event} evt Event - */ -ol.events.Event.preventDefault = function(evt) { - evt.preventDefault(); -}; - -goog.provide('ol.events.EventTarget'); - -goog.require('ol'); -goog.require('ol.Disposable'); -goog.require('ol.events'); -goog.require('ol.events.Event'); - - -/** - * @classdesc - * A simplified implementation of the W3C DOM Level 2 EventTarget interface. - * @see {@link https://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget} - * - * There are two important simplifications compared to the specification: - * - * 1. The handling of `useCapture` in `addEventListener` and - * `removeEventListener`. There is no real capture model. - * 2. The handling of `stopPropagation` and `preventDefault` on `dispatchEvent`. - * There is no event target hierarchy. When a listener calls - * `stopPropagation` or `preventDefault` on an event object, it means that no - * more listeners after this one will be called. Same as when the listener - * returns false. - * - * @constructor - * @extends {ol.Disposable} - */ -ol.events.EventTarget = function() { - - ol.Disposable.call(this); - - /** - * @private - * @type {!Object.<string, number>} - */ - this.pendingRemovals_ = {}; - - /** - * @private - * @type {!Object.<string, number>} - */ - this.dispatching_ = {}; - - /** - * @private - * @type {!Object.<string, Array.<ol.EventsListenerFunctionType>>} - */ - this.listeners_ = {}; - -}; -ol.inherits(ol.events.EventTarget, ol.Disposable); - - -/** - * @param {string} type Type. - * @param {ol.EventsListenerFunctionType} listener Listener. - */ -ol.events.EventTarget.prototype.addEventListener = function(type, listener) { - var listeners = this.listeners_[type]; - if (!listeners) { - listeners = this.listeners_[type] = []; - } - if (listeners.indexOf(listener) === -1) { - listeners.push(listener); - } -}; - - -/** - * @param {{type: string, - * target: (EventTarget|ol.events.EventTarget|undefined)}|ol.events.Event| - * string} event Event or event type. - * @return {boolean|undefined} `false` if anyone called preventDefault on the - * event object or if any of the listeners returned false. - */ -ol.events.EventTarget.prototype.dispatchEvent = function(event) { - var evt = typeof event === 'string' ? new ol.events.Event(event) : event; - var type = evt.type; - evt.target = this; - var listeners = this.listeners_[type]; - var propagate; - if (listeners) { - if (!(type in this.dispatching_)) { - this.dispatching_[type] = 0; - this.pendingRemovals_[type] = 0; - } - ++this.dispatching_[type]; - for (var i = 0, ii = listeners.length; i < ii; ++i) { - if (listeners[i].call(this, evt) === false || evt.propagationStopped) { - propagate = false; - break; - } - } - --this.dispatching_[type]; - if (this.dispatching_[type] === 0) { - var pendingRemovals = this.pendingRemovals_[type]; - delete this.pendingRemovals_[type]; - while (pendingRemovals--) { - this.removeEventListener(type, ol.nullFunction); - } - delete this.dispatching_[type]; - } - return propagate; - } -}; - - -/** - * @inheritDoc - */ -ol.events.EventTarget.prototype.disposeInternal = function() { - ol.events.unlistenAll(this); -}; - - -/** - * Get the listeners for a specified event type. Listeners are returned in the - * order that they will be called in. - * - * @param {string} type Type. - * @return {Array.<ol.EventsListenerFunctionType>} Listeners. - */ -ol.events.EventTarget.prototype.getListeners = function(type) { - return this.listeners_[type]; -}; - - -/** - * @param {string=} opt_type Type. If not provided, - * `true` will be returned if this EventTarget has any listeners. - * @return {boolean} Has listeners. - */ -ol.events.EventTarget.prototype.hasListener = function(opt_type) { - return opt_type ? - opt_type in this.listeners_ : - Object.keys(this.listeners_).length > 0; -}; - - -/** - * @param {string} type Type. - * @param {ol.EventsListenerFunctionType} listener Listener. - */ -ol.events.EventTarget.prototype.removeEventListener = function(type, listener) { - var listeners = this.listeners_[type]; - if (listeners) { - var index = listeners.indexOf(listener); - if (type in this.pendingRemovals_) { - // make listener a no-op, and remove later in #dispatchEvent() - listeners[index] = ol.nullFunction; - ++this.pendingRemovals_[type]; - } else { - listeners.splice(index, 1); - if (listeners.length === 0) { - delete this.listeners_[type]; - } - } - } -}; - -goog.provide('ol.events.EventType'); - -/** - * @enum {string} - * @const - */ -ol.events.EventType = { - /** - * Generic change event. Triggered when the revision counter is increased. - * @event ol.events.Event#change - * @api - */ - CHANGE: 'change', - - CLICK: 'click', - DBLCLICK: 'dblclick', - DRAGENTER: 'dragenter', - DRAGOVER: 'dragover', - DROP: 'drop', - ERROR: 'error', - KEYDOWN: 'keydown', - KEYPRESS: 'keypress', - LOAD: 'load', - MOUSEDOWN: 'mousedown', - MOUSEMOVE: 'mousemove', - MOUSEOUT: 'mouseout', - MOUSEUP: 'mouseup', - MOUSEWHEEL: 'mousewheel', - MSPOINTERDOWN: 'MSPointerDown', - RESIZE: 'resize', - TOUCHSTART: 'touchstart', - TOUCHMOVE: 'touchmove', - TOUCHEND: 'touchend', - WHEEL: 'wheel' -}; - -goog.provide('ol.Observable'); - -goog.require('ol'); -goog.require('ol.events'); -goog.require('ol.events.EventTarget'); -goog.require('ol.events.EventType'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * An event target providing convenient methods for listener registration - * and unregistration. A generic `change` event is always available through - * {@link ol.Observable#changed}. - * - * @constructor - * @extends {ol.events.EventTarget} - * @fires ol.events.Event - * @struct - * @api - */ -ol.Observable = function() { - - ol.events.EventTarget.call(this); - - /** - * @private - * @type {number} - */ - this.revision_ = 0; - -}; -ol.inherits(ol.Observable, ol.events.EventTarget); - - -/** - * Removes an event listener using the key returned by `on()` or `once()`. - * @param {ol.EventsKey|Array.<ol.EventsKey>} key The key returned by `on()` - * or `once()` (or an array of keys). - * @api - */ -ol.Observable.unByKey = function(key) { - if (Array.isArray(key)) { - for (var i = 0, ii = key.length; i < ii; ++i) { - ol.events.unlistenByKey(key[i]); - } - } else { - ol.events.unlistenByKey(/** @type {ol.EventsKey} */ (key)); - } -}; - - -/** - * Increases the revision counter and dispatches a 'change' event. - * @api - */ -ol.Observable.prototype.changed = function() { - ++this.revision_; - this.dispatchEvent(ol.events.EventType.CHANGE); -}; - - -/** - * Dispatches an event and calls all listeners listening for events - * of this type. The event parameter can either be a string or an - * Object with a `type` property. - * - * @param {{type: string, - * target: (EventTarget|ol.events.EventTarget|undefined)}|ol.events.Event| - * string} event Event object. - * @function - * @api - */ -ol.Observable.prototype.dispatchEvent; - - -/** - * Get the version number for this object. Each time the object is modified, - * its version number will be incremented. - * @return {number} Revision. - * @api - */ -ol.Observable.prototype.getRevision = function() { - return this.revision_; -}; - - -/** - * Listen for a certain type of event. - * @param {string|Array.<string>} type The event type or array of event types. - * @param {function(?): ?} listener The listener function. - * @param {Object=} opt_this The object to use as `this` in `listener`. - * @return {ol.EventsKey|Array.<ol.EventsKey>} Unique key for the listener. If - * called with an array of event types as the first argument, the return - * will be an array of keys. - * @api - */ -ol.Observable.prototype.on = function(type, listener, opt_this) { - if (Array.isArray(type)) { - var len = type.length; - var keys = new Array(len); - for (var i = 0; i < len; ++i) { - keys[i] = ol.events.listen(this, type[i], listener, opt_this); - } - return keys; - } else { - return ol.events.listen( - this, /** @type {string} */ (type), listener, opt_this); - } -}; - - -/** - * Listen once for a certain type of event. - * @param {string|Array.<string>} type The event type or array of event types. - * @param {function(?): ?} listener The listener function. - * @param {Object=} opt_this The object to use as `this` in `listener`. - * @return {ol.EventsKey|Array.<ol.EventsKey>} Unique key for the listener. If - * called with an array of event types as the first argument, the return - * will be an array of keys. - * @api - */ -ol.Observable.prototype.once = function(type, listener, opt_this) { - if (Array.isArray(type)) { - var len = type.length; - var keys = new Array(len); - for (var i = 0; i < len; ++i) { - keys[i] = ol.events.listenOnce(this, type[i], listener, opt_this); - } - return keys; - } else { - return ol.events.listenOnce( - this, /** @type {string} */ (type), listener, opt_this); - } -}; - - -/** - * Unlisten for a certain type of event. - * @param {string|Array.<string>} type The event type or array of event types. - * @param {function(?): ?} listener The listener function. - * @param {Object=} opt_this The object which was used as `this` by the - * `listener`. - * @api - */ -ol.Observable.prototype.un = function(type, listener, opt_this) { - if (Array.isArray(type)) { - for (var i = 0, ii = type.length; i < ii; ++i) { - ol.events.unlisten(this, type[i], listener, opt_this); - } - return; - } else { - ol.events.unlisten(this, /** @type {string} */ (type), listener, opt_this); - } -}; - -goog.provide('ol.Object'); - -goog.require('ol'); -goog.require('ol.ObjectEventType'); -goog.require('ol.Observable'); -goog.require('ol.events.Event'); -goog.require('ol.obj'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Most non-trivial classes inherit from this. - * - * This extends {@link ol.Observable} with observable properties, where each - * property is observable as well as the object as a whole. - * - * Classes that inherit from this have pre-defined properties, to which you can - * add your owns. The pre-defined properties are listed in this documentation as - * 'Observable Properties', and have their own accessors; for example, - * {@link ol.Map} has a `target` property, accessed with `getTarget()` and - * changed with `setTarget()`. Not all properties are however settable. There - * are also general-purpose accessors `get()` and `set()`. For example, - * `get('target')` is equivalent to `getTarget()`. - * - * The `set` accessors trigger a change event, and you can monitor this by - * registering a listener. For example, {@link ol.View} has a `center` - * property, so `view.on('change:center', function(evt) {...});` would call the - * function whenever the value of the center property changes. Within the - * function, `evt.target` would be the view, so `evt.target.getCenter()` would - * return the new center. - * - * You can add your own observable properties with - * `object.set('prop', 'value')`, and retrieve that with `object.get('prop')`. - * You can listen for changes on that property value with - * `object.on('change:prop', listener)`. You can get a list of all - * properties with {@link ol.Object#getProperties object.getProperties()}. - * - * Note that the observable properties are separate from standard JS properties. - * You can, for example, give your map object a title with - * `map.title='New title'` and with `map.set('title', 'Another title')`. The - * first will be a `hasOwnProperty`; the second will appear in - * `getProperties()`. Only the second is observable. - * - * Properties can be deleted by using the unset method. E.g. - * object.unset('foo'). - * - * @constructor - * @extends {ol.Observable} - * @param {Object.<string, *>=} opt_values An object with key-value pairs. - * @fires ol.Object.Event - * @api - */ -ol.Object = function(opt_values) { - ol.Observable.call(this); - - // Call ol.getUid to ensure that the order of objects' ids is the same as - // the order in which they were created. This also helps to ensure that - // object properties are always added in the same order, which helps many - // JavaScript engines generate faster code. - ol.getUid(this); - - /** - * @private - * @type {!Object.<string, *>} - */ - this.values_ = {}; - - if (opt_values !== undefined) { - this.setProperties(opt_values); - } -}; -ol.inherits(ol.Object, ol.Observable); - - -/** - * @private - * @type {Object.<string, string>} - */ -ol.Object.changeEventTypeCache_ = {}; - - -/** - * @param {string} key Key name. - * @return {string} Change name. - */ -ol.Object.getChangeEventType = function(key) { - return ol.Object.changeEventTypeCache_.hasOwnProperty(key) ? - ol.Object.changeEventTypeCache_[key] : - (ol.Object.changeEventTypeCache_[key] = 'change:' + key); -}; - - -/** - * Gets a value. - * @param {string} key Key name. - * @return {*} Value. - * @api - */ -ol.Object.prototype.get = function(key) { - var value; - if (this.values_.hasOwnProperty(key)) { - value = this.values_[key]; - } - return value; -}; - - -/** - * Get a list of object property names. - * @return {Array.<string>} List of property names. - * @api - */ -ol.Object.prototype.getKeys = function() { - return Object.keys(this.values_); -}; - - -/** - * Get an object of all property names and values. - * @return {Object.<string, *>} Object. - * @api - */ -ol.Object.prototype.getProperties = function() { - return ol.obj.assign({}, this.values_); -}; - - -/** - * @param {string} key Key name. - * @param {*} oldValue Old value. - */ -ol.Object.prototype.notify = function(key, oldValue) { - var eventType; - eventType = ol.Object.getChangeEventType(key); - this.dispatchEvent(new ol.Object.Event(eventType, key, oldValue)); - eventType = ol.ObjectEventType.PROPERTYCHANGE; - this.dispatchEvent(new ol.Object.Event(eventType, key, oldValue)); -}; - - -/** - * Sets a value. - * @param {string} key Key name. - * @param {*} value Value. - * @param {boolean=} opt_silent Update without triggering an event. - * @api - */ -ol.Object.prototype.set = function(key, value, opt_silent) { - if (opt_silent) { - this.values_[key] = value; - } else { - var oldValue = this.values_[key]; - this.values_[key] = value; - if (oldValue !== value) { - this.notify(key, oldValue); - } - } -}; - - -/** - * Sets a collection of key-value pairs. Note that this changes any existing - * properties and adds new ones (it does not remove any existing properties). - * @param {Object.<string, *>} values Values. - * @param {boolean=} opt_silent Update without triggering an event. - * @api - */ -ol.Object.prototype.setProperties = function(values, opt_silent) { - var key; - for (key in values) { - this.set(key, values[key], opt_silent); - } -}; - - -/** - * Unsets a property. - * @param {string} key Key name. - * @param {boolean=} opt_silent Unset without triggering an event. - * @api - */ -ol.Object.prototype.unset = function(key, opt_silent) { - if (key in this.values_) { - var oldValue = this.values_[key]; - delete this.values_[key]; - if (!opt_silent) { - this.notify(key, oldValue); - } - } -}; - - -/** - * @classdesc - * Events emitted by {@link ol.Object} instances are instances of this type. - * - * @param {string} type The event type. - * @param {string} key The property name. - * @param {*} oldValue The old value for `key`. - * @extends {ol.events.Event} - * @implements {oli.Object.Event} - * @constructor - */ -ol.Object.Event = function(type, key, oldValue) { - ol.events.Event.call(this, type); - - /** - * The name of the property whose value is changing. - * @type {string} - * @api - */ - this.key = key; - - /** - * The old value. To get the new value use `e.target.get(e.key)` where - * `e` is the event object. - * @type {*} - * @api - */ - this.oldValue = oldValue; - -}; -ol.inherits(ol.Object.Event, ol.events.Event); - -/** - * An implementation of Google Maps' MVCArray. - * @see https://developers.google.com/maps/documentation/javascript/reference - */ - -goog.provide('ol.Collection'); - -goog.require('ol'); -goog.require('ol.AssertionError'); -goog.require('ol.CollectionEventType'); -goog.require('ol.Object'); -goog.require('ol.events.Event'); - - -/** - * @classdesc - * An expanded version of standard JS Array, adding convenience methods for - * manipulation. Add and remove changes to the Collection trigger a Collection - * event. Note that this does not cover changes to the objects _within_ the - * Collection; they trigger events on the appropriate object, not on the - * Collection as a whole. - * - * @constructor - * @extends {ol.Object} - * @fires ol.Collection.Event - * @param {!Array.<T>=} opt_array Array. - * @param {olx.CollectionOptions=} opt_options Collection options. - * @template T - * @api - */ -ol.Collection = function(opt_array, opt_options) { - - ol.Object.call(this); - - var options = opt_options || {}; - - /** - * @private - * @type {boolean} - */ - this.unique_ = !!options.unique; - - /** - * @private - * @type {!Array.<T>} - */ - this.array_ = opt_array ? opt_array : []; - - if (this.unique_) { - for (var i = 0, ii = this.array_.length; i < ii; ++i) { - this.assertUnique_(this.array_[i], i); - } - } - - this.updateLength_(); - -}; -ol.inherits(ol.Collection, ol.Object); - - -/** - * Remove all elements from the collection. - * @api - */ -ol.Collection.prototype.clear = function() { - while (this.getLength() > 0) { - this.pop(); - } -}; - - -/** - * Add elements to the collection. This pushes each item in the provided array - * to the end of the collection. - * @param {!Array.<T>} arr Array. - * @return {ol.Collection.<T>} This collection. - * @api - */ -ol.Collection.prototype.extend = function(arr) { - var i, ii; - for (i = 0, ii = arr.length; i < ii; ++i) { - this.push(arr[i]); - } - return this; -}; - - -/** - * Iterate over each element, calling the provided callback. - * @param {function(this: S, T, number, Array.<T>): *} f The function to call - * for every element. This function takes 3 arguments (the element, the - * index and the array). The return value is ignored. - * @param {S=} opt_this The object to use as `this` in `f`. - * @template S - * @api - */ -ol.Collection.prototype.forEach = function(f, opt_this) { - this.array_.forEach(f, opt_this); -}; - - -/** - * Get a reference to the underlying Array object. Warning: if the array - * is mutated, no events will be dispatched by the collection, and the - * collection's "length" property won't be in sync with the actual length - * of the array. - * @return {!Array.<T>} Array. - * @api - */ -ol.Collection.prototype.getArray = function() { - return this.array_; -}; - - -/** - * Get the element at the provided index. - * @param {number} index Index. - * @return {T} Element. - * @api - */ -ol.Collection.prototype.item = function(index) { - return this.array_[index]; -}; - - -/** - * Get the length of this collection. - * @return {number} The length of the array. - * @observable - * @api - */ -ol.Collection.prototype.getLength = function() { - return /** @type {number} */ (this.get(ol.Collection.Property_.LENGTH)); -}; - - -/** - * Insert an element at the provided index. - * @param {number} index Index. - * @param {T} elem Element. - * @api - */ -ol.Collection.prototype.insertAt = function(index, elem) { - if (this.unique_) { - this.assertUnique_(elem); - } - this.array_.splice(index, 0, elem); - this.updateLength_(); - this.dispatchEvent( - new ol.Collection.Event(ol.CollectionEventType.ADD, elem)); -}; - - -/** - * Remove the last element of the collection and return it. - * Return `undefined` if the collection is empty. - * @return {T|undefined} Element. - * @api - */ -ol.Collection.prototype.pop = function() { - return this.removeAt(this.getLength() - 1); -}; - - -/** - * Insert the provided element at the end of the collection. - * @param {T} elem Element. - * @return {number} New length of the collection. - * @api - */ -ol.Collection.prototype.push = function(elem) { - if (this.unique_) { - this.assertUnique_(elem); - } - var n = this.getLength(); - this.insertAt(n, elem); - return this.getLength(); -}; - - -/** - * Remove the first occurrence of an element from the collection. - * @param {T} elem Element. - * @return {T|undefined} The removed element or undefined if none found. - * @api - */ -ol.Collection.prototype.remove = function(elem) { - var arr = this.array_; - var i, ii; - for (i = 0, ii = arr.length; i < ii; ++i) { - if (arr[i] === elem) { - return this.removeAt(i); - } - } - return undefined; -}; - - -/** - * Remove the element at the provided index and return it. - * Return `undefined` if the collection does not contain this index. - * @param {number} index Index. - * @return {T|undefined} Value. - * @api - */ -ol.Collection.prototype.removeAt = function(index) { - var prev = this.array_[index]; - this.array_.splice(index, 1); - this.updateLength_(); - this.dispatchEvent( - new ol.Collection.Event(ol.CollectionEventType.REMOVE, prev)); - return prev; -}; - - -/** - * Set the element at the provided index. - * @param {number} index Index. - * @param {T} elem Element. - * @api - */ -ol.Collection.prototype.setAt = function(index, elem) { - var n = this.getLength(); - if (index < n) { - if (this.unique_) { - this.assertUnique_(elem, index); - } - var prev = this.array_[index]; - this.array_[index] = elem; - this.dispatchEvent( - new ol.Collection.Event(ol.CollectionEventType.REMOVE, prev)); - this.dispatchEvent( - new ol.Collection.Event(ol.CollectionEventType.ADD, elem)); - } else { - var j; - for (j = n; j < index; ++j) { - this.insertAt(j, undefined); - } - this.insertAt(index, elem); - } -}; - - -/** - * @private - */ -ol.Collection.prototype.updateLength_ = function() { - this.set(ol.Collection.Property_.LENGTH, this.array_.length); -}; - - -/** - * @private - * @param {T} elem Element. - * @param {number=} opt_except Optional index to ignore. - */ -ol.Collection.prototype.assertUnique_ = function(elem, opt_except) { - for (var i = 0, ii = this.array_.length; i < ii; ++i) { - if (this.array_[i] === elem && i !== opt_except) { - throw new ol.AssertionError(58); - } - } -}; - - -/** - * @enum {string} - * @private - */ -ol.Collection.Property_ = { - LENGTH: 'length' -}; - - -/** - * @classdesc - * Events emitted by {@link ol.Collection} instances are instances of this - * type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.Collection.Event} - * @param {ol.CollectionEventType} type Type. - * @param {*=} opt_element Element. - */ -ol.Collection.Event = function(type, opt_element) { - - ol.events.Event.call(this, type); - - /** - * The element that is added to or removed from the collection. - * @type {*} - * @api - */ - this.element = opt_element; - -}; -ol.inherits(ol.Collection.Event, ol.events.Event); - -goog.provide('ol.color'); - -goog.require('ol.asserts'); -goog.require('ol.math'); - - -/** - * This RegExp matches # followed by 3 or 6 hex digits. - * @const - * @type {RegExp} - * @private - */ -ol.color.HEX_COLOR_RE_ = /^#(?:[0-9a-f]{3}){1,2}$/i; - - -/** - * Regular expression for matching potential named color style strings. - * @const - * @type {RegExp} - * @private - */ -ol.color.NAMED_COLOR_RE_ = /^([a-z]*)$/i; - - -/** - * Return the color as an array. This function maintains a cache of calculated - * arrays which means the result should not be modified. - * @param {ol.Color|string} color Color. - * @return {ol.Color} Color. - * @api - */ -ol.color.asArray = function(color) { - if (Array.isArray(color)) { - return color; - } else { - return ol.color.fromString(/** @type {string} */ (color)); - } -}; - - -/** - * Return the color as an rgba string. - * @param {ol.Color|string} color Color. - * @return {string} Rgba string. - * @api - */ -ol.color.asString = function(color) { - if (typeof color === 'string') { - return color; - } else { - return ol.color.toString(color); - } -}; - -/** - * Return named color as an rgba string. - * @param {string} color Named color. - * @return {string} Rgb string. - */ -ol.color.fromNamed = function(color) { - var el = document.createElement('div'); - el.style.color = color; - document.body.appendChild(el); - var rgb = getComputedStyle(el).color; - document.body.removeChild(el); - return rgb; -}; - - -/** - * @param {string} s String. - * @return {ol.Color} Color. - */ -ol.color.fromString = ( - function() { - - // We maintain a small cache of parsed strings. To provide cheap LRU-like - // semantics, whenever the cache grows too large we simply delete an - // arbitrary 25% of the entries. - - /** - * @const - * @type {number} - */ - var MAX_CACHE_SIZE = 1024; - - /** - * @type {Object.<string, ol.Color>} - */ - var cache = {}; - - /** - * @type {number} - */ - var cacheSize = 0; - - return ( - /** - * @param {string} s String. - * @return {ol.Color} Color. - */ - function(s) { - var color; - if (cache.hasOwnProperty(s)) { - color = cache[s]; - } else { - if (cacheSize >= MAX_CACHE_SIZE) { - var i = 0; - var key; - for (key in cache) { - if ((i++ & 3) === 0) { - delete cache[key]; - --cacheSize; - } - } - } - color = ol.color.fromStringInternal_(s); - cache[s] = color; - ++cacheSize; - } - return color; - }); - - })(); - - -/** - * @param {string} s String. - * @private - * @return {ol.Color} Color. - */ -ol.color.fromStringInternal_ = function(s) { - var r, g, b, a, color, parts; - - if (ol.color.NAMED_COLOR_RE_.exec(s)) { - s = ol.color.fromNamed(s); - } - - if (ol.color.HEX_COLOR_RE_.exec(s)) { // hex - var n = s.length - 1; // number of hex digits - ol.asserts.assert(n == 3 || n == 6, 54); // Hex color should have 3 or 6 digits - var d = n == 3 ? 1 : 2; // number of digits per channel - r = parseInt(s.substr(1 + 0 * d, d), 16); - g = parseInt(s.substr(1 + 1 * d, d), 16); - b = parseInt(s.substr(1 + 2 * d, d), 16); - if (d == 1) { - r = (r << 4) + r; - g = (g << 4) + g; - b = (b << 4) + b; - } - a = 1; - color = [r, g, b, a]; - } else if (s.indexOf('rgba(') == 0) { // rgba() - parts = s.slice(5, -1).split(',').map(Number); - color = ol.color.normalize(parts); - } else if (s.indexOf('rgb(') == 0) { // rgb() - parts = s.slice(4, -1).split(',').map(Number); - parts.push(1); - color = ol.color.normalize(parts); - } else { - ol.asserts.assert(false, 14); // Invalid color - } - return /** @type {ol.Color} */ (color); -}; - - -/** - * @param {ol.Color} color Color. - * @param {ol.Color=} opt_color Color. - * @return {ol.Color} Clamped color. - */ -ol.color.normalize = function(color, opt_color) { - var result = opt_color || []; - result[0] = ol.math.clamp((color[0] + 0.5) | 0, 0, 255); - result[1] = ol.math.clamp((color[1] + 0.5) | 0, 0, 255); - result[2] = ol.math.clamp((color[2] + 0.5) | 0, 0, 255); - result[3] = ol.math.clamp(color[3], 0, 1); - return result; -}; - - -/** - * @param {ol.Color} color Color. - * @return {string} String. - */ -ol.color.toString = function(color) { - var r = color[0]; - if (r != (r | 0)) { - r = (r + 0.5) | 0; - } - var g = color[1]; - if (g != (g | 0)) { - g = (g + 0.5) | 0; - } - var b = color[2]; - if (b != (b | 0)) { - b = (b + 0.5) | 0; - } - var a = color[3] === undefined ? 1 : color[3]; - return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; -}; - -goog.provide('ol.colorlike'); - -goog.require('ol.color'); - - -/** - * @param {ol.Color|ol.ColorLike} color Color. - * @return {ol.ColorLike} The color as an ol.ColorLike - * @api - */ -ol.colorlike.asColorLike = function(color) { - if (ol.colorlike.isColorLike(color)) { - return /** @type {string|CanvasPattern|CanvasGradient} */ (color); - } else { - return ol.color.asString(/** @type {ol.Color} */ (color)); - } -}; - - -/** - * @param {?} color The value that is potentially an ol.ColorLike - * @return {boolean} Whether the color is an ol.ColorLike - */ -ol.colorlike.isColorLike = function(color) { - return ( - typeof color === 'string' || - color instanceof CanvasPattern || - color instanceof CanvasGradient - ); -}; - -goog.provide('ol.dom'); - - -/** - * Create an html canvas element and returns its 2d context. - * @param {number=} opt_width Canvas width. - * @param {number=} opt_height Canvas height. - * @return {CanvasRenderingContext2D} The context. - */ -ol.dom.createCanvasContext2D = function(opt_width, opt_height) { - var canvas = document.createElement('CANVAS'); - if (opt_width) { - canvas.width = opt_width; - } - if (opt_height) { - canvas.height = opt_height; - } - return canvas.getContext('2d'); -}; - - -/** - * Get the current computed width for the given element including margin, - * padding and border. - * Equivalent to jQuery's `$(el).outerWidth(true)`. - * @param {!Element} element Element. - * @return {number} The width. - */ -ol.dom.outerWidth = function(element) { - var width = element.offsetWidth; - var style = getComputedStyle(element); - width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10); - - return width; -}; - - -/** - * Get the current computed height for the given element including margin, - * padding and border. - * Equivalent to jQuery's `$(el).outerHeight(true)`. - * @param {!Element} element Element. - * @return {number} The height. - */ -ol.dom.outerHeight = function(element) { - var height = element.offsetHeight; - var style = getComputedStyle(element); - height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10); - - return height; -}; - -/** - * @param {Node} newNode Node to replace old node - * @param {Node} oldNode The node to be replaced - */ -ol.dom.replaceNode = function(newNode, oldNode) { - var parent = oldNode.parentNode; - if (parent) { - parent.replaceChild(newNode, oldNode); - } -}; - -/** - * @param {Node} node The node to remove. - * @returns {Node} The node that was removed or null. - */ -ol.dom.removeNode = function(node) { - return node && node.parentNode ? node.parentNode.removeChild(node) : null; -}; - -/** - * @param {Node} node The node to remove the children from. - */ -ol.dom.removeChildren = function(node) { - while (node.lastChild) { - node.removeChild(node.lastChild); - } -}; - -goog.provide('ol.MapEventType'); - -/** - * @enum {string} - */ -ol.MapEventType = { - - /** - * Triggered after a map frame is rendered. - * @event ol.MapEvent#postrender - * @api - */ - POSTRENDER: 'postrender', - - /** - * Triggered when the map starts moving. - * @event ol.MapEvent#movestart - * @api - */ - MOVESTART: 'movestart', - - /** - * Triggered after the map is moved. - * @event ol.MapEvent#moveend - * @api - */ - MOVEEND: 'moveend' - -}; - -goog.provide('ol.control.Control'); - -goog.require('ol'); -goog.require('ol.MapEventType'); -goog.require('ol.Object'); -goog.require('ol.dom'); -goog.require('ol.events'); - - -/** - * @classdesc - * A control is a visible widget with a DOM element in a fixed position on the - * screen. They can involve user input (buttons), or be informational only; - * the position is determined using CSS. By default these are placed in the - * container with CSS class name `ol-overlaycontainer-stopevent`, but can use - * any outside DOM element. - * - * This is the base class for controls. You can use it for simple custom - * controls by creating the element with listeners, creating an instance: - * ```js - * var myControl = new ol.control.Control({element: myElement}); - * ``` - * and then adding this to the map. - * - * The main advantage of having this as a control rather than a simple separate - * DOM element is that preventing propagation is handled for you. Controls - * will also be `ol.Object`s in a `ol.Collection`, so you can use their - * methods. - * - * You can also extend this base for your own control class. See - * examples/custom-controls for an example of how to do this. - * - * @constructor - * @extends {ol.Object} - * @implements {oli.control.Control} - * @param {olx.control.ControlOptions} options Control options. - * @api - */ -ol.control.Control = function(options) { - - ol.Object.call(this); - - /** - * @protected - * @type {Element} - */ - this.element = options.element ? options.element : null; - - /** - * @private - * @type {Element} - */ - this.target_ = null; - - /** - * @private - * @type {ol.Map} - */ - this.map_ = null; - - /** - * @protected - * @type {!Array.<ol.EventsKey>} - */ - this.listenerKeys = []; - - /** - * @type {function(ol.MapEvent)} - */ - this.render = options.render ? options.render : ol.nullFunction; - - if (options.target) { - this.setTarget(options.target); - } - -}; -ol.inherits(ol.control.Control, ol.Object); - - -/** - * @inheritDoc - */ -ol.control.Control.prototype.disposeInternal = function() { - ol.dom.removeNode(this.element); - ol.Object.prototype.disposeInternal.call(this); -}; - - -/** - * Get the map associated with this control. - * @return {ol.Map} Map. - * @api - */ -ol.control.Control.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Remove the control from its current map and attach it to the new map. - * Subclasses may set up event handlers to get notified about changes to - * the map here. - * @param {ol.Map} map Map. - * @override - * @api - */ -ol.control.Control.prototype.setMap = function(map) { - if (this.map_) { - ol.dom.removeNode(this.element); - } - for (var i = 0, ii = this.listenerKeys.length; i < ii; ++i) { - ol.events.unlistenByKey(this.listenerKeys[i]); - } - this.listenerKeys.length = 0; - this.map_ = map; - if (this.map_) { - var target = this.target_ ? - this.target_ : map.getOverlayContainerStopEvent(); - target.appendChild(this.element); - if (this.render !== ol.nullFunction) { - this.listenerKeys.push(ol.events.listen(map, - ol.MapEventType.POSTRENDER, this.render, this)); - } - map.render(); - } -}; - - -/** - * This function is used to set a target element for the control. It has no - * effect if it is called after the control has been added to the map (i.e. - * after `setMap` is called on the control). If no `target` is set in the - * options passed to the control constructor and if `setTarget` is not called - * then the control is added to the map's overlay container. - * @param {Element|string} target Target. - * @api - */ -ol.control.Control.prototype.setTarget = function(target) { - this.target_ = typeof target === 'string' ? - document.getElementById(target) : - target; -}; - -goog.provide('ol.css'); - - -/** - * The CSS class for hidden feature. - * - * @const - * @type {string} - */ -ol.css.CLASS_HIDDEN = 'ol-hidden'; - - -/** - * The CSS class that we'll give the DOM elements to have them selectable. - * - * @const - * @type {string} - */ -ol.css.CLASS_SELECTABLE = 'ol-selectable'; - -/** - * The CSS class that we'll give the DOM elements to have them unselectable. - * - * @const - * @type {string} - */ -ol.css.CLASS_UNSELECTABLE = 'ol-unselectable'; - - -/** - * The CSS class for unsupported feature. - * - * @const - * @type {string} - */ -ol.css.CLASS_UNSUPPORTED = 'ol-unsupported'; - - -/** - * The CSS class for controls. - * - * @const - * @type {string} - */ -ol.css.CLASS_CONTROL = 'ol-control'; - -// FIXME handle date line wrap - -goog.provide('ol.control.Attribution'); - -goog.require('ol'); -goog.require('ol.dom'); -goog.require('ol.control.Control'); -goog.require('ol.css'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.obj'); - - -/** - * @classdesc - * Control to show all the attributions associated with the layer sources - * in the map. This control is one of the default controls included in maps. - * By default it will show in the bottom right portion of the map, but this can - * be changed by using a css selector for `.ol-attribution`. - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.AttributionOptions=} opt_options Attribution options. - * @api - */ -ol.control.Attribution = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {Element} - */ - this.ulElement_ = document.createElement('UL'); - - /** - * @private - * @type {Element} - */ - this.logoLi_ = document.createElement('LI'); - - this.ulElement_.appendChild(this.logoLi_); - this.logoLi_.style.display = 'none'; - - /** - * @private - * @type {boolean} - */ - this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; - - /** - * @private - * @type {boolean} - */ - this.collapsible_ = options.collapsible !== undefined ? - options.collapsible : true; - - if (!this.collapsible_) { - this.collapsed_ = false; - } - - var className = options.className !== undefined ? options.className : 'ol-attribution'; - - var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions'; - - var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB'; - - if (typeof collapseLabel === 'string') { - /** - * @private - * @type {Node} - */ - this.collapseLabel_ = document.createElement('span'); - this.collapseLabel_.textContent = collapseLabel; - } else { - this.collapseLabel_ = collapseLabel; - } - - var label = options.label !== undefined ? options.label : 'i'; - - if (typeof label === 'string') { - /** - * @private - * @type {Node} - */ - this.label_ = document.createElement('span'); - this.label_.textContent = label; - } else { - this.label_ = label; - } - - - var activeLabel = (this.collapsible_ && !this.collapsed_) ? - this.collapseLabel_ : this.label_; - var button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(activeLabel); - - ol.events.listen(button, ol.events.EventType.CLICK, this.handleClick_, this); - - var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + - ol.css.CLASS_CONTROL + - (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') + - (this.collapsible_ ? '' : ' ol-uncollapsible'); - var element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(this.ulElement_); - element.appendChild(button); - - var render = options.render ? options.render : ol.control.Attribution.render; - - ol.control.Control.call(this, { - element: element, - render: render, - target: options.target - }); - - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = true; - - /** - * @private - * @type {Object.<string, Element>} - */ - this.attributionElements_ = {}; - - /** - * @private - * @type {Object.<string, boolean>} - */ - this.attributionElementRenderedVisible_ = {}; - - /** - * @private - * @type {Object.<string, Element>} - */ - this.logoElements_ = {}; - -}; -ol.inherits(ol.control.Attribution, ol.control.Control); - - -/** - * @param {?olx.FrameState} frameState Frame state. - * @return {Array.<Object.<string, ol.Attribution>>} Attributions. - */ -ol.control.Attribution.prototype.getSourceAttributions = function(frameState) { - var i, ii, j, jj, tileRanges, source, sourceAttribution, - sourceAttributionKey, sourceAttributions, sourceKey; - var intersectsTileRange; - var layerStatesArray = frameState.layerStatesArray; - /** @type {Object.<string, ol.Attribution>} */ - var attributions = ol.obj.assign({}, frameState.attributions); - /** @type {Object.<string, ol.Attribution>} */ - var hiddenAttributions = {}; - var uniqueAttributions = {}; - var projection = /** @type {!ol.proj.Projection} */ (frameState.viewState.projection); - for (i = 0, ii = layerStatesArray.length; i < ii; i++) { - source = layerStatesArray[i].layer.getSource(); - if (!source) { - continue; - } - sourceKey = ol.getUid(source).toString(); - sourceAttributions = source.getAttributions(); - if (!sourceAttributions) { - continue; - } - for (j = 0, jj = sourceAttributions.length; j < jj; j++) { - sourceAttribution = sourceAttributions[j]; - sourceAttributionKey = ol.getUid(sourceAttribution).toString(); - if (sourceAttributionKey in attributions) { - continue; - } - tileRanges = frameState.usedTiles[sourceKey]; - if (tileRanges) { - var tileGrid = /** @type {ol.source.Tile} */ (source).getTileGridForProjection(projection); - intersectsTileRange = sourceAttribution.intersectsAnyTileRange( - tileRanges, tileGrid, projection); - } else { - intersectsTileRange = false; - } - if (intersectsTileRange) { - if (sourceAttributionKey in hiddenAttributions) { - delete hiddenAttributions[sourceAttributionKey]; - } - var html = sourceAttribution.getHTML(); - if (!(html in uniqueAttributions)) { - uniqueAttributions[html] = true; - attributions[sourceAttributionKey] = sourceAttribution; - } - } else { - hiddenAttributions[sourceAttributionKey] = sourceAttribution; - } - } - } - return [attributions, hiddenAttributions]; -}; - - -/** - * Update the attribution element. - * @param {ol.MapEvent} mapEvent Map event. - * @this {ol.control.Attribution} - * @api - */ -ol.control.Attribution.render = function(mapEvent) { - this.updateElement_(mapEvent.frameState); -}; - - -/** - * @private - * @param {?olx.FrameState} frameState Frame state. - */ -ol.control.Attribution.prototype.updateElement_ = function(frameState) { - - if (!frameState) { - if (this.renderedVisible_) { - this.element.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - var attributions = this.getSourceAttributions(frameState); - /** @type {Object.<string, ol.Attribution>} */ - var visibleAttributions = attributions[0]; - /** @type {Object.<string, ol.Attribution>} */ - var hiddenAttributions = attributions[1]; - - var attributionElement, attributionKey; - for (attributionKey in this.attributionElements_) { - if (attributionKey in visibleAttributions) { - if (!this.attributionElementRenderedVisible_[attributionKey]) { - this.attributionElements_[attributionKey].style.display = ''; - this.attributionElementRenderedVisible_[attributionKey] = true; - } - delete visibleAttributions[attributionKey]; - } else if (attributionKey in hiddenAttributions) { - if (this.attributionElementRenderedVisible_[attributionKey]) { - this.attributionElements_[attributionKey].style.display = 'none'; - delete this.attributionElementRenderedVisible_[attributionKey]; - } - delete hiddenAttributions[attributionKey]; - } else { - ol.dom.removeNode(this.attributionElements_[attributionKey]); - delete this.attributionElements_[attributionKey]; - delete this.attributionElementRenderedVisible_[attributionKey]; - } - } - for (attributionKey in visibleAttributions) { - attributionElement = document.createElement('LI'); - attributionElement.innerHTML = - visibleAttributions[attributionKey].getHTML(); - this.ulElement_.appendChild(attributionElement); - this.attributionElements_[attributionKey] = attributionElement; - this.attributionElementRenderedVisible_[attributionKey] = true; - } - for (attributionKey in hiddenAttributions) { - attributionElement = document.createElement('LI'); - attributionElement.innerHTML = - hiddenAttributions[attributionKey].getHTML(); - attributionElement.style.display = 'none'; - this.ulElement_.appendChild(attributionElement); - this.attributionElements_[attributionKey] = attributionElement; - } - - var renderVisible = - !ol.obj.isEmpty(this.attributionElementRenderedVisible_) || - !ol.obj.isEmpty(frameState.logos); - if (this.renderedVisible_ != renderVisible) { - this.element.style.display = renderVisible ? '' : 'none'; - this.renderedVisible_ = renderVisible; - } - if (renderVisible && - ol.obj.isEmpty(this.attributionElementRenderedVisible_)) { - this.element.classList.add('ol-logo-only'); - } else { - this.element.classList.remove('ol-logo-only'); - } - - this.insertLogos_(frameState); - -}; - - -/** - * @param {?olx.FrameState} frameState Frame state. - * @private - */ -ol.control.Attribution.prototype.insertLogos_ = function(frameState) { - - var logo; - var logos = frameState.logos; - var logoElements = this.logoElements_; - - for (logo in logoElements) { - if (!(logo in logos)) { - ol.dom.removeNode(logoElements[logo]); - delete logoElements[logo]; - } - } - - var image, logoElement, logoKey; - for (logoKey in logos) { - var logoValue = logos[logoKey]; - if (logoValue instanceof HTMLElement) { - this.logoLi_.appendChild(logoValue); - logoElements[logoKey] = logoValue; - } - if (!(logoKey in logoElements)) { - image = new Image(); - image.src = logoKey; - if (logoValue === '') { - logoElement = image; - } else { - logoElement = document.createElement('a'); - logoElement.href = logoValue; - logoElement.appendChild(image); - } - this.logoLi_.appendChild(logoElement); - logoElements[logoKey] = logoElement; - } - } - - this.logoLi_.style.display = !ol.obj.isEmpty(logos) ? '' : 'none'; - -}; - - -/** - * @param {Event} event The event to handle - * @private - */ -ol.control.Attribution.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleToggle_(); -}; - - -/** - * @private - */ -ol.control.Attribution.prototype.handleToggle_ = function() { - this.element.classList.toggle('ol-collapsed'); - if (this.collapsed_) { - ol.dom.replaceNode(this.collapseLabel_, this.label_); - } else { - ol.dom.replaceNode(this.label_, this.collapseLabel_); - } - this.collapsed_ = !this.collapsed_; -}; - - -/** - * Return `true` if the attribution is collapsible, `false` otherwise. - * @return {boolean} True if the widget is collapsible. - * @api - */ -ol.control.Attribution.prototype.getCollapsible = function() { - return this.collapsible_; -}; - - -/** - * Set whether the attribution should be collapsible. - * @param {boolean} collapsible True if the widget is collapsible. - * @api - */ -ol.control.Attribution.prototype.setCollapsible = function(collapsible) { - if (this.collapsible_ === collapsible) { - return; - } - this.collapsible_ = collapsible; - this.element.classList.toggle('ol-uncollapsible'); - if (!collapsible && this.collapsed_) { - this.handleToggle_(); - } -}; - - -/** - * Collapse or expand the attribution according to the passed parameter. Will - * not do anything if the attribution isn't collapsible or if the current - * collapsed state is already the one requested. - * @param {boolean} collapsed True if the widget is collapsed. - * @api - */ -ol.control.Attribution.prototype.setCollapsed = function(collapsed) { - if (!this.collapsible_ || this.collapsed_ === collapsed) { - return; - } - this.handleToggle_(); -}; - - -/** - * Return `true` when the attribution is currently collapsed or `false` - * otherwise. - * @return {boolean} True if the widget is collapsed. - * @api - */ -ol.control.Attribution.prototype.getCollapsed = function() { - return this.collapsed_; -}; - -goog.provide('ol.easing'); - - -/** - * Start slow and speed up. - * @param {number} t Input between 0 and 1. - * @return {number} Output between 0 and 1. - * @api - */ -ol.easing.easeIn = function(t) { - return Math.pow(t, 3); -}; - - -/** - * Start fast and slow down. - * @param {number} t Input between 0 and 1. - * @return {number} Output between 0 and 1. - * @api - */ -ol.easing.easeOut = function(t) { - return 1 - ol.easing.easeIn(1 - t); -}; - - -/** - * Start slow, speed up, and then slow down again. - * @param {number} t Input between 0 and 1. - * @return {number} Output between 0 and 1. - * @api - */ -ol.easing.inAndOut = function(t) { - return 3 * t * t - 2 * t * t * t; -}; - - -/** - * Maintain a constant speed over time. - * @param {number} t Input between 0 and 1. - * @return {number} Output between 0 and 1. - * @api - */ -ol.easing.linear = function(t) { - return t; -}; - - -/** - * Start slow, speed up, and at the very end slow down again. This has the - * same general behavior as {@link ol.easing.inAndOut}, but the final slowdown - * is delayed. - * @param {number} t Input between 0 and 1. - * @return {number} Output between 0 and 1. - * @api - */ -ol.easing.upAndDown = function(t) { - if (t < 0.5) { - return ol.easing.inAndOut(2 * t); - } else { - return 1 - ol.easing.inAndOut(2 * (t - 0.5)); - } -}; - -goog.provide('ol.control.Rotate'); - -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol'); -goog.require('ol.control.Control'); -goog.require('ol.css'); -goog.require('ol.easing'); - - -/** - * @classdesc - * A button control to reset rotation to 0. - * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css - * selector is added to the button when the rotation is 0. - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.RotateOptions=} opt_options Rotate options. - * @api - */ -ol.control.Rotate = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var className = options.className !== undefined ? options.className : 'ol-rotate'; - - var label = options.label !== undefined ? options.label : '\u21E7'; - - /** - * @type {Element} - * @private - */ - this.label_ = null; - - if (typeof label === 'string') { - this.label_ = document.createElement('span'); - this.label_.className = 'ol-compass'; - this.label_.textContent = label; - } else { - this.label_ = label; - this.label_.classList.add('ol-compass'); - } - - var tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation'; - - var button = document.createElement('button'); - button.className = className + '-reset'; - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(this.label_); - - ol.events.listen(button, ol.events.EventType.CLICK, - ol.control.Rotate.prototype.handleClick_, this); - - var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + - ol.css.CLASS_CONTROL; - var element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); - - var render = options.render ? options.render : ol.control.Rotate.render; - - this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined; - - ol.control.Control.call(this, { - element: element, - render: render, - target: options.target - }); - - /** - * @type {number} - * @private - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; - - /** - * @type {boolean} - * @private - */ - this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true; - - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = undefined; - - if (this.autoHide_) { - this.element.classList.add(ol.css.CLASS_HIDDEN); - } - -}; -ol.inherits(ol.control.Rotate, ol.control.Control); - - -/** - * @param {Event} event The event to handle - * @private - */ -ol.control.Rotate.prototype.handleClick_ = function(event) { - event.preventDefault(); - if (this.callResetNorth_ !== undefined) { - this.callResetNorth_(); - } else { - this.resetNorth_(); - } -}; - - -/** - * @private - */ -ol.control.Rotate.prototype.resetNorth_ = function() { - var map = this.getMap(); - var view = map.getView(); - if (!view) { - // the map does not have a view, so we can't act - // upon it - return; - } - if (view.getRotation() !== undefined) { - if (this.duration_ > 0) { - view.animate({ - rotation: 0, - duration: this.duration_, - easing: ol.easing.easeOut - }); - } else { - view.setRotation(0); - } - } -}; - - -/** - * Update the rotate control element. - * @param {ol.MapEvent} mapEvent Map event. - * @this {ol.control.Rotate} - * @api - */ -ol.control.Rotate.render = function(mapEvent) { - var frameState = mapEvent.frameState; - if (!frameState) { - return; - } - var rotation = frameState.viewState.rotation; - if (rotation != this.rotation_) { - var transform = 'rotate(' + rotation + 'rad)'; - if (this.autoHide_) { - var contains = this.element.classList.contains(ol.css.CLASS_HIDDEN); - if (!contains && rotation === 0) { - this.element.classList.add(ol.css.CLASS_HIDDEN); - } else if (contains && rotation !== 0) { - this.element.classList.remove(ol.css.CLASS_HIDDEN); - } - } - this.label_.style.msTransform = transform; - this.label_.style.webkitTransform = transform; - this.label_.style.transform = transform; - } - this.rotation_ = rotation; -}; - -goog.provide('ol.control.Zoom'); - -goog.require('ol'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.control.Control'); -goog.require('ol.css'); -goog.require('ol.easing'); - - -/** - * @classdesc - * A control with 2 buttons, one for zoom in and one for zoom out. - * This control is one of the default controls of a map. To style this control - * use css selectors `.ol-zoom-in` and `.ol-zoom-out`. - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.ZoomOptions=} opt_options Zoom options. - * @api - */ -ol.control.Zoom = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var className = options.className !== undefined ? options.className : 'ol-zoom'; - - var delta = options.delta !== undefined ? options.delta : 1; - - var zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+'; - var zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212'; - - var zoomInTipLabel = options.zoomInTipLabel !== undefined ? - options.zoomInTipLabel : 'Zoom in'; - var zoomOutTipLabel = options.zoomOutTipLabel !== undefined ? - options.zoomOutTipLabel : 'Zoom out'; - - var inElement = document.createElement('button'); - inElement.className = className + '-in'; - inElement.setAttribute('type', 'button'); - inElement.title = zoomInTipLabel; - inElement.appendChild( - typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel - ); - - ol.events.listen(inElement, ol.events.EventType.CLICK, - ol.control.Zoom.prototype.handleClick_.bind(this, delta)); - - var outElement = document.createElement('button'); - outElement.className = className + '-out'; - outElement.setAttribute('type', 'button'); - outElement.title = zoomOutTipLabel; - outElement.appendChild( - typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel - ); - - ol.events.listen(outElement, ol.events.EventType.CLICK, - ol.control.Zoom.prototype.handleClick_.bind(this, -delta)); - - var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + - ol.css.CLASS_CONTROL; - var element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(inElement); - element.appendChild(outElement); - - ol.control.Control.call(this, { - element: element, - target: options.target - }); - - /** - * @type {number} - * @private - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; - -}; -ol.inherits(ol.control.Zoom, ol.control.Control); - - -/** - * @param {number} delta Zoom delta. - * @param {Event} event The event to handle - * @private - */ -ol.control.Zoom.prototype.handleClick_ = function(delta, event) { - event.preventDefault(); - this.zoomByDelta_(delta); -}; - - -/** - * @param {number} delta Zoom delta. - * @private - */ -ol.control.Zoom.prototype.zoomByDelta_ = function(delta) { - var map = this.getMap(); - var view = map.getView(); - if (!view) { - // the map does not have a view, so we can't act - // upon it - return; - } - var currentResolution = view.getResolution(); - if (currentResolution) { - var newResolution = view.constrainResolution(currentResolution, delta); - if (this.duration_ > 0) { - if (view.getAnimating()) { - view.cancelAnimations(); - } - view.animate({ - resolution: newResolution, - duration: this.duration_, - easing: ol.easing.easeOut - }); - } else { - view.setResolution(newResolution); - } - } -}; - -goog.provide('ol.control'); - -goog.require('ol.Collection'); -goog.require('ol.control.Attribution'); -goog.require('ol.control.Rotate'); -goog.require('ol.control.Zoom'); - - -/** - * Set of controls included in maps by default. Unless configured otherwise, - * this returns a collection containing an instance of each of the following - * controls: - * * {@link ol.control.Zoom} - * * {@link ol.control.Rotate} - * * {@link ol.control.Attribution} - * - * @param {olx.control.DefaultsOptions=} opt_options Defaults options. - * @return {ol.Collection.<ol.control.Control>} Controls. - * @api - */ -ol.control.defaults = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var controls = new ol.Collection(); - - var zoomControl = options.zoom !== undefined ? options.zoom : true; - if (zoomControl) { - controls.push(new ol.control.Zoom(options.zoomOptions)); - } - - var rotateControl = options.rotate !== undefined ? options.rotate : true; - if (rotateControl) { - controls.push(new ol.control.Rotate(options.rotateOptions)); - } - - var attributionControl = options.attribution !== undefined ? - options.attribution : true; - if (attributionControl) { - controls.push(new ol.control.Attribution(options.attributionOptions)); - } - - return controls; - -}; - -goog.provide('ol.control.FullScreen'); - -goog.require('ol'); -goog.require('ol.control.Control'); -goog.require('ol.css'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); - - -/** - * @classdesc - * Provides a button that when clicked fills up the full screen with the map. - * The full screen source element is by default the element containing the map viewport unless - * overridden by providing the `source` option. In which case, the dom - * element introduced using this parameter will be displayed in full screen. - * - * When in full screen mode, a close button is shown to exit full screen mode. - * The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to - * toggle the map in full screen mode. - * - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.FullScreenOptions=} opt_options Options. - * @api - */ -ol.control.FullScreen = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {string} - */ - this.cssClassName_ = options.className !== undefined ? options.className : - 'ol-full-screen'; - - var label = options.label !== undefined ? options.label : '\u2922'; - - /** - * @private - * @type {Node} - */ - this.labelNode_ = typeof label === 'string' ? - document.createTextNode(label) : label; - - var labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7'; - - /** - * @private - * @type {Node} - */ - this.labelActiveNode_ = typeof labelActive === 'string' ? - document.createTextNode(labelActive) : labelActive; - - var tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen'; - var button = document.createElement('button'); - button.className = this.cssClassName_ + '-' + ol.control.FullScreen.isFullScreen(); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(this.labelNode_); - - ol.events.listen(button, ol.events.EventType.CLICK, - this.handleClick_, this); - - var cssClasses = this.cssClassName_ + ' ' + ol.css.CLASS_UNSELECTABLE + - ' ' + ol.css.CLASS_CONTROL + ' ' + - (!ol.control.FullScreen.isFullScreenSupported() ? ol.css.CLASS_UNSUPPORTED : ''); - var element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); - - ol.control.Control.call(this, { - element: element, - target: options.target - }); - - /** - * @private - * @type {boolean} - */ - this.keys_ = options.keys !== undefined ? options.keys : false; - - /** - * @private - * @type {Element|string|undefined} - */ - this.source_ = options.source; - -}; -ol.inherits(ol.control.FullScreen, ol.control.Control); - - -/** - * @param {Event} event The event to handle - * @private - */ -ol.control.FullScreen.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleFullScreen_(); -}; - - -/** - * @private - */ -ol.control.FullScreen.prototype.handleFullScreen_ = function() { - if (!ol.control.FullScreen.isFullScreenSupported()) { - return; - } - var map = this.getMap(); - if (!map) { - return; - } - if (ol.control.FullScreen.isFullScreen()) { - ol.control.FullScreen.exitFullScreen(); - } else { - var element; - if (this.source_) { - element = typeof this.source_ === 'string' ? - document.getElementById(this.source_) : - this.source_; - } else { - element = map.getTargetElement(); - } - if (this.keys_) { - ol.control.FullScreen.requestFullScreenWithKeys(element); - - } else { - ol.control.FullScreen.requestFullScreen(element); - } - } -}; - - -/** - * @private - */ -ol.control.FullScreen.prototype.handleFullScreenChange_ = function() { - var button = this.element.firstElementChild; - var map = this.getMap(); - if (ol.control.FullScreen.isFullScreen()) { - button.className = this.cssClassName_ + '-true'; - ol.dom.replaceNode(this.labelActiveNode_, this.labelNode_); - } else { - button.className = this.cssClassName_ + '-false'; - ol.dom.replaceNode(this.labelNode_, this.labelActiveNode_); - } - if (map) { - map.updateSize(); - } -}; - - -/** - * @inheritDoc - * @api - */ -ol.control.FullScreen.prototype.setMap = function(map) { - ol.control.Control.prototype.setMap.call(this, map); - if (map) { - this.listenerKeys.push(ol.events.listen(document, - ol.control.FullScreen.getChangeType_(), - this.handleFullScreenChange_, this) - ); - } -}; - -/** - * @return {boolean} Fullscreen is supported by the current platform. - */ -ol.control.FullScreen.isFullScreenSupported = function() { - var body = document.body; - return !!( - body.webkitRequestFullscreen || - (body.mozRequestFullScreen && document.mozFullScreenEnabled) || - (body.msRequestFullscreen && document.msFullscreenEnabled) || - (body.requestFullscreen && document.fullscreenEnabled) - ); -}; - -/** - * @return {boolean} Element is currently in fullscreen. - */ -ol.control.FullScreen.isFullScreen = function() { - return !!( - document.webkitIsFullScreen || document.mozFullScreen || - document.msFullscreenElement || document.fullscreenElement - ); -}; - -/** - * Request to fullscreen an element. - * @param {Node} element Element to request fullscreen - */ -ol.control.FullScreen.requestFullScreen = function(element) { - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(); - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(); - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(); - } -}; - -/** - * Request to fullscreen an element with keyboard input. - * @param {Node} element Element to request fullscreen - */ -ol.control.FullScreen.requestFullScreenWithKeys = function(element) { - if (element.mozRequestFullScreenWithKeys) { - element.mozRequestFullScreenWithKeys(); - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } else { - ol.control.FullScreen.requestFullScreen(element); - } -}; - -/** - * Exit fullscreen. - */ -ol.control.FullScreen.exitFullScreen = function() { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } -}; - -/** - * @return {string} Change type. - * @private - */ -ol.control.FullScreen.getChangeType_ = (function() { - var changeType; - return function() { - if (!changeType) { - var body = document.body; - if (body.webkitRequestFullscreen) { - changeType = 'webkitfullscreenchange'; - } else if (body.mozRequestFullScreen) { - changeType = 'mozfullscreenchange'; - } else if (body.msRequestFullscreen) { - changeType = 'MSFullscreenChange'; - } else if (body.requestFullscreen) { - changeType = 'fullscreenchange'; - } - } - return changeType; - }; -})(); - -// FIXME should listen on appropriate pane, once it is defined - -goog.provide('ol.control.MousePosition'); - -goog.require('ol'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.Object'); -goog.require('ol.control.Control'); -goog.require('ol.proj'); - - -/** - * @classdesc - * A control to show the 2D coordinates of the mouse cursor. By default, these - * are in the view projection, but can be in any supported projection. - * By default the control is shown in the top right corner of the map, but this - * can be changed by using the css selector `.ol-mouse-position`. - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.MousePositionOptions=} opt_options Mouse position - * options. - * @api - */ -ol.control.MousePosition = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var element = document.createElement('DIV'); - element.className = options.className !== undefined ? options.className : 'ol-mouse-position'; - - var render = options.render ? - options.render : ol.control.MousePosition.render; - - ol.control.Control.call(this, { - element: element, - render: render, - target: options.target - }); - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.control.MousePosition.Property_.PROJECTION), - this.handleProjectionChanged_, this); - - if (options.coordinateFormat) { - this.setCoordinateFormat(options.coordinateFormat); - } - if (options.projection) { - this.setProjection(options.projection); - } - - /** - * @private - * @type {string} - */ - this.undefinedHTML_ = options.undefinedHTML !== undefined ? options.undefinedHTML : ''; - - /** - * @private - * @type {string} - */ - this.renderedHTML_ = element.innerHTML; - - /** - * @private - * @type {ol.proj.Projection} - */ - this.mapProjection_ = null; - - /** - * @private - * @type {?ol.TransformFunction} - */ - this.transform_ = null; - - /** - * @private - * @type {ol.Pixel} - */ - this.lastMouseMovePixel_ = null; - -}; -ol.inherits(ol.control.MousePosition, ol.control.Control); - - -/** - * Update the mouseposition element. - * @param {ol.MapEvent} mapEvent Map event. - * @this {ol.control.MousePosition} - * @api - */ -ol.control.MousePosition.render = function(mapEvent) { - var frameState = mapEvent.frameState; - if (!frameState) { - this.mapProjection_ = null; - } else { - if (this.mapProjection_ != frameState.viewState.projection) { - this.mapProjection_ = frameState.viewState.projection; - this.transform_ = null; - } - } - this.updateHTML_(this.lastMouseMovePixel_); -}; - - -/** - * @private - */ -ol.control.MousePosition.prototype.handleProjectionChanged_ = function() { - this.transform_ = null; -}; - - -/** - * Return the coordinate format type used to render the current position or - * undefined. - * @return {ol.CoordinateFormatType|undefined} The format to render the current - * position in. - * @observable - * @api - */ -ol.control.MousePosition.prototype.getCoordinateFormat = function() { - return /** @type {ol.CoordinateFormatType|undefined} */ ( - this.get(ol.control.MousePosition.Property_.COORDINATE_FORMAT)); -}; - - -/** - * Return the projection that is used to report the mouse position. - * @return {ol.proj.Projection|undefined} The projection to report mouse - * position in. - * @observable - * @api - */ -ol.control.MousePosition.prototype.getProjection = function() { - return /** @type {ol.proj.Projection|undefined} */ ( - this.get(ol.control.MousePosition.Property_.PROJECTION)); -}; - - -/** - * @param {Event} event Browser event. - * @protected - */ -ol.control.MousePosition.prototype.handleMouseMove = function(event) { - var map = this.getMap(); - this.lastMouseMovePixel_ = map.getEventPixel(event); - this.updateHTML_(this.lastMouseMovePixel_); -}; - - -/** - * @param {Event} event Browser event. - * @protected - */ -ol.control.MousePosition.prototype.handleMouseOut = function(event) { - this.updateHTML_(null); - this.lastMouseMovePixel_ = null; -}; - - -/** - * @inheritDoc - * @api - */ -ol.control.MousePosition.prototype.setMap = function(map) { - ol.control.Control.prototype.setMap.call(this, map); - if (map) { - var viewport = map.getViewport(); - this.listenerKeys.push( - ol.events.listen(viewport, ol.events.EventType.MOUSEMOVE, - this.handleMouseMove, this), - ol.events.listen(viewport, ol.events.EventType.MOUSEOUT, - this.handleMouseOut, this) - ); - } -}; - - -/** - * Set the coordinate format type used to render the current position. - * @param {ol.CoordinateFormatType} format The format to render the current - * position in. - * @observable - * @api - */ -ol.control.MousePosition.prototype.setCoordinateFormat = function(format) { - this.set(ol.control.MousePosition.Property_.COORDINATE_FORMAT, format); -}; - - -/** - * Set the projection that is used to report the mouse position. - * @param {ol.ProjectionLike} projection The projection to report mouse - * position in. - * @observable - * @api - */ -ol.control.MousePosition.prototype.setProjection = function(projection) { - this.set(ol.control.MousePosition.Property_.PROJECTION, ol.proj.get(projection)); -}; - - -/** - * @param {?ol.Pixel} pixel Pixel. - * @private - */ -ol.control.MousePosition.prototype.updateHTML_ = function(pixel) { - var html = this.undefinedHTML_; - if (pixel && this.mapProjection_) { - if (!this.transform_) { - var projection = this.getProjection(); - if (projection) { - this.transform_ = ol.proj.getTransformFromProjections( - this.mapProjection_, projection); - } else { - this.transform_ = ol.proj.identityTransform; - } - } - var map = this.getMap(); - var coordinate = map.getCoordinateFromPixel(pixel); - if (coordinate) { - this.transform_(coordinate, coordinate); - var coordinateFormat = this.getCoordinateFormat(); - if (coordinateFormat) { - html = coordinateFormat(coordinate); - } else { - html = coordinate.toString(); - } - } - } - if (!this.renderedHTML_ || html != this.renderedHTML_) { - this.element.innerHTML = html; - this.renderedHTML_ = html; - } -}; - - -/** - * @enum {string} - * @private - */ -ol.control.MousePosition.Property_ = { - PROJECTION: 'projection', - COORDINATE_FORMAT: 'coordinateFormat' -}; - -goog.provide('ol.MapEvent'); - -goog.require('ol'); -goog.require('ol.events.Event'); - - -/** - * @classdesc - * Events emitted as map events are instances of this type. - * See {@link ol.Map} for which events trigger a map event. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.MapEvent} - * @param {string} type Event type. - * @param {ol.Map} map Map. - * @param {?olx.FrameState=} opt_frameState Frame state. - */ -ol.MapEvent = function(type, map, opt_frameState) { - - ol.events.Event.call(this, type); - - /** - * The map where the event occurred. - * @type {ol.Map} - * @api - */ - this.map = map; - - /** - * The frame state at the time of the event. - * @type {?olx.FrameState} - * @api - */ - this.frameState = opt_frameState !== undefined ? opt_frameState : null; - -}; -ol.inherits(ol.MapEvent, ol.events.Event); - -goog.provide('ol.MapBrowserEvent'); - -goog.require('ol'); -goog.require('ol.MapEvent'); - - -/** - * @classdesc - * Events emitted as map browser events are instances of this type. - * See {@link ol.Map} for which events trigger a map browser event. - * - * @constructor - * @extends {ol.MapEvent} - * @implements {oli.MapBrowserEvent} - * @param {string} type Event type. - * @param {ol.Map} map Map. - * @param {Event} browserEvent Browser event. - * @param {boolean=} opt_dragging Is the map currently being dragged? - * @param {?olx.FrameState=} opt_frameState Frame state. - */ -ol.MapBrowserEvent = function(type, map, browserEvent, opt_dragging, - opt_frameState) { - - ol.MapEvent.call(this, type, map, opt_frameState); - - /** - * The original browser event. - * @const - * @type {Event} - * @api - */ - this.originalEvent = browserEvent; - - /** - * The map pixel relative to the viewport corresponding to the original browser event. - * @type {ol.Pixel} - * @api - */ - this.pixel = map.getEventPixel(browserEvent); - - /** - * The coordinate in view projection corresponding to the original browser event. - * @type {ol.Coordinate} - * @api - */ - this.coordinate = map.getCoordinateFromPixel(this.pixel); - - /** - * Indicates if the map is currently being dragged. Only set for - * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. - * - * @type {boolean} - * @api - */ - this.dragging = opt_dragging !== undefined ? opt_dragging : false; - -}; -ol.inherits(ol.MapBrowserEvent, ol.MapEvent); - - -/** - * Prevents the default browser action. - * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault - * @override - * @api - */ -ol.MapBrowserEvent.prototype.preventDefault = function() { - ol.MapEvent.prototype.preventDefault.call(this); - this.originalEvent.preventDefault(); -}; - - -/** - * Prevents further propagation of the current event. - * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation - * @override - * @api - */ -ol.MapBrowserEvent.prototype.stopPropagation = function() { - ol.MapEvent.prototype.stopPropagation.call(this); - this.originalEvent.stopPropagation(); -}; - -goog.provide('ol.webgl'); - -goog.require('ol'); - - -if (ol.ENABLE_WEBGL) { - - /** Constants taken from goog.webgl - */ - - - /** - * @const - * @type {number} - */ - ol.webgl.ONE = 1; - - - /** - * @const - * @type {number} - */ - ol.webgl.SRC_ALPHA = 0x0302; - - - /** - * @const - * @type {number} - */ - ol.webgl.COLOR_ATTACHMENT0 = 0x8CE0; - - - /** - * @const - * @type {number} - */ - ol.webgl.COLOR_BUFFER_BIT = 0x00004000; - - - /** - * @const - * @type {number} - */ - ol.webgl.TRIANGLES = 0x0004; - - - /** - * @const - * @type {number} - */ - ol.webgl.TRIANGLE_STRIP = 0x0005; - - - /** - * @const - * @type {number} - */ - ol.webgl.ONE_MINUS_SRC_ALPHA = 0x0303; - - - /** - * @const - * @type {number} - */ - ol.webgl.ARRAY_BUFFER = 0x8892; - - - /** - * @const - * @type {number} - */ - ol.webgl.ELEMENT_ARRAY_BUFFER = 0x8893; - - - /** - * @const - * @type {number} - */ - ol.webgl.STREAM_DRAW = 0x88E0; - - - /** - * @const - * @type {number} - */ - ol.webgl.STATIC_DRAW = 0x88E4; - - - /** - * @const - * @type {number} - */ - ol.webgl.DYNAMIC_DRAW = 0x88E8; - - - /** - * @const - * @type {number} - */ - ol.webgl.CULL_FACE = 0x0B44; - - - /** - * @const - * @type {number} - */ - ol.webgl.BLEND = 0x0BE2; - - - /** - * @const - * @type {number} - */ - ol.webgl.STENCIL_TEST = 0x0B90; - - - /** - * @const - * @type {number} - */ - ol.webgl.DEPTH_TEST = 0x0B71; - - - /** - * @const - * @type {number} - */ - ol.webgl.SCISSOR_TEST = 0x0C11; - - - /** - * @const - * @type {number} - */ - ol.webgl.UNSIGNED_BYTE = 0x1401; - - - /** - * @const - * @type {number} - */ - ol.webgl.UNSIGNED_SHORT = 0x1403; - - - /** - * @const - * @type {number} - */ - ol.webgl.UNSIGNED_INT = 0x1405; - - - /** - * @const - * @type {number} - */ - ol.webgl.FLOAT = 0x1406; - - - /** - * @const - * @type {number} - */ - ol.webgl.RGBA = 0x1908; - - - /** - * @const - * @type {number} - */ - ol.webgl.FRAGMENT_SHADER = 0x8B30; - - - /** - * @const - * @type {number} - */ - ol.webgl.VERTEX_SHADER = 0x8B31; - - - /** - * @const - * @type {number} - */ - ol.webgl.LINK_STATUS = 0x8B82; - - - /** - * @const - * @type {number} - */ - ol.webgl.LINEAR = 0x2601; - - - /** - * @const - * @type {number} - */ - ol.webgl.TEXTURE_MAG_FILTER = 0x2800; - - - /** - * @const - * @type {number} - */ - ol.webgl.TEXTURE_MIN_FILTER = 0x2801; - - - /** - * @const - * @type {number} - */ - ol.webgl.TEXTURE_WRAP_S = 0x2802; - - - /** - * @const - * @type {number} - */ - ol.webgl.TEXTURE_WRAP_T = 0x2803; - - - /** - * @const - * @type {number} - */ - ol.webgl.TEXTURE_2D = 0x0DE1; - - - /** - * @const - * @type {number} - */ - ol.webgl.TEXTURE0 = 0x84C0; - - - /** - * @const - * @type {number} - */ - ol.webgl.CLAMP_TO_EDGE = 0x812F; - - - /** - * @const - * @type {number} - */ - ol.webgl.COMPILE_STATUS = 0x8B81; - - - /** - * @const - * @type {number} - */ - ol.webgl.FRAMEBUFFER = 0x8D40; - - - /** end of goog.webgl constants - */ - - - /** - * @const - * @private - * @type {Array.<string>} - */ - ol.webgl.CONTEXT_IDS_ = [ - 'experimental-webgl', - 'webgl', - 'webkit-3d', - 'moz-webgl' - ]; - - - /** - * @param {HTMLCanvasElement} canvas Canvas. - * @param {Object=} opt_attributes Attributes. - * @return {WebGLRenderingContext} WebGL rendering context. - */ - ol.webgl.getContext = function(canvas, opt_attributes) { - var context, i, ii = ol.webgl.CONTEXT_IDS_.length; - for (i = 0; i < ii; ++i) { - try { - context = canvas.getContext(ol.webgl.CONTEXT_IDS_[i], opt_attributes); - if (context) { - return /** @type {!WebGLRenderingContext} */ (context); - } - } catch (e) { - // pass - } - } - return null; - }; - -} - -goog.provide('ol.has'); - -goog.require('ol'); -goog.require('ol.webgl'); - -var ua = typeof navigator !== 'undefined' ? - navigator.userAgent.toLowerCase() : ''; - -/** - * User agent string says we are dealing with Firefox as browser. - * @type {boolean} - */ -ol.has.FIREFOX = ua.indexOf('firefox') !== -1; - -/** - * User agent string says we are dealing with Safari as browser. - * @type {boolean} - */ -ol.has.SAFARI = ua.indexOf('safari') !== -1 && ua.indexOf('chrom') == -1; - -/** - * User agent string says we are dealing with a WebKit engine. - * @type {boolean} - */ -ol.has.WEBKIT = ua.indexOf('webkit') !== -1 && ua.indexOf('edge') == -1; - -/** - * User agent string says we are dealing with a Mac as platform. - * @type {boolean} - */ -ol.has.MAC = ua.indexOf('macintosh') !== -1; - - -/** - * The ratio between physical pixels and device-independent pixels - * (dips) on the device (`window.devicePixelRatio`). - * @const - * @type {number} - * @api - */ -ol.has.DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1; - - -/** - * True if the browser's Canvas implementation implements {get,set}LineDash. - * @type {boolean} - */ -ol.has.CANVAS_LINE_DASH = false; - - -/** - * True if both the library and browser support Canvas. Always `false` - * if `ol.ENABLE_CANVAS` is set to `false` at compile time. - * @const - * @type {boolean} - * @api - */ -ol.has.CANVAS = ol.ENABLE_CANVAS && ( - /** - * @return {boolean} Canvas supported. - */ - function() { - if (!('HTMLCanvasElement' in window)) { - return false; - } - try { - var context = document.createElement('CANVAS').getContext('2d'); - if (!context) { - return false; - } else { - if (context.setLineDash !== undefined) { - ol.has.CANVAS_LINE_DASH = true; - } - return true; - } - } catch (e) { - return false; - } - })(); - - -/** - * Indicates if DeviceOrientation is supported in the user's browser. - * @const - * @type {boolean} - * @api - */ -ol.has.DEVICE_ORIENTATION = 'DeviceOrientationEvent' in window; - - -/** - * Is HTML5 geolocation supported in the current browser? - * @const - * @type {boolean} - * @api - */ -ol.has.GEOLOCATION = 'geolocation' in navigator; - - -/** - * True if browser supports touch events. - * @const - * @type {boolean} - * @api - */ -ol.has.TOUCH = ol.ASSUME_TOUCH || 'ontouchstart' in window; - - -/** - * True if browser supports pointer events. - * @const - * @type {boolean} - */ -ol.has.POINTER = 'PointerEvent' in window; - - -/** - * True if browser supports ms pointer events (IE 10). - * @const - * @type {boolean} - */ -ol.has.MSPOINTER = !!(navigator.msPointerEnabled); - - -/** - * True if both OpenLayers and browser support WebGL. Always `false` - * if `ol.ENABLE_WEBGL` is set to `false` at compile time. - * @const - * @type {boolean} - * @api - */ -ol.has.WEBGL; - - -(function() { - if (ol.ENABLE_WEBGL) { - var hasWebGL = false; - var textureSize; - var /** @type {Array.<string>} */ extensions = []; - - if ('WebGLRenderingContext' in window) { - try { - var canvas = /** @type {HTMLCanvasElement} */ - (document.createElement('CANVAS')); - var gl = ol.webgl.getContext(canvas, { - failIfMajorPerformanceCaveat: true - }); - if (gl) { - hasWebGL = true; - textureSize = /** @type {number} */ - (gl.getParameter(gl.MAX_TEXTURE_SIZE)); - extensions = gl.getSupportedExtensions(); - } - } catch (e) { - // pass - } - } - ol.has.WEBGL = hasWebGL; - ol.WEBGL_EXTENSIONS = extensions; - ol.WEBGL_MAX_TEXTURE_SIZE = textureSize; - } -})(); - -goog.provide('ol.MapBrowserEventType'); - -goog.require('ol.events.EventType'); - - -/** - * Constants for event names. - * @enum {string} - */ -ol.MapBrowserEventType = { - - /** - * A true single click with no dragging and no double click. Note that this - * event is delayed by 250 ms to ensure that it is not a double click. - * @event ol.MapBrowserEvent#singleclick - * @api - */ - SINGLECLICK: 'singleclick', - - /** - * A click with no dragging. A double click will fire two of this. - * @event ol.MapBrowserEvent#click - * @api - */ - CLICK: ol.events.EventType.CLICK, - - /** - * A true double click, with no dragging. - * @event ol.MapBrowserEvent#dblclick - * @api - */ - DBLCLICK: ol.events.EventType.DBLCLICK, - - /** - * Triggered when a pointer is dragged. - * @event ol.MapBrowserEvent#pointerdrag - * @api - */ - POINTERDRAG: 'pointerdrag', - - /** - * Triggered when a pointer is moved. Note that on touch devices this is - * triggered when the map is panned, so is not the same as mousemove. - * @event ol.MapBrowserEvent#pointermove - * @api - */ - POINTERMOVE: 'pointermove', - - POINTERDOWN: 'pointerdown', - POINTERUP: 'pointerup', - POINTEROVER: 'pointerover', - POINTEROUT: 'pointerout', - POINTERENTER: 'pointerenter', - POINTERLEAVE: 'pointerleave', - POINTERCANCEL: 'pointercancel' -}; - -goog.provide('ol.MapBrowserPointerEvent'); - -goog.require('ol'); -goog.require('ol.MapBrowserEvent'); - - -/** - * @constructor - * @extends {ol.MapBrowserEvent} - * @param {string} type Event type. - * @param {ol.Map} map Map. - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @param {boolean=} opt_dragging Is the map currently being dragged? - * @param {?olx.FrameState=} opt_frameState Frame state. - */ -ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_dragging, - opt_frameState) { - - ol.MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging, - opt_frameState); - - /** - * @const - * @type {ol.pointer.PointerEvent} - */ - this.pointerEvent = pointerEvent; - -}; -ol.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent); - -goog.provide('ol.pointer.EventType'); - - -/** - * Constants for event names. - * @enum {string} - */ -ol.pointer.EventType = { - POINTERMOVE: 'pointermove', - POINTERDOWN: 'pointerdown', - POINTERUP: 'pointerup', - POINTEROVER: 'pointerover', - POINTEROUT: 'pointerout', - POINTERENTER: 'pointerenter', - POINTERLEAVE: 'pointerleave', - POINTERCANCEL: 'pointercancel' -}; - -goog.provide('ol.pointer.EventSource'); - - -/** - * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. - * @param {!Object.<string, function(Event)>} mapping Event - * mapping. - * @constructor - */ -ol.pointer.EventSource = function(dispatcher, mapping) { - /** - * @type {ol.pointer.PointerEventHandler} - */ - this.dispatcher = dispatcher; - - /** - * @private - * @const - * @type {!Object.<string, function(Event)>} - */ - this.mapping_ = mapping; -}; - - -/** - * List of events supported by this source. - * @return {Array.<string>} Event names - */ -ol.pointer.EventSource.prototype.getEvents = function() { - return Object.keys(this.mapping_); -}; - - -/** - * Returns the handler that should handle a given event type. - * @param {string} eventType The event type. - * @return {function(Event)} Handler - */ -ol.pointer.EventSource.prototype.getHandlerForEvent = function(eventType) { - return this.mapping_[eventType]; -}; - -// Based on https://github.com/Polymer/PointerEvents - -// Copyright (c) 2013 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.pointer.MouseSource'); - -goog.require('ol'); -goog.require('ol.pointer.EventSource'); - - -/** - * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {ol.pointer.EventSource} - */ -ol.pointer.MouseSource = function(dispatcher) { - var mapping = { - 'mousedown': this.mousedown, - 'mousemove': this.mousemove, - 'mouseup': this.mouseup, - 'mouseover': this.mouseover, - 'mouseout': this.mouseout - }; - ol.pointer.EventSource.call(this, dispatcher, mapping); - - /** - * @const - * @type {!Object.<string, Event|Object>} - */ - this.pointerMap = dispatcher.pointerMap; - - /** - * @const - * @type {Array.<ol.Pixel>} - */ - this.lastTouches = []; -}; -ol.inherits(ol.pointer.MouseSource, ol.pointer.EventSource); - - -/** - * @const - * @type {number} - */ -ol.pointer.MouseSource.POINTER_ID = 1; - - -/** - * @const - * @type {string} - */ -ol.pointer.MouseSource.POINTER_TYPE = 'mouse'; - - -/** - * Radius around touchend that swallows mouse events. - * - * @const - * @type {number} - */ -ol.pointer.MouseSource.DEDUP_DIST = 25; - - -/** - * Detect if a mouse event was simulated from a touch by - * checking if previously there was a touch event at the - * same position. - * - * FIXME - Known problem with the native Android browser on - * Samsung GT-I9100 (Android 4.1.2): - * In case the page is scrolled, this function does not work - * correctly when a canvas is used (WebGL or canvas renderer). - * Mouse listeners on canvas elements (for this browser), create - * two mouse events: One 'good' and one 'bad' one (on other browsers or - * when a div is used, there is only one event). For the 'bad' one, - * clientX/clientY and also pageX/pageY are wrong when the page - * is scrolled. Because of that, this function can not detect if - * the events were simulated from a touch event. As result, a - * pointer event at a wrong position is dispatched, which confuses - * the map interactions. - * It is unclear, how one can get the correct position for the event - * or detect that the positions are invalid. - * - * @private - * @param {Event} inEvent The in event. - * @return {boolean} True, if the event was generated by a touch. - */ -ol.pointer.MouseSource.prototype.isEventSimulatedFromTouch_ = function(inEvent) { - var lts = this.lastTouches; - var x = inEvent.clientX, y = inEvent.clientY; - for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { - // simulated mouse events will be swallowed near a primary touchend - var dx = Math.abs(x - t[0]), dy = Math.abs(y - t[1]); - if (dx <= ol.pointer.MouseSource.DEDUP_DIST && - dy <= ol.pointer.MouseSource.DEDUP_DIST) { - return true; - } - } - return false; -}; - - -/** - * Creates a copy of the original event that will be used - * for the fake pointer event. - * - * @param {Event} inEvent The in event. - * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. - * @return {Object} The copied event. - */ -ol.pointer.MouseSource.prepareEvent = function(inEvent, dispatcher) { - var e = dispatcher.cloneEvent(inEvent, inEvent); - - // forward mouse preventDefault - var pd = e.preventDefault; - e.preventDefault = function() { - inEvent.preventDefault(); - pd(); - }; - - e.pointerId = ol.pointer.MouseSource.POINTER_ID; - e.isPrimary = true; - e.pointerType = ol.pointer.MouseSource.POINTER_TYPE; - - return e; -}; - - -/** - * Handler for `mousedown`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MouseSource.prototype.mousedown = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - // TODO(dfreedman) workaround for some elements not sending mouseup - // http://crbug/149091 - if (ol.pointer.MouseSource.POINTER_ID.toString() in this.pointerMap) { - this.cancel(inEvent); - } - var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); - this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()] = inEvent; - this.dispatcher.down(e, inEvent); - } -}; - - -/** - * Handler for `mousemove`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MouseSource.prototype.mousemove = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); - this.dispatcher.move(e, inEvent); - } -}; - - -/** - * Handler for `mouseup`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MouseSource.prototype.mouseup = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - var p = this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()]; - - if (p && p.button === inEvent.button) { - var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); - this.dispatcher.up(e, inEvent); - this.cleanupMouse(); - } - } -}; - - -/** - * Handler for `mouseover`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MouseSource.prototype.mouseover = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); - this.dispatcher.enterOver(e, inEvent); - } -}; - - -/** - * Handler for `mouseout`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MouseSource.prototype.mouseout = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); - this.dispatcher.leaveOut(e, inEvent); - } -}; - - -/** - * Dispatches a `pointercancel` event. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MouseSource.prototype.cancel = function(inEvent) { - var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); - this.dispatcher.cancel(e, inEvent); - this.cleanupMouse(); -}; - - -/** - * Remove the mouse from the list of active pointers. - */ -ol.pointer.MouseSource.prototype.cleanupMouse = function() { - delete this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()]; -}; - -// Based on https://github.com/Polymer/PointerEvents - -// Copyright (c) 2013 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.pointer.MsSource'); - -goog.require('ol'); -goog.require('ol.pointer.EventSource'); - - -/** - * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {ol.pointer.EventSource} - */ -ol.pointer.MsSource = function(dispatcher) { - var mapping = { - 'MSPointerDown': this.msPointerDown, - 'MSPointerMove': this.msPointerMove, - 'MSPointerUp': this.msPointerUp, - 'MSPointerOut': this.msPointerOut, - 'MSPointerOver': this.msPointerOver, - 'MSPointerCancel': this.msPointerCancel, - 'MSGotPointerCapture': this.msGotPointerCapture, - 'MSLostPointerCapture': this.msLostPointerCapture - }; - ol.pointer.EventSource.call(this, dispatcher, mapping); - - /** - * @const - * @type {!Object.<string, Event|Object>} - */ - this.pointerMap = dispatcher.pointerMap; - - /** - * @const - * @type {Array.<string>} - */ - this.POINTER_TYPES = [ - '', - 'unavailable', - 'touch', - 'pen', - 'mouse' - ]; -}; -ol.inherits(ol.pointer.MsSource, ol.pointer.EventSource); - - -/** - * Creates a copy of the original event that will be used - * for the fake pointer event. - * - * @private - * @param {Event} inEvent The in event. - * @return {Object} The copied event. - */ -ol.pointer.MsSource.prototype.prepareEvent_ = function(inEvent) { - var e = inEvent; - if (typeof inEvent.pointerType === 'number') { - e = this.dispatcher.cloneEvent(inEvent, inEvent); - e.pointerType = this.POINTER_TYPES[inEvent.pointerType]; - } - - return e; -}; - - -/** - * Remove this pointer from the list of active pointers. - * @param {number} pointerId Pointer identifier. - */ -ol.pointer.MsSource.prototype.cleanup = function(pointerId) { - delete this.pointerMap[pointerId.toString()]; -}; - - -/** - * Handler for `msPointerDown`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msPointerDown = function(inEvent) { - this.pointerMap[inEvent.pointerId.toString()] = inEvent; - var e = this.prepareEvent_(inEvent); - this.dispatcher.down(e, inEvent); -}; - - -/** - * Handler for `msPointerMove`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msPointerMove = function(inEvent) { - var e = this.prepareEvent_(inEvent); - this.dispatcher.move(e, inEvent); -}; - - -/** - * Handler for `msPointerUp`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msPointerUp = function(inEvent) { - var e = this.prepareEvent_(inEvent); - this.dispatcher.up(e, inEvent); - this.cleanup(inEvent.pointerId); -}; - - -/** - * Handler for `msPointerOut`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msPointerOut = function(inEvent) { - var e = this.prepareEvent_(inEvent); - this.dispatcher.leaveOut(e, inEvent); -}; - - -/** - * Handler for `msPointerOver`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msPointerOver = function(inEvent) { - var e = this.prepareEvent_(inEvent); - this.dispatcher.enterOver(e, inEvent); -}; - - -/** - * Handler for `msPointerCancel`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msPointerCancel = function(inEvent) { - var e = this.prepareEvent_(inEvent); - this.dispatcher.cancel(e, inEvent); - this.cleanup(inEvent.pointerId); -}; - - -/** - * Handler for `msLostPointerCapture`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msLostPointerCapture = function(inEvent) { - var e = this.dispatcher.makeEvent('lostpointercapture', - inEvent, inEvent); - this.dispatcher.dispatchEvent(e); -}; - - -/** - * Handler for `msGotPointerCapture`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.MsSource.prototype.msGotPointerCapture = function(inEvent) { - var e = this.dispatcher.makeEvent('gotpointercapture', - inEvent, inEvent); - this.dispatcher.dispatchEvent(e); -}; - -// Based on https://github.com/Polymer/PointerEvents - -// Copyright (c) 2013 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.pointer.NativeSource'); - -goog.require('ol'); -goog.require('ol.pointer.EventSource'); - - -/** - * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {ol.pointer.EventSource} - */ -ol.pointer.NativeSource = function(dispatcher) { - var mapping = { - 'pointerdown': this.pointerDown, - 'pointermove': this.pointerMove, - 'pointerup': this.pointerUp, - 'pointerout': this.pointerOut, - 'pointerover': this.pointerOver, - 'pointercancel': this.pointerCancel, - 'gotpointercapture': this.gotPointerCapture, - 'lostpointercapture': this.lostPointerCapture - }; - ol.pointer.EventSource.call(this, dispatcher, mapping); -}; -ol.inherits(ol.pointer.NativeSource, ol.pointer.EventSource); - - -/** - * Handler for `pointerdown`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.pointerDown = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointermove`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.pointerMove = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointerup`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.pointerUp = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointerout`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.pointerOut = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointerover`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.pointerOver = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointercancel`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.pointerCancel = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `lostpointercapture`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.lostPointerCapture = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `gotpointercapture`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.NativeSource.prototype.gotPointerCapture = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - -// Based on https://github.com/Polymer/PointerEvents - -// Copyright (c) 2013 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.pointer.PointerEvent'); - - -goog.require('ol'); -goog.require('ol.events.Event'); - - -/** - * A class for pointer events. - * - * This class is used as an abstraction for mouse events, - * touch events and even native pointer events. - * - * @constructor - * @extends {ol.events.Event} - * @param {string} type The type of the event to create. - * @param {Event} originalEvent The event. - * @param {Object.<string, ?>=} opt_eventDict An optional dictionary of - * initial event properties. - */ -ol.pointer.PointerEvent = function(type, originalEvent, opt_eventDict) { - ol.events.Event.call(this, type); - - /** - * @const - * @type {Event} - */ - this.originalEvent = originalEvent; - - var eventDict = opt_eventDict ? opt_eventDict : {}; - - /** - * @type {number} - */ - this.buttons = this.getButtons_(eventDict); - - /** - * @type {number} - */ - this.pressure = this.getPressure_(eventDict, this.buttons); - - // MouseEvent related properties - - /** - * @type {boolean} - */ - this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; - - /** - * @type {boolean} - */ - this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; - - /** - * @type {Object} - */ - this.view = 'view' in eventDict ? eventDict['view'] : null; - - /** - * @type {number} - */ - this.detail = 'detail' in eventDict ? eventDict['detail'] : null; - - /** - * @type {number} - */ - this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; - - /** - * @type {number} - */ - this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; - - /** - * @type {number} - */ - this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; - - /** - * @type {number} - */ - this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; - - /** - * @type {boolean} - */ - this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; - - /** - * @type {boolean} - */ - this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; - - /** - * @type {boolean} - */ - this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; - - /** - * @type {boolean} - */ - this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; - - /** - * @type {number} - */ - this.button = 'button' in eventDict ? eventDict['button'] : 0; - - /** - * @type {Node} - */ - this.relatedTarget = 'relatedTarget' in eventDict ? - eventDict['relatedTarget'] : null; - - // PointerEvent related properties - - /** - * @const - * @type {number} - */ - this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; - - /** - * @type {number} - */ - this.width = 'width' in eventDict ? eventDict['width'] : 0; - - /** - * @type {number} - */ - this.height = 'height' in eventDict ? eventDict['height'] : 0; - - /** - * @type {number} - */ - this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; - - /** - * @type {number} - */ - this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; - - /** - * @type {string} - */ - this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; - - /** - * @type {number} - */ - this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; - - /** - * @type {boolean} - */ - this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; - - // keep the semantics of preventDefault - if (originalEvent.preventDefault) { - this.preventDefault = function() { - originalEvent.preventDefault(); - }; - } -}; -ol.inherits(ol.pointer.PointerEvent, ol.events.Event); - - -/** - * @private - * @param {Object.<string, ?>} eventDict The event dictionary. - * @return {number} Button indicator. - */ -ol.pointer.PointerEvent.prototype.getButtons_ = function(eventDict) { - // According to the w3c spec, - // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button - // MouseEvent.button == 0 can mean either no mouse button depressed, or the - // left mouse button depressed. - // - // As of now, the only way to distinguish between the two states of - // MouseEvent.button is by using the deprecated MouseEvent.which property, as - // this maps mouse buttons to positive integers > 0, and uses 0 to mean that - // no mouse button is held. - // - // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation, - // but initMouseEvent does not expose an argument with which to set - // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set - // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations - // of app developers. - // - // The only way to propagate the correct state of MouseEvent.which and - // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0 - // is to call initMouseEvent with a buttonArg value of -1. - // - // This is fixed with DOM Level 4's use of buttons - var buttons; - if (eventDict.buttons || ol.pointer.PointerEvent.HAS_BUTTONS) { - buttons = eventDict.buttons; - } else { - switch (eventDict.which) { - case 1: buttons = 1; break; - case 2: buttons = 4; break; - case 3: buttons = 2; break; - default: buttons = 0; - } - } - return buttons; -}; - - -/** - * @private - * @param {Object.<string, ?>} eventDict The event dictionary. - * @param {number} buttons Button indicator. - * @return {number} The pressure. - */ -ol.pointer.PointerEvent.prototype.getPressure_ = function(eventDict, buttons) { - // Spec requires that pointers without pressure specified use 0.5 for down - // state and 0 for up state. - var pressure = 0; - if (eventDict.pressure) { - pressure = eventDict.pressure; - } else { - pressure = buttons ? 0.5 : 0; - } - return pressure; -}; - - -/** - * Is the `buttons` property supported? - * @type {boolean} - */ -ol.pointer.PointerEvent.HAS_BUTTONS = false; - - -/** - * Checks if the `buttons` property is supported. - */ -(function() { - try { - var ev = new MouseEvent('click', {buttons: 1}); - ol.pointer.PointerEvent.HAS_BUTTONS = ev.buttons === 1; - } catch (e) { - // pass - } -})(); - -// Based on https://github.com/Polymer/PointerEvents - -// Copyright (c) 2013 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.pointer.TouchSource'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.pointer.EventSource'); -goog.require('ol.pointer.MouseSource'); - - -/** - * @constructor - * @param {ol.pointer.PointerEventHandler} dispatcher The event handler. - * @param {ol.pointer.MouseSource} mouseSource Mouse source. - * @extends {ol.pointer.EventSource} - */ -ol.pointer.TouchSource = function(dispatcher, mouseSource) { - var mapping = { - 'touchstart': this.touchstart, - 'touchmove': this.touchmove, - 'touchend': this.touchend, - 'touchcancel': this.touchcancel - }; - ol.pointer.EventSource.call(this, dispatcher, mapping); - - /** - * @const - * @type {!Object.<string, Event|Object>} - */ - this.pointerMap = dispatcher.pointerMap; - - /** - * @const - * @type {ol.pointer.MouseSource} - */ - this.mouseSource = mouseSource; - - /** - * @private - * @type {number|undefined} - */ - this.firstTouchId_ = undefined; - - /** - * @private - * @type {number} - */ - this.clickCount_ = 0; - - /** - * @private - * @type {number|undefined} - */ - this.resetId_ = undefined; -}; -ol.inherits(ol.pointer.TouchSource, ol.pointer.EventSource); - - -/** - * Mouse event timeout: This should be long enough to - * ignore compat mouse events made by touch. - * @const - * @type {number} - */ -ol.pointer.TouchSource.DEDUP_TIMEOUT = 2500; - - -/** - * @const - * @type {number} - */ -ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT = 200; - - -/** - * @const - * @type {string} - */ -ol.pointer.TouchSource.POINTER_TYPE = 'touch'; - - -/** - * @private - * @param {Touch} inTouch The in touch. - * @return {boolean} True, if this is the primary touch. - */ -ol.pointer.TouchSource.prototype.isPrimaryTouch_ = function(inTouch) { - return this.firstTouchId_ === inTouch.identifier; -}; - - -/** - * Set primary touch if there are no pointers, or the only pointer is the mouse. - * @param {Touch} inTouch The in touch. - * @private - */ -ol.pointer.TouchSource.prototype.setPrimaryTouch_ = function(inTouch) { - var count = Object.keys(this.pointerMap).length; - if (count === 0 || (count === 1 && - ol.pointer.MouseSource.POINTER_ID.toString() in this.pointerMap)) { - this.firstTouchId_ = inTouch.identifier; - this.cancelResetClickCount_(); - } -}; - - -/** - * @private - * @param {Object} inPointer The in pointer object. - */ -ol.pointer.TouchSource.prototype.removePrimaryPointer_ = function(inPointer) { - if (inPointer.isPrimary) { - this.firstTouchId_ = undefined; - this.resetClickCount_(); - } -}; - - -/** - * @private - */ -ol.pointer.TouchSource.prototype.resetClickCount_ = function() { - this.resetId_ = setTimeout( - this.resetClickCountHandler_.bind(this), - ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT); -}; - - -/** - * @private - */ -ol.pointer.TouchSource.prototype.resetClickCountHandler_ = function() { - this.clickCount_ = 0; - this.resetId_ = undefined; -}; - - -/** - * @private - */ -ol.pointer.TouchSource.prototype.cancelResetClickCount_ = function() { - if (this.resetId_ !== undefined) { - clearTimeout(this.resetId_); - } -}; - - -/** - * @private - * @param {Event} browserEvent Browser event - * @param {Touch} inTouch Touch event - * @return {Object} A pointer object. - */ -ol.pointer.TouchSource.prototype.touchToPointer_ = function(browserEvent, inTouch) { - var e = this.dispatcher.cloneEvent(browserEvent, inTouch); - // Spec specifies that pointerId 1 is reserved for Mouse. - // Touch identifiers can start at 0. - // Add 2 to the touch identifier for compatibility. - e.pointerId = inTouch.identifier + 2; - // TODO: check if this is necessary? - //e.target = findTarget(e); - e.bubbles = true; - e.cancelable = true; - e.detail = this.clickCount_; - e.button = 0; - e.buttons = 1; - e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; - e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; - e.pressure = inTouch.webkitForce || inTouch.force || 0.5; - e.isPrimary = this.isPrimaryTouch_(inTouch); - e.pointerType = ol.pointer.TouchSource.POINTER_TYPE; - - // make sure that the properties that are different for - // each `Touch` object are not copied from the BrowserEvent object - e.clientX = inTouch.clientX; - e.clientY = inTouch.clientY; - e.screenX = inTouch.screenX; - e.screenY = inTouch.screenY; - - return e; -}; - - -/** - * @private - * @param {Event} inEvent Touch event - * @param {function(Event, Object)} inFunction In function. - */ -ol.pointer.TouchSource.prototype.processTouches_ = function(inEvent, inFunction) { - var touches = Array.prototype.slice.call( - inEvent.changedTouches); - var count = touches.length; - function preventDefault() { - inEvent.preventDefault(); - } - var i, pointer; - for (i = 0; i < count; ++i) { - pointer = this.touchToPointer_(inEvent, touches[i]); - // forward touch preventDefaults - pointer.preventDefault = preventDefault; - inFunction.call(this, inEvent, pointer); - } -}; - - -/** - * @private - * @param {TouchList} touchList The touch list. - * @param {number} searchId Search identifier. - * @return {boolean} True, if the `Touch` with the given id is in the list. - */ -ol.pointer.TouchSource.prototype.findTouch_ = function(touchList, searchId) { - var l = touchList.length; - var touch; - for (var i = 0; i < l; i++) { - touch = touchList[i]; - if (touch.identifier === searchId) { - return true; - } - } - return false; -}; - - -/** - * In some instances, a touchstart can happen without a touchend. This - * leaves the pointermap in a broken state. - * Therefore, on every touchstart, we remove the touches that did not fire a - * touchend event. - * To keep state globally consistent, we fire a pointercancel for - * this "abandoned" touch - * - * @private - * @param {Event} inEvent The in event. - */ -ol.pointer.TouchSource.prototype.vacuumTouches_ = function(inEvent) { - var touchList = inEvent.touches; - // pointerMap.getCount() should be < touchList.length here, - // as the touchstart has not been processed yet. - var keys = Object.keys(this.pointerMap); - var count = keys.length; - if (count >= touchList.length) { - var d = []; - var i, key, value; - for (i = 0; i < count; ++i) { - key = keys[i]; - value = this.pointerMap[key]; - // Never remove pointerId == 1, which is mouse. - // Touch identifiers are 2 smaller than their pointerId, which is the - // index in pointermap. - if (key != ol.pointer.MouseSource.POINTER_ID && - !this.findTouch_(touchList, key - 2)) { - d.push(value.out); - } - } - for (i = 0; i < d.length; ++i) { - this.cancelOut_(inEvent, d[i]); - } - } -}; - - -/** - * Handler for `touchstart`, triggers `pointerover`, - * `pointerenter` and `pointerdown` events. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.TouchSource.prototype.touchstart = function(inEvent) { - this.vacuumTouches_(inEvent); - this.setPrimaryTouch_(inEvent.changedTouches[0]); - this.dedupSynthMouse_(inEvent); - this.clickCount_++; - this.processTouches_(inEvent, this.overDown_); -}; - - -/** - * @private - * @param {Event} browserEvent The event. - * @param {Object} inPointer The in pointer object. - */ -ol.pointer.TouchSource.prototype.overDown_ = function(browserEvent, inPointer) { - this.pointerMap[inPointer.pointerId] = { - target: inPointer.target, - out: inPointer, - outTarget: inPointer.target - }; - this.dispatcher.over(inPointer, browserEvent); - this.dispatcher.enter(inPointer, browserEvent); - this.dispatcher.down(inPointer, browserEvent); -}; - - -/** - * Handler for `touchmove`. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.TouchSource.prototype.touchmove = function(inEvent) { - inEvent.preventDefault(); - this.processTouches_(inEvent, this.moveOverOut_); -}; - - -/** - * @private - * @param {Event} browserEvent The event. - * @param {Object} inPointer The in pointer. - */ -ol.pointer.TouchSource.prototype.moveOverOut_ = function(browserEvent, inPointer) { - var event = inPointer; - var pointer = this.pointerMap[event.pointerId]; - // a finger drifted off the screen, ignore it - if (!pointer) { - return; - } - var outEvent = pointer.out; - var outTarget = pointer.outTarget; - this.dispatcher.move(event, browserEvent); - if (outEvent && outTarget !== event.target) { - outEvent.relatedTarget = event.target; - event.relatedTarget = outTarget; - // recover from retargeting by shadow - outEvent.target = outTarget; - if (event.target) { - this.dispatcher.leaveOut(outEvent, browserEvent); - this.dispatcher.enterOver(event, browserEvent); - } else { - // clean up case when finger leaves the screen - event.target = outTarget; - event.relatedTarget = null; - this.cancelOut_(browserEvent, event); - } - } - pointer.out = event; - pointer.outTarget = event.target; -}; - - -/** - * Handler for `touchend`, triggers `pointerup`, - * `pointerout` and `pointerleave` events. - * - * @param {Event} inEvent The event. - */ -ol.pointer.TouchSource.prototype.touchend = function(inEvent) { - this.dedupSynthMouse_(inEvent); - this.processTouches_(inEvent, this.upOut_); -}; - - -/** - * @private - * @param {Event} browserEvent An event. - * @param {Object} inPointer The inPointer object. - */ -ol.pointer.TouchSource.prototype.upOut_ = function(browserEvent, inPointer) { - this.dispatcher.up(inPointer, browserEvent); - this.dispatcher.out(inPointer, browserEvent); - this.dispatcher.leave(inPointer, browserEvent); - this.cleanUpPointer_(inPointer); -}; - - -/** - * Handler for `touchcancel`, triggers `pointercancel`, - * `pointerout` and `pointerleave` events. - * - * @param {Event} inEvent The in event. - */ -ol.pointer.TouchSource.prototype.touchcancel = function(inEvent) { - this.processTouches_(inEvent, this.cancelOut_); -}; - - -/** - * @private - * @param {Event} browserEvent The event. - * @param {Object} inPointer The in pointer. - */ -ol.pointer.TouchSource.prototype.cancelOut_ = function(browserEvent, inPointer) { - this.dispatcher.cancel(inPointer, browserEvent); - this.dispatcher.out(inPointer, browserEvent); - this.dispatcher.leave(inPointer, browserEvent); - this.cleanUpPointer_(inPointer); -}; - - -/** - * @private - * @param {Object} inPointer The inPointer object. - */ -ol.pointer.TouchSource.prototype.cleanUpPointer_ = function(inPointer) { - delete this.pointerMap[inPointer.pointerId]; - this.removePrimaryPointer_(inPointer); -}; - - -/** - * Prevent synth mouse events from creating pointer events. - * - * @private - * @param {Event} inEvent The in event. - */ -ol.pointer.TouchSource.prototype.dedupSynthMouse_ = function(inEvent) { - var lts = this.mouseSource.lastTouches; - var t = inEvent.changedTouches[0]; - // only the primary finger will synth mouse events - if (this.isPrimaryTouch_(t)) { - // remember x/y of last touch - var lt = [t.clientX, t.clientY]; - lts.push(lt); - - setTimeout(function() { - // remove touch after timeout - ol.array.remove(lts, lt); - }, ol.pointer.TouchSource.DEDUP_TIMEOUT); - } -}; - -// Based on https://github.com/Polymer/PointerEvents - -// Copyright (c) 2013 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.pointer.PointerEventHandler'); - -goog.require('ol'); -goog.require('ol.events'); -goog.require('ol.events.EventTarget'); - -goog.require('ol.has'); -goog.require('ol.pointer.EventType'); -goog.require('ol.pointer.MouseSource'); -goog.require('ol.pointer.MsSource'); -goog.require('ol.pointer.NativeSource'); -goog.require('ol.pointer.PointerEvent'); -goog.require('ol.pointer.TouchSource'); - - -/** - * @constructor - * @extends {ol.events.EventTarget} - * @param {Element|HTMLDocument} element Viewport element. - */ -ol.pointer.PointerEventHandler = function(element) { - ol.events.EventTarget.call(this); - - /** - * @const - * @private - * @type {Element|HTMLDocument} - */ - this.element_ = element; - - /** - * @const - * @type {!Object.<string, Event|Object>} - */ - this.pointerMap = {}; - - /** - * @type {Object.<string, function(Event)>} - * @private - */ - this.eventMap_ = {}; - - /** - * @type {Array.<ol.pointer.EventSource>} - * @private - */ - this.eventSourceList_ = []; - - this.registerSources(); -}; -ol.inherits(ol.pointer.PointerEventHandler, ol.events.EventTarget); - - -/** - * Set up the event sources (mouse, touch and native pointers) - * that generate pointer events. - */ -ol.pointer.PointerEventHandler.prototype.registerSources = function() { - if (ol.has.POINTER) { - this.registerSource('native', new ol.pointer.NativeSource(this)); - } else if (ol.has.MSPOINTER) { - this.registerSource('ms', new ol.pointer.MsSource(this)); - } else { - var mouseSource = new ol.pointer.MouseSource(this); - this.registerSource('mouse', mouseSource); - - if (ol.has.TOUCH) { - this.registerSource('touch', - new ol.pointer.TouchSource(this, mouseSource)); - } - } - - // register events on the viewport element - this.register_(); -}; - - -/** - * Add a new event source that will generate pointer events. - * - * @param {string} name A name for the event source - * @param {ol.pointer.EventSource} source The source event. - */ -ol.pointer.PointerEventHandler.prototype.registerSource = function(name, source) { - var s = source; - var newEvents = s.getEvents(); - - if (newEvents) { - newEvents.forEach(function(e) { - var handler = s.getHandlerForEvent(e); - - if (handler) { - this.eventMap_[e] = handler.bind(s); - } - }, this); - this.eventSourceList_.push(s); - } -}; - - -/** - * Set up the events for all registered event sources. - * @private - */ -ol.pointer.PointerEventHandler.prototype.register_ = function() { - var l = this.eventSourceList_.length; - var eventSource; - for (var i = 0; i < l; i++) { - eventSource = this.eventSourceList_[i]; - this.addEvents_(eventSource.getEvents()); - } -}; - - -/** - * Remove all registered events. - * @private - */ -ol.pointer.PointerEventHandler.prototype.unregister_ = function() { - var l = this.eventSourceList_.length; - var eventSource; - for (var i = 0; i < l; i++) { - eventSource = this.eventSourceList_[i]; - this.removeEvents_(eventSource.getEvents()); - } -}; - - -/** - * Calls the right handler for a new event. - * @private - * @param {Event} inEvent Browser event. - */ -ol.pointer.PointerEventHandler.prototype.eventHandler_ = function(inEvent) { - var type = inEvent.type; - var handler = this.eventMap_[type]; - if (handler) { - handler(inEvent); - } -}; - - -/** - * Setup listeners for the given events. - * @private - * @param {Array.<string>} events List of events. - */ -ol.pointer.PointerEventHandler.prototype.addEvents_ = function(events) { - events.forEach(function(eventName) { - ol.events.listen(this.element_, eventName, this.eventHandler_, this); - }, this); -}; - - -/** - * Unregister listeners for the given events. - * @private - * @param {Array.<string>} events List of events. - */ -ol.pointer.PointerEventHandler.prototype.removeEvents_ = function(events) { - events.forEach(function(e) { - ol.events.unlisten(this.element_, e, this.eventHandler_, this); - }, this); -}; - - -/** - * Returns a snapshot of inEvent, with writable properties. - * - * @param {Event} event Browser event. - * @param {Event|Touch} inEvent An event that contains - * properties to copy. - * @return {Object} An object containing shallow copies of - * `inEvent`'s properties. - */ -ol.pointer.PointerEventHandler.prototype.cloneEvent = function(event, inEvent) { - var eventCopy = {}, p; - for (var i = 0, ii = ol.pointer.PointerEventHandler.CLONE_PROPS.length; i < ii; i++) { - p = ol.pointer.PointerEventHandler.CLONE_PROPS[i][0]; - eventCopy[p] = event[p] || inEvent[p] || ol.pointer.PointerEventHandler.CLONE_PROPS[i][1]; - } - - return eventCopy; -}; - - -// EVENTS - - -/** - * Triggers a 'pointerdown' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.down = function(data, event) { - this.fireEvent(ol.pointer.EventType.POINTERDOWN, data, event); -}; - - -/** - * Triggers a 'pointermove' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.move = function(data, event) { - this.fireEvent(ol.pointer.EventType.POINTERMOVE, data, event); -}; - - -/** - * Triggers a 'pointerup' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.up = function(data, event) { - this.fireEvent(ol.pointer.EventType.POINTERUP, data, event); -}; - - -/** - * Triggers a 'pointerenter' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.enter = function(data, event) { - data.bubbles = false; - this.fireEvent(ol.pointer.EventType.POINTERENTER, data, event); -}; - - -/** - * Triggers a 'pointerleave' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.leave = function(data, event) { - data.bubbles = false; - this.fireEvent(ol.pointer.EventType.POINTERLEAVE, data, event); -}; - - -/** - * Triggers a 'pointerover' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.over = function(data, event) { - data.bubbles = true; - this.fireEvent(ol.pointer.EventType.POINTEROVER, data, event); -}; - - -/** - * Triggers a 'pointerout' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.out = function(data, event) { - data.bubbles = true; - this.fireEvent(ol.pointer.EventType.POINTEROUT, data, event); -}; - - -/** - * Triggers a 'pointercancel' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.cancel = function(data, event) { - this.fireEvent(ol.pointer.EventType.POINTERCANCEL, data, event); -}; - - -/** - * Triggers a combination of 'pointerout' and 'pointerleave' events. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.leaveOut = function(data, event) { - this.out(data, event); - if (!this.contains_(data.target, data.relatedTarget)) { - this.leave(data, event); - } -}; - - -/** - * Triggers a combination of 'pointerover' and 'pointerevents' events. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.enterOver = function(data, event) { - this.over(data, event); - if (!this.contains_(data.target, data.relatedTarget)) { - this.enter(data, event); - } -}; - - -/** - * @private - * @param {Element} container The container element. - * @param {Element} contained The contained element. - * @return {boolean} Returns true if the container element - * contains the other element. - */ -ol.pointer.PointerEventHandler.prototype.contains_ = function(container, contained) { - if (!container || !contained) { - return false; - } - return container.contains(contained); -}; - - -// EVENT CREATION AND TRACKING -/** - * Creates a new Event of type `inType`, based on the information in - * `data`. - * - * @param {string} inType A string representing the type of event to create. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - * @return {ol.pointer.PointerEvent} A PointerEvent of type `inType`. - */ -ol.pointer.PointerEventHandler.prototype.makeEvent = function(inType, data, event) { - return new ol.pointer.PointerEvent(inType, event, data); -}; - - -/** - * Make and dispatch an event in one call. - * @param {string} inType A string representing the type of event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -ol.pointer.PointerEventHandler.prototype.fireEvent = function(inType, data, event) { - var e = this.makeEvent(inType, data, event); - this.dispatchEvent(e); -}; - - -/** - * Creates a pointer event from a native pointer event - * and dispatches this event. - * @param {Event} event A platform event with a target. - */ -ol.pointer.PointerEventHandler.prototype.fireNativeEvent = function(event) { - var e = this.makeEvent(event.type, event, event); - this.dispatchEvent(e); -}; - - -/** - * Wrap a native mouse event into a pointer event. - * This proxy method is required for the legacy IE support. - * @param {string} eventType The pointer event type. - * @param {Event} event The event. - * @return {ol.pointer.PointerEvent} The wrapped event. - */ -ol.pointer.PointerEventHandler.prototype.wrapMouseEvent = function(eventType, event) { - var pointerEvent = this.makeEvent( - eventType, ol.pointer.MouseSource.prepareEvent(event, this), event); - return pointerEvent; -}; - - -/** - * @inheritDoc - */ -ol.pointer.PointerEventHandler.prototype.disposeInternal = function() { - this.unregister_(); - ol.events.EventTarget.prototype.disposeInternal.call(this); -}; - - -/** - * Properties to copy when cloning an event, with default values. - * @type {Array.<Array>} - */ -ol.pointer.PointerEventHandler.CLONE_PROPS = [ - // MouseEvent - ['bubbles', false], - ['cancelable', false], - ['view', null], - ['detail', null], - ['screenX', 0], - ['screenY', 0], - ['clientX', 0], - ['clientY', 0], - ['ctrlKey', false], - ['altKey', false], - ['shiftKey', false], - ['metaKey', false], - ['button', 0], - ['relatedTarget', null], - // DOM Level 3 - ['buttons', 0], - // PointerEvent - ['pointerId', 0], - ['width', 0], - ['height', 0], - ['pressure', 0], - ['tiltX', 0], - ['tiltY', 0], - ['pointerType', ''], - ['hwTimestamp', 0], - ['isPrimary', false], - // event instance - ['type', ''], - ['target', null], - ['currentTarget', null], - ['which', 0] -]; - -goog.provide('ol.MapBrowserEventHandler'); - -goog.require('ol'); -goog.require('ol.has'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.MapBrowserPointerEvent'); -goog.require('ol.events'); -goog.require('ol.events.EventTarget'); -goog.require('ol.pointer.EventType'); -goog.require('ol.pointer.PointerEventHandler'); - - -/** - * @param {ol.Map} map The map with the viewport to listen to events on. - * @param {number|undefined} moveTolerance The minimal distance the pointer must travel to trigger a move. - * @constructor - * @extends {ol.events.EventTarget} - */ -ol.MapBrowserEventHandler = function(map, moveTolerance) { - - ol.events.EventTarget.call(this); - - /** - * This is the element that we will listen to the real events on. - * @type {ol.Map} - * @private - */ - this.map_ = map; - - /** - * @type {number} - * @private - */ - this.clickTimeoutId_ = 0; - - /** - * @type {boolean} - * @private - */ - this.dragging_ = false; - - /** - * @type {!Array.<ol.EventsKey>} - * @private - */ - this.dragListenerKeys_ = []; - - /** - * @type {number} - * @private - */ - this.moveTolerance_ = moveTolerance ? - moveTolerance * ol.has.DEVICE_PIXEL_RATIO : ol.has.DEVICE_PIXEL_RATIO; - - /** - * The most recent "down" type event (or null if none have occurred). - * Set on pointerdown. - * @type {ol.pointer.PointerEvent} - * @private - */ - this.down_ = null; - - var element = this.map_.getViewport(); - - /** - * @type {number} - * @private - */ - this.activePointers_ = 0; - - /** - * @type {!Object.<number, boolean>} - * @private - */ - this.trackedTouches_ = {}; - - /** - * Event handler which generates pointer events for - * the viewport element. - * - * @type {ol.pointer.PointerEventHandler} - * @private - */ - this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element); - - /** - * Event handler which generates pointer events for - * the document (used when dragging). - * - * @type {ol.pointer.PointerEventHandler} - * @private - */ - this.documentPointerEventHandler_ = null; - - /** - * @type {?ol.EventsKey} - * @private - */ - this.pointerdownListenerKey_ = ol.events.listen(this.pointerEventHandler_, - ol.pointer.EventType.POINTERDOWN, - this.handlePointerDown_, this); - - /** - * @type {?ol.EventsKey} - * @private - */ - this.relayedListenerKey_ = ol.events.listen(this.pointerEventHandler_, - ol.pointer.EventType.POINTERMOVE, - this.relayEvent_, this); - -}; -ol.inherits(ol.MapBrowserEventHandler, ol.events.EventTarget); - - -/** - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @private - */ -ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) { - var newEvent = new ol.MapBrowserPointerEvent( - ol.MapBrowserEventType.CLICK, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - if (this.clickTimeoutId_ !== 0) { - // double-click - clearTimeout(this.clickTimeoutId_); - this.clickTimeoutId_ = 0; - newEvent = new ol.MapBrowserPointerEvent( - ol.MapBrowserEventType.DBLCLICK, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - } else { - // click - this.clickTimeoutId_ = setTimeout(function() { - this.clickTimeoutId_ = 0; - var newEvent = new ol.MapBrowserPointerEvent( - ol.MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - }.bind(this), 250); - } -}; - - -/** - * Keeps track on how many pointers are currently active. - * - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @private - */ -ol.MapBrowserEventHandler.prototype.updateActivePointers_ = function(pointerEvent) { - var event = pointerEvent; - - if (event.type == ol.MapBrowserEventType.POINTERUP || - event.type == ol.MapBrowserEventType.POINTERCANCEL) { - delete this.trackedTouches_[event.pointerId]; - } else if (event.type == ol.MapBrowserEventType.POINTERDOWN) { - this.trackedTouches_[event.pointerId] = true; - } - this.activePointers_ = Object.keys(this.trackedTouches_).length; -}; - - -/** - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @private - */ -ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) { - this.updateActivePointers_(pointerEvent); - var newEvent = new ol.MapBrowserPointerEvent( - ol.MapBrowserEventType.POINTERUP, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - - // We emulate click events on left mouse button click, touch contact, and pen - // contact. isMouseActionButton returns true in these cases (evt.button is set - // to 0). - // See http://www.w3.org/TR/pointerevents/#button-states - if (!this.dragging_ && this.isMouseActionButton_(pointerEvent)) { - this.emulateClick_(this.down_); - } - - if (this.activePointers_ === 0) { - this.dragListenerKeys_.forEach(ol.events.unlistenByKey); - this.dragListenerKeys_.length = 0; - this.dragging_ = false; - this.down_ = null; - this.documentPointerEventHandler_.dispose(); - this.documentPointerEventHandler_ = null; - } -}; - - -/** - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @return {boolean} If the left mouse button was pressed. - * @private - */ -ol.MapBrowserEventHandler.prototype.isMouseActionButton_ = function(pointerEvent) { - return pointerEvent.button === 0; -}; - - -/** - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @private - */ -ol.MapBrowserEventHandler.prototype.handlePointerDown_ = function(pointerEvent) { - this.updateActivePointers_(pointerEvent); - var newEvent = new ol.MapBrowserPointerEvent( - ol.MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - - this.down_ = pointerEvent; - - if (this.dragListenerKeys_.length === 0) { - /* Set up a pointer event handler on the `document`, - * which is required when the pointer is moved outside - * the viewport when dragging. - */ - this.documentPointerEventHandler_ = - new ol.pointer.PointerEventHandler(document); - - this.dragListenerKeys_.push( - ol.events.listen(this.documentPointerEventHandler_, - ol.MapBrowserEventType.POINTERMOVE, - this.handlePointerMove_, this), - ol.events.listen(this.documentPointerEventHandler_, - ol.MapBrowserEventType.POINTERUP, - this.handlePointerUp_, this), - /* Note that the listener for `pointercancel is set up on - * `pointerEventHandler_` and not `documentPointerEventHandler_` like - * the `pointerup` and `pointermove` listeners. - * - * The reason for this is the following: `TouchSource.vacuumTouches_()` - * issues `pointercancel` events, when there was no `touchend` for a - * `touchstart`. Now, let's say a first `touchstart` is registered on - * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up. - * But `documentPointerEventHandler_` doesn't know about the first - * `touchstart`. If there is no `touchend` for the `touchstart`, we can - * only receive a `touchcancel` from `pointerEventHandler_`, because it is - * only registered there. - */ - ol.events.listen(this.pointerEventHandler_, - ol.MapBrowserEventType.POINTERCANCEL, - this.handlePointerUp_, this) - ); - } -}; - - -/** - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @private - */ -ol.MapBrowserEventHandler.prototype.handlePointerMove_ = function(pointerEvent) { - // Between pointerdown and pointerup, pointermove events are triggered. - // To avoid a 'false' touchmove event to be dispatched, we test if the pointer - // moved a significant distance. - if (this.isMoving_(pointerEvent)) { - this.dragging_ = true; - var newEvent = new ol.MapBrowserPointerEvent( - ol.MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent, - this.dragging_); - this.dispatchEvent(newEvent); - } - - // Some native android browser triggers mousemove events during small period - // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or - // https://code.google.com/p/android/issues/detail?id=19827 - // ex: Galaxy Tab P3110 + Android 4.1.1 - pointerEvent.preventDefault(); -}; - - -/** - * Wrap and relay a pointer event. Note that this requires that the type - * string for the MapBrowserPointerEvent matches the PointerEvent type. - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @private - */ -ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) { - var dragging = !!(this.down_ && this.isMoving_(pointerEvent)); - this.dispatchEvent(new ol.MapBrowserPointerEvent( - pointerEvent.type, this.map_, pointerEvent, dragging)); -}; - - -/** - * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. - * @return {boolean} Is moving. - * @private - */ -ol.MapBrowserEventHandler.prototype.isMoving_ = function(pointerEvent) { - return Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ || - Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_; -}; - - -/** - * @inheritDoc - */ -ol.MapBrowserEventHandler.prototype.disposeInternal = function() { - if (this.relayedListenerKey_) { - ol.events.unlistenByKey(this.relayedListenerKey_); - this.relayedListenerKey_ = null; - } - if (this.pointerdownListenerKey_) { - ol.events.unlistenByKey(this.pointerdownListenerKey_); - this.pointerdownListenerKey_ = null; - } - - this.dragListenerKeys_.forEach(ol.events.unlistenByKey); - this.dragListenerKeys_.length = 0; - - if (this.documentPointerEventHandler_) { - this.documentPointerEventHandler_.dispose(); - this.documentPointerEventHandler_ = null; - } - if (this.pointerEventHandler_) { - this.pointerEventHandler_.dispose(); - this.pointerEventHandler_ = null; - } - ol.events.EventTarget.prototype.disposeInternal.call(this); -}; - -goog.provide('ol.MapProperty'); - -/** - * @enum {string} - */ -ol.MapProperty = { - LAYERGROUP: 'layergroup', - SIZE: 'size', - TARGET: 'target', - VIEW: 'view' -}; - -goog.provide('ol.TileState'); - -/** - * @enum {number} - */ -ol.TileState = { - IDLE: 0, - LOADING: 1, - LOADED: 2, - ERROR: 3, - EMPTY: 4, - ABORT: 5 -}; - -goog.provide('ol.structs.PriorityQueue'); - -goog.require('ol.asserts'); -goog.require('ol.obj'); - - -/** - * Priority queue. - * - * The implementation is inspired from the Closure Library's Heap class and - * Python's heapq module. - * - * @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html - * @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py - * - * @constructor - * @param {function(T): number} priorityFunction Priority function. - * @param {function(T): string} keyFunction Key function. - * @struct - * @template T - */ -ol.structs.PriorityQueue = function(priorityFunction, keyFunction) { - - /** - * @type {function(T): number} - * @private - */ - this.priorityFunction_ = priorityFunction; - - /** - * @type {function(T): string} - * @private - */ - this.keyFunction_ = keyFunction; - - /** - * @type {Array.<T>} - * @private - */ - this.elements_ = []; - - /** - * @type {Array.<number>} - * @private - */ - this.priorities_ = []; - - /** - * @type {Object.<string, boolean>} - * @private - */ - this.queuedElements_ = {}; - -}; - - -/** - * @const - * @type {number} - */ -ol.structs.PriorityQueue.DROP = Infinity; - - -/** - * FIXME empty description for jsdoc - */ -ol.structs.PriorityQueue.prototype.clear = function() { - this.elements_.length = 0; - this.priorities_.length = 0; - ol.obj.clear(this.queuedElements_); -}; - - -/** - * Remove and return the highest-priority element. O(log N). - * @return {T} Element. - */ -ol.structs.PriorityQueue.prototype.dequeue = function() { - var elements = this.elements_; - var priorities = this.priorities_; - var element = elements[0]; - if (elements.length == 1) { - elements.length = 0; - priorities.length = 0; - } else { - elements[0] = elements.pop(); - priorities[0] = priorities.pop(); - this.siftUp_(0); - } - var elementKey = this.keyFunction_(element); - delete this.queuedElements_[elementKey]; - return element; -}; - - -/** - * Enqueue an element. O(log N). - * @param {T} element Element. - * @return {boolean} The element was added to the queue. - */ -ol.structs.PriorityQueue.prototype.enqueue = function(element) { - ol.asserts.assert(!(this.keyFunction_(element) in this.queuedElements_), - 31); // Tried to enqueue an `element` that was already added to the queue - var priority = this.priorityFunction_(element); - if (priority != ol.structs.PriorityQueue.DROP) { - this.elements_.push(element); - this.priorities_.push(priority); - this.queuedElements_[this.keyFunction_(element)] = true; - this.siftDown_(0, this.elements_.length - 1); - return true; - } - return false; -}; - - -/** - * @return {number} Count. - */ -ol.structs.PriorityQueue.prototype.getCount = function() { - return this.elements_.length; -}; - - -/** - * Gets the index of the left child of the node at the given index. - * @param {number} index The index of the node to get the left child for. - * @return {number} The index of the left child. - * @private - */ -ol.structs.PriorityQueue.prototype.getLeftChildIndex_ = function(index) { - return index * 2 + 1; -}; - - -/** - * Gets the index of the right child of the node at the given index. - * @param {number} index The index of the node to get the right child for. - * @return {number} The index of the right child. - * @private - */ -ol.structs.PriorityQueue.prototype.getRightChildIndex_ = function(index) { - return index * 2 + 2; -}; - - -/** - * Gets the index of the parent of the node at the given index. - * @param {number} index The index of the node to get the parent for. - * @return {number} The index of the parent. - * @private - */ -ol.structs.PriorityQueue.prototype.getParentIndex_ = function(index) { - return (index - 1) >> 1; -}; - - -/** - * Make this a heap. O(N). - * @private - */ -ol.structs.PriorityQueue.prototype.heapify_ = function() { - var i; - for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) { - this.siftUp_(i); - } -}; - - -/** - * @return {boolean} Is empty. - */ -ol.structs.PriorityQueue.prototype.isEmpty = function() { - return this.elements_.length === 0; -}; - - -/** - * @param {string} key Key. - * @return {boolean} Is key queued. - */ -ol.structs.PriorityQueue.prototype.isKeyQueued = function(key) { - return key in this.queuedElements_; -}; - - -/** - * @param {T} element Element. - * @return {boolean} Is queued. - */ -ol.structs.PriorityQueue.prototype.isQueued = function(element) { - return this.isKeyQueued(this.keyFunction_(element)); -}; - - -/** - * @param {number} index The index of the node to move down. - * @private - */ -ol.structs.PriorityQueue.prototype.siftUp_ = function(index) { - var elements = this.elements_; - var priorities = this.priorities_; - var count = elements.length; - var element = elements[index]; - var priority = priorities[index]; - var startIndex = index; - - while (index < (count >> 1)) { - var lIndex = this.getLeftChildIndex_(index); - var rIndex = this.getRightChildIndex_(index); - - var smallerChildIndex = rIndex < count && - priorities[rIndex] < priorities[lIndex] ? - rIndex : lIndex; - - elements[index] = elements[smallerChildIndex]; - priorities[index] = priorities[smallerChildIndex]; - index = smallerChildIndex; - } - - elements[index] = element; - priorities[index] = priority; - this.siftDown_(startIndex, index); -}; - - -/** - * @param {number} startIndex The index of the root. - * @param {number} index The index of the node to move up. - * @private - */ -ol.structs.PriorityQueue.prototype.siftDown_ = function(startIndex, index) { - var elements = this.elements_; - var priorities = this.priorities_; - var element = elements[index]; - var priority = priorities[index]; - - while (index > startIndex) { - var parentIndex = this.getParentIndex_(index); - if (priorities[parentIndex] > priority) { - elements[index] = elements[parentIndex]; - priorities[index] = priorities[parentIndex]; - index = parentIndex; - } else { - break; - } - } - elements[index] = element; - priorities[index] = priority; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.structs.PriorityQueue.prototype.reprioritize = function() { - var priorityFunction = this.priorityFunction_; - var elements = this.elements_; - var priorities = this.priorities_; - var index = 0; - var n = elements.length; - var element, i, priority; - for (i = 0; i < n; ++i) { - element = elements[i]; - priority = priorityFunction(element); - if (priority == ol.structs.PriorityQueue.DROP) { - delete this.queuedElements_[this.keyFunction_(element)]; - } else { - priorities[index] = priority; - elements[index++] = element; - } - } - elements.length = index; - priorities.length = index; - this.heapify_(); -}; - -goog.provide('ol.TileQueue'); - -goog.require('ol'); -goog.require('ol.TileState'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.structs.PriorityQueue'); - - -/** - * @constructor - * @extends {ol.structs.PriorityQueue.<Array>} - * @param {ol.TilePriorityFunction} tilePriorityFunction - * Tile priority function. - * @param {function(): ?} tileChangeCallback - * Function called on each tile change event. - * @struct - */ -ol.TileQueue = function(tilePriorityFunction, tileChangeCallback) { - - ol.structs.PriorityQueue.call( - this, - /** - * @param {Array} element Element. - * @return {number} Priority. - */ - function(element) { - return tilePriorityFunction.apply(null, element); - }, - /** - * @param {Array} element Element. - * @return {string} Key. - */ - function(element) { - return /** @type {ol.Tile} */ (element[0]).getKey(); - }); - - /** - * @private - * @type {function(): ?} - */ - this.tileChangeCallback_ = tileChangeCallback; - - /** - * @private - * @type {number} - */ - this.tilesLoading_ = 0; - - /** - * @private - * @type {!Object.<string,boolean>} - */ - this.tilesLoadingKeys_ = {}; - -}; -ol.inherits(ol.TileQueue, ol.structs.PriorityQueue); - - -/** - * @inheritDoc - */ -ol.TileQueue.prototype.enqueue = function(element) { - var added = ol.structs.PriorityQueue.prototype.enqueue.call(this, element); - if (added) { - var tile = element[0]; - ol.events.listen(tile, ol.events.EventType.CHANGE, - this.handleTileChange, this); - } - return added; -}; - - -/** - * @return {number} Number of tiles loading. - */ -ol.TileQueue.prototype.getTilesLoading = function() { - return this.tilesLoading_; -}; - - -/** - * @param {ol.events.Event} event Event. - * @protected - */ -ol.TileQueue.prototype.handleTileChange = function(event) { - var tile = /** @type {ol.Tile} */ (event.target); - var state = tile.getState(); - if (state === ol.TileState.LOADED || state === ol.TileState.ERROR || - state === ol.TileState.EMPTY || state === ol.TileState.ABORT) { - ol.events.unlisten(tile, ol.events.EventType.CHANGE, - this.handleTileChange, this); - var tileKey = tile.getKey(); - if (tileKey in this.tilesLoadingKeys_) { - delete this.tilesLoadingKeys_[tileKey]; - --this.tilesLoading_; - } - this.tileChangeCallback_(); - } -}; - - -/** - * @param {number} maxTotalLoading Maximum number tiles to load simultaneously. - * @param {number} maxNewLoads Maximum number of new tiles to load. - */ -ol.TileQueue.prototype.loadMoreTiles = function(maxTotalLoading, maxNewLoads) { - var newLoads = 0; - var tile, tileKey; - while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && - this.getCount() > 0) { - tile = /** @type {ol.Tile} */ (this.dequeue()[0]); - tileKey = tile.getKey(); - if (tile.getState() === ol.TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) { - this.tilesLoadingKeys_[tileKey] = true; - ++this.tilesLoading_; - ++newLoads; - tile.load(); - } - } -}; - -goog.provide('ol.ResolutionConstraint'); - -goog.require('ol.array'); -goog.require('ol.math'); - - -/** - * @param {Array.<number>} resolutions Resolutions. - * @return {ol.ResolutionConstraintType} Zoom function. - */ -ol.ResolutionConstraint.createSnapToResolutions = function(resolutions) { - return ( - /** - * @param {number|undefined} resolution Resolution. - * @param {number} delta Delta. - * @param {number} direction Direction. - * @return {number|undefined} Resolution. - */ - function(resolution, delta, direction) { - if (resolution !== undefined) { - var z = - ol.array.linearFindNearest(resolutions, resolution, direction); - z = ol.math.clamp(z + delta, 0, resolutions.length - 1); - var index = Math.floor(z); - if (z != index && index < resolutions.length - 1) { - var power = resolutions[index] / resolutions[index + 1]; - return resolutions[index] / Math.pow(power, z - index); - } else { - return resolutions[index]; - } - } else { - return undefined; - } - }); -}; - - -/** - * @param {number} power Power. - * @param {number} maxResolution Maximum resolution. - * @param {number=} opt_maxLevel Maximum level. - * @return {ol.ResolutionConstraintType} Zoom function. - */ -ol.ResolutionConstraint.createSnapToPower = function(power, maxResolution, opt_maxLevel) { - return ( - /** - * @param {number|undefined} resolution Resolution. - * @param {number} delta Delta. - * @param {number} direction Direction. - * @return {number|undefined} Resolution. - */ - function(resolution, delta, direction) { - if (resolution !== undefined) { - var offset = -direction / 2 + 0.5; - var oldLevel = Math.floor( - Math.log(maxResolution / resolution) / Math.log(power) + offset); - var newLevel = Math.max(oldLevel + delta, 0); - if (opt_maxLevel !== undefined) { - newLevel = Math.min(newLevel, opt_maxLevel); - } - return maxResolution / Math.pow(power, newLevel); - } else { - return undefined; - } - }); -}; - -goog.provide('ol.RotationConstraint'); - -goog.require('ol.math'); - - -/** - * @param {number|undefined} rotation Rotation. - * @param {number} delta Delta. - * @return {number|undefined} Rotation. - */ -ol.RotationConstraint.disable = function(rotation, delta) { - if (rotation !== undefined) { - return 0; - } else { - return undefined; - } -}; - - -/** - * @param {number|undefined} rotation Rotation. - * @param {number} delta Delta. - * @return {number|undefined} Rotation. - */ -ol.RotationConstraint.none = function(rotation, delta) { - if (rotation !== undefined) { - return rotation + delta; - } else { - return undefined; - } -}; - - -/** - * @param {number} n N. - * @return {ol.RotationConstraintType} Rotation constraint. - */ -ol.RotationConstraint.createSnapToN = function(n) { - var theta = 2 * Math.PI / n; - return ( - /** - * @param {number|undefined} rotation Rotation. - * @param {number} delta Delta. - * @return {number|undefined} Rotation. - */ - function(rotation, delta) { - if (rotation !== undefined) { - rotation = Math.floor((rotation + delta) / theta + 0.5) * theta; - return rotation; - } else { - return undefined; - } - }); -}; - - -/** - * @param {number=} opt_tolerance Tolerance. - * @return {ol.RotationConstraintType} Rotation constraint. - */ -ol.RotationConstraint.createSnapToZero = function(opt_tolerance) { - var tolerance = opt_tolerance || ol.math.toRadians(5); - return ( - /** - * @param {number|undefined} rotation Rotation. - * @param {number} delta Delta. - * @return {number|undefined} Rotation. - */ - function(rotation, delta) { - if (rotation !== undefined) { - if (Math.abs(rotation + delta) <= tolerance) { - return 0; - } else { - return rotation + delta; - } - } else { - return undefined; - } - }); -}; - -goog.provide('ol.ViewHint'); - -/** - * @enum {number} - */ -ol.ViewHint = { - ANIMATING: 0, - INTERACTING: 1 -}; - -goog.provide('ol.ViewProperty'); - -/** - * @enum {string} - */ -ol.ViewProperty = { - CENTER: 'center', - RESOLUTION: 'resolution', - ROTATION: 'rotation' -}; - -goog.provide('ol.string'); - -/** - * @param {number} number Number to be formatted - * @param {number} width The desired width - * @param {number=} opt_precision Precision of the output string (i.e. number of decimal places) - * @returns {string} Formatted string -*/ -ol.string.padNumber = function(number, width, opt_precision) { - var numberString = opt_precision !== undefined ? number.toFixed(opt_precision) : '' + number; - var decimal = numberString.indexOf('.'); - decimal = decimal === -1 ? numberString.length : decimal; - return decimal > width ? numberString : new Array(1 + width - decimal).join('0') + numberString; -}; - -/** - * Adapted from https://github.com/omichelsen/compare-versions/blob/master/index.js - * @param {string|number} v1 First version - * @param {string|number} v2 Second version - * @returns {number} Value - */ -ol.string.compareVersions = function(v1, v2) { - var s1 = ('' + v1).split('.'); - var s2 = ('' + v2).split('.'); - - for (var i = 0; i < Math.max(s1.length, s2.length); i++) { - var n1 = parseInt(s1[i] || '0', 10); - var n2 = parseInt(s2[i] || '0', 10); - - if (n1 > n2) { - return 1; - } - if (n2 > n1) { - return -1; - } - } - - return 0; -}; - -goog.provide('ol.coordinate'); - -goog.require('ol.math'); -goog.require('ol.string'); - - -/** - * Add `delta` to `coordinate`. `coordinate` is modified in place and returned - * by the function. - * - * Example: - * - * var coord = [7.85, 47.983333]; - * ol.coordinate.add(coord, [-2, 4]); - * // coord is now [5.85, 51.983333] - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.Coordinate} delta Delta. - * @return {ol.Coordinate} The input coordinate adjusted by the given delta. - * @api - */ -ol.coordinate.add = function(coordinate, delta) { - coordinate[0] += delta[0]; - coordinate[1] += delta[1]; - return coordinate; -}; - - -/** - * Calculates the point closest to the passed coordinate on the passed circle. - * - * @param {ol.Coordinate} coordinate The coordinate. - * @param {ol.geom.Circle} circle The circle. - * @return {ol.Coordinate} Closest point on the circumference - */ -ol.coordinate.closestOnCircle = function(coordinate, circle) { - var r = circle.getRadius(); - var center = circle.getCenter(); - var x0 = center[0]; - var y0 = center[1]; - var x1 = coordinate[0]; - var y1 = coordinate[1]; - - var dx = x1 - x0; - var dy = y1 - y0; - if (dx === 0 && dy === 0) { - dx = 1; - } - var d = Math.sqrt(dx * dx + dy * dy); - - var x, y; - - x = x0 + r * dx / d; - y = y0 + r * dy / d; - - return [x, y]; -}; - - -/** - * Calculates the point closest to the passed coordinate on the passed segment. - * This is the foot of the perpendicular of the coordinate to the segment when - * the foot is on the segment, or the closest segment coordinate when the foot - * is outside the segment. - * - * @param {ol.Coordinate} coordinate The coordinate. - * @param {Array.<ol.Coordinate>} segment The two coordinates of the segment. - * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to - * the segment. - */ -ol.coordinate.closestOnSegment = function(coordinate, segment) { - var x0 = coordinate[0]; - var y0 = coordinate[1]; - var start = segment[0]; - var end = segment[1]; - var x1 = start[0]; - var y1 = start[1]; - var x2 = end[0]; - var y2 = end[1]; - var dx = x2 - x1; - var dy = y2 - y1; - var along = (dx === 0 && dy === 0) ? 0 : - ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0); - var x, y; - if (along <= 0) { - x = x1; - y = y1; - } else if (along >= 1) { - x = x2; - y = y2; - } else { - x = x1 + along * dx; - y = y1 + along * dy; - } - return [x, y]; -}; - - -/** - * Returns a {@link ol.CoordinateFormatType} function that can be used to format - * a {ol.Coordinate} to a string. - * - * Example without specifying the fractional digits: - * - * var coord = [7.85, 47.983333]; - * var stringifyFunc = ol.coordinate.createStringXY(); - * var out = stringifyFunc(coord); - * // out is now '8, 48' - * - * Example with explicitly specifying 2 fractional digits: - * - * var coord = [7.85, 47.983333]; - * var stringifyFunc = ol.coordinate.createStringXY(2); - * var out = stringifyFunc(coord); - * // out is now '7.85, 47.98' - * - * @param {number=} opt_fractionDigits The number of digits to include - * after the decimal point. Default is `0`. - * @return {ol.CoordinateFormatType} Coordinate format. - * @api - */ -ol.coordinate.createStringXY = function(opt_fractionDigits) { - return ( - /** - * @param {ol.Coordinate|undefined} coordinate Coordinate. - * @return {string} String XY. - */ - function(coordinate) { - return ol.coordinate.toStringXY(coordinate, opt_fractionDigits); - }); -}; - - -/** - * @param {string} hemispheres Hemispheres. - * @param {number} degrees Degrees. - * @param {number=} opt_fractionDigits The number of digits to include - * after the decimal point. Default is `0`. - * @return {string} String. - */ -ol.coordinate.degreesToStringHDMS = function(hemispheres, degrees, opt_fractionDigits) { - var normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180; - var x = Math.abs(3600 * normalizedDegrees); - var dflPrecision = opt_fractionDigits || 0; - var precision = Math.pow(10, dflPrecision); - - var deg = Math.floor(x / 3600); - var min = Math.floor((x - deg * 3600) / 60); - var sec = x - (deg * 3600) - (min * 60); - sec = Math.ceil(sec * precision) / precision; - - if (sec >= 60) { - sec = 0; - min += 1; - } - - if (min >= 60) { - min = 0; - deg += 1; - } - - return deg + '\u00b0 ' + ol.string.padNumber(min, 2) + '\u2032 ' + - ol.string.padNumber(sec, 2, dflPrecision) + '\u2033' + - (normalizedDegrees == 0 ? '' : ' ' + hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0)); -}; - - -/** - * Transforms the given {@link ol.Coordinate} to a string using the given string - * template. The strings `{x}` and `{y}` in the template will be replaced with - * the first and second coordinate values respectively. - * - * Example without specifying the fractional digits: - * - * var coord = [7.85, 47.983333]; - * var template = 'Coordinate is ({x}|{y}).'; - * var out = ol.coordinate.format(coord, template); - * // out is now 'Coordinate is (8|48).' - * - * Example explicitly specifying the fractional digits: - * - * var coord = [7.85, 47.983333]; - * var template = 'Coordinate is ({x}|{y}).'; - * var out = ol.coordinate.format(coord, template, 2); - * // out is now 'Coordinate is (7.85|47.98).' - * - * @param {ol.Coordinate|undefined} coordinate Coordinate. - * @param {string} template A template string with `{x}` and `{y}` placeholders - * that will be replaced by first and second coordinate values. - * @param {number=} opt_fractionDigits The number of digits to include - * after the decimal point. Default is `0`. - * @return {string} Formatted coordinate. - * @api - */ -ol.coordinate.format = function(coordinate, template, opt_fractionDigits) { - if (coordinate) { - return template - .replace('{x}', coordinate[0].toFixed(opt_fractionDigits)) - .replace('{y}', coordinate[1].toFixed(opt_fractionDigits)); - } else { - return ''; - } -}; - - -/** - * @param {ol.Coordinate} coordinate1 First coordinate. - * @param {ol.Coordinate} coordinate2 Second coordinate. - * @return {boolean} Whether the passed coordinates are equal. - */ -ol.coordinate.equals = function(coordinate1, coordinate2) { - var equals = true; - for (var i = coordinate1.length - 1; i >= 0; --i) { - if (coordinate1[i] != coordinate2[i]) { - equals = false; - break; - } - } - return equals; -}; - - -/** - * Rotate `coordinate` by `angle`. `coordinate` is modified in place and - * returned by the function. - * - * Example: - * - * var coord = [7.85, 47.983333]; - * var rotateRadians = Math.PI / 2; // 90 degrees - * ol.coordinate.rotate(coord, rotateRadians); - * // coord is now [-47.983333, 7.85] - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} angle Angle in radian. - * @return {ol.Coordinate} Coordinate. - * @api - */ -ol.coordinate.rotate = function(coordinate, angle) { - var cosAngle = Math.cos(angle); - var sinAngle = Math.sin(angle); - var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle; - var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle; - coordinate[0] = x; - coordinate[1] = y; - return coordinate; -}; - - -/** - * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned - * by the function. - * - * Example: - * - * var coord = [7.85, 47.983333]; - * var scale = 1.2; - * ol.coordinate.scale(coord, scale); - * // coord is now [9.42, 57.5799996] - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} scale Scale factor. - * @return {ol.Coordinate} Coordinate. - */ -ol.coordinate.scale = function(coordinate, scale) { - coordinate[0] *= scale; - coordinate[1] *= scale; - return coordinate; -}; - - -/** - * Subtract `delta` to `coordinate`. `coordinate` is modified in place and - * returned by the function. - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.Coordinate} delta Delta. - * @return {ol.Coordinate} Coordinate. - */ -ol.coordinate.sub = function(coordinate, delta) { - coordinate[0] -= delta[0]; - coordinate[1] -= delta[1]; - return coordinate; -}; - - -/** - * @param {ol.Coordinate} coord1 First coordinate. - * @param {ol.Coordinate} coord2 Second coordinate. - * @return {number} Squared distance between coord1 and coord2. - */ -ol.coordinate.squaredDistance = function(coord1, coord2) { - var dx = coord1[0] - coord2[0]; - var dy = coord1[1] - coord2[1]; - return dx * dx + dy * dy; -}; - - -/** - * @param {ol.Coordinate} coord1 First coordinate. - * @param {ol.Coordinate} coord2 Second coordinate. - * @return {number} Distance between coord1 and coord2. - */ -ol.coordinate.distance = function(coord1, coord2) { - return Math.sqrt(ol.coordinate.squaredDistance(coord1, coord2)); -}; - - -/** - * Calculate the squared distance from a coordinate to a line segment. - * - * @param {ol.Coordinate} coordinate Coordinate of the point. - * @param {Array.<ol.Coordinate>} segment Line segment (2 coordinates). - * @return {number} Squared distance from the point to the line segment. - */ -ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) { - return ol.coordinate.squaredDistance(coordinate, - ol.coordinate.closestOnSegment(coordinate, segment)); -}; - - -/** - * Format a geographic coordinate with the hemisphere, degrees, minutes, and - * seconds. - * - * Example without specifying fractional digits: - * - * var coord = [7.85, 47.983333]; - * var out = ol.coordinate.toStringHDMS(coord); - * // out is now '47° 58′ 60″ N 7° 50′ 60″ E' - * - * Example explicitly specifying 1 fractional digit: - * - * var coord = [7.85, 47.983333]; - * var out = ol.coordinate.toStringHDMS(coord, 1); - * // out is now '47° 58′ 60.0″ N 7° 50′ 60.0″ E' - * - * @param {ol.Coordinate|undefined} coordinate Coordinate. - * @param {number=} opt_fractionDigits The number of digits to include - * after the decimal point. Default is `0`. - * @return {string} Hemisphere, degrees, minutes and seconds. - * @api - */ -ol.coordinate.toStringHDMS = function(coordinate, opt_fractionDigits) { - if (coordinate) { - return ol.coordinate.degreesToStringHDMS('NS', coordinate[1], opt_fractionDigits) + ' ' + - ol.coordinate.degreesToStringHDMS('EW', coordinate[0], opt_fractionDigits); - } else { - return ''; - } -}; - - -/** - * Format a coordinate as a comma delimited string. - * - * Example without specifying fractional digits: - * - * var coord = [7.85, 47.983333]; - * var out = ol.coordinate.toStringXY(coord); - * // out is now '8, 48' - * - * Example explicitly specifying 1 fractional digit: - * - * var coord = [7.85, 47.983333]; - * var out = ol.coordinate.toStringXY(coord, 1); - * // out is now '7.8, 48.0' - * - * @param {ol.Coordinate|undefined} coordinate Coordinate. - * @param {number=} opt_fractionDigits The number of digits to include - * after the decimal point. Default is `0`. - * @return {string} XY. - * @api - */ -ol.coordinate.toStringXY = function(coordinate, opt_fractionDigits) { - return ol.coordinate.format(coordinate, '{x}, {y}', opt_fractionDigits); -}; - -goog.provide('ol.geom.GeometryType'); - - -/** - * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`, - * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`, - * `'GeometryCollection'`, `'Circle'`. - * @enum {string} - */ -ol.geom.GeometryType = { - POINT: 'Point', - LINE_STRING: 'LineString', - LINEAR_RING: 'LinearRing', - POLYGON: 'Polygon', - MULTI_POINT: 'MultiPoint', - MULTI_LINE_STRING: 'MultiLineString', - MULTI_POLYGON: 'MultiPolygon', - GEOMETRY_COLLECTION: 'GeometryCollection', - CIRCLE: 'Circle' -}; - -goog.provide('ol.geom.GeometryLayout'); - - -/** - * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z') - * or measure ('M') coordinate is available. Supported values are `'XY'`, - * `'XYZ'`, `'XYM'`, `'XYZM'`. - * @enum {string} - */ -ol.geom.GeometryLayout = { - XY: 'XY', - XYZ: 'XYZ', - XYM: 'XYM', - XYZM: 'XYZM' -}; - -goog.provide('ol.functions'); - -/** - * Always returns true. - * @returns {boolean} true. - */ -ol.functions.TRUE = function() { - return true; -}; - -/** - * Always returns false. - * @returns {boolean} false. - */ -ol.functions.FALSE = function() { - return false; -}; - -goog.provide('ol.geom.Geometry'); - -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for vector geometries. - * - * To get notified of changes to the geometry, register a listener for the - * generic `change` event on your geometry instance. - * - * @constructor - * @abstract - * @extends {ol.Object} - * @api - */ -ol.geom.Geometry = function() { - - ol.Object.call(this); - - /** - * @private - * @type {ol.Extent} - */ - this.extent_ = ol.extent.createEmpty(); - - /** - * @private - * @type {number} - */ - this.extentRevision_ = -1; - - /** - * @protected - * @type {Object.<string, ol.geom.Geometry>} - */ - this.simplifiedGeometryCache = {}; - - /** - * @protected - * @type {number} - */ - this.simplifiedGeometryMaxMinSquaredTolerance = 0; - - /** - * @protected - * @type {number} - */ - this.simplifiedGeometryRevision = 0; - -}; -ol.inherits(ol.geom.Geometry, ol.Object); - - -/** - * Make a complete copy of the geometry. - * @abstract - * @return {!ol.geom.Geometry} Clone. - */ -ol.geom.Geometry.prototype.clone = function() {}; - - -/** - * @abstract - * @param {number} x X. - * @param {number} y Y. - * @param {ol.Coordinate} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @return {number} Minimum squared distance. - */ -ol.geom.Geometry.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {}; - - -/** - * Return the closest point of the geometry to the passed point as - * {@link ol.Coordinate coordinate}. - * @param {ol.Coordinate} point Point. - * @param {ol.Coordinate=} opt_closestPoint Closest point. - * @return {ol.Coordinate} Closest point. - * @api - */ -ol.geom.Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) { - var closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; - this.closestPointXY(point[0], point[1], closestPoint, Infinity); - return closestPoint; -}; - - -/** - * Returns true if this geometry includes the specified coordinate. If the - * coordinate is on the boundary of the geometry, returns false. - * @param {ol.Coordinate} coordinate Coordinate. - * @return {boolean} Contains coordinate. - * @api - */ -ol.geom.Geometry.prototype.intersectsCoordinate = function(coordinate) { - return this.containsXY(coordinate[0], coordinate[1]); -}; - - -/** - * @abstract - * @param {ol.Extent} extent Extent. - * @protected - * @return {ol.Extent} extent Extent. - */ -ol.geom.Geometry.prototype.computeExtent = function(extent) {}; - - -/** - * @param {number} x X. - * @param {number} y Y. - * @return {boolean} Contains (x, y). - */ -ol.geom.Geometry.prototype.containsXY = ol.functions.FALSE; - - -/** - * Get the extent of the geometry. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} extent Extent. - * @api - */ -ol.geom.Geometry.prototype.getExtent = function(opt_extent) { - if (this.extentRevision_ != this.getRevision()) { - this.extent_ = this.computeExtent(this.extent_); - this.extentRevision_ = this.getRevision(); - } - return ol.extent.returnOrUpdate(this.extent_, opt_extent); -}; - - -/** - * Rotate the geometry around a given coordinate. This modifies the geometry - * coordinates in place. - * @abstract - * @param {number} angle Rotation angle in radians. - * @param {ol.Coordinate} anchor The rotation center. - * @api - */ -ol.geom.Geometry.prototype.rotate = function(angle, anchor) {}; - - -/** - * Scale the geometry (with an optional origin). This modifies the geometry - * coordinates in place. - * @abstract - * @param {number} sx The scaling factor in the x-direction. - * @param {number=} opt_sy The scaling factor in the y-direction (defaults to - * sx). - * @param {ol.Coordinate=} opt_anchor The scale origin (defaults to the center - * of the geometry extent). - * @api - */ -ol.geom.Geometry.prototype.scale = function(sx, opt_sy, opt_anchor) {}; - - -/** - * Create a simplified version of this geometry. For linestrings, this uses - * the the {@link - * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm - * Douglas Peucker} algorithm. For polygons, a quantization-based - * simplification is used to preserve topology. - * @function - * @param {number} tolerance The tolerance distance for simplification. - * @return {ol.geom.Geometry} A new, simplified version of the original - * geometry. - * @api - */ -ol.geom.Geometry.prototype.simplify = function(tolerance) { - return this.getSimplifiedGeometry(tolerance * tolerance); -}; - - -/** - * Create a simplified version of this geometry using the Douglas Peucker - * algorithm. - * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm - * @abstract - * @param {number} squaredTolerance Squared tolerance. - * @return {ol.geom.Geometry} Simplified geometry. - */ -ol.geom.Geometry.prototype.getSimplifiedGeometry = function(squaredTolerance) {}; - - -/** - * Get the type of this geometry. - * @abstract - * @return {ol.geom.GeometryType} Geometry type. - */ -ol.geom.Geometry.prototype.getType = function() {}; - - -/** - * Apply a transform function to each coordinate of the geometry. - * The geometry is modified in place. - * If you do not want the geometry modified in place, first `clone()` it and - * then use this function on the clone. - * @abstract - * @param {ol.TransformFunction} transformFn Transform. - */ -ol.geom.Geometry.prototype.applyTransform = function(transformFn) {}; - - -/** - * Test if the geometry and the passed extent intersect. - * @abstract - * @param {ol.Extent} extent Extent. - * @return {boolean} `true` if the geometry and the extent intersect. - */ -ol.geom.Geometry.prototype.intersectsExtent = function(extent) {}; - - -/** - * Translate the geometry. This modifies the geometry coordinates in place. If - * instead you want a new geometry, first `clone()` this geometry. - * @abstract - * @param {number} deltaX Delta X. - * @param {number} deltaY Delta Y. - */ -ol.geom.Geometry.prototype.translate = function(deltaX, deltaY) {}; - - -/** - * Transform each coordinate of the geometry from one coordinate reference - * system to another. The geometry is modified in place. - * For example, a line will be transformed to a line and a circle to a circle. - * If you do not want the geometry modified in place, first `clone()` it and - * then use this function on the clone. - * - * @param {ol.ProjectionLike} source The current projection. Can be a - * string identifier or a {@link ol.proj.Projection} object. - * @param {ol.ProjectionLike} destination The desired projection. Can be a - * string identifier or a {@link ol.proj.Projection} object. - * @return {ol.geom.Geometry} This geometry. Note that original geometry is - * modified in place. - * @api - */ -ol.geom.Geometry.prototype.transform = function(source, destination) { - this.applyTransform(ol.proj.getTransform(source, destination)); - return this; -}; - -goog.provide('ol.geom.flat.transform'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.Transform} transform Transform. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Transformed coordinates. - */ -ol.geom.flat.transform.transform2D = function(flatCoordinates, offset, end, stride, transform, opt_dest) { - var dest = opt_dest ? opt_dest : []; - var i = 0; - var j; - for (j = offset; j < end; j += stride) { - var x = flatCoordinates[j]; - var y = flatCoordinates[j + 1]; - dest[i++] = transform[0] * x + transform[2] * y + transform[4]; - dest[i++] = transform[1] * x + transform[3] * y + transform[5]; - } - if (opt_dest && dest.length != i) { - dest.length = i; - } - return dest; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} angle Angle. - * @param {Array.<number>} anchor Rotation anchor point. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Transformed coordinates. - */ -ol.geom.flat.transform.rotate = function(flatCoordinates, offset, end, stride, angle, anchor, opt_dest) { - var dest = opt_dest ? opt_dest : []; - var cos = Math.cos(angle); - var sin = Math.sin(angle); - var anchorX = anchor[0]; - var anchorY = anchor[1]; - var i = 0; - for (var j = offset; j < end; j += stride) { - var deltaX = flatCoordinates[j] - anchorX; - var deltaY = flatCoordinates[j + 1] - anchorY; - dest[i++] = anchorX + deltaX * cos - deltaY * sin; - dest[i++] = anchorY + deltaX * sin + deltaY * cos; - for (var k = j + 2; k < j + stride; ++k) { - dest[i++] = flatCoordinates[k]; - } - } - if (opt_dest && dest.length != i) { - dest.length = i; - } - return dest; -}; - - -/** - * Scale the coordinates. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} sx Scale factor in the x-direction. - * @param {number} sy Scale factor in the y-direction. - * @param {Array.<number>} anchor Scale anchor point. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Transformed coordinates. - */ -ol.geom.flat.transform.scale = function(flatCoordinates, offset, end, stride, sx, sy, anchor, opt_dest) { - var dest = opt_dest ? opt_dest : []; - var anchorX = anchor[0]; - var anchorY = anchor[1]; - var i = 0; - for (var j = offset; j < end; j += stride) { - var deltaX = flatCoordinates[j] - anchorX; - var deltaY = flatCoordinates[j + 1] - anchorY; - dest[i++] = anchorX + sx * deltaX; - dest[i++] = anchorY + sy * deltaY; - for (var k = j + 2; k < j + stride; ++k) { - dest[i++] = flatCoordinates[k]; - } - } - if (opt_dest && dest.length != i) { - dest.length = i; - } - return dest; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} deltaX Delta X. - * @param {number} deltaY Delta Y. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Transformed coordinates. - */ -ol.geom.flat.transform.translate = function(flatCoordinates, offset, end, stride, deltaX, deltaY, opt_dest) { - var dest = opt_dest ? opt_dest : []; - var i = 0; - var j, k; - for (j = offset; j < end; j += stride) { - dest[i++] = flatCoordinates[j] + deltaX; - dest[i++] = flatCoordinates[j + 1] + deltaY; - for (k = j + 2; k < j + stride; ++k) { - dest[i++] = flatCoordinates[k]; - } - } - if (opt_dest && dest.length != i) { - dest.length = i; - } - return dest; -}; - -goog.provide('ol.geom.SimpleGeometry'); - -goog.require('ol'); -goog.require('ol.functions'); -goog.require('ol.extent'); -goog.require('ol.geom.Geometry'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.obj'); - - -/** - * @classdesc - * Abstract base class; only used for creating subclasses; do not instantiate - * in apps, as cannot be rendered. - * - * @constructor - * @abstract - * @extends {ol.geom.Geometry} - * @api - */ -ol.geom.SimpleGeometry = function() { - - ol.geom.Geometry.call(this); - - /** - * @protected - * @type {ol.geom.GeometryLayout} - */ - this.layout = ol.geom.GeometryLayout.XY; - - /** - * @protected - * @type {number} - */ - this.stride = 2; - - /** - * @protected - * @type {Array.<number>} - */ - this.flatCoordinates = null; - -}; -ol.inherits(ol.geom.SimpleGeometry, ol.geom.Geometry); - - -/** - * @param {number} stride Stride. - * @private - * @return {ol.geom.GeometryLayout} layout Layout. - */ -ol.geom.SimpleGeometry.getLayoutForStride_ = function(stride) { - var layout; - if (stride == 2) { - layout = ol.geom.GeometryLayout.XY; - } else if (stride == 3) { - layout = ol.geom.GeometryLayout.XYZ; - } else if (stride == 4) { - layout = ol.geom.GeometryLayout.XYZM; - } - return /** @type {ol.geom.GeometryLayout} */ (layout); -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @return {number} Stride. - */ -ol.geom.SimpleGeometry.getStrideForLayout = function(layout) { - var stride; - if (layout == ol.geom.GeometryLayout.XY) { - stride = 2; - } else if (layout == ol.geom.GeometryLayout.XYZ || layout == ol.geom.GeometryLayout.XYM) { - stride = 3; - } else if (layout == ol.geom.GeometryLayout.XYZM) { - stride = 4; - } - return /** @type {number} */ (stride); -}; - - -/** - * @inheritDoc - */ -ol.geom.SimpleGeometry.prototype.containsXY = ol.functions.FALSE; - - -/** - * @inheritDoc - */ -ol.geom.SimpleGeometry.prototype.computeExtent = function(extent) { - return ol.extent.createOrUpdateFromFlatCoordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - extent); -}; - - -/** - * @abstract - * @return {Array} Coordinates. - */ -ol.geom.SimpleGeometry.prototype.getCoordinates = function() {}; - - -/** - * Return the first coordinate of the geometry. - * @return {ol.Coordinate} First coordinate. - * @api - */ -ol.geom.SimpleGeometry.prototype.getFirstCoordinate = function() { - return this.flatCoordinates.slice(0, this.stride); -}; - - -/** - * @return {Array.<number>} Flat coordinates. - */ -ol.geom.SimpleGeometry.prototype.getFlatCoordinates = function() { - return this.flatCoordinates; -}; - - -/** - * Return the last coordinate of the geometry. - * @return {ol.Coordinate} Last point. - * @api - */ -ol.geom.SimpleGeometry.prototype.getLastCoordinate = function() { - return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride); -}; - - -/** - * Return the {@link ol.geom.GeometryLayout layout} of the geometry. - * @return {ol.geom.GeometryLayout} Layout. - * @api - */ -ol.geom.SimpleGeometry.prototype.getLayout = function() { - return this.layout; -}; - - -/** - * @inheritDoc - */ -ol.geom.SimpleGeometry.prototype.getSimplifiedGeometry = function(squaredTolerance) { - if (this.simplifiedGeometryRevision != this.getRevision()) { - ol.obj.clear(this.simplifiedGeometryCache); - this.simplifiedGeometryMaxMinSquaredTolerance = 0; - this.simplifiedGeometryRevision = this.getRevision(); - } - // If squaredTolerance is negative or if we know that simplification will not - // have any effect then just return this. - if (squaredTolerance < 0 || - (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && - squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) { - return this; - } - var key = squaredTolerance.toString(); - if (this.simplifiedGeometryCache.hasOwnProperty(key)) { - return this.simplifiedGeometryCache[key]; - } else { - var simplifiedGeometry = - this.getSimplifiedGeometryInternal(squaredTolerance); - var simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates(); - if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) { - this.simplifiedGeometryCache[key] = simplifiedGeometry; - return simplifiedGeometry; - } else { - // Simplification did not actually remove any coordinates. We now know - // that any calls to getSimplifiedGeometry with a squaredTolerance less - // than or equal to the current squaredTolerance will also not have any - // effect. This allows us to short circuit simplification (saving CPU - // cycles) and prevents the cache of simplified geometries from filling - // up with useless identical copies of this geometry (saving memory). - this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; - return this; - } - } -}; - - -/** - * @param {number} squaredTolerance Squared tolerance. - * @return {ol.geom.SimpleGeometry} Simplified geometry. - * @protected - */ -ol.geom.SimpleGeometry.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - return this; -}; - - -/** - * @return {number} Stride. - */ -ol.geom.SimpleGeometry.prototype.getStride = function() { - return this.stride; -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @protected - */ -ol.geom.SimpleGeometry.prototype.setFlatCoordinatesInternal = function(layout, flatCoordinates) { - this.stride = ol.geom.SimpleGeometry.getStrideForLayout(layout); - this.layout = layout; - this.flatCoordinates = flatCoordinates; -}; - - -/** - * @abstract - * @param {Array} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - */ -ol.geom.SimpleGeometry.prototype.setCoordinates = function(coordinates, opt_layout) {}; - - -/** - * @param {ol.geom.GeometryLayout|undefined} layout Layout. - * @param {Array} coordinates Coordinates. - * @param {number} nesting Nesting. - * @protected - */ -ol.geom.SimpleGeometry.prototype.setLayout = function(layout, coordinates, nesting) { - /** @type {number} */ - var stride; - if (layout) { - stride = ol.geom.SimpleGeometry.getStrideForLayout(layout); - } else { - var i; - for (i = 0; i < nesting; ++i) { - if (coordinates.length === 0) { - this.layout = ol.geom.GeometryLayout.XY; - this.stride = 2; - return; - } else { - coordinates = /** @type {Array} */ (coordinates[0]); - } - } - stride = coordinates.length; - layout = ol.geom.SimpleGeometry.getLayoutForStride_(stride); - } - this.layout = layout; - this.stride = stride; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.SimpleGeometry.prototype.applyTransform = function(transformFn) { - if (this.flatCoordinates) { - transformFn(this.flatCoordinates, this.flatCoordinates, this.stride); - this.changed(); - } -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.SimpleGeometry.prototype.rotate = function(angle, anchor) { - var flatCoordinates = this.getFlatCoordinates(); - if (flatCoordinates) { - var stride = this.getStride(); - ol.geom.flat.transform.rotate( - flatCoordinates, 0, flatCoordinates.length, - stride, angle, anchor, flatCoordinates); - this.changed(); - } -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.SimpleGeometry.prototype.scale = function(sx, opt_sy, opt_anchor) { - var sy = opt_sy; - if (sy === undefined) { - sy = sx; - } - var anchor = opt_anchor; - if (!anchor) { - anchor = ol.extent.getCenter(this.getExtent()); - } - var flatCoordinates = this.getFlatCoordinates(); - if (flatCoordinates) { - var stride = this.getStride(); - ol.geom.flat.transform.scale( - flatCoordinates, 0, flatCoordinates.length, - stride, sx, sy, anchor, flatCoordinates); - this.changed(); - } -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.SimpleGeometry.prototype.translate = function(deltaX, deltaY) { - var flatCoordinates = this.getFlatCoordinates(); - if (flatCoordinates) { - var stride = this.getStride(); - ol.geom.flat.transform.translate( - flatCoordinates, 0, flatCoordinates.length, stride, - deltaX, deltaY, flatCoordinates); - this.changed(); - } -}; - - -/** - * @param {ol.geom.SimpleGeometry} simpleGeometry Simple geometry. - * @param {ol.Transform} transform Transform. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Transformed flat coordinates. - */ -ol.geom.SimpleGeometry.transform2D = function(simpleGeometry, transform, opt_dest) { - var flatCoordinates = simpleGeometry.getFlatCoordinates(); - if (!flatCoordinates) { - return null; - } else { - var stride = simpleGeometry.getStride(); - return ol.geom.flat.transform.transform2D( - flatCoordinates, 0, flatCoordinates.length, stride, - transform, opt_dest); - } -}; - -goog.provide('ol.geom.flat.area'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {number} Area. - */ -ol.geom.flat.area.linearRing = function(flatCoordinates, offset, end, stride) { - var twiceArea = 0; - var x1 = flatCoordinates[end - stride]; - var y1 = flatCoordinates[end - stride + 1]; - for (; offset < end; offset += stride) { - var x2 = flatCoordinates[offset]; - var y2 = flatCoordinates[offset + 1]; - twiceArea += y1 * x2 - x1 * y2; - x1 = x2; - y1 = y2; - } - return twiceArea / 2; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @return {number} Area. - */ -ol.geom.flat.area.linearRings = function(flatCoordinates, offset, ends, stride) { - var area = 0; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - area += ol.geom.flat.area.linearRing(flatCoordinates, offset, end, stride); - offset = end; - } - return area; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @return {number} Area. - */ -ol.geom.flat.area.linearRingss = function(flatCoordinates, offset, endss, stride) { - var area = 0; - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - area += - ol.geom.flat.area.linearRings(flatCoordinates, offset, ends, stride); - offset = ends[ends.length - 1]; - } - return area; -}; - -goog.provide('ol.geom.flat.closest'); - -goog.require('ol.math'); - - -/** - * Returns the point on the 2D line segment flatCoordinates[offset1] to - * flatCoordinates[offset2] that is closest to the point (x, y). Extra - * dimensions are linearly interpolated. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset1 Offset 1. - * @param {number} offset2 Offset 2. - * @param {number} stride Stride. - * @param {number} x X. - * @param {number} y Y. - * @param {Array.<number>} closestPoint Closest point. - */ -ol.geom.flat.closest.point = function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) { - var x1 = flatCoordinates[offset1]; - var y1 = flatCoordinates[offset1 + 1]; - var dx = flatCoordinates[offset2] - x1; - var dy = flatCoordinates[offset2 + 1] - y1; - var i, offset; - if (dx === 0 && dy === 0) { - offset = offset1; - } else { - var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); - if (t > 1) { - offset = offset2; - } else if (t > 0) { - for (i = 0; i < stride; ++i) { - closestPoint[i] = ol.math.lerp(flatCoordinates[offset1 + i], - flatCoordinates[offset2 + i], t); - } - closestPoint.length = stride; - return; - } else { - offset = offset1; - } - } - for (i = 0; i < stride; ++i) { - closestPoint[i] = flatCoordinates[offset + i]; - } - closestPoint.length = stride; -}; - - -/** - * Return the squared of the largest distance between any pair of consecutive - * coordinates. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} maxSquaredDelta Max squared delta. - * @return {number} Max squared delta. - */ -ol.geom.flat.closest.getMaxSquaredDelta = function(flatCoordinates, offset, end, stride, maxSquaredDelta) { - var x1 = flatCoordinates[offset]; - var y1 = flatCoordinates[offset + 1]; - for (offset += stride; offset < end; offset += stride) { - var x2 = flatCoordinates[offset]; - var y2 = flatCoordinates[offset + 1]; - var squaredDelta = ol.math.squaredDistance(x1, y1, x2, y2); - if (squaredDelta > maxSquaredDelta) { - maxSquaredDelta = squaredDelta; - } - x1 = x2; - y1 = y2; - } - return maxSquaredDelta; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {number} maxSquaredDelta Max squared delta. - * @return {number} Max squared delta. - */ -ol.geom.flat.closest.getsMaxSquaredDelta = function(flatCoordinates, offset, ends, stride, maxSquaredDelta) { - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - maxSquaredDelta = ol.geom.flat.closest.getMaxSquaredDelta( - flatCoordinates, offset, end, stride, maxSquaredDelta); - offset = end; - } - return maxSquaredDelta; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {number} maxSquaredDelta Max squared delta. - * @return {number} Max squared delta. - */ -ol.geom.flat.closest.getssMaxSquaredDelta = function(flatCoordinates, offset, endss, stride, maxSquaredDelta) { - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - maxSquaredDelta = ol.geom.flat.closest.getsMaxSquaredDelta( - flatCoordinates, offset, ends, stride, maxSquaredDelta); - offset = ends[ends.length - 1]; - } - return maxSquaredDelta; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} maxDelta Max delta. - * @param {boolean} isRing Is ring. - * @param {number} x X. - * @param {number} y Y. - * @param {Array.<number>} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @param {Array.<number>=} opt_tmpPoint Temporary point object. - * @return {number} Minimum squared distance. - */ -ol.geom.flat.closest.getClosestPoint = function(flatCoordinates, offset, end, - stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, - opt_tmpPoint) { - if (offset == end) { - return minSquaredDistance; - } - var i, squaredDistance; - if (maxDelta === 0) { - // All points are identical, so just test the first point. - squaredDistance = ol.math.squaredDistance( - x, y, flatCoordinates[offset], flatCoordinates[offset + 1]); - if (squaredDistance < minSquaredDistance) { - for (i = 0; i < stride; ++i) { - closestPoint[i] = flatCoordinates[offset + i]; - } - closestPoint.length = stride; - return squaredDistance; - } else { - return minSquaredDistance; - } - } - var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN]; - var index = offset + stride; - while (index < end) { - ol.geom.flat.closest.point( - flatCoordinates, index - stride, index, stride, x, y, tmpPoint); - squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]); - if (squaredDistance < minSquaredDistance) { - minSquaredDistance = squaredDistance; - for (i = 0; i < stride; ++i) { - closestPoint[i] = tmpPoint[i]; - } - closestPoint.length = stride; - index += stride; - } else { - // Skip ahead multiple points, because we know that all the skipped - // points cannot be any closer than the closest point we have found so - // far. We know this because we know how close the current point is, how - // close the closest point we have found so far is, and the maximum - // distance between consecutive points. For example, if we're currently - // at distance 10, the best we've found so far is 3, and that the maximum - // distance between consecutive points is 2, then we'll need to skip at - // least (10 - 3) / 2 == 3 (rounded down) points to have any chance of - // finding a closer point. We use Math.max(..., 1) to ensure that we - // always advance at least one point, to avoid an infinite loop. - index += stride * Math.max( - ((Math.sqrt(squaredDistance) - - Math.sqrt(minSquaredDistance)) / maxDelta) | 0, 1); - } - } - if (isRing) { - // Check the closing segment. - ol.geom.flat.closest.point( - flatCoordinates, end - stride, offset, stride, x, y, tmpPoint); - squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]); - if (squaredDistance < minSquaredDistance) { - minSquaredDistance = squaredDistance; - for (i = 0; i < stride; ++i) { - closestPoint[i] = tmpPoint[i]; - } - closestPoint.length = stride; - } - } - return minSquaredDistance; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {number} maxDelta Max delta. - * @param {boolean} isRing Is ring. - * @param {number} x X. - * @param {number} y Y. - * @param {Array.<number>} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @param {Array.<number>=} opt_tmpPoint Temporary point object. - * @return {number} Minimum squared distance. - */ -ol.geom.flat.closest.getsClosestPoint = function(flatCoordinates, offset, ends, - stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, - opt_tmpPoint) { - var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN]; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - minSquaredDistance = ol.geom.flat.closest.getClosestPoint( - flatCoordinates, offset, end, stride, - maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint); - offset = end; - } - return minSquaredDistance; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {number} maxDelta Max delta. - * @param {boolean} isRing Is ring. - * @param {number} x X. - * @param {number} y Y. - * @param {Array.<number>} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @param {Array.<number>=} opt_tmpPoint Temporary point object. - * @return {number} Minimum squared distance. - */ -ol.geom.flat.closest.getssClosestPoint = function(flatCoordinates, offset, - endss, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, - opt_tmpPoint) { - var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN]; - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - minSquaredDistance = ol.geom.flat.closest.getsClosestPoint( - flatCoordinates, offset, ends, stride, - maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint); - offset = ends[ends.length - 1]; - } - return minSquaredDistance; -}; - -goog.provide('ol.geom.flat.deflate'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} stride Stride. - * @return {number} offset Offset. - */ -ol.geom.flat.deflate.coordinate = function(flatCoordinates, offset, coordinate, stride) { - var i, ii; - for (i = 0, ii = coordinate.length; i < ii; ++i) { - flatCoordinates[offset++] = coordinate[i]; - } - return offset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {number} stride Stride. - * @return {number} offset Offset. - */ -ol.geom.flat.deflate.coordinates = function(flatCoordinates, offset, coordinates, stride) { - var i, ii; - for (i = 0, ii = coordinates.length; i < ii; ++i) { - var coordinate = coordinates[i]; - var j; - for (j = 0; j < stride; ++j) { - flatCoordinates[offset++] = coordinate[j]; - } - } - return offset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<ol.Coordinate>>} coordinatess Coordinatess. - * @param {number} stride Stride. - * @param {Array.<number>=} opt_ends Ends. - * @return {Array.<number>} Ends. - */ -ol.geom.flat.deflate.coordinatess = function(flatCoordinates, offset, coordinatess, stride, opt_ends) { - var ends = opt_ends ? opt_ends : []; - var i = 0; - var j, jj; - for (j = 0, jj = coordinatess.length; j < jj; ++j) { - var end = ol.geom.flat.deflate.coordinates( - flatCoordinates, offset, coordinatess[j], stride); - ends[i++] = end; - offset = end; - } - ends.length = i; - return ends; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinatesss Coordinatesss. - * @param {number} stride Stride. - * @param {Array.<Array.<number>>=} opt_endss Endss. - * @return {Array.<Array.<number>>} Endss. - */ -ol.geom.flat.deflate.coordinatesss = function(flatCoordinates, offset, coordinatesss, stride, opt_endss) { - var endss = opt_endss ? opt_endss : []; - var i = 0; - var j, jj; - for (j = 0, jj = coordinatesss.length; j < jj; ++j) { - var ends = ol.geom.flat.deflate.coordinatess( - flatCoordinates, offset, coordinatesss[j], stride, endss[i]); - endss[i++] = ends; - offset = ends[ends.length - 1]; - } - endss.length = i; - return endss; -}; - -goog.provide('ol.geom.flat.inflate'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {Array.<ol.Coordinate>=} opt_coordinates Coordinates. - * @return {Array.<ol.Coordinate>} Coordinates. - */ -ol.geom.flat.inflate.coordinates = function(flatCoordinates, offset, end, stride, opt_coordinates) { - var coordinates = opt_coordinates !== undefined ? opt_coordinates : []; - var i = 0; - var j; - for (j = offset; j < end; j += stride) { - coordinates[i++] = flatCoordinates.slice(j, j + stride); - } - coordinates.length = i; - return coordinates; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {Array.<Array.<ol.Coordinate>>=} opt_coordinatess Coordinatess. - * @return {Array.<Array.<ol.Coordinate>>} Coordinatess. - */ -ol.geom.flat.inflate.coordinatess = function(flatCoordinates, offset, ends, stride, opt_coordinatess) { - var coordinatess = opt_coordinatess !== undefined ? opt_coordinatess : []; - var i = 0; - var j, jj; - for (j = 0, jj = ends.length; j < jj; ++j) { - var end = ends[j]; - coordinatess[i++] = ol.geom.flat.inflate.coordinates( - flatCoordinates, offset, end, stride, coordinatess[i]); - offset = end; - } - coordinatess.length = i; - return coordinatess; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {Array.<Array.<Array.<ol.Coordinate>>>=} opt_coordinatesss - * Coordinatesss. - * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinatesss. - */ -ol.geom.flat.inflate.coordinatesss = function(flatCoordinates, offset, endss, stride, opt_coordinatesss) { - var coordinatesss = opt_coordinatesss !== undefined ? opt_coordinatesss : []; - var i = 0; - var j, jj; - for (j = 0, jj = endss.length; j < jj; ++j) { - var ends = endss[j]; - coordinatesss[i++] = ol.geom.flat.inflate.coordinatess( - flatCoordinates, offset, ends, stride, coordinatesss[i]); - offset = ends[ends.length - 1]; - } - coordinatesss.length = i; - return coordinatesss; -}; - -// Based on simplify-js https://github.com/mourner/simplify-js -// Copyright (c) 2012, Vladimir Agafonkin -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -goog.provide('ol.geom.flat.simplify'); - -goog.require('ol.math'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} squaredTolerance Squared tolerance. - * @param {boolean} highQuality Highest quality. - * @param {Array.<number>=} opt_simplifiedFlatCoordinates Simplified flat - * coordinates. - * @return {Array.<number>} Simplified line string. - */ -ol.geom.flat.simplify.lineString = function(flatCoordinates, offset, end, - stride, squaredTolerance, highQuality, opt_simplifiedFlatCoordinates) { - var simplifiedFlatCoordinates = opt_simplifiedFlatCoordinates !== undefined ? - opt_simplifiedFlatCoordinates : []; - if (!highQuality) { - end = ol.geom.flat.simplify.radialDistance(flatCoordinates, offset, end, - stride, squaredTolerance, - simplifiedFlatCoordinates, 0); - flatCoordinates = simplifiedFlatCoordinates; - offset = 0; - stride = 2; - } - simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker( - flatCoordinates, offset, end, stride, squaredTolerance, - simplifiedFlatCoordinates, 0); - return simplifiedFlatCoordinates; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} squaredTolerance Squared tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.douglasPeucker = function(flatCoordinates, offset, end, - stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) { - var n = (end - offset) / stride; - if (n < 3) { - for (; offset < end; offset += stride) { - simplifiedFlatCoordinates[simplifiedOffset++] = - flatCoordinates[offset]; - simplifiedFlatCoordinates[simplifiedOffset++] = - flatCoordinates[offset + 1]; - } - return simplifiedOffset; - } - /** @type {Array.<number>} */ - var markers = new Array(n); - markers[0] = 1; - markers[n - 1] = 1; - /** @type {Array.<number>} */ - var stack = [offset, end - stride]; - var index = 0; - var i; - while (stack.length > 0) { - var last = stack.pop(); - var first = stack.pop(); - var maxSquaredDistance = 0; - var x1 = flatCoordinates[first]; - var y1 = flatCoordinates[first + 1]; - var x2 = flatCoordinates[last]; - var y2 = flatCoordinates[last + 1]; - for (i = first + stride; i < last; i += stride) { - var x = flatCoordinates[i]; - var y = flatCoordinates[i + 1]; - var squaredDistance = ol.math.squaredSegmentDistance( - x, y, x1, y1, x2, y2); - if (squaredDistance > maxSquaredDistance) { - index = i; - maxSquaredDistance = squaredDistance; - } - } - if (maxSquaredDistance > squaredTolerance) { - markers[(index - offset) / stride] = 1; - if (first + stride < index) { - stack.push(first, index); - } - if (index + stride < last) { - stack.push(index, last); - } - } - } - for (i = 0; i < n; ++i) { - if (markers[i]) { - simplifiedFlatCoordinates[simplifiedOffset++] = - flatCoordinates[offset + i * stride]; - simplifiedFlatCoordinates[simplifiedOffset++] = - flatCoordinates[offset + i * stride + 1]; - } - } - return simplifiedOffset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {number} squaredTolerance Squared tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @param {Array.<number>} simplifiedEnds Simplified ends. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.douglasPeuckers = function(flatCoordinates, offset, - ends, stride, squaredTolerance, simplifiedFlatCoordinates, - simplifiedOffset, simplifiedEnds) { - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - simplifiedOffset = ol.geom.flat.simplify.douglasPeucker( - flatCoordinates, offset, end, stride, squaredTolerance, - simplifiedFlatCoordinates, simplifiedOffset); - simplifiedEnds.push(simplifiedOffset); - offset = end; - } - return simplifiedOffset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {number} squaredTolerance Squared tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.douglasPeuckerss = function( - flatCoordinates, offset, endss, stride, squaredTolerance, - simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) { - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - var simplifiedEnds = []; - simplifiedOffset = ol.geom.flat.simplify.douglasPeuckers( - flatCoordinates, offset, ends, stride, squaredTolerance, - simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds); - simplifiedEndss.push(simplifiedEnds); - offset = ends[ends.length - 1]; - } - return simplifiedOffset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} squaredTolerance Squared tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.radialDistance = function(flatCoordinates, offset, end, - stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) { - if (end <= offset + stride) { - // zero or one point, no simplification possible, so copy and return - for (; offset < end; offset += stride) { - simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset]; - simplifiedFlatCoordinates[simplifiedOffset++] = - flatCoordinates[offset + 1]; - } - return simplifiedOffset; - } - var x1 = flatCoordinates[offset]; - var y1 = flatCoordinates[offset + 1]; - // copy first point - simplifiedFlatCoordinates[simplifiedOffset++] = x1; - simplifiedFlatCoordinates[simplifiedOffset++] = y1; - var x2 = x1; - var y2 = y1; - for (offset += stride; offset < end; offset += stride) { - x2 = flatCoordinates[offset]; - y2 = flatCoordinates[offset + 1]; - if (ol.math.squaredDistance(x1, y1, x2, y2) > squaredTolerance) { - // copy point at offset - simplifiedFlatCoordinates[simplifiedOffset++] = x2; - simplifiedFlatCoordinates[simplifiedOffset++] = y2; - x1 = x2; - y1 = y2; - } - } - if (x2 != x1 || y2 != y1) { - // copy last point - simplifiedFlatCoordinates[simplifiedOffset++] = x2; - simplifiedFlatCoordinates[simplifiedOffset++] = y2; - } - return simplifiedOffset; -}; - - -/** - * @param {number} value Value. - * @param {number} tolerance Tolerance. - * @return {number} Rounded value. - */ -ol.geom.flat.simplify.snap = function(value, tolerance) { - return tolerance * Math.round(value / tolerance); -}; - - -/** - * Simplifies a line string using an algorithm designed by Tim Schaub. - * Coordinates are snapped to the nearest value in a virtual grid and - * consecutive duplicate coordinates are discarded. This effectively preserves - * topology as the simplification of any subsection of a line string is - * independent of the rest of the line string. This means that, for examples, - * the common edge between two polygons will be simplified to the same line - * string independently in both polygons. This implementation uses a single - * pass over the coordinates and eliminates intermediate collinear points. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} tolerance Tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.quantize = function(flatCoordinates, offset, end, stride, - tolerance, simplifiedFlatCoordinates, simplifiedOffset) { - // do nothing if the line is empty - if (offset == end) { - return simplifiedOffset; - } - // snap the first coordinate (P1) - var x1 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance); - var y1 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance); - offset += stride; - // add the first coordinate to the output - simplifiedFlatCoordinates[simplifiedOffset++] = x1; - simplifiedFlatCoordinates[simplifiedOffset++] = y1; - // find the next coordinate that does not snap to the same value as the first - // coordinate (P2) - var x2, y2; - do { - x2 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance); - y2 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance); - offset += stride; - if (offset == end) { - // all coordinates snap to the same value, the line collapses to a point - // push the last snapped value anyway to ensure that the output contains - // at least two points - // FIXME should we really return at least two points anyway? - simplifiedFlatCoordinates[simplifiedOffset++] = x2; - simplifiedFlatCoordinates[simplifiedOffset++] = y2; - return simplifiedOffset; - } - } while (x2 == x1 && y2 == y1); - while (offset < end) { - var x3, y3; - // snap the next coordinate (P3) - x3 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance); - y3 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance); - offset += stride; - // skip P3 if it is equal to P2 - if (x3 == x2 && y3 == y2) { - continue; - } - // calculate the delta between P1 and P2 - var dx1 = x2 - x1; - var dy1 = y2 - y1; - // calculate the delta between P3 and P1 - var dx2 = x3 - x1; - var dy2 = y3 - y1; - // if P1, P2, and P3 are colinear and P3 is further from P1 than P2 is from - // P1 in the same direction then P2 is on the straight line between P1 and - // P3 - if ((dx1 * dy2 == dy1 * dx2) && - ((dx1 < 0 && dx2 < dx1) || dx1 == dx2 || (dx1 > 0 && dx2 > dx1)) && - ((dy1 < 0 && dy2 < dy1) || dy1 == dy2 || (dy1 > 0 && dy2 > dy1))) { - // discard P2 and set P2 = P3 - x2 = x3; - y2 = y3; - continue; - } - // either P1, P2, and P3 are not colinear, or they are colinear but P3 is - // between P3 and P1 or on the opposite half of the line to P2. add P2, - // and continue with P1 = P2 and P2 = P3 - simplifiedFlatCoordinates[simplifiedOffset++] = x2; - simplifiedFlatCoordinates[simplifiedOffset++] = y2; - x1 = x2; - y1 = y2; - x2 = x3; - y2 = y3; - } - // add the last point (P2) - simplifiedFlatCoordinates[simplifiedOffset++] = x2; - simplifiedFlatCoordinates[simplifiedOffset++] = y2; - return simplifiedOffset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {number} tolerance Tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @param {Array.<number>} simplifiedEnds Simplified ends. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.quantizes = function( - flatCoordinates, offset, ends, stride, - tolerance, - simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) { - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - simplifiedOffset = ol.geom.flat.simplify.quantize( - flatCoordinates, offset, end, stride, - tolerance, - simplifiedFlatCoordinates, simplifiedOffset); - simplifiedEnds.push(simplifiedOffset); - offset = end; - } - return simplifiedOffset; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {number} tolerance Tolerance. - * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat - * coordinates. - * @param {number} simplifiedOffset Simplified offset. - * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss. - * @return {number} Simplified offset. - */ -ol.geom.flat.simplify.quantizess = function( - flatCoordinates, offset, endss, stride, - tolerance, - simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) { - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - var simplifiedEnds = []; - simplifiedOffset = ol.geom.flat.simplify.quantizes( - flatCoordinates, offset, ends, stride, - tolerance, - simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds); - simplifiedEndss.push(simplifiedEnds); - offset = ends[ends.length - 1]; - } - return simplifiedOffset; -}; - -goog.provide('ol.geom.LinearRing'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.area'); -goog.require('ol.geom.flat.closest'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.geom.flat.simplify'); - - -/** - * @classdesc - * Linear ring geometry. Only used as part of polygon; cannot be rendered - * on its own. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.LinearRing = function(coordinates, opt_layout) { - - ol.geom.SimpleGeometry.call(this); - - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; - - this.setCoordinates(coordinates, opt_layout); - -}; -ol.inherits(ol.geom.LinearRing, ol.geom.SimpleGeometry); - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.LinearRing} Clone. - * @override - * @api - */ -ol.geom.LinearRing.prototype.clone = function() { - var linearRing = new ol.geom.LinearRing(null); - linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); - return linearRing; -}; - - -/** - * @inheritDoc - */ -ol.geom.LinearRing.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return ol.geom.flat.closest.getClosestPoint( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * Return the area of the linear ring on projected plane. - * @return {number} Area (on projected plane). - * @api - */ -ol.geom.LinearRing.prototype.getArea = function() { - return ol.geom.flat.area.linearRing( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * Return the coordinates of the linear ring. - * @return {Array.<ol.Coordinate>} Coordinates. - * @override - * @api - */ -ol.geom.LinearRing.prototype.getCoordinates = function() { - return ol.geom.flat.inflate.coordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * @inheritDoc - */ -ol.geom.LinearRing.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - var simplifiedFlatCoordinates = []; - simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - squaredTolerance, simplifiedFlatCoordinates, 0); - var simplifiedLinearRing = new ol.geom.LinearRing(null); - simplifiedLinearRing.setFlatCoordinates( - ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates); - return simplifiedLinearRing; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.LinearRing.prototype.getType = function() { - return ol.geom.GeometryType.LINEAR_RING; -}; - - -/** - * @inheritDoc - */ -ol.geom.LinearRing.prototype.intersectsExtent = function(extent) {}; - - -/** - * Set the coordinates of the linear ring. - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -ol.geom.LinearRing.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); - } else { - this.setLayout(opt_layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = ol.geom.flat.deflate.coordinates( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - */ -ol.geom.LinearRing.prototype.setFlatCoordinates = function(layout, flatCoordinates) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.changed(); -}; - -goog.provide('ol.geom.Point'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.math'); - - -/** - * @classdesc - * Point geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {ol.Coordinate} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.Point = function(coordinates, opt_layout) { - ol.geom.SimpleGeometry.call(this); - this.setCoordinates(coordinates, opt_layout); -}; -ol.inherits(ol.geom.Point, ol.geom.SimpleGeometry); - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.Point} Clone. - * @override - * @api - */ -ol.geom.Point.prototype.clone = function() { - var point = new ol.geom.Point(null); - point.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); - return point; -}; - - -/** - * @inheritDoc - */ -ol.geom.Point.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - var flatCoordinates = this.flatCoordinates; - var squaredDistance = ol.math.squaredDistance( - x, y, flatCoordinates[0], flatCoordinates[1]); - if (squaredDistance < minSquaredDistance) { - var stride = this.stride; - var i; - for (i = 0; i < stride; ++i) { - closestPoint[i] = flatCoordinates[i]; - } - closestPoint.length = stride; - return squaredDistance; - } else { - return minSquaredDistance; - } -}; - - -/** - * Return the coordinate of the point. - * @return {ol.Coordinate} Coordinates. - * @override - * @api - */ -ol.geom.Point.prototype.getCoordinates = function() { - return !this.flatCoordinates ? [] : this.flatCoordinates.slice(); -}; - - -/** - * @inheritDoc - */ -ol.geom.Point.prototype.computeExtent = function(extent) { - return ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates, extent); -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Point.prototype.getType = function() { - return ol.geom.GeometryType.POINT; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Point.prototype.intersectsExtent = function(extent) { - return ol.extent.containsXY(extent, - this.flatCoordinates[0], this.flatCoordinates[1]); -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Point.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); - } else { - this.setLayout(opt_layout, coordinates, 0); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = ol.geom.flat.deflate.coordinate( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - */ -ol.geom.Point.prototype.setFlatCoordinates = function(layout, flatCoordinates) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.changed(); -}; - -goog.provide('ol.geom.flat.contains'); - -goog.require('ol.extent'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.Extent} extent Extent. - * @return {boolean} Contains extent. - */ -ol.geom.flat.contains.linearRingContainsExtent = function(flatCoordinates, offset, end, stride, extent) { - var outside = ol.extent.forEachCorner(extent, - /** - * @param {ol.Coordinate} coordinate Coordinate. - * @return {boolean} Contains (x, y). - */ - function(coordinate) { - return !ol.geom.flat.contains.linearRingContainsXY(flatCoordinates, - offset, end, stride, coordinate[0], coordinate[1]); - }); - return !outside; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} x X. - * @param {number} y Y. - * @return {boolean} Contains (x, y). - */ -ol.geom.flat.contains.linearRingContainsXY = function(flatCoordinates, offset, end, stride, x, y) { - // http://geomalgorithms.com/a03-_inclusion.html - // Copyright 2000 softSurfer, 2012 Dan Sunday - // This code may be freely used and modified for any purpose - // providing that this copyright notice is included with it. - // SoftSurfer makes no warranty for this code, and cannot be held - // liable for any real or imagined damage resulting from its use. - // Users of this code must verify correctness for their application. - var wn = 0; - var x1 = flatCoordinates[end - stride]; - var y1 = flatCoordinates[end - stride + 1]; - for (; offset < end; offset += stride) { - var x2 = flatCoordinates[offset]; - var y2 = flatCoordinates[offset + 1]; - if (y1 <= y) { - if (y2 > y && ((x2 - x1) * (y - y1)) - ((x - x1) * (y2 - y1)) > 0) { - wn++; - } - } else if (y2 <= y && ((x2 - x1) * (y - y1)) - ((x - x1) * (y2 - y1)) < 0) { - wn--; - } - x1 = x2; - y1 = y2; - } - return wn !== 0; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {number} x X. - * @param {number} y Y. - * @return {boolean} Contains (x, y). - */ -ol.geom.flat.contains.linearRingsContainsXY = function(flatCoordinates, offset, ends, stride, x, y) { - if (ends.length === 0) { - return false; - } - if (!ol.geom.flat.contains.linearRingContainsXY( - flatCoordinates, offset, ends[0], stride, x, y)) { - return false; - } - var i, ii; - for (i = 1, ii = ends.length; i < ii; ++i) { - if (ol.geom.flat.contains.linearRingContainsXY( - flatCoordinates, ends[i - 1], ends[i], stride, x, y)) { - return false; - } - } - return true; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {number} x X. - * @param {number} y Y. - * @return {boolean} Contains (x, y). - */ -ol.geom.flat.contains.linearRingssContainsXY = function(flatCoordinates, offset, endss, stride, x, y) { - if (endss.length === 0) { - return false; - } - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - if (ol.geom.flat.contains.linearRingsContainsXY( - flatCoordinates, offset, ends, stride, x, y)) { - return true; - } - offset = ends[ends.length - 1]; - } - return false; -}; - -goog.provide('ol.geom.flat.interiorpoint'); - -goog.require('ol.array'); -goog.require('ol.geom.flat.contains'); - - -/** - * Calculates a point that is likely to lie in the interior of the linear rings. - * Inspired by JTS's com.vividsolutions.jts.geom.Geometry#getInteriorPoint. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {Array.<number>} flatCenters Flat centers. - * @param {number} flatCentersOffset Flat center offset. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Destination. - */ -ol.geom.flat.interiorpoint.linearRings = function(flatCoordinates, offset, - ends, stride, flatCenters, flatCentersOffset, opt_dest) { - var i, ii, x, x1, x2, y1, y2; - var y = flatCenters[flatCentersOffset + 1]; - /** @type {Array.<number>} */ - var intersections = []; - // Calculate intersections with the horizontal line - var end = ends[0]; - x1 = flatCoordinates[end - stride]; - y1 = flatCoordinates[end - stride + 1]; - for (i = offset; i < end; i += stride) { - x2 = flatCoordinates[i]; - y2 = flatCoordinates[i + 1]; - if ((y <= y1 && y2 <= y) || (y1 <= y && y <= y2)) { - x = (y - y1) / (y2 - y1) * (x2 - x1) + x1; - intersections.push(x); - } - x1 = x2; - y1 = y2; - } - // Find the longest segment of the horizontal line that has its center point - // inside the linear ring. - var pointX = NaN; - var maxSegmentLength = -Infinity; - intersections.sort(ol.array.numberSafeCompareFunction); - x1 = intersections[0]; - for (i = 1, ii = intersections.length; i < ii; ++i) { - x2 = intersections[i]; - var segmentLength = Math.abs(x2 - x1); - if (segmentLength > maxSegmentLength) { - x = (x1 + x2) / 2; - if (ol.geom.flat.contains.linearRingsContainsXY( - flatCoordinates, offset, ends, stride, x, y)) { - pointX = x; - maxSegmentLength = segmentLength; - } - } - x1 = x2; - } - if (isNaN(pointX)) { - // There is no horizontal line that has its center point inside the linear - // ring. Use the center of the the linear ring's extent. - pointX = flatCenters[flatCentersOffset]; - } - if (opt_dest) { - opt_dest.push(pointX, y); - return opt_dest; - } else { - return [pointX, y]; - } -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {Array.<number>} flatCenters Flat centers. - * @return {Array.<number>} Interior points. - */ -ol.geom.flat.interiorpoint.linearRingss = function(flatCoordinates, offset, endss, stride, flatCenters) { - var interiorPoints = []; - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - interiorPoints = ol.geom.flat.interiorpoint.linearRings(flatCoordinates, - offset, ends, stride, flatCenters, 2 * i, interiorPoints); - offset = ends[ends.length - 1]; - } - return interiorPoints; -}; - -goog.provide('ol.geom.flat.segments'); - - -/** - * This function calls `callback` for each segment of the flat coordinates - * array. If the callback returns a truthy value the function returns that - * value immediately. Otherwise the function returns `false`. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function - * called for each segment. - * @param {S=} opt_this The object to be used as the value of 'this' - * within callback. - * @return {T|boolean} Value. - * @template T,S - */ -ol.geom.flat.segments.forEach = function(flatCoordinates, offset, end, stride, callback, opt_this) { - var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]]; - var point2 = []; - var ret; - for (; (offset + stride) < end; offset += stride) { - point2[0] = flatCoordinates[offset + stride]; - point2[1] = flatCoordinates[offset + stride + 1]; - ret = callback.call(opt_this, point1, point2); - if (ret) { - return ret; - } - point1[0] = point2[0]; - point1[1] = point2[1]; - } - return false; -}; - -goog.provide('ol.geom.flat.intersectsextent'); - -goog.require('ol.extent'); -goog.require('ol.geom.flat.contains'); -goog.require('ol.geom.flat.segments'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.Extent} extent Extent. - * @return {boolean} True if the geometry and the extent intersect. - */ -ol.geom.flat.intersectsextent.lineString = function(flatCoordinates, offset, end, stride, extent) { - var coordinatesExtent = ol.extent.extendFlatCoordinates( - ol.extent.createEmpty(), flatCoordinates, offset, end, stride); - if (!ol.extent.intersects(extent, coordinatesExtent)) { - return false; - } - if (ol.extent.containsExtent(extent, coordinatesExtent)) { - return true; - } - if (coordinatesExtent[0] >= extent[0] && - coordinatesExtent[2] <= extent[2]) { - return true; - } - if (coordinatesExtent[1] >= extent[1] && - coordinatesExtent[3] <= extent[3]) { - return true; - } - return ol.geom.flat.segments.forEach(flatCoordinates, offset, end, stride, - /** - * @param {ol.Coordinate} point1 Start point. - * @param {ol.Coordinate} point2 End point. - * @return {boolean} `true` if the segment and the extent intersect, - * `false` otherwise. - */ - function(point1, point2) { - return ol.extent.intersectsSegment(extent, point1, point2); - }); -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {ol.Extent} extent Extent. - * @return {boolean} True if the geometry and the extent intersect. - */ -ol.geom.flat.intersectsextent.lineStrings = function(flatCoordinates, offset, ends, stride, extent) { - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - if (ol.geom.flat.intersectsextent.lineString( - flatCoordinates, offset, ends[i], stride, extent)) { - return true; - } - offset = ends[i]; - } - return false; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.Extent} extent Extent. - * @return {boolean} True if the geometry and the extent intersect. - */ -ol.geom.flat.intersectsextent.linearRing = function(flatCoordinates, offset, end, stride, extent) { - if (ol.geom.flat.intersectsextent.lineString( - flatCoordinates, offset, end, stride, extent)) { - return true; - } - if (ol.geom.flat.contains.linearRingContainsXY( - flatCoordinates, offset, end, stride, extent[0], extent[1])) { - return true; - } - if (ol.geom.flat.contains.linearRingContainsXY( - flatCoordinates, offset, end, stride, extent[0], extent[3])) { - return true; - } - if (ol.geom.flat.contains.linearRingContainsXY( - flatCoordinates, offset, end, stride, extent[2], extent[1])) { - return true; - } - if (ol.geom.flat.contains.linearRingContainsXY( - flatCoordinates, offset, end, stride, extent[2], extent[3])) { - return true; - } - return false; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {ol.Extent} extent Extent. - * @return {boolean} True if the geometry and the extent intersect. - */ -ol.geom.flat.intersectsextent.linearRings = function(flatCoordinates, offset, ends, stride, extent) { - if (!ol.geom.flat.intersectsextent.linearRing( - flatCoordinates, offset, ends[0], stride, extent)) { - return false; - } - if (ends.length === 1) { - return true; - } - var i, ii; - for (i = 1, ii = ends.length; i < ii; ++i) { - if (ol.geom.flat.contains.linearRingContainsExtent( - flatCoordinates, ends[i - 1], ends[i], stride, extent)) { - return false; - } - } - return true; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @param {ol.Extent} extent Extent. - * @return {boolean} True if the geometry and the extent intersect. - */ -ol.geom.flat.intersectsextent.linearRingss = function(flatCoordinates, offset, endss, stride, extent) { - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - if (ol.geom.flat.intersectsextent.linearRings( - flatCoordinates, offset, ends, stride, extent)) { - return true; - } - offset = ends[ends.length - 1]; - } - return false; -}; - -goog.provide('ol.geom.flat.reverse'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - */ -ol.geom.flat.reverse.coordinates = function(flatCoordinates, offset, end, stride) { - while (offset < end - stride) { - var i; - for (i = 0; i < stride; ++i) { - var tmp = flatCoordinates[offset + i]; - flatCoordinates[offset + i] = flatCoordinates[end - stride + i]; - flatCoordinates[end - stride + i] = tmp; - } - offset += stride; - end -= stride; - } -}; - -goog.provide('ol.geom.flat.orient'); - -goog.require('ol.geom.flat.reverse'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {boolean} Is clockwise. - */ -ol.geom.flat.orient.linearRingIsClockwise = function(flatCoordinates, offset, end, stride) { - // http://tinyurl.com/clockwise-method - // https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogrlinearring.cpp - var edge = 0; - var x1 = flatCoordinates[end - stride]; - var y1 = flatCoordinates[end - stride + 1]; - for (; offset < end; offset += stride) { - var x2 = flatCoordinates[offset]; - var y2 = flatCoordinates[offset + 1]; - edge += (x2 - x1) * (y2 + y1); - x1 = x2; - y1 = y2; - } - return edge > 0; -}; - - -/** - * Determines if linear rings are oriented. By default, left-hand orientation - * is tested (first ring must be clockwise, remaining rings counter-clockwise). - * To test for right-hand orientation, use the `opt_right` argument. - * - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Array of end indexes. - * @param {number} stride Stride. - * @param {boolean=} opt_right Test for right-hand orientation - * (counter-clockwise exterior ring and clockwise interior rings). - * @return {boolean} Rings are correctly oriented. - */ -ol.geom.flat.orient.linearRingsAreOriented = function(flatCoordinates, offset, ends, stride, opt_right) { - var right = opt_right !== undefined ? opt_right : false; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( - flatCoordinates, offset, end, stride); - if (i === 0) { - if ((right && isClockwise) || (!right && !isClockwise)) { - return false; - } - } else { - if ((right && !isClockwise) || (!right && isClockwise)) { - return false; - } - } - offset = end; - } - return true; -}; - - -/** - * Determines if linear rings are oriented. By default, left-hand orientation - * is tested (first ring must be clockwise, remaining rings counter-clockwise). - * To test for right-hand orientation, use the `opt_right` argument. - * - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Array of array of end indexes. - * @param {number} stride Stride. - * @param {boolean=} opt_right Test for right-hand orientation - * (counter-clockwise exterior ring and clockwise interior rings). - * @return {boolean} Rings are correctly oriented. - */ -ol.geom.flat.orient.linearRingssAreOriented = function(flatCoordinates, offset, endss, stride, opt_right) { - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - if (!ol.geom.flat.orient.linearRingsAreOriented( - flatCoordinates, offset, endss[i], stride, opt_right)) { - return false; - } - } - return true; -}; - - -/** - * Orient coordinates in a flat array of linear rings. By default, rings - * are oriented following the left-hand rule (clockwise for exterior and - * counter-clockwise for interior rings). To orient according to the - * right-hand rule, use the `opt_right` argument. - * - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {boolean=} opt_right Follow the right-hand rule for orientation. - * @return {number} End. - */ -ol.geom.flat.orient.orientLinearRings = function(flatCoordinates, offset, ends, stride, opt_right) { - var right = opt_right !== undefined ? opt_right : false; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( - flatCoordinates, offset, end, stride); - var reverse = i === 0 ? - (right && isClockwise) || (!right && !isClockwise) : - (right && !isClockwise) || (!right && isClockwise); - if (reverse) { - ol.geom.flat.reverse.coordinates(flatCoordinates, offset, end, stride); - } - offset = end; - } - return offset; -}; - - -/** - * Orient coordinates in a flat array of linear rings. By default, rings - * are oriented following the left-hand rule (clockwise for exterior and - * counter-clockwise for interior rings). To orient according to the - * right-hand rule, use the `opt_right` argument. - * - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Array of array of end indexes. - * @param {number} stride Stride. - * @param {boolean=} opt_right Follow the right-hand rule for orientation. - * @return {number} End. - */ -ol.geom.flat.orient.orientLinearRingss = function(flatCoordinates, offset, endss, stride, opt_right) { - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - offset = ol.geom.flat.orient.orientLinearRings( - flatCoordinates, offset, endss[i], stride, opt_right); - } - return offset; -}; - -goog.provide('ol.geom.Polygon'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LinearRing'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.area'); -goog.require('ol.geom.flat.closest'); -goog.require('ol.geom.flat.contains'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.geom.flat.interiorpoint'); -goog.require('ol.geom.flat.intersectsextent'); -goog.require('ol.geom.flat.orient'); -goog.require('ol.geom.flat.simplify'); -goog.require('ol.math'); - - -/** - * @classdesc - * Polygon geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {Array.<Array.<ol.Coordinate>>} coordinates Array of linear - * rings that define the polygon. The first linear ring of the array - * defines the outer-boundary or surface of the polygon. Each subsequent - * linear ring defines a hole in the surface of the polygon. A linear ring - * is an array of vertices' coordinates where the first coordinate and the - * last are equivalent. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.Polygon = function(coordinates, opt_layout) { - - ol.geom.SimpleGeometry.call(this); - - /** - * @type {Array.<number>} - * @private - */ - this.ends_ = []; - - /** - * @private - * @type {number} - */ - this.flatInteriorPointRevision_ = -1; - - /** - * @private - * @type {ol.Coordinate} - */ - this.flatInteriorPoint_ = null; - - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; - - /** - * @private - * @type {number} - */ - this.orientedRevision_ = -1; - - /** - * @private - * @type {Array.<number>} - */ - this.orientedFlatCoordinates_ = null; - - this.setCoordinates(coordinates, opt_layout); - -}; -ol.inherits(ol.geom.Polygon, ol.geom.SimpleGeometry); - - -/** - * Append the passed linear ring to this polygon. - * @param {ol.geom.LinearRing} linearRing Linear ring. - * @api - */ -ol.geom.Polygon.prototype.appendLinearRing = function(linearRing) { - if (!this.flatCoordinates) { - this.flatCoordinates = linearRing.getFlatCoordinates().slice(); - } else { - ol.array.extend(this.flatCoordinates, linearRing.getFlatCoordinates()); - } - this.ends_.push(this.flatCoordinates.length); - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.Polygon} Clone. - * @override - * @api - */ -ol.geom.Polygon.prototype.clone = function() { - var polygon = new ol.geom.Polygon(null); - polygon.setFlatCoordinates( - this.layout, this.flatCoordinates.slice(), this.ends_.slice()); - return polygon; -}; - - -/** - * @inheritDoc - */ -ol.geom.Polygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta( - this.flatCoordinates, 0, this.ends_, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return ol.geom.flat.closest.getsClosestPoint( - this.flatCoordinates, 0, this.ends_, this.stride, - this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * @inheritDoc - */ -ol.geom.Polygon.prototype.containsXY = function(x, y) { - return ol.geom.flat.contains.linearRingsContainsXY( - this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y); -}; - - -/** - * Return the area of the polygon on projected plane. - * @return {number} Area (on projected plane). - * @api - */ -ol.geom.Polygon.prototype.getArea = function() { - return ol.geom.flat.area.linearRings( - this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride); -}; - - -/** - * Get the coordinate array for this geometry. This array has the structure - * of a GeoJSON coordinate array for polygons. - * - * @param {boolean=} opt_right Orient coordinates according to the right-hand - * rule (counter-clockwise for exterior and clockwise for interior rings). - * If `false`, coordinates will be oriented according to the left-hand rule - * (clockwise for exterior and counter-clockwise for interior rings). - * By default, coordinate orientation will depend on how the geometry was - * constructed. - * @return {Array.<Array.<ol.Coordinate>>} Coordinates. - * @override - * @api - */ -ol.geom.Polygon.prototype.getCoordinates = function(opt_right) { - var flatCoordinates; - if (opt_right !== undefined) { - flatCoordinates = this.getOrientedFlatCoordinates().slice(); - ol.geom.flat.orient.orientLinearRings( - flatCoordinates, 0, this.ends_, this.stride, opt_right); - } else { - flatCoordinates = this.flatCoordinates; - } - - return ol.geom.flat.inflate.coordinatess( - flatCoordinates, 0, this.ends_, this.stride); -}; - - -/** - * @return {Array.<number>} Ends. - */ -ol.geom.Polygon.prototype.getEnds = function() { - return this.ends_; -}; - - -/** - * @return {Array.<number>} Interior point. - */ -ol.geom.Polygon.prototype.getFlatInteriorPoint = function() { - if (this.flatInteriorPointRevision_ != this.getRevision()) { - var flatCenter = ol.extent.getCenter(this.getExtent()); - this.flatInteriorPoint_ = ol.geom.flat.interiorpoint.linearRings( - this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, - flatCenter, 0); - this.flatInteriorPointRevision_ = this.getRevision(); - } - return this.flatInteriorPoint_; -}; - - -/** - * Return an interior point of the polygon. - * @return {ol.geom.Point} Interior point. - * @api - */ -ol.geom.Polygon.prototype.getInteriorPoint = function() { - return new ol.geom.Point(this.getFlatInteriorPoint()); -}; - - -/** - * Return the number of rings of the polygon, this includes the exterior - * ring and any interior rings. - * - * @return {number} Number of rings. - * @api - */ -ol.geom.Polygon.prototype.getLinearRingCount = function() { - return this.ends_.length; -}; - - -/** - * Return the Nth linear ring of the polygon geometry. Return `null` if the - * given index is out of range. - * The exterior linear ring is available at index `0` and the interior rings - * at index `1` and beyond. - * - * @param {number} index Index. - * @return {ol.geom.LinearRing} Linear ring. - * @api - */ -ol.geom.Polygon.prototype.getLinearRing = function(index) { - if (index < 0 || this.ends_.length <= index) { - return null; - } - var linearRing = new ol.geom.LinearRing(null); - linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice( - index === 0 ? 0 : this.ends_[index - 1], this.ends_[index])); - return linearRing; -}; - - -/** - * Return the linear rings of the polygon. - * @return {Array.<ol.geom.LinearRing>} Linear rings. - * @api - */ -ol.geom.Polygon.prototype.getLinearRings = function() { - var layout = this.layout; - var flatCoordinates = this.flatCoordinates; - var ends = this.ends_; - var linearRings = []; - var offset = 0; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - var linearRing = new ol.geom.LinearRing(null); - linearRing.setFlatCoordinates(layout, flatCoordinates.slice(offset, end)); - linearRings.push(linearRing); - offset = end; - } - return linearRings; -}; - - -/** - * @return {Array.<number>} Oriented flat coordinates. - */ -ol.geom.Polygon.prototype.getOrientedFlatCoordinates = function() { - if (this.orientedRevision_ != this.getRevision()) { - var flatCoordinates = this.flatCoordinates; - if (ol.geom.flat.orient.linearRingsAreOriented( - flatCoordinates, 0, this.ends_, this.stride)) { - this.orientedFlatCoordinates_ = flatCoordinates; - } else { - this.orientedFlatCoordinates_ = flatCoordinates.slice(); - this.orientedFlatCoordinates_.length = - ol.geom.flat.orient.orientLinearRings( - this.orientedFlatCoordinates_, 0, this.ends_, this.stride); - } - this.orientedRevision_ = this.getRevision(); - } - return this.orientedFlatCoordinates_; -}; - - -/** - * @inheritDoc - */ -ol.geom.Polygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - var simplifiedFlatCoordinates = []; - var simplifiedEnds = []; - simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizes( - this.flatCoordinates, 0, this.ends_, this.stride, - Math.sqrt(squaredTolerance), - simplifiedFlatCoordinates, 0, simplifiedEnds); - var simplifiedPolygon = new ol.geom.Polygon(null); - simplifiedPolygon.setFlatCoordinates( - ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds); - return simplifiedPolygon; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Polygon.prototype.getType = function() { - return ol.geom.GeometryType.POLYGON; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Polygon.prototype.intersectsExtent = function(extent) { - return ol.geom.flat.intersectsextent.linearRings( - this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent); -}; - - -/** - * Set the coordinates of the polygon. - * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -ol.geom.Polygon.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_); - } else { - this.setLayout(opt_layout, coordinates, 2); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - var ends = ol.geom.flat.deflate.coordinatess( - this.flatCoordinates, 0, coordinates, this.stride, this.ends_); - this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {Array.<number>} ends Ends. - */ -ol.geom.Polygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.ends_ = ends; - this.changed(); -}; - - -/** - * Create an approximation of a circle on the surface of a sphere. - * @param {ol.Sphere} sphere The sphere. - * @param {ol.Coordinate} center Center (`[lon, lat]` in degrees). - * @param {number} radius The great-circle distance from the center to - * the polygon vertices. - * @param {number=} opt_n Optional number of vertices for the resulting - * polygon. Default is `32`. - * @return {ol.geom.Polygon} The "circular" polygon. - * @api - */ -ol.geom.Polygon.circular = function(sphere, center, radius, opt_n) { - var n = opt_n ? opt_n : 32; - /** @type {Array.<number>} */ - var flatCoordinates = []; - var i; - for (i = 0; i < n; ++i) { - ol.array.extend( - flatCoordinates, sphere.offset(center, radius, 2 * Math.PI * i / n)); - } - flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]); - var polygon = new ol.geom.Polygon(null); - polygon.setFlatCoordinates( - ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]); - return polygon; -}; - - -/** - * Create a polygon from an extent. The layout used is `XY`. - * @param {ol.Extent} extent The extent. - * @return {ol.geom.Polygon} The polygon. - * @api - */ -ol.geom.Polygon.fromExtent = function(extent) { - var minX = extent[0]; - var minY = extent[1]; - var maxX = extent[2]; - var maxY = extent[3]; - var flatCoordinates = - [minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY]; - var polygon = new ol.geom.Polygon(null); - polygon.setFlatCoordinates( - ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]); - return polygon; -}; - - -/** - * Create a regular polygon from a circle. - * @param {ol.geom.Circle} circle Circle geometry. - * @param {number=} opt_sides Number of sides of the polygon. Default is 32. - * @param {number=} opt_angle Start angle for the first vertex of the polygon in - * radians. Default is 0. - * @return {ol.geom.Polygon} Polygon geometry. - * @api - */ -ol.geom.Polygon.fromCircle = function(circle, opt_sides, opt_angle) { - var sides = opt_sides ? opt_sides : 32; - var stride = circle.getStride(); - var layout = circle.getLayout(); - var polygon = new ol.geom.Polygon(null, layout); - var arrayLength = stride * (sides + 1); - var flatCoordinates = new Array(arrayLength); - for (var i = 0; i < arrayLength; i++) { - flatCoordinates[i] = 0; - } - var ends = [flatCoordinates.length]; - polygon.setFlatCoordinates(layout, flatCoordinates, ends); - ol.geom.Polygon.makeRegular( - polygon, circle.getCenter(), circle.getRadius(), opt_angle); - return polygon; -}; - - -/** - * Modify the coordinates of a polygon to make it a regular polygon. - * @param {ol.geom.Polygon} polygon Polygon geometry. - * @param {ol.Coordinate} center Center of the regular polygon. - * @param {number} radius Radius of the regular polygon. - * @param {number=} opt_angle Start angle for the first vertex of the polygon in - * radians. Default is 0. - */ -ol.geom.Polygon.makeRegular = function(polygon, center, radius, opt_angle) { - var flatCoordinates = polygon.getFlatCoordinates(); - var layout = polygon.getLayout(); - var stride = polygon.getStride(); - var ends = polygon.getEnds(); - var sides = flatCoordinates.length / stride - 1; - var startAngle = opt_angle ? opt_angle : 0; - var angle, offset; - for (var i = 0; i <= sides; ++i) { - offset = i * stride; - angle = startAngle + (ol.math.modulo(i, sides) * 2 * Math.PI / sides); - flatCoordinates[offset] = center[0] + (radius * Math.cos(angle)); - flatCoordinates[offset + 1] = center[1] + (radius * Math.sin(angle)); - } - polygon.setFlatCoordinates(layout, flatCoordinates, ends); -}; - -goog.provide('ol.View'); - -goog.require('ol'); -goog.require('ol.CenterConstraint'); -goog.require('ol.Object'); -goog.require('ol.ResolutionConstraint'); -goog.require('ol.RotationConstraint'); -goog.require('ol.ViewHint'); -goog.require('ol.ViewProperty'); -goog.require('ol.array'); -goog.require('ol.asserts'); -goog.require('ol.coordinate'); -goog.require('ol.easing'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Polygon'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.proj.Units'); - - -/** - * @classdesc - * An ol.View object represents a simple 2D view of the map. - * - * This is the object to act upon to change the center, resolution, - * and rotation of the map. - * - * ### The view states - * - * An `ol.View` is determined by three states: `center`, `resolution`, - * and `rotation`. Each state has a corresponding getter and setter, e.g. - * `getCenter` and `setCenter` for the `center` state. - * - * An `ol.View` has a `projection`. The projection determines the - * coordinate system of the center, and its units determine the units of the - * resolution (projection units per pixel). The default projection is - * Spherical Mercator (EPSG:3857). - * - * ### The constraints - * - * `setCenter`, `setResolution` and `setRotation` can be used to change the - * states of the view. Any value can be passed to the setters. And the value - * that is passed to a setter will effectively be the value set in the view, - * and returned by the corresponding getter. - * - * But an `ol.View` object also has a *resolution constraint*, a - * *rotation constraint* and a *center constraint*. - * - * As said above, no constraints are applied when the setters are used to set - * new states for the view. Applying constraints is done explicitly through - * the use of the `constrain*` functions (`constrainResolution` and - * `constrainRotation` and `constrainCenter`). - * - * The main users of the constraints are the interactions and the - * controls. For example, double-clicking on the map changes the view to - * the "next" resolution. And releasing the fingers after pinch-zooming - * snaps to the closest resolution (with an animation). - * - * The *resolution constraint* snaps to specific resolutions. It is - * determined by the following options: `resolutions`, `maxResolution`, - * `maxZoom`, and `zoomFactor`. If `resolutions` is set, the other three - * options are ignored. See documentation for each option for more - * information. - * - * The *rotation constraint* snaps to specific angles. It is determined - * by the following options: `enableRotation` and `constrainRotation`. - * By default the rotation value is snapped to zero when approaching the - * horizontal. - * - * The *center constraint* is determined by the `extent` option. By - * default the center is not constrained at all. - * - * @constructor - * @extends {ol.Object} - * @param {olx.ViewOptions=} opt_options View options. - * @api - */ -ol.View = function(opt_options) { - ol.Object.call(this); - - var options = ol.obj.assign({}, opt_options); - - /** - * @private - * @type {Array.<number>} - */ - this.hints_ = [0, 0]; - - /** - * @private - * @type {Array.<Array.<ol.ViewAnimation>>} - */ - this.animations_ = []; - - /** - * @private - * @type {number|undefined} - */ - this.updateAnimationKey_; - - this.updateAnimations_ = this.updateAnimations_.bind(this); - - /** - * @private - * @const - * @type {ol.proj.Projection} - */ - this.projection_ = ol.proj.createProjection(options.projection, 'EPSG:3857'); - - this.applyOptions_(options); -}; -ol.inherits(ol.View, ol.Object); - - -/** - * Set up the view with the given options. - * @param {olx.ViewOptions} options View options. - */ -ol.View.prototype.applyOptions_ = function(options) { - - /** - * @type {Object.<string, *>} - */ - var properties = {}; - properties[ol.ViewProperty.CENTER] = options.center !== undefined ? - options.center : null; - - var resolutionConstraintInfo = ol.View.createResolutionConstraint_( - options); - - /** - * @private - * @type {number} - */ - this.maxResolution_ = resolutionConstraintInfo.maxResolution; - - /** - * @private - * @type {number} - */ - this.minResolution_ = resolutionConstraintInfo.minResolution; - - /** - * @private - * @type {number} - */ - this.zoomFactor_ = resolutionConstraintInfo.zoomFactor; - - /** - * @private - * @type {Array.<number>|undefined} - */ - this.resolutions_ = options.resolutions; - - /** - * @private - * @type {number} - */ - this.minZoom_ = resolutionConstraintInfo.minZoom; - - var centerConstraint = ol.View.createCenterConstraint_(options); - var resolutionConstraint = resolutionConstraintInfo.constraint; - var rotationConstraint = ol.View.createRotationConstraint_(options); - - /** - * @private - * @type {ol.Constraints} - */ - this.constraints_ = { - center: centerConstraint, - resolution: resolutionConstraint, - rotation: rotationConstraint - }; - - if (options.resolution !== undefined) { - properties[ol.ViewProperty.RESOLUTION] = options.resolution; - } else if (options.zoom !== undefined) { - properties[ol.ViewProperty.RESOLUTION] = this.constrainResolution( - this.maxResolution_, options.zoom - this.minZoom_); - } - properties[ol.ViewProperty.ROTATION] = - options.rotation !== undefined ? options.rotation : 0; - this.setProperties(properties); - - /** - * @private - * @type {olx.ViewOptions} - */ - this.options_ = options; - -}; - -/** - * Get an updated version of the view options used to construct the view. The - * current resolution (or zoom), center, and rotation are applied to any stored - * options. The provided options can be uesd to apply new min/max zoom or - * resolution limits. - * @param {olx.ViewOptions} newOptions New options to be applied. - * @return {olx.ViewOptions} New options updated with the current view state. - */ -ol.View.prototype.getUpdatedOptions_ = function(newOptions) { - var options = ol.obj.assign({}, this.options_); - - // preserve resolution (or zoom) - if (options.resolution !== undefined) { - options.resolution = this.getResolution(); - } else { - options.zoom = this.getZoom(); - } - - // preserve center - options.center = this.getCenter(); - - // preserve rotation - options.rotation = this.getRotation(); - - return ol.obj.assign({}, options, newOptions); -}; - - -/** - * Animate the view. The view's center, zoom (or resolution), and rotation - * can be animated for smooth transitions between view states. For example, - * to animate the view to a new zoom level: - * - * view.animate({zoom: view.getZoom() + 1}); - * - * By default, the animation lasts one second and uses in-and-out easing. You - * can customize this behavior by including `duration` (in milliseconds) and - * `easing` options (see {@link ol.easing}). - * - * To chain together multiple animations, call the method with multiple - * animation objects. For example, to first zoom and then pan: - * - * view.animate({zoom: 10}, {center: [0, 0]}); - * - * If you provide a function as the last argument to the animate method, it - * will get called at the end of an animation series. The callback will be - * called with `true` if the animation series completed on its own or `false` - * if it was cancelled. - * - * Animations are cancelled by user interactions (e.g. dragging the map) or by - * calling `view.setCenter()`, `view.setResolution()`, or `view.setRotation()` - * (or another method that calls one of these). - * - * @param {...(olx.AnimationOptions|function(boolean))} var_args Animation - * options. Multiple animations can be run in series by passing multiple - * options objects. To run multiple animations in parallel, call the method - * multiple times. An optional callback can be provided as a final - * argument. The callback will be called with a boolean indicating whether - * the animation completed without being cancelled. - * @api - */ -ol.View.prototype.animate = function(var_args) { - var start = Date.now(); - var center = this.getCenter().slice(); - var resolution = this.getResolution(); - var rotation = this.getRotation(); - var animationCount = arguments.length; - var callback; - if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') { - callback = arguments[animationCount - 1]; - --animationCount; - } - var series = []; - for (var i = 0; i < animationCount; ++i) { - var options = /** @type {olx.AnimationOptions} */ (arguments[i]); - - var animation = /** @type {ol.ViewAnimation} */ ({ - start: start, - complete: false, - anchor: options.anchor, - duration: options.duration !== undefined ? options.duration : 1000, - easing: options.easing || ol.easing.inAndOut - }); - - if (options.center) { - animation.sourceCenter = center; - animation.targetCenter = options.center; - center = animation.targetCenter; - } - - if (options.zoom !== undefined) { - animation.sourceResolution = resolution; - animation.targetResolution = this.constrainResolution( - this.maxResolution_, options.zoom - this.minZoom_, 0); - resolution = animation.targetResolution; - } else if (options.resolution) { - animation.sourceResolution = resolution; - animation.targetResolution = options.resolution; - resolution = animation.targetResolution; - } - - if (options.rotation !== undefined) { - animation.sourceRotation = rotation; - animation.targetRotation = options.rotation; - rotation = animation.targetRotation; - } - - animation.callback = callback; - start += animation.duration; - series.push(animation); - } - this.animations_.push(series); - this.setHint(ol.ViewHint.ANIMATING, 1); - this.updateAnimations_(); -}; - - -/** - * Determine if the view is being animated. - * @return {boolean} The view is being animated. - * @api - */ -ol.View.prototype.getAnimating = function() { - return this.getHints()[ol.ViewHint.ANIMATING] > 0; -}; - - -/** - * Determine if the user is interacting with the view, such as panning or zooming. - * @return {boolean} The view is being interacted with. - * @api - */ -ol.View.prototype.getInteracting = function() { - return this.getHints()[ol.ViewHint.INTERACTING] > 0; -}; - - -/** - * Cancel any ongoing animations. - * @api - */ -ol.View.prototype.cancelAnimations = function() { - this.setHint(ol.ViewHint.ANIMATING, -this.getHints()[ol.ViewHint.ANIMATING]); - for (var i = 0, ii = this.animations_.length; i < ii; ++i) { - var series = this.animations_[i]; - if (series[0].callback) { - series[0].callback(false); - } - } - this.animations_.length = 0; -}; - -/** - * Update all animations. - */ -ol.View.prototype.updateAnimations_ = function() { - if (this.updateAnimationKey_ !== undefined) { - cancelAnimationFrame(this.updateAnimationKey_); - this.updateAnimationKey_ = undefined; - } - if (!this.getAnimating()) { - return; - } - var now = Date.now(); - var more = false; - for (var i = this.animations_.length - 1; i >= 0; --i) { - var series = this.animations_[i]; - var seriesComplete = true; - for (var j = 0, jj = series.length; j < jj; ++j) { - var animation = series[j]; - if (animation.complete) { - continue; - } - var elapsed = now - animation.start; - var fraction = animation.duration > 0 ? elapsed / animation.duration : 1; - if (fraction >= 1) { - animation.complete = true; - fraction = 1; - } else { - seriesComplete = false; - } - var progress = animation.easing(fraction); - if (animation.sourceCenter) { - var x0 = animation.sourceCenter[0]; - var y0 = animation.sourceCenter[1]; - var x1 = animation.targetCenter[0]; - var y1 = animation.targetCenter[1]; - var x = x0 + progress * (x1 - x0); - var y = y0 + progress * (y1 - y0); - this.set(ol.ViewProperty.CENTER, [x, y]); - } - if (animation.sourceResolution && animation.targetResolution) { - var resolution = progress === 1 ? - animation.targetResolution : - animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); - if (animation.anchor) { - this.set(ol.ViewProperty.CENTER, - this.calculateCenterZoom(resolution, animation.anchor)); - } - this.set(ol.ViewProperty.RESOLUTION, resolution); - } - if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) { - var rotation = progress === 1 ? - animation.targetRotation : - animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation); - if (animation.anchor) { - this.set(ol.ViewProperty.CENTER, - this.calculateCenterRotate(rotation, animation.anchor)); - } - this.set(ol.ViewProperty.ROTATION, rotation); - } - more = true; - if (!animation.complete) { - break; - } - } - if (seriesComplete) { - this.animations_[i] = null; - this.setHint(ol.ViewHint.ANIMATING, -1); - var callback = series[0].callback; - if (callback) { - callback(true); - } - } - } - // prune completed series - this.animations_ = this.animations_.filter(Boolean); - if (more && this.updateAnimationKey_ === undefined) { - this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_); - } -}; - -/** - * @param {number} rotation Target rotation. - * @param {ol.Coordinate} anchor Rotation anchor. - * @return {ol.Coordinate|undefined} Center for rotation and anchor. - */ -ol.View.prototype.calculateCenterRotate = function(rotation, anchor) { - var center; - var currentCenter = this.getCenter(); - if (currentCenter !== undefined) { - center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]]; - ol.coordinate.rotate(center, rotation - this.getRotation()); - ol.coordinate.add(center, anchor); - } - return center; -}; - - -/** - * @param {number} resolution Target resolution. - * @param {ol.Coordinate} anchor Zoom anchor. - * @return {ol.Coordinate|undefined} Center for resolution and anchor. - */ -ol.View.prototype.calculateCenterZoom = function(resolution, anchor) { - var center; - var currentCenter = this.getCenter(); - var currentResolution = this.getResolution(); - if (currentCenter !== undefined && currentResolution !== undefined) { - var x = anchor[0] - - resolution * (anchor[0] - currentCenter[0]) / currentResolution; - var y = anchor[1] - - resolution * (anchor[1] - currentCenter[1]) / currentResolution; - center = [x, y]; - } - return center; -}; - - -/** - * @private - * @return {ol.Size} Viewport size or `[100, 100]` when no viewport is found. - */ -ol.View.prototype.getSizeFromViewport_ = function() { - var size = [100, 100]; - var selector = '.ol-viewport[data-view="' + ol.getUid(this) + '"]'; - var element = document.querySelector(selector); - if (element) { - var metrics = getComputedStyle(element); - size[0] = parseInt(metrics.width, 10); - size[1] = parseInt(metrics.height, 10); - } - return size; -}; - - -/** - * Get the constrained center of this view. - * @param {ol.Coordinate|undefined} center Center. - * @return {ol.Coordinate|undefined} Constrained center. - * @api - */ -ol.View.prototype.constrainCenter = function(center) { - return this.constraints_.center(center); -}; - - -/** - * Get the constrained resolution of this view. - * @param {number|undefined} resolution Resolution. - * @param {number=} opt_delta Delta. Default is `0`. - * @param {number=} opt_direction Direction. Default is `0`. - * @return {number|undefined} Constrained resolution. - * @api - */ -ol.View.prototype.constrainResolution = function( - resolution, opt_delta, opt_direction) { - var delta = opt_delta || 0; - var direction = opt_direction || 0; - return this.constraints_.resolution(resolution, delta, direction); -}; - - -/** - * Get the constrained rotation of this view. - * @param {number|undefined} rotation Rotation. - * @param {number=} opt_delta Delta. Default is `0`. - * @return {number|undefined} Constrained rotation. - * @api - */ -ol.View.prototype.constrainRotation = function(rotation, opt_delta) { - var delta = opt_delta || 0; - return this.constraints_.rotation(rotation, delta); -}; - - -/** - * Get the view center. - * @return {ol.Coordinate|undefined} The center of the view. - * @observable - * @api - */ -ol.View.prototype.getCenter = function() { - return /** @type {ol.Coordinate|undefined} */ ( - this.get(ol.ViewProperty.CENTER)); -}; - - -/** - * @return {ol.Constraints} Constraints. - */ -ol.View.prototype.getConstraints = function() { - return this.constraints_; -}; - - -/** - * @param {Array.<number>=} opt_hints Destination array. - * @return {Array.<number>} Hint. - */ -ol.View.prototype.getHints = function(opt_hints) { - if (opt_hints !== undefined) { - opt_hints[0] = this.hints_[0]; - opt_hints[1] = this.hints_[1]; - return opt_hints; - } else { - return this.hints_.slice(); - } -}; - - -/** - * Calculate the extent for the current view state and the passed size. - * The size is the pixel dimensions of the box into which the calculated extent - * should fit. In most cases you want to get the extent of the entire map, - * that is `map.getSize()`. - * @param {ol.Size=} opt_size Box pixel size. If not provided, the size of the - * first map that uses this view will be used. - * @return {ol.Extent} Extent. - * @api - */ -ol.View.prototype.calculateExtent = function(opt_size) { - var size = opt_size || this.getSizeFromViewport_(); - var center = /** @type {!ol.Coordinate} */ (this.getCenter()); - ol.asserts.assert(center, 1); // The view center is not defined - var resolution = /** @type {!number} */ (this.getResolution()); - ol.asserts.assert(resolution !== undefined, 2); // The view resolution is not defined - var rotation = /** @type {!number} */ (this.getRotation()); - ol.asserts.assert(rotation !== undefined, 3); // The view rotation is not defined - - return ol.extent.getForViewAndSize(center, resolution, rotation, size); -}; - - -/** - * Get the maximum resolution of the view. - * @return {number} The maximum resolution of the view. - * @api - */ -ol.View.prototype.getMaxResolution = function() { - return this.maxResolution_; -}; - - -/** - * Get the minimum resolution of the view. - * @return {number} The minimum resolution of the view. - * @api - */ -ol.View.prototype.getMinResolution = function() { - return this.minResolution_; -}; - - -/** - * Get the maximum zoom level for the view. - * @return {number} The maximum zoom level. - * @api - */ -ol.View.prototype.getMaxZoom = function() { - return /** @type {number} */ (this.getZoomForResolution(this.minResolution_)); -}; - - -/** - * Set a new maximum zoom level for the view. - * @param {number} zoom The maximum zoom level. - * @api - */ -ol.View.prototype.setMaxZoom = function(zoom) { - this.applyOptions_(this.getUpdatedOptions_({maxZoom: zoom})); -}; - - -/** - * Get the minimum zoom level for the view. - * @return {number} The minimum zoom level. - * @api - */ -ol.View.prototype.getMinZoom = function() { - return /** @type {number} */ (this.getZoomForResolution(this.maxResolution_)); -}; - - -/** - * Set a new minimum zoom level for the view. - * @param {number} zoom The minimum zoom level. - * @api - */ -ol.View.prototype.setMinZoom = function(zoom) { - this.applyOptions_(this.getUpdatedOptions_({minZoom: zoom})); -}; - - -/** - * Get the view projection. - * @return {ol.proj.Projection} The projection of the view. - * @api - */ -ol.View.prototype.getProjection = function() { - return this.projection_; -}; - - -/** - * Get the view resolution. - * @return {number|undefined} The resolution of the view. - * @observable - * @api - */ -ol.View.prototype.getResolution = function() { - return /** @type {number|undefined} */ ( - this.get(ol.ViewProperty.RESOLUTION)); -}; - - -/** - * Get the resolutions for the view. This returns the array of resolutions - * passed to the constructor of the {ol.View}, or undefined if none were given. - * @return {Array.<number>|undefined} The resolutions of the view. - * @api - */ -ol.View.prototype.getResolutions = function() { - return this.resolutions_; -}; - - -/** - * Get the resolution for a provided extent (in map units) and size (in pixels). - * @param {ol.Extent} extent Extent. - * @param {ol.Size=} opt_size Box pixel size. - * @return {number} The resolution at which the provided extent will render at - * the given size. - * @api - */ -ol.View.prototype.getResolutionForExtent = function(extent, opt_size) { - var size = opt_size || this.getSizeFromViewport_(); - var xResolution = ol.extent.getWidth(extent) / size[0]; - var yResolution = ol.extent.getHeight(extent) / size[1]; - return Math.max(xResolution, yResolution); -}; - - -/** - * Return a function that returns a value between 0 and 1 for a - * resolution. Exponential scaling is assumed. - * @param {number=} opt_power Power. - * @return {function(number): number} Resolution for value function. - */ -ol.View.prototype.getResolutionForValueFunction = function(opt_power) { - var power = opt_power || 2; - var maxResolution = this.maxResolution_; - var minResolution = this.minResolution_; - var max = Math.log(maxResolution / minResolution) / Math.log(power); - return ( - /** - * @param {number} value Value. - * @return {number} Resolution. - */ - function(value) { - var resolution = maxResolution / Math.pow(power, value * max); - return resolution; - }); -}; - - -/** - * Get the view rotation. - * @return {number} The rotation of the view in radians. - * @observable - * @api - */ -ol.View.prototype.getRotation = function() { - return /** @type {number} */ (this.get(ol.ViewProperty.ROTATION)); -}; - - -/** - * Return a function that returns a resolution for a value between - * 0 and 1. Exponential scaling is assumed. - * @param {number=} opt_power Power. - * @return {function(number): number} Value for resolution function. - */ -ol.View.prototype.getValueForResolutionFunction = function(opt_power) { - var power = opt_power || 2; - var maxResolution = this.maxResolution_; - var minResolution = this.minResolution_; - var max = Math.log(maxResolution / minResolution) / Math.log(power); - return ( - /** - * @param {number} resolution Resolution. - * @return {number} Value. - */ - function(resolution) { - var value = - (Math.log(maxResolution / resolution) / Math.log(power)) / max; - return value; - }); -}; - - -/** - * @return {olx.ViewState} View state. - */ -ol.View.prototype.getState = function() { - var center = /** @type {ol.Coordinate} */ (this.getCenter()); - var projection = this.getProjection(); - var resolution = /** @type {number} */ (this.getResolution()); - var rotation = this.getRotation(); - return /** @type {olx.ViewState} */ ({ - center: center.slice(), - projection: projection !== undefined ? projection : null, - resolution: resolution, - rotation: rotation - }); -}; - - -/** - * Get the current zoom level. Return undefined if the current - * resolution is undefined or not within the "resolution constraints". - * @return {number|undefined} Zoom. - * @api - */ -ol.View.prototype.getZoom = function() { - var zoom; - var resolution = this.getResolution(); - if (resolution !== undefined) { - zoom = this.getZoomForResolution(resolution); - } - return zoom; -}; - - -/** - * Get the zoom level for a resolution. - * @param {number} resolution The resolution. - * @return {number|undefined} The zoom level for the provided resolution. - * @api - */ -ol.View.prototype.getZoomForResolution = function(resolution) { - var zoom; - if (resolution >= this.minResolution_ && resolution <= this.maxResolution_) { - var offset = this.minZoom_ || 0; - var max, zoomFactor; - if (this.resolutions_) { - var nearest = ol.array.linearFindNearest(this.resolutions_, resolution, 1); - offset += nearest; - if (nearest == this.resolutions_.length - 1) { - return offset; - } - max = this.resolutions_[nearest]; - zoomFactor = max / this.resolutions_[nearest + 1]; - } else { - max = this.maxResolution_; - zoomFactor = this.zoomFactor_; - } - zoom = offset + Math.log(max / resolution) / Math.log(zoomFactor); - } - return zoom; -}; - - -/** - * Fit the given geometry or extent based on the given map size and border. - * The size is pixel dimensions of the box to fit the extent into. - * In most cases you will want to use the map size, that is `map.getSize()`. - * Takes care of the map angle. - * @param {ol.geom.SimpleGeometry|ol.Extent} geometryOrExtent The geometry or - * extent to fit the view to. - * @param {olx.view.FitOptions=} opt_options Options. - * @api - */ -ol.View.prototype.fit = function(geometryOrExtent, opt_options) { - var options = opt_options || {}; - var size = options.size; - if (!size) { - size = this.getSizeFromViewport_(); - } - /** @type {ol.geom.SimpleGeometry} */ - var geometry; - if (!(geometryOrExtent instanceof ol.geom.SimpleGeometry)) { - ol.asserts.assert(Array.isArray(geometryOrExtent), - 24); // Invalid extent or geometry provided as `geometry` - ol.asserts.assert(!ol.extent.isEmpty(geometryOrExtent), - 25); // Cannot fit empty extent provided as `geometry` - geometry = ol.geom.Polygon.fromExtent(geometryOrExtent); - } else if (geometryOrExtent.getType() === ol.geom.GeometryType.CIRCLE) { - geometryOrExtent = geometryOrExtent.getExtent(); - geometry = ol.geom.Polygon.fromExtent(geometryOrExtent); - geometry.rotate(this.getRotation(), ol.extent.getCenter(geometryOrExtent)); - } else { - geometry = geometryOrExtent; - } - - var padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; - var constrainResolution = options.constrainResolution !== undefined ? - options.constrainResolution : true; - var nearest = options.nearest !== undefined ? options.nearest : false; - var minResolution; - if (options.minResolution !== undefined) { - minResolution = options.minResolution; - } else if (options.maxZoom !== undefined) { - minResolution = this.constrainResolution( - this.maxResolution_, options.maxZoom - this.minZoom_, 0); - } else { - minResolution = 0; - } - var coords = geometry.getFlatCoordinates(); - - // calculate rotated extent - var rotation = this.getRotation(); - var cosAngle = Math.cos(-rotation); - var sinAngle = Math.sin(-rotation); - var minRotX = +Infinity; - var minRotY = +Infinity; - var maxRotX = -Infinity; - var maxRotY = -Infinity; - var stride = geometry.getStride(); - for (var i = 0, ii = coords.length; i < ii; i += stride) { - var rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; - var rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; - minRotX = Math.min(minRotX, rotX); - minRotY = Math.min(minRotY, rotY); - maxRotX = Math.max(maxRotX, rotX); - maxRotY = Math.max(maxRotY, rotY); - } - - // calculate resolution - var resolution = this.getResolutionForExtent( - [minRotX, minRotY, maxRotX, maxRotY], - [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]); - resolution = isNaN(resolution) ? minResolution : - Math.max(resolution, minResolution); - if (constrainResolution) { - var constrainedResolution = this.constrainResolution(resolution, 0, 0); - if (!nearest && constrainedResolution < resolution) { - constrainedResolution = this.constrainResolution( - constrainedResolution, -1, 0); - } - resolution = constrainedResolution; - } - - // calculate center - sinAngle = -sinAngle; // go back to original rotation - var centerRotX = (minRotX + maxRotX) / 2; - var centerRotY = (minRotY + maxRotY) / 2; - centerRotX += (padding[1] - padding[3]) / 2 * resolution; - centerRotY += (padding[0] - padding[2]) / 2 * resolution; - var centerX = centerRotX * cosAngle - centerRotY * sinAngle; - var centerY = centerRotY * cosAngle + centerRotX * sinAngle; - var center = [centerX, centerY]; - var callback = options.callback ? options.callback : ol.nullFunction; - - if (options.duration !== undefined) { - this.animate({ - resolution: resolution, - center: center, - duration: options.duration, - easing: options.easing - }, callback); - } else { - this.setResolution(resolution); - this.setCenter(center); - setTimeout(callback.bind(undefined, true), 0); - } -}; - - -/** - * Center on coordinate and view position. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.Size} size Box pixel size. - * @param {ol.Pixel} position Position on the view to center on. - * @api - */ -ol.View.prototype.centerOn = function(coordinate, size, position) { - // calculate rotated position - var rotation = this.getRotation(); - var cosAngle = Math.cos(-rotation); - var sinAngle = Math.sin(-rotation); - var rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle; - var rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle; - var resolution = this.getResolution(); - rotX += (size[0] / 2 - position[0]) * resolution; - rotY += (position[1] - size[1] / 2) * resolution; - - // go back to original angle - sinAngle = -sinAngle; // go back to original rotation - var centerX = rotX * cosAngle - rotY * sinAngle; - var centerY = rotY * cosAngle + rotX * sinAngle; - - this.setCenter([centerX, centerY]); -}; - - -/** - * @return {boolean} Is defined. - */ -ol.View.prototype.isDef = function() { - return !!this.getCenter() && this.getResolution() !== undefined; -}; - - -/** - * Rotate the view around a given coordinate. - * @param {number} rotation New rotation value for the view. - * @param {ol.Coordinate=} opt_anchor The rotation center. - * @api - */ -ol.View.prototype.rotate = function(rotation, opt_anchor) { - if (opt_anchor !== undefined) { - var center = this.calculateCenterRotate(rotation, opt_anchor); - this.setCenter(center); - } - this.setRotation(rotation); -}; - - -/** - * Set the center of the current view. - * @param {ol.Coordinate|undefined} center The center of the view. - * @observable - * @api - */ -ol.View.prototype.setCenter = function(center) { - this.set(ol.ViewProperty.CENTER, center); - if (this.getAnimating()) { - this.cancelAnimations(); - } -}; - - -/** - * @param {ol.ViewHint} hint Hint. - * @param {number} delta Delta. - * @return {number} New value. - */ -ol.View.prototype.setHint = function(hint, delta) { - this.hints_[hint] += delta; - this.changed(); - return this.hints_[hint]; -}; - - -/** - * Set the resolution for this view. - * @param {number|undefined} resolution The resolution of the view. - * @observable - * @api - */ -ol.View.prototype.setResolution = function(resolution) { - this.set(ol.ViewProperty.RESOLUTION, resolution); - if (this.getAnimating()) { - this.cancelAnimations(); - } -}; - - -/** - * Set the rotation for this view. - * @param {number} rotation The rotation of the view in radians. - * @observable - * @api - */ -ol.View.prototype.setRotation = function(rotation) { - this.set(ol.ViewProperty.ROTATION, rotation); - if (this.getAnimating()) { - this.cancelAnimations(); - } -}; - - -/** - * Zoom to a specific zoom level. - * @param {number} zoom Zoom level. - * @api - */ -ol.View.prototype.setZoom = function(zoom) { - var resolution = this.constrainResolution( - this.maxResolution_, zoom - this.minZoom_, 0); - this.setResolution(resolution); -}; - - -/** - * @param {olx.ViewOptions} options View options. - * @private - * @return {ol.CenterConstraintType} The constraint. - */ -ol.View.createCenterConstraint_ = function(options) { - if (options.extent !== undefined) { - return ol.CenterConstraint.createExtent(options.extent); - } else { - return ol.CenterConstraint.none; - } -}; - - -/** - * @private - * @param {olx.ViewOptions} options View options. - * @return {{constraint: ol.ResolutionConstraintType, maxResolution: number, - * minResolution: number, zoomFactor: number}} The constraint. - */ -ol.View.createResolutionConstraint_ = function(options) { - var resolutionConstraint; - var maxResolution; - var minResolution; - - // TODO: move these to be ol constants - // see https://github.com/openlayers/openlayers/issues/2076 - var defaultMaxZoom = 28; - var defaultZoomFactor = 2; - - var minZoom = options.minZoom !== undefined ? - options.minZoom : ol.DEFAULT_MIN_ZOOM; - - var maxZoom = options.maxZoom !== undefined ? - options.maxZoom : defaultMaxZoom; - - var zoomFactor = options.zoomFactor !== undefined ? - options.zoomFactor : defaultZoomFactor; - - if (options.resolutions !== undefined) { - var resolutions = options.resolutions; - maxResolution = resolutions[0]; - minResolution = resolutions[resolutions.length - 1]; - resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions( - resolutions); - } else { - // calculate the default min and max resolution - var projection = ol.proj.createProjection(options.projection, 'EPSG:3857'); - var extent = projection.getExtent(); - var size = !extent ? - // use an extent that can fit the whole world if need be - 360 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] / - projection.getMetersPerUnit() : - Math.max(ol.extent.getWidth(extent), ol.extent.getHeight(extent)); - - var defaultMaxResolution = size / ol.DEFAULT_TILE_SIZE / Math.pow( - defaultZoomFactor, ol.DEFAULT_MIN_ZOOM); - - var defaultMinResolution = defaultMaxResolution / Math.pow( - defaultZoomFactor, defaultMaxZoom - ol.DEFAULT_MIN_ZOOM); - - // user provided maxResolution takes precedence - maxResolution = options.maxResolution; - if (maxResolution !== undefined) { - minZoom = 0; - } else { - maxResolution = defaultMaxResolution / Math.pow(zoomFactor, minZoom); - } - - // user provided minResolution takes precedence - minResolution = options.minResolution; - if (minResolution === undefined) { - if (options.maxZoom !== undefined) { - if (options.maxResolution !== undefined) { - minResolution = maxResolution / Math.pow(zoomFactor, maxZoom); - } else { - minResolution = defaultMaxResolution / Math.pow(zoomFactor, maxZoom); - } - } else { - minResolution = defaultMinResolution; - } - } - - // given discrete zoom levels, minResolution may be different than provided - maxZoom = minZoom + Math.floor( - Math.log(maxResolution / minResolution) / Math.log(zoomFactor)); - minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom); - - resolutionConstraint = ol.ResolutionConstraint.createSnapToPower( - zoomFactor, maxResolution, maxZoom - minZoom); - } - return {constraint: resolutionConstraint, maxResolution: maxResolution, - minResolution: minResolution, minZoom: minZoom, zoomFactor: zoomFactor}; -}; - - -/** - * @private - * @param {olx.ViewOptions} options View options. - * @return {ol.RotationConstraintType} Rotation constraint. - */ -ol.View.createRotationConstraint_ = function(options) { - var enableRotation = options.enableRotation !== undefined ? - options.enableRotation : true; - if (enableRotation) { - var constrainRotation = options.constrainRotation; - if (constrainRotation === undefined || constrainRotation === true) { - return ol.RotationConstraint.createSnapToZero(); - } else if (constrainRotation === false) { - return ol.RotationConstraint.none; - } else if (typeof constrainRotation === 'number') { - return ol.RotationConstraint.createSnapToN(constrainRotation); - } else { - return ol.RotationConstraint.none; - } - } else { - return ol.RotationConstraint.disable; - } -}; - -goog.provide('ol.Kinetic'); - - -/** - * @classdesc - * Implementation of inertial deceleration for map movement. - * - * @constructor - * @param {number} decay Rate of decay (must be negative). - * @param {number} minVelocity Minimum velocity (pixels/millisecond). - * @param {number} delay Delay to consider to calculate the kinetic - * initial values (milliseconds). - * @struct - * @api - */ -ol.Kinetic = function(decay, minVelocity, delay) { - - /** - * @private - * @type {number} - */ - this.decay_ = decay; - - /** - * @private - * @type {number} - */ - this.minVelocity_ = minVelocity; - - /** - * @private - * @type {number} - */ - this.delay_ = delay; - - /** - * @private - * @type {Array.<number>} - */ - this.points_ = []; - - /** - * @private - * @type {number} - */ - this.angle_ = 0; - - /** - * @private - * @type {number} - */ - this.initialVelocity_ = 0; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.Kinetic.prototype.begin = function() { - this.points_.length = 0; - this.angle_ = 0; - this.initialVelocity_ = 0; -}; - - -/** - * @param {number} x X. - * @param {number} y Y. - */ -ol.Kinetic.prototype.update = function(x, y) { - this.points_.push(x, y, Date.now()); -}; - - -/** - * @return {boolean} Whether we should do kinetic animation. - */ -ol.Kinetic.prototype.end = function() { - if (this.points_.length < 6) { - // at least 2 points are required (i.e. there must be at least 6 elements - // in the array) - return false; - } - var delay = Date.now() - this.delay_; - var lastIndex = this.points_.length - 3; - if (this.points_[lastIndex + 2] < delay) { - // the last tracked point is too old, which means that the user stopped - // panning before releasing the map - return false; - } - - // get the first point which still falls into the delay time - var firstIndex = lastIndex - 3; - while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) { - firstIndex -= 3; - } - - var duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2]; - // we don't want a duration of 0 (divide by zero) - // we also make sure the user panned for a duration of at least one frame - // (1/60s) to compute sane displacement values - if (duration < 1000 / 60) { - return false; - } - - var dx = this.points_[lastIndex] - this.points_[firstIndex]; - var dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1]; - this.angle_ = Math.atan2(dy, dx); - this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration; - return this.initialVelocity_ > this.minVelocity_; -}; - - -/** - * @return {number} Total distance travelled (pixels). - */ -ol.Kinetic.prototype.getDistance = function() { - return (this.minVelocity_ - this.initialVelocity_) / this.decay_; -}; - - -/** - * @return {number} Angle of the kinetic panning animation (radians). - */ -ol.Kinetic.prototype.getAngle = function() { - return this.angle_; -}; - -goog.provide('ol.interaction.Property'); - -/** - * @enum {string} - */ -ol.interaction.Property = { - ACTIVE: 'active' -}; - -// FIXME factor out key precondition (shift et. al) - -goog.provide('ol.interaction.Interaction'); - -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.easing'); -goog.require('ol.interaction.Property'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * User actions that change the state of the map. Some are similar to controls, - * but are not associated with a DOM element. - * For example, {@link ol.interaction.KeyboardZoom} is functionally the same as - * {@link ol.control.Zoom}, but triggered by a keyboard event not a button - * element event. - * Although interactions do not have a DOM element, some of them do render - * vectors and so are visible on the screen. - * - * @constructor - * @param {olx.interaction.InteractionOptions} options Options. - * @extends {ol.Object} - * @api - */ -ol.interaction.Interaction = function(options) { - - ol.Object.call(this); - - /** - * @private - * @type {ol.Map} - */ - this.map_ = null; - - this.setActive(true); - - /** - * @type {function(ol.MapBrowserEvent):boolean} - */ - this.handleEvent = options.handleEvent; - -}; -ol.inherits(ol.interaction.Interaction, ol.Object); - - -/** - * Return whether the interaction is currently active. - * @return {boolean} `true` if the interaction is active, `false` otherwise. - * @observable - * @api - */ -ol.interaction.Interaction.prototype.getActive = function() { - return /** @type {boolean} */ ( - this.get(ol.interaction.Property.ACTIVE)); -}; - - -/** - * Get the map associated with this interaction. - * @return {ol.Map} Map. - * @api - */ -ol.interaction.Interaction.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Activate or deactivate the interaction. - * @param {boolean} active Active. - * @observable - * @api - */ -ol.interaction.Interaction.prototype.setActive = function(active) { - this.set(ol.interaction.Property.ACTIVE, active); -}; - - -/** - * Remove the interaction from its current map and attach it to the new map. - * Subclasses may set up event handlers to get notified about changes to - * the map here. - * @param {ol.Map} map Map. - */ -ol.interaction.Interaction.prototype.setMap = function(map) { - this.map_ = map; -}; - - -/** - * @param {ol.View} view View. - * @param {ol.Coordinate} delta Delta. - * @param {number=} opt_duration Duration. - */ -ol.interaction.Interaction.pan = function(view, delta, opt_duration) { - var currentCenter = view.getCenter(); - if (currentCenter) { - var center = view.constrainCenter( - [currentCenter[0] + delta[0], currentCenter[1] + delta[1]]); - if (opt_duration) { - view.animate({ - duration: opt_duration, - easing: ol.easing.linear, - center: center - }); - } else { - view.setCenter(center); - } - } -}; - - -/** - * @param {ol.View} view View. - * @param {number|undefined} rotation Rotation. - * @param {ol.Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - */ -ol.interaction.Interaction.rotate = function(view, rotation, opt_anchor, opt_duration) { - rotation = view.constrainRotation(rotation, 0); - ol.interaction.Interaction.rotateWithoutConstraints( - view, rotation, opt_anchor, opt_duration); -}; - - -/** - * @param {ol.View} view View. - * @param {number|undefined} rotation Rotation. - * @param {ol.Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - */ -ol.interaction.Interaction.rotateWithoutConstraints = function(view, rotation, opt_anchor, opt_duration) { - if (rotation !== undefined) { - var currentRotation = view.getRotation(); - var currentCenter = view.getCenter(); - if (currentRotation !== undefined && currentCenter && opt_duration > 0) { - view.animate({ - rotation: rotation, - anchor: opt_anchor, - duration: opt_duration, - easing: ol.easing.easeOut - }); - } else { - view.rotate(rotation, opt_anchor); - } - } -}; - - -/** - * @param {ol.View} view View. - * @param {number|undefined} resolution Resolution to go to. - * @param {ol.Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - * @param {number=} opt_direction Zooming direction; > 0 indicates - * zooming out, in which case the constraints system will select - * the largest nearest resolution; < 0 indicates zooming in, in - * which case the constraints system will select the smallest - * nearest resolution; == 0 indicates that the zooming direction - * is unknown/not relevant, in which case the constraints system - * will select the nearest resolution. If not defined 0 is - * assumed. - */ -ol.interaction.Interaction.zoom = function(view, resolution, opt_anchor, opt_duration, opt_direction) { - resolution = view.constrainResolution(resolution, 0, opt_direction); - ol.interaction.Interaction.zoomWithoutConstraints( - view, resolution, opt_anchor, opt_duration); -}; - - -/** - * @param {ol.View} view View. - * @param {number} delta Delta from previous zoom level. - * @param {ol.Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - */ -ol.interaction.Interaction.zoomByDelta = function(view, delta, opt_anchor, opt_duration) { - var currentResolution = view.getResolution(); - var resolution = view.constrainResolution(currentResolution, delta, 0); - - // If we have a constraint on center, we need to change the anchor so that the - // new center is within the extent. We first calculate the new center, apply - // the constraint to it, and then calculate back the anchor - if (opt_anchor && resolution !== undefined && resolution !== currentResolution) { - var currentCenter = view.getCenter(); - var center = view.calculateCenterZoom(resolution, opt_anchor); - center = view.constrainCenter(center); - - opt_anchor = [ - (resolution * currentCenter[0] - currentResolution * center[0]) / - (resolution - currentResolution), - (resolution * currentCenter[1] - currentResolution * center[1]) / - (resolution - currentResolution) - ]; - } - - ol.interaction.Interaction.zoomWithoutConstraints( - view, resolution, opt_anchor, opt_duration); -}; - - -/** - * @param {ol.View} view View. - * @param {number|undefined} resolution Resolution to go to. - * @param {ol.Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - */ -ol.interaction.Interaction.zoomWithoutConstraints = function(view, resolution, opt_anchor, opt_duration) { - if (resolution) { - var currentResolution = view.getResolution(); - var currentCenter = view.getCenter(); - if (currentResolution !== undefined && currentCenter && - resolution !== currentResolution && opt_duration) { - view.animate({ - resolution: resolution, - anchor: opt_anchor, - duration: opt_duration, - easing: ol.easing.easeOut - }); - } else { - if (opt_anchor) { - var center = view.calculateCenterZoom(resolution, opt_anchor); - view.setCenter(center); - } - view.setResolution(resolution); - } - } -}; - -goog.provide('ol.interaction.DoubleClickZoom'); - -goog.require('ol'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.interaction.Interaction'); - - -/** - * @classdesc - * Allows the user to zoom by double-clicking on the map. - * - * @constructor - * @extends {ol.interaction.Interaction} - * @param {olx.interaction.DoubleClickZoomOptions=} opt_options Options. - * @api - */ -ol.interaction.DoubleClickZoom = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {number} - */ - this.delta_ = options.delta ? options.delta : 1; - - ol.interaction.Interaction.call(this, { - handleEvent: ol.interaction.DoubleClickZoom.handleEvent - }); - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; - -}; -ol.inherits(ol.interaction.DoubleClickZoom, ol.interaction.Interaction); - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} (if it was a - * doubleclick) and eventually zooms the map. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.DoubleClickZoom} - * @api - */ -ol.interaction.DoubleClickZoom.handleEvent = function(mapBrowserEvent) { - var stopEvent = false; - var browserEvent = mapBrowserEvent.originalEvent; - if (mapBrowserEvent.type == ol.MapBrowserEventType.DBLCLICK) { - var map = mapBrowserEvent.map; - var anchor = mapBrowserEvent.coordinate; - var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_; - var view = map.getView(); - ol.interaction.Interaction.zoomByDelta( - view, delta, anchor, this.duration_); - mapBrowserEvent.preventDefault(); - stopEvent = true; - } - return !stopEvent; -}; - -goog.provide('ol.events.condition'); - -goog.require('ol.MapBrowserEventType'); -goog.require('ol.asserts'); -goog.require('ol.functions'); -goog.require('ol.has'); - - -/** - * Return `true` if only the alt-key is pressed, `false` otherwise (e.g. when - * additionally the shift-key is pressed). - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if only the alt key is pressed. - * @api - */ -ol.events.condition.altKeyOnly = function(mapBrowserEvent) { - var originalEvent = mapBrowserEvent.originalEvent; - return ( - originalEvent.altKey && - !(originalEvent.metaKey || originalEvent.ctrlKey) && - !originalEvent.shiftKey); -}; - - -/** - * Return `true` if only the alt-key and shift-key is pressed, `false` otherwise - * (e.g. when additionally the platform-modifier-key is pressed). - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if only the alt and shift keys are pressed. - * @api - */ -ol.events.condition.altShiftKeysOnly = function(mapBrowserEvent) { - var originalEvent = mapBrowserEvent.originalEvent; - return ( - originalEvent.altKey && - !(originalEvent.metaKey || originalEvent.ctrlKey) && - originalEvent.shiftKey); -}; - - -/** - * Return always true. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True. - * @function - * @api - */ -ol.events.condition.always = ol.functions.TRUE; - - -/** - * Return `true` if the event is a `click` event, `false` otherwise. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if the event is a map `click` event. - * @api - */ -ol.events.condition.click = function(mapBrowserEvent) { - return mapBrowserEvent.type == ol.MapBrowserEventType.CLICK; -}; - - -/** - * Return `true` if the event has an "action"-producing mouse button. - * - * By definition, this includes left-click on windows/linux, and left-click - * without the ctrl key on Macs. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} The result. - */ -ol.events.condition.mouseActionButton = function(mapBrowserEvent) { - var originalEvent = mapBrowserEvent.originalEvent; - return originalEvent.button == 0 && - !(ol.has.WEBKIT && ol.has.MAC && originalEvent.ctrlKey); -}; - - -/** - * Return always false. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} False. - * @function - * @api - */ -ol.events.condition.never = ol.functions.FALSE; - - -/** - * Return `true` if the browser event is a `pointermove` event, `false` - * otherwise. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if the browser event is a `pointermove` event. - * @api - */ -ol.events.condition.pointerMove = function(mapBrowserEvent) { - return mapBrowserEvent.type == 'pointermove'; -}; - - -/** - * Return `true` if the event is a map `singleclick` event, `false` otherwise. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if the event is a map `singleclick` event. - * @api - */ -ol.events.condition.singleClick = function(mapBrowserEvent) { - return mapBrowserEvent.type == ol.MapBrowserEventType.SINGLECLICK; -}; - - -/** - * Return `true` if the event is a map `dblclick` event, `false` otherwise. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if the event is a map `dblclick` event. - * @api - */ -ol.events.condition.doubleClick = function(mapBrowserEvent) { - return mapBrowserEvent.type == ol.MapBrowserEventType.DBLCLICK; -}; - - -/** - * Return `true` if no modifier key (alt-, shift- or platform-modifier-key) is - * pressed. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True only if there no modifier keys are pressed. - * @api - */ -ol.events.condition.noModifierKeys = function(mapBrowserEvent) { - var originalEvent = mapBrowserEvent.originalEvent; - return ( - !originalEvent.altKey && - !(originalEvent.metaKey || originalEvent.ctrlKey) && - !originalEvent.shiftKey); -}; - - -/** - * Return `true` if only the platform-modifier-key (the meta-key on Mac, - * ctrl-key otherwise) is pressed, `false` otherwise (e.g. when additionally - * the shift-key is pressed). - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if only the platform modifier key is pressed. - * @api - */ -ol.events.condition.platformModifierKeyOnly = function(mapBrowserEvent) { - var originalEvent = mapBrowserEvent.originalEvent; - return ( - !originalEvent.altKey && - (ol.has.MAC ? originalEvent.metaKey : originalEvent.ctrlKey) && - !originalEvent.shiftKey); -}; - - -/** - * Return `true` if only the shift-key is pressed, `false` otherwise (e.g. when - * additionally the alt-key is pressed). - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if only the shift key is pressed. - * @api - */ -ol.events.condition.shiftKeyOnly = function(mapBrowserEvent) { - var originalEvent = mapBrowserEvent.originalEvent; - return ( - !originalEvent.altKey && - !(originalEvent.metaKey || originalEvent.ctrlKey) && - originalEvent.shiftKey); -}; - - -/** - * Return `true` if the target element is not editable, i.e. not a `<input>`-, - * `<select>`- or `<textarea>`-element, `false` otherwise. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True only if the target element is not editable. - * @api - */ -ol.events.condition.targetNotEditable = function(mapBrowserEvent) { - var target = mapBrowserEvent.originalEvent.target; - var tagName = target.tagName; - return ( - tagName !== 'INPUT' && - tagName !== 'SELECT' && - tagName !== 'TEXTAREA'); -}; - - -/** - * Return `true` if the event originates from a mouse device. - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if the event originates from a mouse device. - * @api - */ -ol.events.condition.mouseOnly = function(mapBrowserEvent) { - ol.asserts.assert(mapBrowserEvent.pointerEvent, 56); // mapBrowserEvent must originate from a pointer event - // see http://www.w3.org/TR/pointerevents/#widl-PointerEvent-pointerType - return /** @type {ol.MapBrowserEvent} */ (mapBrowserEvent).pointerEvent.pointerType == 'mouse'; -}; - - -/** - * Return `true` if the event originates from a primary pointer in - * contact with the surface or if the left mouse button is pressed. - * @see http://www.w3.org/TR/pointerevents/#button-states - * - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} True if the event originates from a primary pointer. - * @api - */ -ol.events.condition.primaryAction = function(mapBrowserEvent) { - var pointerEvent = mapBrowserEvent.pointerEvent; - return pointerEvent.isPrimary && pointerEvent.button === 0; -}; - -goog.provide('ol.interaction.Pointer'); - -goog.require('ol'); -goog.require('ol.functions'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.MapBrowserPointerEvent'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.obj'); - - -/** - * @classdesc - * Base class that calls user-defined functions on `down`, `move` and `up` - * events. This class also manages "drag sequences". - * - * When the `handleDownEvent` user function returns `true` a drag sequence is - * started. During a drag sequence the `handleDragEvent` user function is - * called on `move` events. The drag sequence ends when the `handleUpEvent` - * user function is called and returns `false`. - * - * @constructor - * @param {olx.interaction.PointerOptions=} opt_options Options. - * @extends {ol.interaction.Interaction} - * @api - */ -ol.interaction.Pointer = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var handleEvent = options.handleEvent ? - options.handleEvent : ol.interaction.Pointer.handleEvent; - - ol.interaction.Interaction.call(this, { - handleEvent: handleEvent - }); - - /** - * @type {function(ol.MapBrowserPointerEvent):boolean} - * @private - */ - this.handleDownEvent_ = options.handleDownEvent ? - options.handleDownEvent : ol.interaction.Pointer.handleDownEvent; - - /** - * @type {function(ol.MapBrowserPointerEvent)} - * @private - */ - this.handleDragEvent_ = options.handleDragEvent ? - options.handleDragEvent : ol.interaction.Pointer.handleDragEvent; - - /** - * @type {function(ol.MapBrowserPointerEvent)} - * @private - */ - this.handleMoveEvent_ = options.handleMoveEvent ? - options.handleMoveEvent : ol.interaction.Pointer.handleMoveEvent; - - /** - * @type {function(ol.MapBrowserPointerEvent):boolean} - * @private - */ - this.handleUpEvent_ = options.handleUpEvent ? - options.handleUpEvent : ol.interaction.Pointer.handleUpEvent; - - /** - * @type {boolean} - * @protected - */ - this.handlingDownUpSequence = false; - - /** - * @type {Object.<number, ol.pointer.PointerEvent>} - * @private - */ - this.trackedPointers_ = {}; - - /** - * @type {Array.<ol.pointer.PointerEvent>} - * @protected - */ - this.targetPointers = []; - -}; -ol.inherits(ol.interaction.Pointer, ol.interaction.Interaction); - - -/** - * @param {Array.<ol.pointer.PointerEvent>} pointerEvents List of events. - * @return {ol.Pixel} Centroid pixel. - */ -ol.interaction.Pointer.centroid = function(pointerEvents) { - var length = pointerEvents.length; - var clientX = 0; - var clientY = 0; - for (var i = 0; i < length; i++) { - clientX += pointerEvents[i].clientX; - clientY += pointerEvents[i].clientY; - } - return [clientX / length, clientY / length]; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Whether the event is a pointerdown, pointerdrag - * or pointerup event. - * @private - */ -ol.interaction.Pointer.prototype.isPointerDraggingEvent_ = function(mapBrowserEvent) { - var type = mapBrowserEvent.type; - return ( - type === ol.MapBrowserEventType.POINTERDOWN || - type === ol.MapBrowserEventType.POINTERDRAG || - type === ol.MapBrowserEventType.POINTERUP); -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @private - */ -ol.interaction.Pointer.prototype.updateTrackedPointers_ = function(mapBrowserEvent) { - if (this.isPointerDraggingEvent_(mapBrowserEvent)) { - var event = mapBrowserEvent.pointerEvent; - - if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERUP) { - delete this.trackedPointers_[event.pointerId]; - } else if (mapBrowserEvent.type == - ol.MapBrowserEventType.POINTERDOWN) { - this.trackedPointers_[event.pointerId] = event; - } else if (event.pointerId in this.trackedPointers_) { - // update only when there was a pointerdown event for this pointer - this.trackedPointers_[event.pointerId] = event; - } - this.targetPointers = ol.obj.getValues(this.trackedPointers_); - } -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.Pointer} - */ -ol.interaction.Pointer.handleDragEvent = ol.nullFunction; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Capture dragging. - * @this {ol.interaction.Pointer} - */ -ol.interaction.Pointer.handleUpEvent = ol.functions.FALSE; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Capture dragging. - * @this {ol.interaction.Pointer} - */ -ol.interaction.Pointer.handleDownEvent = ol.functions.FALSE; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.Pointer} - */ -ol.interaction.Pointer.handleMoveEvent = ol.nullFunction; - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} and may call into - * other functions, if event sequences like e.g. 'drag' or 'down-up' etc. are - * detected. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.Pointer} - * @api - */ -ol.interaction.Pointer.handleEvent = function(mapBrowserEvent) { - if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) { - return true; - } - - var stopEvent = false; - this.updateTrackedPointers_(mapBrowserEvent); - if (this.handlingDownUpSequence) { - if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERDRAG) { - this.handleDragEvent_(mapBrowserEvent); - } else if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERUP) { - var handledUp = this.handleUpEvent_(mapBrowserEvent); - this.handlingDownUpSequence = handledUp && this.targetPointers.length > 0; - } - } else { - if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERDOWN) { - var handled = this.handleDownEvent_(mapBrowserEvent); - this.handlingDownUpSequence = handled; - stopEvent = this.shouldStopEvent(handled); - } else if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERMOVE) { - this.handleMoveEvent_(mapBrowserEvent); - } - } - return !stopEvent; -}; - - -/** - * This method is used to determine if "down" events should be propagated to - * other interactions or should be stopped. - * - * The method receives the return code of the "handleDownEvent" function. - * - * By default this function is the "identity" function. It's overidden in - * child classes. - * - * @param {boolean} handled Was the event handled by the interaction? - * @return {boolean} Should the event be stopped? - * @protected - */ -ol.interaction.Pointer.prototype.shouldStopEvent = function(handled) { - return handled; -}; - -goog.provide('ol.interaction.DragPan'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.coordinate'); -goog.require('ol.easing'); -goog.require('ol.events.condition'); -goog.require('ol.functions'); -goog.require('ol.interaction.Pointer'); - - -/** - * @classdesc - * Allows the user to pan the map by dragging the map. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.DragPanOptions=} opt_options Options. - * @api - */ -ol.interaction.DragPan = function(opt_options) { - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.DragPan.handleDownEvent_, - handleDragEvent: ol.interaction.DragPan.handleDragEvent_, - handleUpEvent: ol.interaction.DragPan.handleUpEvent_ - }); - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {ol.Kinetic|undefined} - */ - this.kinetic_ = options.kinetic; - - /** - * @type {ol.Pixel} - */ - this.lastCentroid = null; - - /** - * @type {number} - */ - this.lastPointersCount_; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.noModifierKeys; - - /** - * @private - * @type {boolean} - */ - this.noKinetic_ = false; - -}; -ol.inherits(ol.interaction.DragPan, ol.interaction.Pointer); - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.DragPan} - * @private - */ -ol.interaction.DragPan.handleDragEvent_ = function(mapBrowserEvent) { - var targetPointers = this.targetPointers; - var centroid = - ol.interaction.Pointer.centroid(targetPointers); - if (targetPointers.length == this.lastPointersCount_) { - if (this.kinetic_) { - this.kinetic_.update(centroid[0], centroid[1]); - } - if (this.lastCentroid) { - var deltaX = this.lastCentroid[0] - centroid[0]; - var deltaY = centroid[1] - this.lastCentroid[1]; - var map = mapBrowserEvent.map; - var view = map.getView(); - var viewState = view.getState(); - var center = [deltaX, deltaY]; - ol.coordinate.scale(center, viewState.resolution); - ol.coordinate.rotate(center, viewState.rotation); - ol.coordinate.add(center, viewState.center); - center = view.constrainCenter(center); - view.setCenter(center); - } - } else if (this.kinetic_) { - // reset so we don't overestimate the kinetic energy after - // after one finger down, tiny drag, second finger down - this.kinetic_.begin(); - } - this.lastCentroid = centroid; - this.lastPointersCount_ = targetPointers.length; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.DragPan} - * @private - */ -ol.interaction.DragPan.handleUpEvent_ = function(mapBrowserEvent) { - var map = mapBrowserEvent.map; - var view = map.getView(); - if (this.targetPointers.length === 0) { - if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) { - var distance = this.kinetic_.getDistance(); - var angle = this.kinetic_.getAngle(); - var center = /** @type {!ol.Coordinate} */ (view.getCenter()); - var centerpx = map.getPixelFromCoordinate(center); - var dest = map.getCoordinateFromPixel([ - centerpx[0] - distance * Math.cos(angle), - centerpx[1] - distance * Math.sin(angle) - ]); - view.animate({ - center: view.constrainCenter(dest), - duration: 500, - easing: ol.easing.easeOut - }); - } - view.setHint(ol.ViewHint.INTERACTING, -1); - return false; - } else { - if (this.kinetic_) { - // reset so we don't overestimate the kinetic energy after - // after one finger up, tiny drag, second finger up - this.kinetic_.begin(); - } - this.lastCentroid = null; - return true; - } -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.DragPan} - * @private - */ -ol.interaction.DragPan.handleDownEvent_ = function(mapBrowserEvent) { - if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) { - var map = mapBrowserEvent.map; - var view = map.getView(); - this.lastCentroid = null; - if (!this.handlingDownUpSequence) { - view.setHint(ol.ViewHint.INTERACTING, 1); - } - // stop any current animation - if (view.getHints()[ol.ViewHint.ANIMATING]) { - view.setCenter(mapBrowserEvent.frameState.viewState.center); - } - if (this.kinetic_) { - this.kinetic_.begin(); - } - // No kinetic as soon as more than one pointer on the screen is - // detected. This is to prevent nasty pans after pinch. - this.noKinetic_ = this.targetPointers.length > 1; - return true; - } else { - return false; - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.DragPan.prototype.shouldStopEvent = ol.functions.FALSE; - -goog.provide('ol.interaction.DragRotate'); - -goog.require('ol'); -goog.require('ol.RotationConstraint'); -goog.require('ol.ViewHint'); -goog.require('ol.events.condition'); -goog.require('ol.functions'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.interaction.Pointer'); - - -/** - * @classdesc - * Allows the user to rotate the map by clicking and dragging on the map, - * normally combined with an {@link ol.events.condition} that limits - * it to when the alt and shift keys are held down. - * - * This interaction is only supported for mouse devices. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.DragRotateOptions=} opt_options Options. - * @api - */ -ol.interaction.DragRotate = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.DragRotate.handleDownEvent_, - handleDragEvent: ol.interaction.DragRotate.handleDragEvent_, - handleUpEvent: ol.interaction.DragRotate.handleUpEvent_ - }); - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.altShiftKeysOnly; - - /** - * @private - * @type {number|undefined} - */ - this.lastAngle_ = undefined; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; -}; -ol.inherits(ol.interaction.DragRotate, ol.interaction.Pointer); - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.DragRotate} - * @private - */ -ol.interaction.DragRotate.handleDragEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return; - } - - var map = mapBrowserEvent.map; - var view = map.getView(); - if (view.getConstraints().rotation === ol.RotationConstraint.disable) { - return; - } - var size = map.getSize(); - var offset = mapBrowserEvent.pixel; - var theta = - Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2); - if (this.lastAngle_ !== undefined) { - var delta = theta - this.lastAngle_; - var rotation = view.getRotation(); - ol.interaction.Interaction.rotateWithoutConstraints( - view, rotation - delta); - } - this.lastAngle_ = theta; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.DragRotate} - * @private - */ -ol.interaction.DragRotate.handleUpEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return true; - } - - var map = mapBrowserEvent.map; - var view = map.getView(); - view.setHint(ol.ViewHint.INTERACTING, -1); - var rotation = view.getRotation(); - ol.interaction.Interaction.rotate(view, rotation, - undefined, this.duration_); - return false; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.DragRotate} - * @private - */ -ol.interaction.DragRotate.handleDownEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return false; - } - - if (ol.events.condition.mouseActionButton(mapBrowserEvent) && - this.condition_(mapBrowserEvent)) { - var map = mapBrowserEvent.map; - map.getView().setHint(ol.ViewHint.INTERACTING, 1); - this.lastAngle_ = undefined; - return true; - } else { - return false; - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.DragRotate.prototype.shouldStopEvent = ol.functions.FALSE; - -// FIXME add rotation - -goog.provide('ol.render.Box'); - -goog.require('ol'); -goog.require('ol.Disposable'); -goog.require('ol.geom.Polygon'); - - -/** - * @constructor - * @extends {ol.Disposable} - * @param {string} className CSS class name. - */ -ol.render.Box = function(className) { - - /** - * @type {ol.geom.Polygon} - * @private - */ - this.geometry_ = null; - - /** - * @type {HTMLDivElement} - * @private - */ - this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); - this.element_.style.position = 'absolute'; - this.element_.className = 'ol-box ' + className; - - /** - * @private - * @type {ol.Map} - */ - this.map_ = null; - - /** - * @private - * @type {ol.Pixel} - */ - this.startPixel_ = null; - - /** - * @private - * @type {ol.Pixel} - */ - this.endPixel_ = null; - -}; -ol.inherits(ol.render.Box, ol.Disposable); - - -/** - * @inheritDoc - */ -ol.render.Box.prototype.disposeInternal = function() { - this.setMap(null); -}; - - -/** - * @private - */ -ol.render.Box.prototype.render_ = function() { - var startPixel = this.startPixel_; - var endPixel = this.endPixel_; - var px = 'px'; - var style = this.element_.style; - style.left = Math.min(startPixel[0], endPixel[0]) + px; - style.top = Math.min(startPixel[1], endPixel[1]) + px; - style.width = Math.abs(endPixel[0] - startPixel[0]) + px; - style.height = Math.abs(endPixel[1] - startPixel[1]) + px; -}; - - -/** - * @param {ol.Map} map Map. - */ -ol.render.Box.prototype.setMap = function(map) { - if (this.map_) { - this.map_.getOverlayContainer().removeChild(this.element_); - var style = this.element_.style; - style.left = style.top = style.width = style.height = 'inherit'; - } - this.map_ = map; - if (this.map_) { - this.map_.getOverlayContainer().appendChild(this.element_); - } -}; - - -/** - * @param {ol.Pixel} startPixel Start pixel. - * @param {ol.Pixel} endPixel End pixel. - */ -ol.render.Box.prototype.setPixels = function(startPixel, endPixel) { - this.startPixel_ = startPixel; - this.endPixel_ = endPixel; - this.createOrUpdateGeometry(); - this.render_(); -}; - - -/** - * Creates or updates the cached geometry. - */ -ol.render.Box.prototype.createOrUpdateGeometry = function() { - var startPixel = this.startPixel_; - var endPixel = this.endPixel_; - var pixels = [ - startPixel, - [startPixel[0], endPixel[1]], - endPixel, - [endPixel[0], startPixel[1]] - ]; - var coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); - // close the polygon - coordinates[4] = coordinates[0].slice(); - if (!this.geometry_) { - this.geometry_ = new ol.geom.Polygon([coordinates]); - } else { - this.geometry_.setCoordinates([coordinates]); - } -}; - - -/** - * @return {ol.geom.Polygon} Geometry. - */ -ol.render.Box.prototype.getGeometry = function() { - return this.geometry_; -}; - -// FIXME draw drag box -goog.provide('ol.interaction.DragBox'); - -goog.require('ol.events.Event'); -goog.require('ol'); -goog.require('ol.events.condition'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.render.Box'); - - -/** - * @classdesc - * Allows the user to draw a vector box by clicking and dragging on the map, - * normally combined with an {@link ol.events.condition} that limits - * it to when the shift or other key is held down. This is used, for example, - * for zooming to a specific area of the map - * (see {@link ol.interaction.DragZoom} and - * {@link ol.interaction.DragRotateAndZoom}). - * - * This interaction is only supported for mouse devices. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @fires ol.interaction.DragBox.Event - * @param {olx.interaction.DragBoxOptions=} opt_options Options. - * @api - */ -ol.interaction.DragBox = function(opt_options) { - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.DragBox.handleDownEvent_, - handleDragEvent: ol.interaction.DragBox.handleDragEvent_, - handleUpEvent: ol.interaction.DragBox.handleUpEvent_ - }); - - var options = opt_options ? opt_options : {}; - - /** - * @type {ol.render.Box} - * @private - */ - this.box_ = new ol.render.Box(options.className || 'ol-dragbox'); - - /** - * @type {number} - * @private - */ - this.minArea_ = options.minArea !== undefined ? options.minArea : 64; - - /** - * @type {ol.Pixel} - * @private - */ - this.startPixel_ = null; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.always; - - /** - * @private - * @type {ol.DragBoxEndConditionType} - */ - this.boxEndCondition_ = options.boxEndCondition ? - options.boxEndCondition : ol.interaction.DragBox.defaultBoxEndCondition; -}; -ol.inherits(ol.interaction.DragBox, ol.interaction.Pointer); - - -/** - * The default condition for determining whether the boxend event - * should fire. - * @param {ol.MapBrowserEvent} mapBrowserEvent The originating MapBrowserEvent - * leading to the box end. - * @param {ol.Pixel} startPixel The starting pixel of the box. - * @param {ol.Pixel} endPixel The end pixel of the box. - * @return {boolean} Whether or not the boxend condition should be fired. - * @this {ol.interaction.DragBox} - */ -ol.interaction.DragBox.defaultBoxEndCondition = function(mapBrowserEvent, startPixel, endPixel) { - var width = endPixel[0] - startPixel[0]; - var height = endPixel[1] - startPixel[1]; - return width * width + height * height >= this.minArea_; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.DragBox} - * @private - */ -ol.interaction.DragBox.handleDragEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return; - } - - this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel); - - this.dispatchEvent(new ol.interaction.DragBox.Event(ol.interaction.DragBox.EventType_.BOXDRAG, - mapBrowserEvent.coordinate, mapBrowserEvent)); -}; - - -/** - * Returns geometry of last drawn box. - * @return {ol.geom.Polygon} Geometry. - * @api - */ -ol.interaction.DragBox.prototype.getGeometry = function() { - return this.box_.getGeometry(); -}; - - -/** - * To be overridden by child classes. - * FIXME: use constructor option instead of relying on overriding. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @protected - */ -ol.interaction.DragBox.prototype.onBoxEnd = ol.nullFunction; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.DragBox} - * @private - */ -ol.interaction.DragBox.handleUpEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return true; - } - - this.box_.setMap(null); - - if (this.boxEndCondition_(mapBrowserEvent, - this.startPixel_, mapBrowserEvent.pixel)) { - this.onBoxEnd(mapBrowserEvent); - this.dispatchEvent(new ol.interaction.DragBox.Event(ol.interaction.DragBox.EventType_.BOXEND, - mapBrowserEvent.coordinate, mapBrowserEvent)); - } - return false; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.DragBox} - * @private - */ -ol.interaction.DragBox.handleDownEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return false; - } - - if (ol.events.condition.mouseActionButton(mapBrowserEvent) && - this.condition_(mapBrowserEvent)) { - this.startPixel_ = mapBrowserEvent.pixel; - this.box_.setMap(mapBrowserEvent.map); - this.box_.setPixels(this.startPixel_, this.startPixel_); - this.dispatchEvent(new ol.interaction.DragBox.Event(ol.interaction.DragBox.EventType_.BOXSTART, - mapBrowserEvent.coordinate, mapBrowserEvent)); - return true; - } else { - return false; - } -}; - - -/** - * @enum {string} - * @private - */ -ol.interaction.DragBox.EventType_ = { - /** - * Triggered upon drag box start. - * @event ol.interaction.DragBox.Event#boxstart - * @api - */ - BOXSTART: 'boxstart', - - /** - * Triggered on drag when box is active. - * @event ol.interaction.DragBox.Event#boxdrag - * @api - */ - BOXDRAG: 'boxdrag', - - /** - * Triggered upon drag box end. - * @event ol.interaction.DragBox.Event#boxend - * @api - */ - BOXEND: 'boxend' -}; - - -/** - * @classdesc - * Events emitted by {@link ol.interaction.DragBox} instances are instances of - * this type. - * - * @param {string} type The event type. - * @param {ol.Coordinate} coordinate The event coordinate. - * @param {ol.MapBrowserEvent} mapBrowserEvent Originating event. - * @extends {ol.events.Event} - * @constructor - * @implements {oli.DragBoxEvent} - */ -ol.interaction.DragBox.Event = function(type, coordinate, mapBrowserEvent) { - ol.events.Event.call(this, type); - - /** - * The coordinate of the drag event. - * @const - * @type {ol.Coordinate} - * @api - */ - this.coordinate = coordinate; - - /** - * @const - * @type {ol.MapBrowserEvent} - * @api - */ - this.mapBrowserEvent = mapBrowserEvent; - -}; -ol.inherits(ol.interaction.DragBox.Event, ol.events.Event); - -goog.provide('ol.interaction.DragZoom'); - -goog.require('ol'); -goog.require('ol.easing'); -goog.require('ol.events.condition'); -goog.require('ol.extent'); -goog.require('ol.interaction.DragBox'); - - -/** - * @classdesc - * Allows the user to zoom the map by clicking and dragging on the map, - * normally combined with an {@link ol.events.condition} that limits - * it to when a key, shift by default, is held down. - * - * To change the style of the box, use CSS and the `.ol-dragzoom` selector, or - * your custom one configured with `className`. - * - * @constructor - * @extends {ol.interaction.DragBox} - * @param {olx.interaction.DragZoomOptions=} opt_options Options. - * @api - */ -ol.interaction.DragZoom = function(opt_options) { - var options = opt_options ? opt_options : {}; - - var condition = options.condition ? - options.condition : ol.events.condition.shiftKeyOnly; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 200; - - /** - * @private - * @type {boolean} - */ - this.out_ = options.out !== undefined ? options.out : false; - - ol.interaction.DragBox.call(this, { - condition: condition, - className: options.className || 'ol-dragzoom' - }); - -}; -ol.inherits(ol.interaction.DragZoom, ol.interaction.DragBox); - - -/** - * @inheritDoc - */ -ol.interaction.DragZoom.prototype.onBoxEnd = function() { - var map = this.getMap(); - - var view = /** @type {!ol.View} */ (map.getView()); - - var size = /** @type {!ol.Size} */ (map.getSize()); - - var extent = this.getGeometry().getExtent(); - - if (this.out_) { - var mapExtent = view.calculateExtent(size); - var boxPixelExtent = ol.extent.createOrUpdateFromCoordinates([ - map.getPixelFromCoordinate(ol.extent.getBottomLeft(extent)), - map.getPixelFromCoordinate(ol.extent.getTopRight(extent))]); - var factor = view.getResolutionForExtent(boxPixelExtent, size); - - ol.extent.scaleFromCenter(mapExtent, 1 / factor); - extent = mapExtent; - } - - var resolution = view.constrainResolution( - view.getResolutionForExtent(extent, size)); - - var center = ol.extent.getCenter(extent); - center = view.constrainCenter(center); - - view.animate({ - resolution: resolution, - center: center, - duration: this.duration_, - easing: ol.easing.easeOut - }); - -}; - -goog.provide('ol.events.KeyCode'); - -/** - * @enum {number} - * @const - */ -ol.events.KeyCode = { - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40 -}; - -goog.provide('ol.interaction.KeyboardPan'); - -goog.require('ol'); -goog.require('ol.coordinate'); -goog.require('ol.events.EventType'); -goog.require('ol.events.KeyCode'); -goog.require('ol.events.condition'); -goog.require('ol.interaction.Interaction'); - - -/** - * @classdesc - * Allows the user to pan the map using keyboard arrows. - * Note that, although this interaction is by default included in maps, - * the keys can only be used when browser focus is on the element to which - * the keyboard events are attached. By default, this is the map div, - * though you can change this with the `keyboardEventTarget` in - * {@link ol.Map}. `document` never loses focus but, for any other element, - * focus will have to be on, and returned to, this element if the keys are to - * function. - * See also {@link ol.interaction.KeyboardZoom}. - * - * @constructor - * @extends {ol.interaction.Interaction} - * @param {olx.interaction.KeyboardPanOptions=} opt_options Options. - * @api - */ -ol.interaction.KeyboardPan = function(opt_options) { - - ol.interaction.Interaction.call(this, { - handleEvent: ol.interaction.KeyboardPan.handleEvent - }); - - var options = opt_options || {}; - - /** - * @private - * @param {ol.MapBrowserEvent} mapBrowserEvent Browser event. - * @return {boolean} Combined condition result. - */ - this.defaultCondition_ = function(mapBrowserEvent) { - return ol.events.condition.noModifierKeys(mapBrowserEvent) && - ol.events.condition.targetNotEditable(mapBrowserEvent); - }; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition !== undefined ? - options.condition : this.defaultCondition_; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 100; - - /** - * @private - * @type {number} - */ - this.pixelDelta_ = options.pixelDelta !== undefined ? - options.pixelDelta : 128; - -}; -ol.inherits(ol.interaction.KeyboardPan, ol.interaction.Interaction); - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} if it was a - * `KeyEvent`, and decides the direction to pan to (if an arrow key was - * pressed). - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.KeyboardPan} - * @api - */ -ol.interaction.KeyboardPan.handleEvent = function(mapBrowserEvent) { - var stopEvent = false; - if (mapBrowserEvent.type == ol.events.EventType.KEYDOWN) { - var keyEvent = mapBrowserEvent.originalEvent; - var keyCode = keyEvent.keyCode; - if (this.condition_(mapBrowserEvent) && - (keyCode == ol.events.KeyCode.DOWN || - keyCode == ol.events.KeyCode.LEFT || - keyCode == ol.events.KeyCode.RIGHT || - keyCode == ol.events.KeyCode.UP)) { - var map = mapBrowserEvent.map; - var view = map.getView(); - var mapUnitsDelta = view.getResolution() * this.pixelDelta_; - var deltaX = 0, deltaY = 0; - if (keyCode == ol.events.KeyCode.DOWN) { - deltaY = -mapUnitsDelta; - } else if (keyCode == ol.events.KeyCode.LEFT) { - deltaX = -mapUnitsDelta; - } else if (keyCode == ol.events.KeyCode.RIGHT) { - deltaX = mapUnitsDelta; - } else { - deltaY = mapUnitsDelta; - } - var delta = [deltaX, deltaY]; - ol.coordinate.rotate(delta, view.getRotation()); - ol.interaction.Interaction.pan(view, delta, this.duration_); - mapBrowserEvent.preventDefault(); - stopEvent = true; - } - } - return !stopEvent; -}; - -goog.provide('ol.interaction.KeyboardZoom'); - -goog.require('ol'); -goog.require('ol.events.EventType'); -goog.require('ol.events.condition'); -goog.require('ol.interaction.Interaction'); - - -/** - * @classdesc - * Allows the user to zoom the map using keyboard + and -. - * Note that, although this interaction is by default included in maps, - * the keys can only be used when browser focus is on the element to which - * the keyboard events are attached. By default, this is the map div, - * though you can change this with the `keyboardEventTarget` in - * {@link ol.Map}. `document` never loses focus but, for any other element, - * focus will have to be on, and returned to, this element if the keys are to - * function. - * See also {@link ol.interaction.KeyboardPan}. - * - * @constructor - * @param {olx.interaction.KeyboardZoomOptions=} opt_options Options. - * @extends {ol.interaction.Interaction} - * @api - */ -ol.interaction.KeyboardZoom = function(opt_options) { - - ol.interaction.Interaction.call(this, { - handleEvent: ol.interaction.KeyboardZoom.handleEvent - }); - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? options.condition : - ol.events.condition.targetNotEditable; - - /** - * @private - * @type {number} - */ - this.delta_ = options.delta ? options.delta : 1; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 100; - -}; -ol.inherits(ol.interaction.KeyboardZoom, ol.interaction.Interaction); - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} if it was a - * `KeyEvent`, and decides whether to zoom in or out (depending on whether the - * key pressed was '+' or '-'). - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.KeyboardZoom} - * @api - */ -ol.interaction.KeyboardZoom.handleEvent = function(mapBrowserEvent) { - var stopEvent = false; - if (mapBrowserEvent.type == ol.events.EventType.KEYDOWN || - mapBrowserEvent.type == ol.events.EventType.KEYPRESS) { - var keyEvent = mapBrowserEvent.originalEvent; - var charCode = keyEvent.charCode; - if (this.condition_(mapBrowserEvent) && - (charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0))) { - var map = mapBrowserEvent.map; - var delta = (charCode == '+'.charCodeAt(0)) ? this.delta_ : -this.delta_; - var view = map.getView(); - ol.interaction.Interaction.zoomByDelta( - view, delta, undefined, this.duration_); - mapBrowserEvent.preventDefault(); - stopEvent = true; - } - } - return !stopEvent; -}; - -goog.provide('ol.interaction.MouseWheelZoom'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.easing'); -goog.require('ol.events.EventType'); -goog.require('ol.has'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.math'); - - -/** - * @classdesc - * Allows the user to zoom the map by scrolling the mouse wheel. - * - * @constructor - * @extends {ol.interaction.Interaction} - * @param {olx.interaction.MouseWheelZoomOptions=} opt_options Options. - * @api - */ -ol.interaction.MouseWheelZoom = function(opt_options) { - - ol.interaction.Interaction.call(this, { - handleEvent: ol.interaction.MouseWheelZoom.handleEvent - }); - - var options = opt_options || {}; - - /** - * @private - * @type {number} - */ - this.delta_ = 0; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; - - /** - * @private - * @type {number} - */ - this.timeout_ = options.timeout !== undefined ? options.timeout : 80; - - /** - * @private - * @type {boolean} - */ - this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true; - - /** - * @private - * @type {boolean} - */ - this.constrainResolution_ = options.constrainResolution || false; - - /** - * @private - * @type {?ol.Coordinate} - */ - this.lastAnchor_ = null; - - /** - * @private - * @type {number|undefined} - */ - this.startTime_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.timeoutId_ = undefined; - - /** - * @private - * @type {ol.interaction.MouseWheelZoom.Mode_|undefined} - */ - this.mode_ = undefined; - - /** - * Trackpad events separated by this delay will be considered separate - * interactions. - * @type {number} - */ - this.trackpadEventGap_ = 400; - - /** - * @type {number|undefined} - */ - this.trackpadTimeoutId_ = undefined; - - /** - * The number of delta values per zoom level - * @private - * @type {number} - */ - this.trackpadDeltaPerZoom_ = 300; - - /** - * The zoom factor by which scroll zooming is allowed to exceed the limits. - * @private - * @type {number} - */ - this.trackpadZoomBuffer_ = 1.5; - -}; -ol.inherits(ol.interaction.MouseWheelZoom, ol.interaction.Interaction); - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} (if it was a - * mousewheel-event) and eventually zooms the map. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} Allow event propagation. - * @this {ol.interaction.MouseWheelZoom} - * @api - */ -ol.interaction.MouseWheelZoom.handleEvent = function(mapBrowserEvent) { - var type = mapBrowserEvent.type; - if (type !== ol.events.EventType.WHEEL && type !== ol.events.EventType.MOUSEWHEEL) { - return true; - } - - mapBrowserEvent.preventDefault(); - - var map = mapBrowserEvent.map; - var wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent); - - if (this.useAnchor_) { - this.lastAnchor_ = mapBrowserEvent.coordinate; - } - - // Delta normalisation inspired by - // https://github.com/mapbox/mapbox-gl-js/blob/001c7b9/js/ui/handler/scroll_zoom.js - var delta; - if (mapBrowserEvent.type == ol.events.EventType.WHEEL) { - delta = wheelEvent.deltaY; - if (ol.has.FIREFOX && - wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { - delta /= ol.has.DEVICE_PIXEL_RATIO; - } - if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) { - delta *= 40; - } - } else if (mapBrowserEvent.type == ol.events.EventType.MOUSEWHEEL) { - delta = -wheelEvent.wheelDeltaY; - if (ol.has.SAFARI) { - delta /= 3; - } - } - - if (delta === 0) { - return false; - } - - var now = Date.now(); - - if (this.startTime_ === undefined) { - this.startTime_ = now; - } - - if (!this.mode_ || now - this.startTime_ > this.trackpadEventGap_) { - this.mode_ = Math.abs(delta) < 4 ? - ol.interaction.MouseWheelZoom.Mode_.TRACKPAD : - ol.interaction.MouseWheelZoom.Mode_.WHEEL; - } - - if (this.mode_ === ol.interaction.MouseWheelZoom.Mode_.TRACKPAD) { - var view = map.getView(); - if (this.trackpadTimeoutId_) { - clearTimeout(this.trackpadTimeoutId_); - } else { - view.setHint(ol.ViewHint.INTERACTING, 1); - } - this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_); - var resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_); - var minResolution = view.getMinResolution(); - var maxResolution = view.getMaxResolution(); - var rebound = 0; - if (resolution < minResolution) { - resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_); - rebound = 1; - } else if (resolution > maxResolution) { - resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_); - rebound = -1; - } - if (this.lastAnchor_) { - var center = view.calculateCenterZoom(resolution, this.lastAnchor_); - view.setCenter(view.constrainCenter(center)); - } - view.setResolution(resolution); - - if (rebound === 0 && this.constrainResolution_) { - view.animate({ - resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1), - easing: ol.easing.easeOut, - anchor: this.lastAnchor_, - duration: this.duration_ - }); - } - - if (rebound > 0) { - view.animate({ - resolution: minResolution, - easing: ol.easing.easeOut, - anchor: this.lastAnchor_, - duration: 500 - }); - } else if (rebound < 0) { - view.animate({ - resolution: maxResolution, - easing: ol.easing.easeOut, - anchor: this.lastAnchor_, - duration: 500 - }); - } - this.startTime_ = now; - return false; - } - - this.delta_ += delta; - - var timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0); - - clearTimeout(this.timeoutId_); - this.timeoutId_ = setTimeout(this.handleWheelZoom_.bind(this, map), timeLeft); - - return false; -}; - - -/** - * @private - */ -ol.interaction.MouseWheelZoom.prototype.decrementInteractingHint_ = function() { - this.trackpadTimeoutId_ = undefined; - var view = this.getMap().getView(); - view.setHint(ol.ViewHint.INTERACTING, -1); -}; - - -/** - * @private - * @param {ol.Map} map Map. - */ -ol.interaction.MouseWheelZoom.prototype.handleWheelZoom_ = function(map) { - var view = map.getView(); - if (view.getAnimating()) { - view.cancelAnimations(); - } - var maxDelta = ol.MOUSEWHEELZOOM_MAXDELTA; - var delta = ol.math.clamp(this.delta_, -maxDelta, maxDelta); - ol.interaction.Interaction.zoomByDelta(view, -delta, this.lastAnchor_, - this.duration_); - this.mode_ = undefined; - this.delta_ = 0; - this.lastAnchor_ = null; - this.startTime_ = undefined; - this.timeoutId_ = undefined; -}; - - -/** - * Enable or disable using the mouse's location as an anchor when zooming - * @param {boolean} useAnchor true to zoom to the mouse's location, false - * to zoom to the center of the map - * @api - */ -ol.interaction.MouseWheelZoom.prototype.setMouseAnchor = function(useAnchor) { - this.useAnchor_ = useAnchor; - if (!useAnchor) { - this.lastAnchor_ = null; - } -}; - - -/** - * @enum {string} - * @private - */ -ol.interaction.MouseWheelZoom.Mode_ = { - TRACKPAD: 'trackpad', - WHEEL: 'wheel' -}; - -goog.provide('ol.interaction.PinchRotate'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.functions'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.RotationConstraint'); - - -/** - * @classdesc - * Allows the user to rotate the map by twisting with two fingers - * on a touch screen. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.PinchRotateOptions=} opt_options Options. - * @api - */ -ol.interaction.PinchRotate = function(opt_options) { - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.PinchRotate.handleDownEvent_, - handleDragEvent: ol.interaction.PinchRotate.handleDragEvent_, - handleUpEvent: ol.interaction.PinchRotate.handleUpEvent_ - }); - - var options = opt_options || {}; - - /** - * @private - * @type {ol.Coordinate} - */ - this.anchor_ = null; - - /** - * @private - * @type {number|undefined} - */ - this.lastAngle_ = undefined; - - /** - * @private - * @type {boolean} - */ - this.rotating_ = false; - - /** - * @private - * @type {number} - */ - this.rotationDelta_ = 0.0; - - /** - * @private - * @type {number} - */ - this.threshold_ = options.threshold !== undefined ? options.threshold : 0.3; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; - -}; -ol.inherits(ol.interaction.PinchRotate, ol.interaction.Pointer); - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.PinchRotate} - * @private - */ -ol.interaction.PinchRotate.handleDragEvent_ = function(mapBrowserEvent) { - var rotationDelta = 0.0; - - var touch0 = this.targetPointers[0]; - var touch1 = this.targetPointers[1]; - - // angle between touches - var angle = Math.atan2( - touch1.clientY - touch0.clientY, - touch1.clientX - touch0.clientX); - - if (this.lastAngle_ !== undefined) { - var delta = angle - this.lastAngle_; - this.rotationDelta_ += delta; - if (!this.rotating_ && - Math.abs(this.rotationDelta_) > this.threshold_) { - this.rotating_ = true; - } - rotationDelta = delta; - } - this.lastAngle_ = angle; - - var map = mapBrowserEvent.map; - var view = map.getView(); - if (view.getConstraints().rotation === ol.RotationConstraint.disable) { - return; - } - - // rotate anchor point. - // FIXME: should be the intersection point between the lines: - // touch0,touch1 and previousTouch0,previousTouch1 - var viewportPosition = map.getViewport().getBoundingClientRect(); - var centroid = ol.interaction.Pointer.centroid(this.targetPointers); - centroid[0] -= viewportPosition.left; - centroid[1] -= viewportPosition.top; - this.anchor_ = map.getCoordinateFromPixel(centroid); - - // rotate - if (this.rotating_) { - var rotation = view.getRotation(); - map.render(); - ol.interaction.Interaction.rotateWithoutConstraints(view, - rotation + rotationDelta, this.anchor_); - } -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.PinchRotate} - * @private - */ -ol.interaction.PinchRotate.handleUpEvent_ = function(mapBrowserEvent) { - if (this.targetPointers.length < 2) { - var map = mapBrowserEvent.map; - var view = map.getView(); - view.setHint(ol.ViewHint.INTERACTING, -1); - if (this.rotating_) { - var rotation = view.getRotation(); - ol.interaction.Interaction.rotate( - view, rotation, this.anchor_, this.duration_); - } - return false; - } else { - return true; - } -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.PinchRotate} - * @private - */ -ol.interaction.PinchRotate.handleDownEvent_ = function(mapBrowserEvent) { - if (this.targetPointers.length >= 2) { - var map = mapBrowserEvent.map; - this.anchor_ = null; - this.lastAngle_ = undefined; - this.rotating_ = false; - this.rotationDelta_ = 0.0; - if (!this.handlingDownUpSequence) { - map.getView().setHint(ol.ViewHint.INTERACTING, 1); - } - return true; - } else { - return false; - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.PinchRotate.prototype.shouldStopEvent = ol.functions.FALSE; - -goog.provide('ol.interaction.PinchZoom'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.functions'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.interaction.Pointer'); - - -/** - * @classdesc - * Allows the user to zoom the map by pinching with two fingers - * on a touch screen. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.PinchZoomOptions=} opt_options Options. - * @api - */ -ol.interaction.PinchZoom = function(opt_options) { - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.PinchZoom.handleDownEvent_, - handleDragEvent: ol.interaction.PinchZoom.handleDragEvent_, - handleUpEvent: ol.interaction.PinchZoom.handleUpEvent_ - }); - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {boolean} - */ - this.constrainResolution_ = options.constrainResolution || false; - - /** - * @private - * @type {ol.Coordinate} - */ - this.anchor_ = null; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 400; - - /** - * @private - * @type {number|undefined} - */ - this.lastDistance_ = undefined; - - /** - * @private - * @type {number} - */ - this.lastScaleDelta_ = 1; - -}; -ol.inherits(ol.interaction.PinchZoom, ol.interaction.Pointer); - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.PinchZoom} - * @private - */ -ol.interaction.PinchZoom.handleDragEvent_ = function(mapBrowserEvent) { - var scaleDelta = 1.0; - - var touch0 = this.targetPointers[0]; - var touch1 = this.targetPointers[1]; - var dx = touch0.clientX - touch1.clientX; - var dy = touch0.clientY - touch1.clientY; - - // distance between touches - var distance = Math.sqrt(dx * dx + dy * dy); - - if (this.lastDistance_ !== undefined) { - scaleDelta = this.lastDistance_ / distance; - } - this.lastDistance_ = distance; - - - var map = mapBrowserEvent.map; - var view = map.getView(); - var resolution = view.getResolution(); - var maxResolution = view.getMaxResolution(); - var minResolution = view.getMinResolution(); - var newResolution = resolution * scaleDelta; - if (newResolution > maxResolution) { - scaleDelta = maxResolution / resolution; - newResolution = maxResolution; - } else if (newResolution < minResolution) { - scaleDelta = minResolution / resolution; - newResolution = minResolution; - } - - if (scaleDelta != 1.0) { - this.lastScaleDelta_ = scaleDelta; - } - - // scale anchor point. - var viewportPosition = map.getViewport().getBoundingClientRect(); - var centroid = ol.interaction.Pointer.centroid(this.targetPointers); - centroid[0] -= viewportPosition.left; - centroid[1] -= viewportPosition.top; - this.anchor_ = map.getCoordinateFromPixel(centroid); - - // scale, bypass the resolution constraint - map.render(); - ol.interaction.Interaction.zoomWithoutConstraints(view, newResolution, this.anchor_); -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.PinchZoom} - * @private - */ -ol.interaction.PinchZoom.handleUpEvent_ = function(mapBrowserEvent) { - if (this.targetPointers.length < 2) { - var map = mapBrowserEvent.map; - var view = map.getView(); - view.setHint(ol.ViewHint.INTERACTING, -1); - var resolution = view.getResolution(); - if (this.constrainResolution_ || - resolution < view.getMinResolution() || - resolution > view.getMaxResolution()) { - // Zoom to final resolution, with an animation, and provide a - // direction not to zoom out/in if user was pinching in/out. - // Direction is > 0 if pinching out, and < 0 if pinching in. - var direction = this.lastScaleDelta_ - 1; - ol.interaction.Interaction.zoom(view, resolution, - this.anchor_, this.duration_, direction); - } - return false; - } else { - return true; - } -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.PinchZoom} - * @private - */ -ol.interaction.PinchZoom.handleDownEvent_ = function(mapBrowserEvent) { - if (this.targetPointers.length >= 2) { - var map = mapBrowserEvent.map; - this.anchor_ = null; - this.lastDistance_ = undefined; - this.lastScaleDelta_ = 1; - if (!this.handlingDownUpSequence) { - map.getView().setHint(ol.ViewHint.INTERACTING, 1); - } - return true; - } else { - return false; - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.PinchZoom.prototype.shouldStopEvent = ol.functions.FALSE; - -goog.provide('ol.interaction'); - -goog.require('ol.Collection'); -goog.require('ol.Kinetic'); -goog.require('ol.interaction.DoubleClickZoom'); -goog.require('ol.interaction.DragPan'); -goog.require('ol.interaction.DragRotate'); -goog.require('ol.interaction.DragZoom'); -goog.require('ol.interaction.KeyboardPan'); -goog.require('ol.interaction.KeyboardZoom'); -goog.require('ol.interaction.MouseWheelZoom'); -goog.require('ol.interaction.PinchRotate'); -goog.require('ol.interaction.PinchZoom'); - - -/** - * Set of interactions included in maps by default. Specific interactions can be - * excluded by setting the appropriate option to false in the constructor - * options, but the order of the interactions is fixed. If you want to specify - * a different order for interactions, you will need to create your own - * {@link ol.interaction.Interaction} instances and insert them into a - * {@link ol.Collection} in the order you want before creating your - * {@link ol.Map} instance. The default set of interactions, in sequence, is: - * * {@link ol.interaction.DragRotate} - * * {@link ol.interaction.DoubleClickZoom} - * * {@link ol.interaction.DragPan} - * * {@link ol.interaction.PinchRotate} - * * {@link ol.interaction.PinchZoom} - * * {@link ol.interaction.KeyboardPan} - * * {@link ol.interaction.KeyboardZoom} - * * {@link ol.interaction.MouseWheelZoom} - * * {@link ol.interaction.DragZoom} - * - * @param {olx.interaction.DefaultsOptions=} opt_options Defaults options. - * @return {ol.Collection.<ol.interaction.Interaction>} A collection of - * interactions to be used with the ol.Map constructor's interactions option. - * @api - */ -ol.interaction.defaults = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var interactions = new ol.Collection(); - - var kinetic = new ol.Kinetic(-0.005, 0.05, 100); - - var altShiftDragRotate = options.altShiftDragRotate !== undefined ? - options.altShiftDragRotate : true; - if (altShiftDragRotate) { - interactions.push(new ol.interaction.DragRotate()); - } - - var doubleClickZoom = options.doubleClickZoom !== undefined ? - options.doubleClickZoom : true; - if (doubleClickZoom) { - interactions.push(new ol.interaction.DoubleClickZoom({ - delta: options.zoomDelta, - duration: options.zoomDuration - })); - } - - var dragPan = options.dragPan !== undefined ? options.dragPan : true; - if (dragPan) { - interactions.push(new ol.interaction.DragPan({ - kinetic: kinetic - })); - } - - var pinchRotate = options.pinchRotate !== undefined ? options.pinchRotate : - true; - if (pinchRotate) { - interactions.push(new ol.interaction.PinchRotate()); - } - - var pinchZoom = options.pinchZoom !== undefined ? options.pinchZoom : true; - if (pinchZoom) { - interactions.push(new ol.interaction.PinchZoom({ - constrainResolution: options.constrainResolution, - duration: options.zoomDuration - })); - } - - var keyboard = options.keyboard !== undefined ? options.keyboard : true; - if (keyboard) { - interactions.push(new ol.interaction.KeyboardPan()); - interactions.push(new ol.interaction.KeyboardZoom({ - delta: options.zoomDelta, - duration: options.zoomDuration - })); - } - - var mouseWheelZoom = options.mouseWheelZoom !== undefined ? - options.mouseWheelZoom : true; - if (mouseWheelZoom) { - interactions.push(new ol.interaction.MouseWheelZoom({ - constrainResolution: options.constrainResolution, - duration: options.zoomDuration - })); - } - - var shiftDragZoom = options.shiftDragZoom !== undefined ? - options.shiftDragZoom : true; - if (shiftDragZoom) { - interactions.push(new ol.interaction.DragZoom({ - duration: options.zoomDuration - })); - } - - return interactions; - -}; - -goog.provide('ol.layer.Property'); - -/** - * @enum {string} - */ -ol.layer.Property = { - OPACITY: 'opacity', - VISIBLE: 'visible', - EXTENT: 'extent', - Z_INDEX: 'zIndex', - MAX_RESOLUTION: 'maxResolution', - MIN_RESOLUTION: 'minResolution', - SOURCE: 'source' -}; - -goog.provide('ol.layer.Base'); - -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.layer.Property'); -goog.require('ol.math'); -goog.require('ol.obj'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Note that with `ol.layer.Base` and all its subclasses, any property set in - * the options is set as a {@link ol.Object} property on the layer object, so - * is observable, and has get/set accessors. - * - * @constructor - * @abstract - * @extends {ol.Object} - * @param {olx.layer.BaseOptions} options Layer options. - * @api - */ -ol.layer.Base = function(options) { - - ol.Object.call(this); - - /** - * @type {Object.<string, *>} - */ - var properties = ol.obj.assign({}, options); - properties[ol.layer.Property.OPACITY] = - options.opacity !== undefined ? options.opacity : 1; - properties[ol.layer.Property.VISIBLE] = - options.visible !== undefined ? options.visible : true; - properties[ol.layer.Property.Z_INDEX] = - options.zIndex !== undefined ? options.zIndex : 0; - properties[ol.layer.Property.MAX_RESOLUTION] = - options.maxResolution !== undefined ? options.maxResolution : Infinity; - properties[ol.layer.Property.MIN_RESOLUTION] = - options.minResolution !== undefined ? options.minResolution : 0; - - this.setProperties(properties); - - /** - * @type {ol.LayerState} - * @private - */ - this.state_ = /** @type {ol.LayerState} */ ({ - layer: /** @type {ol.layer.Layer} */ (this), - managed: true - }); - -}; -ol.inherits(ol.layer.Base, ol.Object); - - -/** - * Create a renderer for this layer. - * @abstract - * @param {ol.renderer.Map} mapRenderer The map renderer. - * @return {ol.renderer.Layer} A layer renderer. - */ -ol.layer.Base.prototype.createRenderer = function(mapRenderer) {}; - - -/** - * @return {ol.LayerState} Layer state. - */ -ol.layer.Base.prototype.getLayerState = function() { - this.state_.opacity = ol.math.clamp(this.getOpacity(), 0, 1); - this.state_.sourceState = this.getSourceState(); - this.state_.visible = this.getVisible(); - this.state_.extent = this.getExtent(); - this.state_.zIndex = this.getZIndex(); - this.state_.maxResolution = this.getMaxResolution(); - this.state_.minResolution = Math.max(this.getMinResolution(), 0); - - return this.state_; -}; - - -/** - * @abstract - * @param {Array.<ol.layer.Layer>=} opt_array Array of layers (to be - * modified in place). - * @return {Array.<ol.layer.Layer>} Array of layers. - */ -ol.layer.Base.prototype.getLayersArray = function(opt_array) {}; - - -/** - * @abstract - * @param {Array.<ol.LayerState>=} opt_states Optional list of layer - * states (to be modified in place). - * @return {Array.<ol.LayerState>} List of layer states. - */ -ol.layer.Base.prototype.getLayerStatesArray = function(opt_states) {}; - - -/** - * Return the {@link ol.Extent extent} of the layer or `undefined` if it - * will be visible regardless of extent. - * @return {ol.Extent|undefined} The layer extent. - * @observable - * @api - */ -ol.layer.Base.prototype.getExtent = function() { - return /** @type {ol.Extent|undefined} */ ( - this.get(ol.layer.Property.EXTENT)); -}; - - -/** - * Return the maximum resolution of the layer. - * @return {number} The maximum resolution of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.getMaxResolution = function() { - return /** @type {number} */ ( - this.get(ol.layer.Property.MAX_RESOLUTION)); -}; - - -/** - * Return the minimum resolution of the layer. - * @return {number} The minimum resolution of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.getMinResolution = function() { - return /** @type {number} */ ( - this.get(ol.layer.Property.MIN_RESOLUTION)); -}; - - -/** - * Return the opacity of the layer (between 0 and 1). - * @return {number} The opacity of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.getOpacity = function() { - return /** @type {number} */ (this.get(ol.layer.Property.OPACITY)); -}; - - -/** - * @abstract - * @return {ol.source.State} Source state. - */ -ol.layer.Base.prototype.getSourceState = function() {}; - - -/** - * Return the visibility of the layer (`true` or `false`). - * @return {boolean} The visibility of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.getVisible = function() { - return /** @type {boolean} */ (this.get(ol.layer.Property.VISIBLE)); -}; - - -/** - * Return the Z-index of the layer, which is used to order layers before - * rendering. The default Z-index is 0. - * @return {number} The Z-index of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.getZIndex = function() { - return /** @type {number} */ (this.get(ol.layer.Property.Z_INDEX)); -}; - - -/** - * Set the extent at which the layer is visible. If `undefined`, the layer - * will be visible at all extents. - * @param {ol.Extent|undefined} extent The extent of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.setExtent = function(extent) { - this.set(ol.layer.Property.EXTENT, extent); -}; - - -/** - * Set the maximum resolution at which the layer is visible. - * @param {number} maxResolution The maximum resolution of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.setMaxResolution = function(maxResolution) { - this.set(ol.layer.Property.MAX_RESOLUTION, maxResolution); -}; - - -/** - * Set the minimum resolution at which the layer is visible. - * @param {number} minResolution The minimum resolution of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.setMinResolution = function(minResolution) { - this.set(ol.layer.Property.MIN_RESOLUTION, minResolution); -}; - - -/** - * Set the opacity of the layer, allowed values range from 0 to 1. - * @param {number} opacity The opacity of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.setOpacity = function(opacity) { - this.set(ol.layer.Property.OPACITY, opacity); -}; - - -/** - * Set the visibility of the layer (`true` or `false`). - * @param {boolean} visible The visibility of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.setVisible = function(visible) { - this.set(ol.layer.Property.VISIBLE, visible); -}; - - -/** - * Set Z-index of the layer, which is used to order layers before rendering. - * The default Z-index is 0. - * @param {number} zindex The z-index of the layer. - * @observable - * @api - */ -ol.layer.Base.prototype.setZIndex = function(zindex) { - this.set(ol.layer.Property.Z_INDEX, zindex); -}; - -goog.provide('ol.source.State'); - - -/** - * State of the source, one of 'undefined', 'loading', 'ready' or 'error'. - * @enum {string} - */ -ol.source.State = { - UNDEFINED: 'undefined', - LOADING: 'loading', - READY: 'ready', - ERROR: 'error' -}; - - -goog.provide('ol.layer.Group'); - -goog.require('ol'); -goog.require('ol.Collection'); -goog.require('ol.CollectionEventType'); -goog.require('ol.Object'); -goog.require('ol.ObjectEventType'); -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.layer.Base'); -goog.require('ol.obj'); -goog.require('ol.source.State'); - - -/** - * @classdesc - * A {@link ol.Collection} of layers that are handled together. - * - * A generic `change` event is triggered when the group/Collection changes. - * - * @constructor - * @extends {ol.layer.Base} - * @param {olx.layer.GroupOptions=} opt_options Layer options. - * @api - */ -ol.layer.Group = function(opt_options) { - - var options = opt_options || {}; - var baseOptions = /** @type {olx.layer.GroupOptions} */ - (ol.obj.assign({}, options)); - delete baseOptions.layers; - - var layers = options.layers; - - ol.layer.Base.call(this, baseOptions); - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.layersListenerKeys_ = []; - - /** - * @private - * @type {Object.<string, Array.<ol.EventsKey>>} - */ - this.listenerKeys_ = {}; - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.layer.Group.Property_.LAYERS), - this.handleLayersChanged_, this); - - if (layers) { - if (Array.isArray(layers)) { - layers = new ol.Collection(layers.slice(), {unique: true}); - } else { - ol.asserts.assert(layers instanceof ol.Collection, - 43); // Expected `layers` to be an array or an `ol.Collection` - layers = layers; - } - } else { - layers = new ol.Collection(undefined, {unique: true}); - } - - this.setLayers(layers); - -}; -ol.inherits(ol.layer.Group, ol.layer.Base); - - -/** - * @inheritDoc - */ -ol.layer.Group.prototype.createRenderer = function(mapRenderer) {}; - - -/** - * @private - */ -ol.layer.Group.prototype.handleLayerChange_ = function() { - if (this.getVisible()) { - this.changed(); - } -}; - - -/** - * @param {ol.events.Event} event Event. - * @private - */ -ol.layer.Group.prototype.handleLayersChanged_ = function(event) { - this.layersListenerKeys_.forEach(ol.events.unlistenByKey); - this.layersListenerKeys_.length = 0; - - var layers = this.getLayers(); - this.layersListenerKeys_.push( - ol.events.listen(layers, ol.CollectionEventType.ADD, - this.handleLayersAdd_, this), - ol.events.listen(layers, ol.CollectionEventType.REMOVE, - this.handleLayersRemove_, this)); - - for (var id in this.listenerKeys_) { - this.listenerKeys_[id].forEach(ol.events.unlistenByKey); - } - ol.obj.clear(this.listenerKeys_); - - var layersArray = layers.getArray(); - var i, ii, layer; - for (i = 0, ii = layersArray.length; i < ii; i++) { - layer = layersArray[i]; - this.listenerKeys_[ol.getUid(layer).toString()] = [ - ol.events.listen(layer, ol.ObjectEventType.PROPERTYCHANGE, - this.handleLayerChange_, this), - ol.events.listen(layer, ol.events.EventType.CHANGE, - this.handleLayerChange_, this) - ]; - } - - this.changed(); -}; - - -/** - * @param {ol.Collection.Event} collectionEvent Collection event. - * @private - */ -ol.layer.Group.prototype.handleLayersAdd_ = function(collectionEvent) { - var layer = /** @type {ol.layer.Base} */ (collectionEvent.element); - var key = ol.getUid(layer).toString(); - this.listenerKeys_[key] = [ - ol.events.listen(layer, ol.ObjectEventType.PROPERTYCHANGE, - this.handleLayerChange_, this), - ol.events.listen(layer, ol.events.EventType.CHANGE, - this.handleLayerChange_, this) - ]; - this.changed(); -}; - - -/** - * @param {ol.Collection.Event} collectionEvent Collection event. - * @private - */ -ol.layer.Group.prototype.handleLayersRemove_ = function(collectionEvent) { - var layer = /** @type {ol.layer.Base} */ (collectionEvent.element); - var key = ol.getUid(layer).toString(); - this.listenerKeys_[key].forEach(ol.events.unlistenByKey); - delete this.listenerKeys_[key]; - this.changed(); -}; - - -/** - * Returns the {@link ol.Collection collection} of {@link ol.layer.Layer layers} - * in this group. - * @return {!ol.Collection.<ol.layer.Base>} Collection of - * {@link ol.layer.Base layers} that are part of this group. - * @observable - * @api - */ -ol.layer.Group.prototype.getLayers = function() { - return /** @type {!ol.Collection.<ol.layer.Base>} */ (this.get( - ol.layer.Group.Property_.LAYERS)); -}; - - -/** - * Set the {@link ol.Collection collection} of {@link ol.layer.Layer layers} - * in this group. - * @param {!ol.Collection.<ol.layer.Base>} layers Collection of - * {@link ol.layer.Base layers} that are part of this group. - * @observable - * @api - */ -ol.layer.Group.prototype.setLayers = function(layers) { - this.set(ol.layer.Group.Property_.LAYERS, layers); -}; - - -/** - * @inheritDoc - */ -ol.layer.Group.prototype.getLayersArray = function(opt_array) { - var array = opt_array !== undefined ? opt_array : []; - this.getLayers().forEach(function(layer) { - layer.getLayersArray(array); - }); - return array; -}; - - -/** - * @inheritDoc - */ -ol.layer.Group.prototype.getLayerStatesArray = function(opt_states) { - var states = opt_states !== undefined ? opt_states : []; - - var pos = states.length; - - this.getLayers().forEach(function(layer) { - layer.getLayerStatesArray(states); - }); - - var ownLayerState = this.getLayerState(); - var i, ii, layerState; - for (i = pos, ii = states.length; i < ii; i++) { - layerState = states[i]; - layerState.opacity *= ownLayerState.opacity; - layerState.visible = layerState.visible && ownLayerState.visible; - layerState.maxResolution = Math.min( - layerState.maxResolution, ownLayerState.maxResolution); - layerState.minResolution = Math.max( - layerState.minResolution, ownLayerState.minResolution); - if (ownLayerState.extent !== undefined) { - if (layerState.extent !== undefined) { - layerState.extent = ol.extent.getIntersection( - layerState.extent, ownLayerState.extent); - } else { - layerState.extent = ownLayerState.extent; - } - } - } - - return states; -}; - - -/** - * @inheritDoc - */ -ol.layer.Group.prototype.getSourceState = function() { - return ol.source.State.READY; -}; - -/** - * @enum {string} - * @private - */ -ol.layer.Group.Property_ = { - LAYERS: 'layers' -}; - -goog.provide('ol.render.EventType'); - -/** - * @enum {string} - */ -ol.render.EventType = { - /** - * @event ol.render.Event#postcompose - * @api - */ - POSTCOMPOSE: 'postcompose', - /** - * @event ol.render.Event#precompose - * @api - */ - PRECOMPOSE: 'precompose', - /** - * @event ol.render.Event#render - * @api - */ - RENDER: 'render' -}; - -goog.provide('ol.layer.Layer'); - -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.layer.Base'); -goog.require('ol.layer.Property'); -goog.require('ol.obj'); -goog.require('ol.render.EventType'); -goog.require('ol.source.State'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * A visual representation of raster or vector map data. - * Layers group together those properties that pertain to how the data is to be - * displayed, irrespective of the source of that data. - * - * Layers are usually added to a map with {@link ol.Map#addLayer}. Components - * like {@link ol.interaction.Select} use unmanaged layers internally. These - * unmanaged layers are associated with the map using - * {@link ol.layer.Layer#setMap} instead. - * - * A generic `change` event is fired when the state of the source changes. - * - * @constructor - * @abstract - * @extends {ol.layer.Base} - * @fires ol.render.Event - * @param {olx.layer.LayerOptions} options Layer options. - * @api - */ -ol.layer.Layer = function(options) { - - var baseOptions = ol.obj.assign({}, options); - delete baseOptions.source; - - ol.layer.Base.call(this, /** @type {olx.layer.BaseOptions} */ (baseOptions)); - - /** - * @private - * @type {?ol.EventsKey} - */ - this.mapPrecomposeKey_ = null; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.mapRenderKey_ = null; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.sourceChangeKey_ = null; - - if (options.map) { - this.setMap(options.map); - } - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.layer.Property.SOURCE), - this.handleSourcePropertyChange_, this); - - var source = options.source ? options.source : null; - this.setSource(source); -}; -ol.inherits(ol.layer.Layer, ol.layer.Base); - - -/** - * Return `true` if the layer is visible, and if the passed resolution is - * between the layer's minResolution and maxResolution. The comparison is - * inclusive for `minResolution` and exclusive for `maxResolution`. - * @param {ol.LayerState} layerState Layer state. - * @param {number} resolution Resolution. - * @return {boolean} The layer is visible at the given resolution. - */ -ol.layer.Layer.visibleAtResolution = function(layerState, resolution) { - return layerState.visible && resolution >= layerState.minResolution && - resolution < layerState.maxResolution; -}; - - -/** - * @inheritDoc - */ -ol.layer.Layer.prototype.getLayersArray = function(opt_array) { - var array = opt_array ? opt_array : []; - array.push(this); - return array; -}; - - -/** - * @inheritDoc - */ -ol.layer.Layer.prototype.getLayerStatesArray = function(opt_states) { - var states = opt_states ? opt_states : []; - states.push(this.getLayerState()); - return states; -}; - - -/** - * Get the layer source. - * @return {ol.source.Source} The layer source (or `null` if not yet set). - * @observable - * @api - */ -ol.layer.Layer.prototype.getSource = function() { - var source = this.get(ol.layer.Property.SOURCE); - return /** @type {ol.source.Source} */ (source) || null; -}; - - -/** - * @inheritDoc - */ -ol.layer.Layer.prototype.getSourceState = function() { - var source = this.getSource(); - return !source ? ol.source.State.UNDEFINED : source.getState(); -}; - - -/** - * @private - */ -ol.layer.Layer.prototype.handleSourceChange_ = function() { - this.changed(); -}; - - -/** - * @private - */ -ol.layer.Layer.prototype.handleSourcePropertyChange_ = function() { - if (this.sourceChangeKey_) { - ol.events.unlistenByKey(this.sourceChangeKey_); - this.sourceChangeKey_ = null; - } - var source = this.getSource(); - if (source) { - this.sourceChangeKey_ = ol.events.listen(source, - ol.events.EventType.CHANGE, this.handleSourceChange_, this); - } - this.changed(); -}; - - -/** - * Sets the layer to be rendered on top of other layers on a map. The map will - * not manage this layer in its layers collection, and the callback in - * {@link ol.Map#forEachLayerAtPixel} will receive `null` as layer. This - * is useful for temporary layers. To remove an unmanaged layer from the map, - * use `#setMap(null)`. - * - * To add the layer to a map and have it managed by the map, use - * {@link ol.Map#addLayer} instead. - * @param {ol.Map} map Map. - * @api - */ -ol.layer.Layer.prototype.setMap = function(map) { - if (this.mapPrecomposeKey_) { - ol.events.unlistenByKey(this.mapPrecomposeKey_); - this.mapPrecomposeKey_ = null; - } - if (!map) { - this.changed(); - } - if (this.mapRenderKey_) { - ol.events.unlistenByKey(this.mapRenderKey_); - this.mapRenderKey_ = null; - } - if (map) { - this.mapPrecomposeKey_ = ol.events.listen( - map, ol.render.EventType.PRECOMPOSE, function(evt) { - var layerState = this.getLayerState(); - layerState.managed = false; - layerState.zIndex = Infinity; - evt.frameState.layerStatesArray.push(layerState); - evt.frameState.layerStates[ol.getUid(this)] = layerState; - }, this); - this.mapRenderKey_ = ol.events.listen( - this, ol.events.EventType.CHANGE, map.render, map); - this.changed(); - } -}; - - -/** - * Set the layer source. - * @param {ol.source.Source} source The layer source. - * @observable - * @api - */ -ol.layer.Layer.prototype.setSource = function(source) { - this.set(ol.layer.Property.SOURCE, source); -}; - -goog.provide('ol.style.IconImageCache'); - -goog.require('ol.color'); - - -/** - * @constructor - */ -ol.style.IconImageCache = function() { - - /** - * @type {Object.<string, ol.style.IconImage>} - * @private - */ - this.cache_ = {}; - - /** - * @type {number} - * @private - */ - this.cacheSize_ = 0; - - /** - * @const - * @type {number} - * @private - */ - this.maxCacheSize_ = 32; -}; - - -/** - * @param {string} src Src. - * @param {?string} crossOrigin Cross origin. - * @param {ol.Color} color Color. - * @return {string} Cache key. - */ -ol.style.IconImageCache.getKey = function(src, crossOrigin, color) { - var colorString = color ? ol.color.asString(color) : 'null'; - return crossOrigin + ':' + src + ':' + colorString; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.style.IconImageCache.prototype.clear = function() { - this.cache_ = {}; - this.cacheSize_ = 0; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.style.IconImageCache.prototype.expire = function() { - if (this.cacheSize_ > this.maxCacheSize_) { - var i = 0; - var key, iconImage; - for (key in this.cache_) { - iconImage = this.cache_[key]; - if ((i++ & 3) === 0 && !iconImage.hasListener()) { - delete this.cache_[key]; - --this.cacheSize_; - } - } - } -}; - - -/** - * @param {string} src Src. - * @param {?string} crossOrigin Cross origin. - * @param {ol.Color} color Color. - * @return {ol.style.IconImage} Icon image. - */ -ol.style.IconImageCache.prototype.get = function(src, crossOrigin, color) { - var key = ol.style.IconImageCache.getKey(src, crossOrigin, color); - return key in this.cache_ ? this.cache_[key] : null; -}; - - -/** - * @param {string} src Src. - * @param {?string} crossOrigin Cross origin. - * @param {ol.Color} color Color. - * @param {ol.style.IconImage} iconImage Icon image. - */ -ol.style.IconImageCache.prototype.set = function(src, crossOrigin, color, iconImage) { - var key = ol.style.IconImageCache.getKey(src, crossOrigin, color); - this.cache_[key] = iconImage; - ++this.cacheSize_; -}; - -goog.provide('ol.style'); - -goog.require('ol.style.IconImageCache'); - -ol.style.iconImageCache = new ol.style.IconImageCache(); - -goog.provide('ol.transform'); - -goog.require('ol.asserts'); - - -/** - * Collection of affine 2d transformation functions. The functions work on an - * array of 6 elements. The element order is compatible with the [SVGMatrix - * interface](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix) and is - * a subset (elements a to f) of a 3x3 martrix: - * ``` - * [ a c e ] - * [ b d f ] - * [ 0 0 1 ] - * ``` - */ - - -/** - * @private - * @type {ol.Transform} - */ -ol.transform.tmp_ = new Array(6); - - -/** - * Create an identity transform. - * @return {!ol.Transform} Identity transform. - */ -ol.transform.create = function() { - return [1, 0, 0, 1, 0, 0]; -}; - - -/** - * Resets the given transform to an identity transform. - * @param {!ol.Transform} transform Transform. - * @return {!ol.Transform} Transform. - */ -ol.transform.reset = function(transform) { - return ol.transform.set(transform, 1, 0, 0, 1, 0, 0); -}; - - -/** - * Multiply the underlying matrices of two transforms and return the result in - * the first transform. - * @param {!ol.Transform} transform1 Transform parameters of matrix 1. - * @param {!ol.Transform} transform2 Transform parameters of matrix 2. - * @return {!ol.Transform} transform1 multiplied with transform2. - */ -ol.transform.multiply = function(transform1, transform2) { - var a1 = transform1[0]; - var b1 = transform1[1]; - var c1 = transform1[2]; - var d1 = transform1[3]; - var e1 = transform1[4]; - var f1 = transform1[5]; - var a2 = transform2[0]; - var b2 = transform2[1]; - var c2 = transform2[2]; - var d2 = transform2[3]; - var e2 = transform2[4]; - var f2 = transform2[5]; - - transform1[0] = a1 * a2 + c1 * b2; - transform1[1] = b1 * a2 + d1 * b2; - transform1[2] = a1 * c2 + c1 * d2; - transform1[3] = b1 * c2 + d1 * d2; - transform1[4] = a1 * e2 + c1 * f2 + e1; - transform1[5] = b1 * e2 + d1 * f2 + f1; - - return transform1; -}; - -/** - * Set the transform components a-f on a given transform. - * @param {!ol.Transform} transform Transform. - * @param {number} a The a component of the transform. - * @param {number} b The b component of the transform. - * @param {number} c The c component of the transform. - * @param {number} d The d component of the transform. - * @param {number} e The e component of the transform. - * @param {number} f The f component of the transform. - * @return {!ol.Transform} Matrix with transform applied. - */ -ol.transform.set = function(transform, a, b, c, d, e, f) { - transform[0] = a; - transform[1] = b; - transform[2] = c; - transform[3] = d; - transform[4] = e; - transform[5] = f; - return transform; -}; - - -/** - * Set transform on one matrix from another matrix. - * @param {!ol.Transform} transform1 Matrix to set transform to. - * @param {!ol.Transform} transform2 Matrix to set transform from. - * @return {!ol.Transform} transform1 with transform from transform2 applied. - */ -ol.transform.setFromArray = function(transform1, transform2) { - transform1[0] = transform2[0]; - transform1[1] = transform2[1]; - transform1[2] = transform2[2]; - transform1[3] = transform2[3]; - transform1[4] = transform2[4]; - transform1[5] = transform2[5]; - return transform1; -}; - - -/** - * Transforms the given coordinate with the given transform returning the - * resulting, transformed coordinate. The coordinate will be modified in-place. - * - * @param {ol.Transform} transform The transformation. - * @param {ol.Coordinate|ol.Pixel} coordinate The coordinate to transform. - * @return {ol.Coordinate|ol.Pixel} return coordinate so that operations can be - * chained together. - */ -ol.transform.apply = function(transform, coordinate) { - var x = coordinate[0], y = coordinate[1]; - coordinate[0] = transform[0] * x + transform[2] * y + transform[4]; - coordinate[1] = transform[1] * x + transform[3] * y + transform[5]; - return coordinate; -}; - - -/** - * Applies rotation to the given transform. - * @param {!ol.Transform} transform Transform. - * @param {number} angle Angle in radians. - * @return {!ol.Transform} The rotated transform. - */ -ol.transform.rotate = function(transform, angle) { - var cos = Math.cos(angle); - var sin = Math.sin(angle); - return ol.transform.multiply(transform, - ol.transform.set(ol.transform.tmp_, cos, sin, -sin, cos, 0, 0)); -}; - - -/** - * Applies scale to a given transform. - * @param {!ol.Transform} transform Transform. - * @param {number} x Scale factor x. - * @param {number} y Scale factor y. - * @return {!ol.Transform} The scaled transform. - */ -ol.transform.scale = function(transform, x, y) { - return ol.transform.multiply(transform, - ol.transform.set(ol.transform.tmp_, x, 0, 0, y, 0, 0)); -}; - - -/** - * Applies translation to the given transform. - * @param {!ol.Transform} transform Transform. - * @param {number} dx Translation x. - * @param {number} dy Translation y. - * @return {!ol.Transform} The translated transform. - */ -ol.transform.translate = function(transform, dx, dy) { - return ol.transform.multiply(transform, - ol.transform.set(ol.transform.tmp_, 1, 0, 0, 1, dx, dy)); -}; - - -/** - * Creates a composite transform given an initial translation, scale, rotation, and - * final translation (in that order only, not commutative). - * @param {!ol.Transform} transform The transform (will be modified in place). - * @param {number} dx1 Initial translation x. - * @param {number} dy1 Initial translation y. - * @param {number} sx Scale factor x. - * @param {number} sy Scale factor y. - * @param {number} angle Rotation (in counter-clockwise radians). - * @param {number} dx2 Final translation x. - * @param {number} dy2 Final translation y. - * @return {!ol.Transform} The composite transform. - */ -ol.transform.compose = function(transform, dx1, dy1, sx, sy, angle, dx2, dy2) { - var sin = Math.sin(angle); - var cos = Math.cos(angle); - transform[0] = sx * cos; - transform[1] = sy * sin; - transform[2] = -sx * sin; - transform[3] = sy * cos; - transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1; - transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1; - return transform; -}; - - -/** - * Invert the given transform. - * @param {!ol.Transform} transform Transform. - * @return {!ol.Transform} Inverse of the transform. - */ -ol.transform.invert = function(transform) { - var det = ol.transform.determinant(transform); - ol.asserts.assert(det !== 0, 32); // Transformation matrix cannot be inverted - - var a = transform[0]; - var b = transform[1]; - var c = transform[2]; - var d = transform[3]; - var e = transform[4]; - var f = transform[5]; - - transform[0] = d / det; - transform[1] = -b / det; - transform[2] = -c / det; - transform[3] = a / det; - transform[4] = (c * f - d * e) / det; - transform[5] = -(a * f - b * e) / det; - - return transform; -}; - - -/** - * Returns the determinant of the given matrix. - * @param {!ol.Transform} mat Matrix. - * @return {number} Determinant. - */ -ol.transform.determinant = function(mat) { - return mat[0] * mat[3] - mat[1] * mat[2]; -}; - -goog.provide('ol.renderer.Map'); - -goog.require('ol'); -goog.require('ol.Disposable'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.layer.Layer'); -goog.require('ol.style'); -goog.require('ol.transform'); - - -/** - * @constructor - * @abstract - * @extends {ol.Disposable} - * @param {Element} container Container. - * @param {ol.Map} map Map. - * @struct - */ -ol.renderer.Map = function(container, map) { - - ol.Disposable.call(this); - - - /** - * @private - * @type {ol.Map} - */ - this.map_ = map; - - /** - * @private - * @type {Object.<string, ol.renderer.Layer>} - */ - this.layerRenderers_ = {}; - - /** - * @private - * @type {Object.<string, ol.EventsKey>} - */ - this.layerRendererListeners_ = {}; - -}; -ol.inherits(ol.renderer.Map, ol.Disposable); - - -/** - * @param {olx.FrameState} frameState FrameState. - * @protected - */ -ol.renderer.Map.prototype.calculateMatrices2D = function(frameState) { - var viewState = frameState.viewState; - var coordinateToPixelTransform = frameState.coordinateToPixelTransform; - var pixelToCoordinateTransform = frameState.pixelToCoordinateTransform; - - ol.transform.compose(coordinateToPixelTransform, - frameState.size[0] / 2, frameState.size[1] / 2, - 1 / viewState.resolution, -1 / viewState.resolution, - -viewState.rotation, - -viewState.center[0], -viewState.center[1]); - - ol.transform.invert( - ol.transform.setFromArray(pixelToCoordinateTransform, coordinateToPixelTransform)); -}; - - -/** - * @inheritDoc - */ -ol.renderer.Map.prototype.disposeInternal = function() { - for (var id in this.layerRenderers_) { - this.layerRenderers_[id].dispose(); - } -}; - - -/** - * @param {ol.Map} map Map. - * @param {olx.FrameState} frameState Frame state. - * @private - */ -ol.renderer.Map.expireIconCache_ = function(map, frameState) { - var cache = ol.style.iconImageCache; - cache.expire(); -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {olx.FrameState} frameState FrameState. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: S, (ol.Feature|ol.render.Feature), - * ol.layer.Layer): T} callback Feature callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter - * function, only layers which are visible and for which this function - * returns `true` will be tested for features. By default, all visible - * layers will be tested. - * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -ol.renderer.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) { - var result; - var viewState = frameState.viewState; - var viewResolution = viewState.resolution; - - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {ol.layer.Layer} layer Layer. - * @return {?} Callback result. - */ - function forEachFeatureAtCoordinate(feature, layer) { - var key = ol.getUid(feature).toString(); - var managed = frameState.layerStates[ol.getUid(layer)].managed; - if (!(key in frameState.skippedFeatureUids && !managed)) { - return callback.call(thisArg, feature, managed ? layer : null); - } - } - - var projection = viewState.projection; - - var translatedCoordinate = coordinate; - if (projection.canWrapX()) { - var projectionExtent = projection.getExtent(); - var worldWidth = ol.extent.getWidth(projectionExtent); - var x = coordinate[0]; - if (x < projectionExtent[0] || x > projectionExtent[2]) { - var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); - translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]]; - } - } - - var layerStates = frameState.layerStatesArray; - var numLayers = layerStates.length; - var i; - for (i = numLayers - 1; i >= 0; --i) { - var layerState = layerStates[i]; - var layer = layerState.layer; - if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && - layerFilter.call(thisArg2, layer)) { - var layerRenderer = this.getLayerRenderer(layer); - if (layer.getSource()) { - result = layerRenderer.forEachFeatureAtCoordinate( - layer.getSource().getWrapX() ? translatedCoordinate : coordinate, - frameState, hitTolerance, forEachFeatureAtCoordinate, thisArg); - } - if (result) { - return result; - } - } - } - return undefined; -}; - - -/** - * @abstract - * @param {ol.Pixel} pixel Pixel. - * @param {olx.FrameState} frameState FrameState. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter - * function, only layers which are visible and for which this function - * returns `true` will be tested for features. By default, all visible - * layers will be tested. - * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -ol.renderer.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, - layerFilter, thisArg2) {}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {olx.FrameState} frameState FrameState. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter - * function, only layers which are visible and for which this function - * returns `true` will be tested for features. By default, all visible - * layers will be tested. - * @param {U} thisArg Value to use as `this` when executing `layerFilter`. - * @return {boolean} Is there a feature at the given coordinate? - * @template U - */ -ol.renderer.Map.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, layerFilter, thisArg) { - var hasFeature = this.forEachFeatureAtCoordinate( - coordinate, frameState, hitTolerance, ol.functions.TRUE, this, layerFilter, thisArg); - - return hasFeature !== undefined; -}; - - -/** - * @param {ol.layer.Layer} layer Layer. - * @protected - * @return {ol.renderer.Layer} Layer renderer. - */ -ol.renderer.Map.prototype.getLayerRenderer = function(layer) { - var layerKey = ol.getUid(layer).toString(); - if (layerKey in this.layerRenderers_) { - return this.layerRenderers_[layerKey]; - } else { - var layerRenderer = layer.createRenderer(this); - this.layerRenderers_[layerKey] = layerRenderer; - this.layerRendererListeners_[layerKey] = ol.events.listen(layerRenderer, - ol.events.EventType.CHANGE, this.handleLayerRendererChange_, this); - - return layerRenderer; - } -}; - - -/** - * @param {string} layerKey Layer key. - * @protected - * @return {ol.renderer.Layer} Layer renderer. - */ -ol.renderer.Map.prototype.getLayerRendererByKey = function(layerKey) { - return this.layerRenderers_[layerKey]; -}; - - -/** - * @protected - * @return {Object.<string, ol.renderer.Layer>} Layer renderers. - */ -ol.renderer.Map.prototype.getLayerRenderers = function() { - return this.layerRenderers_; -}; - - -/** - * @return {ol.Map} Map. - */ -ol.renderer.Map.prototype.getMap = function() { - return this.map_; -}; - - -/** - * @abstract - * @return {string} Type - */ -ol.renderer.Map.prototype.getType = function() {}; - - -/** - * Handle changes in a layer renderer. - * @private - */ -ol.renderer.Map.prototype.handleLayerRendererChange_ = function() { - this.map_.render(); -}; - - -/** - * @param {string} layerKey Layer key. - * @return {ol.renderer.Layer} Layer renderer. - * @private - */ -ol.renderer.Map.prototype.removeLayerRendererByKey_ = function(layerKey) { - var layerRenderer = this.layerRenderers_[layerKey]; - delete this.layerRenderers_[layerKey]; - - ol.events.unlistenByKey(this.layerRendererListeners_[layerKey]); - delete this.layerRendererListeners_[layerKey]; - - return layerRenderer; -}; - - -/** - * Render. - * @param {?olx.FrameState} frameState Frame state. - */ -ol.renderer.Map.prototype.renderFrame = ol.nullFunction; - - -/** - * @param {ol.Map} map Map. - * @param {olx.FrameState} frameState Frame state. - * @private - */ -ol.renderer.Map.prototype.removeUnusedLayerRenderers_ = function(map, frameState) { - var layerKey; - for (layerKey in this.layerRenderers_) { - if (!frameState || !(layerKey in frameState.layerStates)) { - this.removeLayerRendererByKey_(layerKey).dispose(); - } - } -}; - - -/** - * @param {olx.FrameState} frameState Frame state. - * @protected - */ -ol.renderer.Map.prototype.scheduleExpireIconCache = function(frameState) { - frameState.postRenderFunctions.push( - /** @type {ol.PostRenderFunction} */ (ol.renderer.Map.expireIconCache_) - ); -}; - - -/** - * @param {!olx.FrameState} frameState Frame state. - * @protected - */ -ol.renderer.Map.prototype.scheduleRemoveUnusedLayerRenderers = function(frameState) { - var layerKey; - for (layerKey in this.layerRenderers_) { - if (!(layerKey in frameState.layerStates)) { - frameState.postRenderFunctions.push( - /** @type {ol.PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this)) - ); - return; - } - } -}; - - -/** - * @param {ol.LayerState} state1 First layer state. - * @param {ol.LayerState} state2 Second layer state. - * @return {number} The zIndex difference. - */ -ol.renderer.Map.sortByZIndex = function(state1, state2) { - return state1.zIndex - state2.zIndex; -}; - -goog.provide('ol.renderer.Type'); - - -/** - * Available renderers: `'canvas'` or `'webgl'`. - * @enum {string} - */ -ol.renderer.Type = { - CANVAS: 'canvas', - WEBGL: 'webgl' -}; - -goog.provide('ol.render.Event'); - -goog.require('ol'); -goog.require('ol.events.Event'); - - -/** - * @constructor - * @extends {ol.events.Event} - * @implements {oli.render.Event} - * @param {ol.render.EventType} type Type. - * @param {ol.render.VectorContext=} opt_vectorContext Vector context. - * @param {olx.FrameState=} opt_frameState Frame state. - * @param {?CanvasRenderingContext2D=} opt_context Context. - * @param {?ol.webgl.Context=} opt_glContext WebGL Context. - */ -ol.render.Event = function( - type, opt_vectorContext, opt_frameState, opt_context, - opt_glContext) { - - ol.events.Event.call(this, type); - - /** - * For canvas, this is an instance of {@link ol.render.canvas.Immediate}. - * @type {ol.render.VectorContext|undefined} - * @api - */ - this.vectorContext = opt_vectorContext; - - /** - * An object representing the current render frame state. - * @type {olx.FrameState|undefined} - * @api - */ - this.frameState = opt_frameState; - - /** - * Canvas context. Only available when a Canvas renderer is used, null - * otherwise. - * @type {CanvasRenderingContext2D|null|undefined} - * @api - */ - this.context = opt_context; - - /** - * WebGL context. Only available when a WebGL renderer is used, null - * otherwise. - * @type {ol.webgl.Context|null|undefined} - * @api - */ - this.glContext = opt_glContext; - -}; -ol.inherits(ol.render.Event, ol.events.Event); - -goog.provide('ol.render.canvas'); - - -/** - * @const - * @type {string} - */ -ol.render.canvas.defaultFont = '10px sans-serif'; - - -/** - * @const - * @type {ol.Color} - */ -ol.render.canvas.defaultFillStyle = [0, 0, 0, 1]; - - -/** - * @const - * @type {string} - */ -ol.render.canvas.defaultLineCap = 'round'; - - -/** - * @const - * @type {Array.<number>} - */ -ol.render.canvas.defaultLineDash = []; - - -/** - * @const - * @type {number} - */ -ol.render.canvas.defaultLineDashOffset = 0; - - -/** - * @const - * @type {string} - */ -ol.render.canvas.defaultLineJoin = 'round'; - - -/** - * @const - * @type {number} - */ -ol.render.canvas.defaultMiterLimit = 10; - - -/** - * @const - * @type {ol.Color} - */ -ol.render.canvas.defaultStrokeStyle = [0, 0, 0, 1]; - - -/** - * @const - * @type {string} - */ -ol.render.canvas.defaultTextAlign = 'center'; - - -/** - * @const - * @type {string} - */ -ol.render.canvas.defaultTextBaseline = 'middle'; - - -/** - * @const - * @type {number} - */ -ol.render.canvas.defaultLineWidth = 1; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {number} rotation Rotation. - * @param {number} offsetX X offset. - * @param {number} offsetY Y offset. - */ -ol.render.canvas.rotateAtOffset = function(context, rotation, offsetX, offsetY) { - if (rotation !== 0) { - context.translate(offsetX, offsetY); - context.rotate(rotation); - context.translate(-offsetX, -offsetY); - } -}; - -goog.provide('ol.render.VectorContext'); - - -/** - * Context for drawing geometries. A vector context is available on render - * events and does not need to be constructed directly. - * @constructor - * @abstract - * @struct - * @api - */ -ol.render.VectorContext = function() { -}; - - -/** - * Render a geometry. - * - * @param {ol.geom.Geometry} geometry The geometry to render. - */ -ol.render.VectorContext.prototype.drawGeometry = function(geometry) {}; - - -/** - * Set the rendering style. - * - * @param {ol.style.Style} style The rendering style. - */ -ol.render.VectorContext.prototype.setStyle = function(style) {}; - - -/** - * @param {ol.geom.Circle} circleGeometry Circle geometry. - * @param {ol.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawCircle = function(circleGeometry, feature) {}; - - -/** - * @param {ol.Feature} feature Feature. - * @param {ol.style.Style} style Style. - */ -ol.render.VectorContext.prototype.drawFeature = function(feature, style) {}; - - -/** - * @param {ol.geom.GeometryCollection} geometryCollectionGeometry Geometry - * collection. - * @param {ol.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawGeometryCollection = function(geometryCollectionGeometry, feature) {}; - - -/** - * @param {ol.geom.LineString|ol.render.Feature} lineStringGeometry Line - * string geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawLineString = function(lineStringGeometry, feature) {}; - - -/** - * @param {ol.geom.MultiLineString|ol.render.Feature} multiLineStringGeometry - * MultiLineString geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {}; - - -/** - * @param {ol.geom.MultiPoint|ol.render.Feature} multiPointGeometry MultiPoint - * geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawMultiPoint = function(multiPointGeometry, feature) {}; - - -/** - * @param {ol.geom.MultiPolygon} multiPolygonGeometry MultiPolygon geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {}; - - -/** - * @param {ol.geom.Point|ol.render.Feature} pointGeometry Point geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawPoint = function(pointGeometry, feature) {}; - - -/** - * @param {ol.geom.Polygon|ol.render.Feature} polygonGeometry Polygon - * geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawPolygon = function(polygonGeometry, feature) {}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.VectorContext.prototype.drawText = function(flatCoordinates, offset, end, stride, geometry, feature) {}; - - -/** - * @param {ol.style.Fill} fillStyle Fill style. - * @param {ol.style.Stroke} strokeStyle Stroke style. - */ -ol.render.VectorContext.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {}; - - -/** - * @param {ol.style.Image} imageStyle Image style. - */ -ol.render.VectorContext.prototype.setImageStyle = function(imageStyle) {}; - - -/** - * @param {ol.style.Text} textStyle Text style. - */ -ol.render.VectorContext.prototype.setTextStyle = function(textStyle) {}; - -// FIXME test, especially polygons with holes and multipolygons -// FIXME need to handle large thick features (where pixel size matters) -// FIXME add offset and end to ol.geom.flat.transform.transform2D? - -goog.provide('ol.render.canvas.Immediate'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.colorlike'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.has'); -goog.require('ol.render.VectorContext'); -goog.require('ol.render.canvas'); -goog.require('ol.transform'); - - -/** - * @classdesc - * A concrete subclass of {@link ol.render.VectorContext} that implements - * direct rendering of features and geometries to an HTML5 Canvas context. - * Instances of this class are created internally by the library and - * provided to application code as vectorContext member of the - * {@link ol.render.Event} object associated with postcompose, precompose and - * render events emitted by layers and maps. - * - * @constructor - * @extends {ol.render.VectorContext} - * @param {CanvasRenderingContext2D} context Context. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Extent} extent Extent. - * @param {ol.Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @struct - */ -ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform, viewRotation) { - ol.render.VectorContext.call(this); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = context; - - /** - * @private - * @type {number} - */ - this.pixelRatio_ = pixelRatio; - - /** - * @private - * @type {ol.Extent} - */ - this.extent_ = extent; - - /** - * @private - * @type {ol.Transform} - */ - this.transform_ = transform; - - /** - * @private - * @type {number} - */ - this.viewRotation_ = viewRotation; - - /** - * @private - * @type {?ol.CanvasFillState} - */ - this.contextFillState_ = null; - - /** - * @private - * @type {?ol.CanvasStrokeState} - */ - this.contextStrokeState_ = null; - - /** - * @private - * @type {?ol.CanvasTextState} - */ - this.contextTextState_ = null; - - /** - * @private - * @type {?ol.CanvasFillState} - */ - this.fillState_ = null; - - /** - * @private - * @type {?ol.CanvasStrokeState} - */ - this.strokeState_ = null; - - /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|Image} - */ - this.image_ = null; - - /** - * @private - * @type {number} - */ - this.imageAnchorX_ = 0; - - /** - * @private - * @type {number} - */ - this.imageAnchorY_ = 0; - - /** - * @private - * @type {number} - */ - this.imageHeight_ = 0; - - /** - * @private - * @type {number} - */ - this.imageOpacity_ = 0; - - /** - * @private - * @type {number} - */ - this.imageOriginX_ = 0; - - /** - * @private - * @type {number} - */ - this.imageOriginY_ = 0; - - /** - * @private - * @type {boolean} - */ - this.imageRotateWithView_ = false; - - /** - * @private - * @type {number} - */ - this.imageRotation_ = 0; - - /** - * @private - * @type {number} - */ - this.imageScale_ = 0; - - /** - * @private - * @type {boolean} - */ - this.imageSnapToPixel_ = false; - - /** - * @private - * @type {number} - */ - this.imageWidth_ = 0; - - /** - * @private - * @type {string} - */ - this.text_ = ''; - - /** - * @private - * @type {number} - */ - this.textOffsetX_ = 0; - - /** - * @private - * @type {number} - */ - this.textOffsetY_ = 0; - - /** - * @private - * @type {boolean} - */ - this.textRotateWithView_ = false; - - /** - * @private - * @type {number} - */ - this.textRotation_ = 0; - - /** - * @private - * @type {number} - */ - this.textScale_ = 0; - - /** - * @private - * @type {?ol.CanvasFillState} - */ - this.textFillState_ = null; - - /** - * @private - * @type {?ol.CanvasStrokeState} - */ - this.textStrokeState_ = null; - - /** - * @private - * @type {?ol.CanvasTextState} - */ - this.textState_ = null; - - /** - * @private - * @type {Array.<number>} - */ - this.pixelCoordinates_ = []; - - /** - * @private - * @type {ol.Transform} - */ - this.tmpLocalTransform_ = ol.transform.create(); - -}; -ol.inherits(ol.render.canvas.Immediate, ol.render.VectorContext); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - */ -ol.render.canvas.Immediate.prototype.drawImages_ = function(flatCoordinates, offset, end, stride) { - if (!this.image_) { - return; - } - var pixelCoordinates = ol.geom.flat.transform.transform2D( - flatCoordinates, offset, end, 2, this.transform_, - this.pixelCoordinates_); - var context = this.context_; - var localTransform = this.tmpLocalTransform_; - var alpha = context.globalAlpha; - if (this.imageOpacity_ != 1) { - context.globalAlpha = alpha * this.imageOpacity_; - } - var rotation = this.imageRotation_; - if (this.imageRotateWithView_) { - rotation += this.viewRotation_; - } - var i, ii; - for (i = 0, ii = pixelCoordinates.length; i < ii; i += 2) { - var x = pixelCoordinates[i] - this.imageAnchorX_; - var y = pixelCoordinates[i + 1] - this.imageAnchorY_; - if (this.imageSnapToPixel_) { - x = Math.round(x); - y = Math.round(y); - } - if (rotation !== 0 || this.imageScale_ != 1) { - var centerX = x + this.imageAnchorX_; - var centerY = y + this.imageAnchorY_; - ol.transform.compose(localTransform, - centerX, centerY, - this.imageScale_, this.imageScale_, - rotation, - -centerX, -centerY); - context.setTransform.apply(context, localTransform); - } - context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_, - this.imageWidth_, this.imageHeight_, x, y, - this.imageWidth_, this.imageHeight_); - } - if (rotation !== 0 || this.imageScale_ != 1) { - context.setTransform(1, 0, 0, 1, 0, 0); - } - if (this.imageOpacity_ != 1) { - context.globalAlpha = alpha; - } -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - */ -ol.render.canvas.Immediate.prototype.drawText_ = function(flatCoordinates, offset, end, stride) { - if (!this.textState_ || this.text_ === '') { - return; - } - if (this.textFillState_) { - this.setContextFillState_(this.textFillState_); - } - if (this.textStrokeState_) { - this.setContextStrokeState_(this.textStrokeState_); - } - this.setContextTextState_(this.textState_); - var pixelCoordinates = ol.geom.flat.transform.transform2D( - flatCoordinates, offset, end, stride, this.transform_, - this.pixelCoordinates_); - var context = this.context_; - var rotation = this.textRotation_; - if (this.textRotateWithView_) { - rotation += this.viewRotation_; - } - for (; offset < end; offset += stride) { - var x = pixelCoordinates[offset] + this.textOffsetX_; - var y = pixelCoordinates[offset + 1] + this.textOffsetY_; - if (rotation !== 0 || this.textScale_ != 1) { - var localTransform = ol.transform.compose(this.tmpLocalTransform_, - x, y, - this.textScale_, this.textScale_, - rotation, - -x, -y); - context.setTransform.apply(context, localTransform); - } - if (this.textStrokeState_) { - context.strokeText(this.text_, x, y); - } - if (this.textFillState_) { - context.fillText(this.text_, x, y); - } - } - if (rotation !== 0 || this.textScale_ != 1) { - context.setTransform(1, 0, 0, 1, 0, 0); - } -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {boolean} close Close. - * @private - * @return {number} end End. - */ -ol.render.canvas.Immediate.prototype.moveToLineTo_ = function(flatCoordinates, offset, end, stride, close) { - var context = this.context_; - var pixelCoordinates = ol.geom.flat.transform.transform2D( - flatCoordinates, offset, end, stride, this.transform_, - this.pixelCoordinates_); - context.moveTo(pixelCoordinates[0], pixelCoordinates[1]); - var length = pixelCoordinates.length; - if (close) { - length -= 2; - } - for (var i = 2; i < length; i += 2) { - context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]); - } - if (close) { - context.closePath(); - } - return end; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @private - * @return {number} End. - */ -ol.render.canvas.Immediate.prototype.drawRings_ = function(flatCoordinates, offset, ends, stride) { - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - offset = this.moveToLineTo_( - flatCoordinates, offset, ends[i], stride, true); - } - return offset; -}; - - -/** - * Render a circle geometry into the canvas. Rendering is immediate and uses - * the current fill and stroke styles. - * - * @param {ol.geom.Circle} geometry Circle geometry. - * @override - * @api - */ -ol.render.canvas.Immediate.prototype.drawCircle = function(geometry) { - if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.fillState_ || this.strokeState_) { - if (this.fillState_) { - this.setContextFillState_(this.fillState_); - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - } - var pixelCoordinates = ol.geom.SimpleGeometry.transform2D( - geometry, this.transform_, this.pixelCoordinates_); - var dx = pixelCoordinates[2] - pixelCoordinates[0]; - var dy = pixelCoordinates[3] - pixelCoordinates[1]; - var radius = Math.sqrt(dx * dx + dy * dy); - var context = this.context_; - context.beginPath(); - context.arc( - pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI); - if (this.fillState_) { - context.fill(); - } - if (this.strokeState_) { - context.stroke(); - } - } - if (this.text_ !== '') { - this.drawText_(geometry.getCenter(), 0, 2, 2); - } -}; - - -/** - * Set the rendering style. Note that since this is an immediate rendering API, - * any `zIndex` on the provided style will be ignored. - * - * @param {ol.style.Style} style The rendering style. - * @override - * @api - */ -ol.render.canvas.Immediate.prototype.setStyle = function(style) { - this.setFillStrokeStyle(style.getFill(), style.getStroke()); - this.setImageStyle(style.getImage()); - this.setTextStyle(style.getText()); -}; - - -/** - * Render a geometry into the canvas. Call - * {@link ol.render.canvas.Immediate#setStyle} first to set the rendering style. - * - * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render. - * @override - * @api - */ -ol.render.canvas.Immediate.prototype.drawGeometry = function(geometry) { - var type = geometry.getType(); - switch (type) { - case ol.geom.GeometryType.POINT: - this.drawPoint(/** @type {ol.geom.Point} */ (geometry)); - break; - case ol.geom.GeometryType.LINE_STRING: - this.drawLineString(/** @type {ol.geom.LineString} */ (geometry)); - break; - case ol.geom.GeometryType.POLYGON: - this.drawPolygon(/** @type {ol.geom.Polygon} */ (geometry)); - break; - case ol.geom.GeometryType.MULTI_POINT: - this.drawMultiPoint(/** @type {ol.geom.MultiPoint} */ (geometry)); - break; - case ol.geom.GeometryType.MULTI_LINE_STRING: - this.drawMultiLineString(/** @type {ol.geom.MultiLineString} */ (geometry)); - break; - case ol.geom.GeometryType.MULTI_POLYGON: - this.drawMultiPolygon(/** @type {ol.geom.MultiPolygon} */ (geometry)); - break; - case ol.geom.GeometryType.GEOMETRY_COLLECTION: - this.drawGeometryCollection(/** @type {ol.geom.GeometryCollection} */ (geometry)); - break; - case ol.geom.GeometryType.CIRCLE: - this.drawCircle(/** @type {ol.geom.Circle} */ (geometry)); - break; - default: - } -}; - - -/** - * Render a feature into the canvas. Note that any `zIndex` on the provided - * style will be ignored - features are rendered immediately in the order that - * this method is called. If you need `zIndex` support, you should be using an - * {@link ol.layer.Vector} instead. - * - * @param {ol.Feature} feature Feature. - * @param {ol.style.Style} style Style. - * @override - * @api - */ -ol.render.canvas.Immediate.prototype.drawFeature = function(feature, style) { - var geometry = style.getGeometryFunction()(feature); - if (!geometry || - !ol.extent.intersects(this.extent_, geometry.getExtent())) { - return; - } - this.setStyle(style); - this.drawGeometry(geometry); -}; - - -/** - * Render a GeometryCollection to the canvas. Rendering is immediate and - * uses the current styles appropriate for each geometry in the collection. - * - * @param {ol.geom.GeometryCollection} geometry Geometry collection. - * @override - */ -ol.render.canvas.Immediate.prototype.drawGeometryCollection = function(geometry) { - var geometries = geometry.getGeometriesArray(); - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - this.drawGeometry(geometries[i]); - } -}; - - -/** - * Render a Point geometry into the canvas. Rendering is immediate and uses - * the current style. - * - * @param {ol.geom.Point|ol.render.Feature} geometry Point geometry. - * @override - */ -ol.render.canvas.Immediate.prototype.drawPoint = function(geometry) { - var flatCoordinates = geometry.getFlatCoordinates(); - var stride = geometry.getStride(); - if (this.image_) { - this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); - } - if (this.text_ !== '') { - this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); - } -}; - - -/** - * Render a MultiPoint geometry into the canvas. Rendering is immediate and - * uses the current style. - * - * @param {ol.geom.MultiPoint|ol.render.Feature} geometry MultiPoint geometry. - * @override - */ -ol.render.canvas.Immediate.prototype.drawMultiPoint = function(geometry) { - var flatCoordinates = geometry.getFlatCoordinates(); - var stride = geometry.getStride(); - if (this.image_) { - this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); - } - if (this.text_ !== '') { - this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); - } -}; - - -/** - * Render a LineString into the canvas. Rendering is immediate and uses - * the current style. - * - * @param {ol.geom.LineString|ol.render.Feature} geometry LineString geometry. - * @override - */ -ol.render.canvas.Immediate.prototype.drawLineString = function(geometry) { - if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - var context = this.context_; - var flatCoordinates = geometry.getFlatCoordinates(); - context.beginPath(); - this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length, - geometry.getStride(), false); - context.stroke(); - } - if (this.text_ !== '') { - var flatMidpoint = geometry.getFlatMidpoint(); - this.drawText_(flatMidpoint, 0, 2, 2); - } -}; - - -/** - * Render a MultiLineString geometry into the canvas. Rendering is immediate - * and uses the current style. - * - * @param {ol.geom.MultiLineString|ol.render.Feature} geometry MultiLineString - * geometry. - * @override - */ -ol.render.canvas.Immediate.prototype.drawMultiLineString = function(geometry) { - var geometryExtent = geometry.getExtent(); - if (!ol.extent.intersects(this.extent_, geometryExtent)) { - return; - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - var context = this.context_; - var flatCoordinates = geometry.getFlatCoordinates(); - var offset = 0; - var ends = geometry.getEnds(); - var stride = geometry.getStride(); - context.beginPath(); - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - offset = this.moveToLineTo_( - flatCoordinates, offset, ends[i], stride, false); - } - context.stroke(); - } - if (this.text_ !== '') { - var flatMidpoints = geometry.getFlatMidpoints(); - this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2); - } -}; - - -/** - * Render a Polygon geometry into the canvas. Rendering is immediate and uses - * the current style. - * - * @param {ol.geom.Polygon|ol.render.Feature} geometry Polygon geometry. - * @override - */ -ol.render.canvas.Immediate.prototype.drawPolygon = function(geometry) { - if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.strokeState_ || this.fillState_) { - if (this.fillState_) { - this.setContextFillState_(this.fillState_); - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - } - var context = this.context_; - context.beginPath(); - this.drawRings_(geometry.getOrientedFlatCoordinates(), - 0, geometry.getEnds(), geometry.getStride()); - if (this.fillState_) { - context.fill(); - } - if (this.strokeState_) { - context.stroke(); - } - } - if (this.text_ !== '') { - var flatInteriorPoint = geometry.getFlatInteriorPoint(); - this.drawText_(flatInteriorPoint, 0, 2, 2); - } -}; - - -/** - * Render MultiPolygon geometry into the canvas. Rendering is immediate and - * uses the current style. - * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry. - * @override - */ -ol.render.canvas.Immediate.prototype.drawMultiPolygon = function(geometry) { - if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.strokeState_ || this.fillState_) { - if (this.fillState_) { - this.setContextFillState_(this.fillState_); - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - } - var context = this.context_; - var flatCoordinates = geometry.getOrientedFlatCoordinates(); - var offset = 0; - var endss = geometry.getEndss(); - var stride = geometry.getStride(); - var i, ii; - context.beginPath(); - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - offset = this.drawRings_(flatCoordinates, offset, ends, stride); - } - if (this.fillState_) { - context.fill(); - } - if (this.strokeState_) { - context.stroke(); - } - } - if (this.text_ !== '') { - var flatInteriorPoints = geometry.getFlatInteriorPoints(); - this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2); - } -}; - - -/** - * @param {ol.CanvasFillState} fillState Fill state. - * @private - */ -ol.render.canvas.Immediate.prototype.setContextFillState_ = function(fillState) { - var context = this.context_; - var contextFillState = this.contextFillState_; - if (!contextFillState) { - context.fillStyle = fillState.fillStyle; - this.contextFillState_ = { - fillStyle: fillState.fillStyle - }; - } else { - if (contextFillState.fillStyle != fillState.fillStyle) { - contextFillState.fillStyle = context.fillStyle = fillState.fillStyle; - } - } -}; - - -/** - * @param {ol.CanvasStrokeState} strokeState Stroke state. - * @private - */ -ol.render.canvas.Immediate.prototype.setContextStrokeState_ = function(strokeState) { - var context = this.context_; - var contextStrokeState = this.contextStrokeState_; - if (!contextStrokeState) { - context.lineCap = strokeState.lineCap; - if (ol.has.CANVAS_LINE_DASH) { - context.setLineDash(strokeState.lineDash); - } - context.lineJoin = strokeState.lineJoin; - context.lineWidth = strokeState.lineWidth; - context.miterLimit = strokeState.miterLimit; - context.strokeStyle = strokeState.strokeStyle; - this.contextStrokeState_ = { - lineCap: strokeState.lineCap, - lineDash: strokeState.lineDash, - lineJoin: strokeState.lineJoin, - lineWidth: strokeState.lineWidth, - miterLimit: strokeState.miterLimit, - strokeStyle: strokeState.strokeStyle - }; - } else { - if (contextStrokeState.lineCap != strokeState.lineCap) { - contextStrokeState.lineCap = context.lineCap = strokeState.lineCap; - } - if (ol.has.CANVAS_LINE_DASH) { - if (!ol.array.equals( - contextStrokeState.lineDash, strokeState.lineDash)) { - context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash); - } - } - if (contextStrokeState.lineJoin != strokeState.lineJoin) { - contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin; - } - if (contextStrokeState.lineWidth != strokeState.lineWidth) { - contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth; - } - if (contextStrokeState.miterLimit != strokeState.miterLimit) { - contextStrokeState.miterLimit = context.miterLimit = - strokeState.miterLimit; - } - if (contextStrokeState.strokeStyle != strokeState.strokeStyle) { - contextStrokeState.strokeStyle = context.strokeStyle = - strokeState.strokeStyle; - } - } -}; - - -/** - * @param {ol.CanvasTextState} textState Text state. - * @private - */ -ol.render.canvas.Immediate.prototype.setContextTextState_ = function(textState) { - var context = this.context_; - var contextTextState = this.contextTextState_; - if (!contextTextState) { - context.font = textState.font; - context.textAlign = textState.textAlign; - context.textBaseline = textState.textBaseline; - this.contextTextState_ = { - font: textState.font, - textAlign: textState.textAlign, - textBaseline: textState.textBaseline - }; - } else { - if (contextTextState.font != textState.font) { - contextTextState.font = context.font = textState.font; - } - if (contextTextState.textAlign != textState.textAlign) { - contextTextState.textAlign = context.textAlign = textState.textAlign; - } - if (contextTextState.textBaseline != textState.textBaseline) { - contextTextState.textBaseline = context.textBaseline = - textState.textBaseline; - } - } -}; - - -/** - * Set the fill and stroke style for subsequent draw operations. To clear - * either fill or stroke styles, pass null for the appropriate parameter. - * - * @param {ol.style.Fill} fillStyle Fill style. - * @param {ol.style.Stroke} strokeStyle Stroke style. - * @override - */ -ol.render.canvas.Immediate.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - if (!fillStyle) { - this.fillState_ = null; - } else { - var fillStyleColor = fillStyle.getColor(); - this.fillState_ = { - fillStyle: ol.colorlike.asColorLike(fillStyleColor ? - fillStyleColor : ol.render.canvas.defaultFillStyle) - }; - } - if (!strokeStyle) { - this.strokeState_ = null; - } else { - var strokeStyleColor = strokeStyle.getColor(); - var strokeStyleLineCap = strokeStyle.getLineCap(); - var strokeStyleLineDash = strokeStyle.getLineDash(); - var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - var strokeStyleLineJoin = strokeStyle.getLineJoin(); - var strokeStyleWidth = strokeStyle.getWidth(); - var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - this.strokeState_ = { - lineCap: strokeStyleLineCap !== undefined ? - strokeStyleLineCap : ol.render.canvas.defaultLineCap, - lineDash: strokeStyleLineDash ? - strokeStyleLineDash : ol.render.canvas.defaultLineDash, - lineDashOffset: strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset, - lineJoin: strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : ol.render.canvas.defaultLineJoin, - lineWidth: this.pixelRatio_ * (strokeStyleWidth !== undefined ? - strokeStyleWidth : ol.render.canvas.defaultLineWidth), - miterLimit: strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit, - strokeStyle: ol.colorlike.asColorLike(strokeStyleColor ? - strokeStyleColor : ol.render.canvas.defaultStrokeStyle) - }; - } -}; - - -/** - * Set the image style for subsequent draw operations. Pass null to remove - * the image style. - * - * @param {ol.style.Image} imageStyle Image style. - * @override - */ -ol.render.canvas.Immediate.prototype.setImageStyle = function(imageStyle) { - if (!imageStyle) { - this.image_ = null; - } else { - var imageAnchor = imageStyle.getAnchor(); - // FIXME pixel ratio - var imageImage = imageStyle.getImage(1); - var imageOrigin = imageStyle.getOrigin(); - var imageSize = imageStyle.getSize(); - this.imageAnchorX_ = imageAnchor[0]; - this.imageAnchorY_ = imageAnchor[1]; - this.imageHeight_ = imageSize[1]; - this.image_ = imageImage; - this.imageOpacity_ = imageStyle.getOpacity(); - this.imageOriginX_ = imageOrigin[0]; - this.imageOriginY_ = imageOrigin[1]; - this.imageRotateWithView_ = imageStyle.getRotateWithView(); - this.imageRotation_ = imageStyle.getRotation(); - this.imageScale_ = imageStyle.getScale(); - this.imageSnapToPixel_ = imageStyle.getSnapToPixel(); - this.imageWidth_ = imageSize[0]; - } -}; - - -/** - * Set the text style for subsequent draw operations. Pass null to - * remove the text style. - * - * @param {ol.style.Text} textStyle Text style. - * @override - */ -ol.render.canvas.Immediate.prototype.setTextStyle = function(textStyle) { - if (!textStyle) { - this.text_ = ''; - } else { - var textFillStyle = textStyle.getFill(); - if (!textFillStyle) { - this.textFillState_ = null; - } else { - var textFillStyleColor = textFillStyle.getColor(); - this.textFillState_ = { - fillStyle: ol.colorlike.asColorLike(textFillStyleColor ? - textFillStyleColor : ol.render.canvas.defaultFillStyle) - }; - } - var textStrokeStyle = textStyle.getStroke(); - if (!textStrokeStyle) { - this.textStrokeState_ = null; - } else { - var textStrokeStyleColor = textStrokeStyle.getColor(); - var textStrokeStyleLineCap = textStrokeStyle.getLineCap(); - var textStrokeStyleLineDash = textStrokeStyle.getLineDash(); - var textStrokeStyleLineDashOffset = textStrokeStyle.getLineDashOffset(); - var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin(); - var textStrokeStyleWidth = textStrokeStyle.getWidth(); - var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit(); - this.textStrokeState_ = { - lineCap: textStrokeStyleLineCap !== undefined ? - textStrokeStyleLineCap : ol.render.canvas.defaultLineCap, - lineDash: textStrokeStyleLineDash ? - textStrokeStyleLineDash : ol.render.canvas.defaultLineDash, - lineDashOffset: textStrokeStyleLineDashOffset ? - textStrokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset, - lineJoin: textStrokeStyleLineJoin !== undefined ? - textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin, - lineWidth: textStrokeStyleWidth !== undefined ? - textStrokeStyleWidth : ol.render.canvas.defaultLineWidth, - miterLimit: textStrokeStyleMiterLimit !== undefined ? - textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit, - strokeStyle: ol.colorlike.asColorLike(textStrokeStyleColor ? - textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle) - }; - } - var textFont = textStyle.getFont(); - var textOffsetX = textStyle.getOffsetX(); - var textOffsetY = textStyle.getOffsetY(); - var textRotateWithView = textStyle.getRotateWithView(); - var textRotation = textStyle.getRotation(); - var textScale = textStyle.getScale(); - var textText = textStyle.getText(); - var textTextAlign = textStyle.getTextAlign(); - var textTextBaseline = textStyle.getTextBaseline(); - this.textState_ = { - font: textFont !== undefined ? - textFont : ol.render.canvas.defaultFont, - textAlign: textTextAlign !== undefined ? - textTextAlign : ol.render.canvas.defaultTextAlign, - textBaseline: textTextBaseline !== undefined ? - textTextBaseline : ol.render.canvas.defaultTextBaseline - }; - this.text_ = textText !== undefined ? textText : ''; - this.textOffsetX_ = - textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0; - this.textOffsetY_ = - textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0; - this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false; - this.textRotation_ = textRotation !== undefined ? textRotation : 0; - this.textScale_ = this.pixelRatio_ * (textScale !== undefined ? - textScale : 1); - } -}; - -// FIXME offset panning - -goog.provide('ol.renderer.canvas.Map'); - -goog.require('ol.transform'); -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.css'); -goog.require('ol.dom'); -goog.require('ol.layer.Layer'); -goog.require('ol.render.Event'); -goog.require('ol.render.EventType'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.Immediate'); -goog.require('ol.renderer.Map'); -goog.require('ol.renderer.Type'); -goog.require('ol.source.State'); - - -/** - * @constructor - * @extends {ol.renderer.Map} - * @param {Element} container Container. - * @param {ol.Map} map Map. - */ -ol.renderer.canvas.Map = function(container, map) { - - ol.renderer.Map.call(this, container, map); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = ol.dom.createCanvasContext2D(); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = this.context_.canvas; - - this.canvas_.style.width = '100%'; - this.canvas_.style.height = '100%'; - this.canvas_.style.display = 'block'; - this.canvas_.className = ol.css.CLASS_UNSELECTABLE; - container.insertBefore(this.canvas_, container.childNodes[0] || null); - - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = true; - - /** - * @private - * @type {ol.Transform} - */ - this.transform_ = ol.transform.create(); - -}; -ol.inherits(ol.renderer.canvas.Map, ol.renderer.Map); - - -/** - * @param {ol.render.EventType} type Event type. - * @param {olx.FrameState} frameState Frame state. - * @private - */ -ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = function(type, frameState) { - var map = this.getMap(); - var context = this.context_; - if (map.hasListener(type)) { - var extent = frameState.extent; - var pixelRatio = frameState.pixelRatio; - var viewState = frameState.viewState; - var rotation = viewState.rotation; - - var transform = this.getTransform(frameState); - - var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio, - extent, transform, rotation); - var composeEvent = new ol.render.Event(type, vectorContext, - frameState, context, null); - map.dispatchEvent(composeEvent); - } -}; - - -/** - * @param {olx.FrameState} frameState Frame state. - * @protected - * @return {!ol.Transform} Transform. - */ -ol.renderer.canvas.Map.prototype.getTransform = function(frameState) { - var viewState = frameState.viewState; - var dx1 = this.canvas_.width / 2; - var dy1 = this.canvas_.height / 2; - var sx = frameState.pixelRatio / viewState.resolution; - var sy = -sx; - var angle = -viewState.rotation; - var dx2 = -viewState.center[0]; - var dy2 = -viewState.center[1]; - return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.getType = function() { - return ol.renderer.Type.CANVAS; -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { - - if (!frameState) { - if (this.renderedVisible_) { - this.canvas_.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - var context = this.context_; - var pixelRatio = frameState.pixelRatio; - var width = Math.round(frameState.size[0] * pixelRatio); - var height = Math.round(frameState.size[1] * pixelRatio); - if (this.canvas_.width != width || this.canvas_.height != height) { - this.canvas_.width = width; - this.canvas_.height = height; - } else { - context.clearRect(0, 0, width, height); - } - - var rotation = frameState.viewState.rotation; - - this.calculateMatrices2D(frameState); - - this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState); - - var layerStatesArray = frameState.layerStatesArray; - ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); - - if (rotation) { - context.save(); - ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); - } - - var viewResolution = frameState.viewState.resolution; - var i, ii, layer, layerRenderer, layerState; - for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerState = layerStatesArray[i]; - layer = layerState.layer; - layerRenderer = /** @type {ol.renderer.canvas.Layer} */ (this.getLayerRenderer(layer)); - if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) || - layerState.sourceState != ol.source.State.READY) { - continue; - } - if (layerRenderer.prepareFrame(frameState, layerState)) { - layerRenderer.composeFrame(frameState, layerState, context); - } - } - - if (rotation) { - context.restore(); - } - - this.dispatchComposeEvent_( - ol.render.EventType.POSTCOMPOSE, frameState); - - if (!this.renderedVisible_) { - this.canvas_.style.display = ''; - this.renderedVisible_ = true; - } - - this.scheduleRemoveUnusedLayerRenderers(frameState); - this.scheduleExpireIconCache(frameState); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, - layerFilter, thisArg2) { - var result; - var viewState = frameState.viewState; - var viewResolution = viewState.resolution; - - var layerStates = frameState.layerStatesArray; - var numLayers = layerStates.length; - - var coordinate = ol.transform.apply( - frameState.pixelToCoordinateTransform, pixel.slice()); - - var i; - for (i = numLayers - 1; i >= 0; --i) { - var layerState = layerStates[i]; - var layer = layerState.layer; - if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && - layerFilter.call(thisArg2, layer)) { - var layerRenderer = /** @type {ol.renderer.canvas.Layer} */ (this.getLayerRenderer(layer)); - result = layerRenderer.forEachLayerAtCoordinate( - coordinate, frameState, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; -}; - -goog.provide('ol.render.ReplayType'); - - -/** - * @enum {string} - */ -ol.render.ReplayType = { - CIRCLE: 'Circle', - IMAGE: 'Image', - LINE_STRING: 'LineString', - POLYGON: 'Polygon', - TEXT: 'Text' -}; - -goog.provide('ol.render.replay'); - -goog.require('ol.render.ReplayType'); - - -/** - * @const - * @type {Array.<ol.render.ReplayType>} - */ -ol.render.replay.ORDER = [ - ol.render.ReplayType.POLYGON, - ol.render.ReplayType.CIRCLE, - ol.render.ReplayType.LINE_STRING, - ol.render.ReplayType.IMAGE, - ol.render.ReplayType.TEXT -]; - -goog.provide('ol.render.ReplayGroup'); - - -/** - * Base class for replay groups. - * @constructor - * @abstract - */ -ol.render.ReplayGroup = function() {}; - - -/** - * @abstract - * @param {number|undefined} zIndex Z index. - * @param {ol.render.ReplayType} replayType Replay type. - * @return {ol.render.VectorContext} Replay. - */ -ol.render.ReplayGroup.prototype.getReplay = function(zIndex, replayType) {}; - - -/** - * @abstract - * @return {boolean} Is empty. - */ -ol.render.ReplayGroup.prototype.isEmpty = function() {}; - -goog.provide('ol.webgl.Shader'); - -goog.require('ol'); -goog.require('ol.functions'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @abstract - * @param {string} source Source. - * @struct - */ - ol.webgl.Shader = function(source) { - - /** - * @private - * @type {string} - */ - this.source_ = source; - - }; - - - /** - * @abstract - * @return {number} Type. - */ - ol.webgl.Shader.prototype.getType = function() {}; - - - /** - * @return {string} Source. - */ - ol.webgl.Shader.prototype.getSource = function() { - return this.source_; - }; - - - /** - * @return {boolean} Is animated? - */ - ol.webgl.Shader.prototype.isAnimated = ol.functions.FALSE; - -} - -goog.provide('ol.webgl.Fragment'); - -goog.require('ol'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Shader'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Shader} - * @param {string} source Source. - * @struct - */ - ol.webgl.Fragment = function(source) { - ol.webgl.Shader.call(this, source); - }; - ol.inherits(ol.webgl.Fragment, ol.webgl.Shader); - - - /** - * @inheritDoc - */ - ol.webgl.Fragment.prototype.getType = function() { - return ol.webgl.FRAGMENT_SHADER; - }; - -} - -goog.provide('ol.webgl.Vertex'); - -goog.require('ol'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Shader'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Shader} - * @param {string} source Source. - * @struct - */ - ol.webgl.Vertex = function(source) { - ol.webgl.Shader.call(this, source); - }; - ol.inherits(ol.webgl.Vertex, ol.webgl.Shader); - - - /** - * @inheritDoc - */ - ol.webgl.Vertex.prototype.getType = function() { - return ol.webgl.VERTEX_SHADER; - }; - -} - -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.render.webgl.circlereplay.defaultshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.render.webgl.circlereplay.defaultshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.render.webgl.circlereplay.defaultshader.Fragment.SOURCE); - }; - ol.inherits(ol.render.webgl.circlereplay.defaultshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.circlereplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_center;\nvarying vec2 v_offset;\nvarying float v_halfWidth;\nvarying float v_pixelRatio;\n\n\n\nuniform float u_opacity;\nuniform vec4 u_fillColor;\nuniform vec4 u_strokeColor;\nuniform vec2 u_size;\n\nvoid main(void) {\n vec2 windowCenter = vec2((v_center.x + 1.0) / 2.0 * u_size.x * v_pixelRatio,\n (v_center.y + 1.0) / 2.0 * u_size.y * v_pixelRatio);\n vec2 windowOffset = vec2((v_offset.x + 1.0) / 2.0 * u_size.x * v_pixelRatio,\n (v_offset.y + 1.0) / 2.0 * u_size.y * v_pixelRatio);\n float radius = length(windowCenter - windowOffset);\n float dist = length(windowCenter - gl_FragCoord.xy);\n if (dist > radius + v_halfWidth) {\n if (u_strokeColor.a == 0.0) {\n gl_FragColor = u_fillColor;\n } else {\n gl_FragColor = u_strokeColor;\n }\n gl_FragColor.a = gl_FragColor.a - (dist - (radius + v_halfWidth));\n } else if (u_fillColor.a == 0.0) {\n // Hooray, no fill, just stroke. We can use real antialiasing.\n gl_FragColor = u_strokeColor;\n if (dist < radius - v_halfWidth) {\n gl_FragColor.a = gl_FragColor.a - (radius - v_halfWidth - dist);\n }\n } else {\n gl_FragColor = u_fillColor;\n float strokeDist = radius - v_halfWidth;\n float antialias = 2.0 * v_pixelRatio;\n if (dist > strokeDist) {\n gl_FragColor = u_strokeColor;\n } else if (dist >= strokeDist - antialias) {\n float step = smoothstep(strokeDist - antialias, strokeDist, dist);\n gl_FragColor = mix(u_fillColor, u_strokeColor, step);\n }\n }\n gl_FragColor.a = gl_FragColor.a * u_opacity;\n if (gl_FragColor.a <= 0.0) {\n discard;\n }\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.circlereplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying vec2 b;varying float c;varying float d;uniform float m;uniform vec4 n;uniform vec4 o;uniform vec2 p;void main(void){vec2 windowCenter=vec2((a.x+1.0)/2.0*p.x*d,(a.y+1.0)/2.0*p.y*d);vec2 windowOffset=vec2((b.x+1.0)/2.0*p.x*d,(b.y+1.0)/2.0*p.y*d);float radius=length(windowCenter-windowOffset);float dist=length(windowCenter-gl_FragCoord.xy);if(dist>radius+c){if(o.a==0.0){gl_FragColor=n;}else{gl_FragColor=o;}gl_FragColor.a=gl_FragColor.a-(dist-(radius+c));}else if(n.a==0.0){gl_FragColor=o;if(dist<radius-c){gl_FragColor.a=gl_FragColor.a-(radius-c-dist);}} else{gl_FragColor=n;float strokeDist=radius-c;float antialias=2.0*d;if(dist>strokeDist){gl_FragColor=o;}else if(dist>=strokeDist-antialias){float step=smoothstep(strokeDist-antialias,strokeDist,dist);gl_FragColor=mix(n,o,step);}} gl_FragColor.a=gl_FragColor.a*m;if(gl_FragColor.a<=0.0){discard;}}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.circlereplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.circlereplay.defaultshader.Fragment.DEBUG_SOURCE : - ol.render.webgl.circlereplay.defaultshader.Fragment.OPTIMIZED_SOURCE; - - - ol.render.webgl.circlereplay.defaultshader.fragment = new ol.render.webgl.circlereplay.defaultshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.render.webgl.circlereplay.defaultshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.render.webgl.circlereplay.defaultshader.Vertex.SOURCE); - }; - ol.inherits(ol.render.webgl.circlereplay.defaultshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.circlereplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_center;\nvarying vec2 v_offset;\nvarying float v_halfWidth;\nvarying float v_pixelRatio;\n\n\nattribute vec2 a_position;\nattribute float a_instruction;\nattribute float a_radius;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\nuniform float u_lineWidth;\nuniform float u_pixelRatio;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n v_center = vec4(u_projectionMatrix * vec4(a_position, 0.0, 1.0)).xy;\n v_pixelRatio = u_pixelRatio;\n float lineWidth = u_lineWidth * u_pixelRatio;\n v_halfWidth = lineWidth / 2.0;\n if (lineWidth == 0.0) {\n lineWidth = 2.0 * u_pixelRatio;\n }\n vec2 offset;\n // Radius with anitaliasing (roughly).\n float radius = a_radius + 3.0 * u_pixelRatio;\n // Until we get gl_VertexID in WebGL, we store an instruction.\n if (a_instruction == 0.0) {\n // Offsetting the edges of the triangle by lineWidth / 2 is necessary, however\n // we should also leave some space for the antialiasing, thus we offset by lineWidth.\n offset = vec2(-1.0, 1.0);\n } else if (a_instruction == 1.0) {\n offset = vec2(-1.0, -1.0);\n } else if (a_instruction == 2.0) {\n offset = vec2(1.0, -1.0);\n } else {\n offset = vec2(1.0, 1.0);\n }\n\n gl_Position = u_projectionMatrix * vec4(a_position + offset * radius, 0.0, 1.0) +\n offsetMatrix * vec4(offset * lineWidth, 0.0, 0.0);\n v_offset = vec4(u_projectionMatrix * vec4(a_position.x + a_radius, a_position.y,\n 0.0, 1.0)).xy;\n\n if (distance(v_center, v_offset) > 20000.0) {\n gl_Position = vec4(v_center, 0.0, 1.0);\n }\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.circlereplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying vec2 b;varying float c;varying float d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;void main(void){mat4 offsetMatrix=i*j;a=vec4(h*vec4(e,0.0,1.0)).xy;d=l;float lineWidth=k*l;c=lineWidth/2.0;if(lineWidth==0.0){lineWidth=2.0*l;}vec2 offset;float radius=g+3.0*l;if(f==0.0){offset=vec2(-1.0,1.0);}else if(f==1.0){offset=vec2(-1.0,-1.0);}else if(f==2.0){offset=vec2(1.0,-1.0);}else{offset=vec2(1.0,1.0);}gl_Position=h*vec4(e+offset*radius,0.0,1.0)+offsetMatrix*vec4(offset*lineWidth,0.0,0.0);b=vec4(h*vec4(e.x+g,e.y,0.0,1.0)).xy;if(distance(a,b)>20000.0){gl_Position=vec4(a,0.0,1.0);}}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.circlereplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.circlereplay.defaultshader.Vertex.DEBUG_SOURCE : - ol.render.webgl.circlereplay.defaultshader.Vertex.OPTIMIZED_SOURCE; - - - ol.render.webgl.circlereplay.defaultshader.vertex = new ol.render.webgl.circlereplay.defaultshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.render.webgl.circlereplay.defaultshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_fillColor = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_fillColor' : 'n'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_lineWidth = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_lineWidth' : 'k'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'm'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_pixelRatio = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_pixelRatio' : 'l'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_size = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_size' : 'p'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_strokeColor = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_strokeColor' : 'o'); - - /** - * @type {number} - */ - this.a_instruction = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_instruction' : 'f'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'e'); - - /** - * @type {number} - */ - this.a_radius = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_radius' : 'g'); - }; - -} - -goog.provide('ol.vec.Mat4'); - - -/** - * @return {Array.<number>} 4x4 matrix representing a 3D identity transform. - */ -ol.vec.Mat4.create = function() { - return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; -}; - - -/** - * @param {Array.<number>} mat4 Flattened 4x4 matrix receiving the result. - * @param {ol.Transform} transform Transformation matrix. - * @return {Array.<number>} 2D transformation matrix as flattened 4x4 matrix. - */ -ol.vec.Mat4.fromTransform = function(mat4, transform) { - mat4[0] = transform[0]; - mat4[1] = transform[1]; - mat4[4] = transform[2]; - mat4[5] = transform[3]; - mat4[12] = transform[4]; - mat4[13] = transform[5]; - return mat4; -}; - -goog.provide('ol.render.webgl.Replay'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.render.VectorContext'); -goog.require('ol.transform'); -goog.require('ol.vec.Mat4'); -goog.require('ol.webgl'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @abstract - * @extends {ol.render.VectorContext} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @struct - */ - ol.render.webgl.Replay = function(tolerance, maxExtent) { - ol.render.VectorContext.call(this); - - /** - * @protected - * @type {number} - */ - this.tolerance = tolerance; - - /** - * @protected - * @const - * @type {ol.Extent} - */ - this.maxExtent = maxExtent; - - /** - * The origin of the coordinate system for the point coordinates sent to - * the GPU. To eliminate jitter caused by precision problems in the GPU - * we use the "Rendering Relative to Eye" technique described in the "3D - * Engine Design for Virtual Globes" book. - * @protected - * @type {ol.Coordinate} - */ - this.origin = ol.extent.getCenter(maxExtent); - - /** - * @private - * @type {ol.Transform} - */ - this.projectionMatrix_ = ol.transform.create(); - - /** - * @private - * @type {ol.Transform} - */ - this.offsetRotateMatrix_ = ol.transform.create(); - - /** - * @private - * @type {ol.Transform} - */ - this.offsetScaleMatrix_ = ol.transform.create(); - - /** - * @private - * @type {Array.<number>} - */ - this.tmpMat4_ = ol.vec.Mat4.create(); - - /** - * @protected - * @type {Array.<number>} - */ - this.indices = []; - - /** - * @protected - * @type {?ol.webgl.Buffer} - */ - this.indicesBuffer = null; - - /** - * Start index per feature (the index). - * @protected - * @type {Array.<number>} - */ - this.startIndices = []; - - /** - * Start index per feature (the feature). - * @protected - * @type {Array.<ol.Feature|ol.render.Feature>} - */ - this.startIndicesFeature = []; - - /** - * @protected - * @type {Array.<number>} - */ - this.vertices = []; - - /** - * @protected - * @type {?ol.webgl.Buffer} - */ - this.verticesBuffer = null; - - /** - * Optional parameter for PolygonReplay instances. - * @protected - * @type {ol.render.webgl.LineStringReplay|undefined} - */ - this.lineStringReplay = undefined; - - }; - ol.inherits(ol.render.webgl.Replay, ol.render.VectorContext); - - - /** - * @abstract - * @param {ol.webgl.Context} context WebGL context. - * @return {function()} Delete resources function. - */ - ol.render.webgl.Replay.prototype.getDeleteResourcesFunction = function(context) {}; - - - /** - * @abstract - * @param {ol.webgl.Context} context Context. - */ - ol.render.webgl.Replay.prototype.finish = function(context) {}; - - - /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @return {ol.render.webgl.circlereplay.defaultshader.Locations| - ol.render.webgl.imagereplay.defaultshader.Locations| - ol.render.webgl.linestringreplay.defaultshader.Locations| - ol.render.webgl.polygonreplay.defaultshader.Locations} Locations. - */ - ol.render.webgl.Replay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {}; - - - /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.render.webgl.circlereplay.defaultshader.Locations| - ol.render.webgl.imagereplay.defaultshader.Locations| - ol.render.webgl.linestringreplay.defaultshader.Locations| - ol.render.webgl.polygonreplay.defaultshader.Locations} locations Locations. - */ - ol.render.webgl.Replay.prototype.shutDownProgram = function(gl, locations) {}; - - - /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {boolean} hitDetection Hit detection mode. - */ - ol.render.webgl.Replay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {}; - - - /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. - * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.Replay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {}; - - - /** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.Replay.prototype.drawHitDetectionReplay = function(gl, context, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent) { - if (!oneByOne) { - // draw all hit-detection features in "once" (by texture group) - return this.drawHitDetectionReplayAll(gl, context, - skippedFeaturesHash, featureCallback); - } else { - // draw hit-detection features one by one - return this.drawHitDetectionReplayOneByOne(gl, context, - skippedFeaturesHash, featureCallback, opt_hitExtent); - } - }; - - - /** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.Replay.prototype.drawHitDetectionReplayAll = function(gl, context, skippedFeaturesHash, - featureCallback) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawReplay(gl, context, skippedFeaturesHash, true); - - var result = featureCallback(null); - if (result) { - return result; - } else { - return undefined; - } - }; - - - /** - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.Replay.prototype.replay = function(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent) { - var gl = context.getGL(); - var tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask, - tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail; - - if (this.lineStringReplay) { - tmpStencil = gl.isEnabled(gl.STENCIL_TEST); - tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC); - tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK); - tmpStencilRef = gl.getParameter(gl.STENCIL_REF); - tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK); - tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL); - tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); - tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); - - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilMask(255); - gl.stencilFunc(gl.ALWAYS, 1, 255); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - - this.lineStringReplay.replay(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent); - - gl.stencilMask(0); - gl.stencilFunc(gl.NOTEQUAL, 1, 255); - } - - context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.verticesBuffer); - - context.bindBuffer(ol.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer); - - var locations = this.setUpProgram(gl, context, size, pixelRatio); - - // set the "uniform" values - var projectionMatrix = ol.transform.reset(this.projectionMatrix_); - ol.transform.scale(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); - ol.transform.rotate(projectionMatrix, -rotation); - ol.transform.translate(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1])); - - var offsetScaleMatrix = ol.transform.reset(this.offsetScaleMatrix_); - ol.transform.scale(offsetScaleMatrix, 2 / size[0], 2 / size[1]); - - var offsetRotateMatrix = ol.transform.reset(this.offsetRotateMatrix_); - if (rotation !== 0) { - ol.transform.rotate(offsetRotateMatrix, -rotation); - } - - gl.uniformMatrix4fv(locations.u_projectionMatrix, false, - ol.vec.Mat4.fromTransform(this.tmpMat4_, projectionMatrix)); - gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, - ol.vec.Mat4.fromTransform(this.tmpMat4_, offsetScaleMatrix)); - gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, - ol.vec.Mat4.fromTransform(this.tmpMat4_, offsetRotateMatrix)); - gl.uniform1f(locations.u_opacity, opacity); - - // draw! - var result; - if (featureCallback === undefined) { - this.drawReplay(gl, context, skippedFeaturesHash, false); - } else { - // draw feature by feature for the hit-detection - result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent); - } - - // disable the vertex attrib arrays - this.shutDownProgram(gl, locations); - - if (this.lineStringReplay) { - if (!tmpStencil) { - gl.disable(gl.STENCIL_TEST); - } - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(/** @type {number} */ (tmpStencilFunc), - /** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal)); - gl.stencilMask(/** @type {number} */ (tmpStencilMask)); - gl.stencilOp(/** @type {number} */ (tmpStencilOpFail), - /** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass)); - } - - return result; - }; - - /** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {number} start Start index. - * @param {number} end End index. - */ - ol.render.webgl.Replay.prototype.drawElements = function( - gl, context, start, end) { - var elementType = context.hasOESElementIndexUint ? - ol.webgl.UNSIGNED_INT : ol.webgl.UNSIGNED_SHORT; - var elementSize = context.hasOESElementIndexUint ? 4 : 2; - - var numItems = end - start; - var offsetInBytes = start * elementSize; - gl.drawElements(ol.webgl.TRIANGLES, numItems, elementType, offsetInBytes); - }; - -} - -goog.provide('ol.render.webgl'); - -goog.require('ol'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @const - * @type {ol.Color} - */ - ol.render.webgl.defaultFillStyle = [0.0, 0.0, 0.0, 1.0]; - - /** - * @const - * @type {string} - */ - ol.render.webgl.defaultLineCap = 'round'; - - - /** - * @const - * @type {Array.<number>} - */ - ol.render.webgl.defaultLineDash = []; - - - /** - * @const - * @type {number} - */ - ol.render.webgl.defaultLineDashOffset = 0; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.defaultLineJoin = 'round'; - - - /** - * @const - * @type {number} - */ - ol.render.webgl.defaultMiterLimit = 10; - - /** - * @const - * @type {ol.Color} - */ - ol.render.webgl.defaultStrokeStyle = [0.0, 0.0, 0.0, 1.0]; - - /** - * @const - * @type {number} - */ - ol.render.webgl.defaultLineWidth = 1; - - /** - * Calculates the orientation of a triangle based on the determinant method. - * @param {number} x1 First X coordinate. - * @param {number} y1 First Y coordinate. - * @param {number} x2 Second X coordinate. - * @param {number} y2 Second Y coordinate. - * @param {number} x3 Third X coordinate. - * @param {number} y3 Third Y coordinate. - * @return {boolean|undefined} Triangle is clockwise. - */ - ol.render.webgl.triangleIsCounterClockwise = function(x1, y1, x2, y2, x3, y3) { - var area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); - return (area <= ol.render.webgl.EPSILON && area >= -ol.render.webgl.EPSILON) ? - undefined : area > 0; - }; - - /** - * @const - * @type {number} - */ - ol.render.webgl.EPSILON = Number.EPSILON || 2.220446049250313e-16; - -} - -goog.provide('ol.webgl.Buffer'); - -goog.require('ol'); -goog.require('ol.webgl'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @param {Array.<number>=} opt_arr Array. - * @param {number=} opt_usage Usage. - * @struct - */ - ol.webgl.Buffer = function(opt_arr, opt_usage) { - - /** - * @private - * @type {Array.<number>} - */ - this.arr_ = opt_arr !== undefined ? opt_arr : []; - - /** - * @private - * @type {number} - */ - this.usage_ = opt_usage !== undefined ? - opt_usage : ol.webgl.Buffer.Usage_.STATIC_DRAW; - - }; - - - /** - * @return {Array.<number>} Array. - */ - ol.webgl.Buffer.prototype.getArray = function() { - return this.arr_; - }; - - - /** - * @return {number} Usage. - */ - ol.webgl.Buffer.prototype.getUsage = function() { - return this.usage_; - }; - - - /** - * @enum {number} - * @private - */ - ol.webgl.Buffer.Usage_ = { - STATIC_DRAW: ol.webgl.STATIC_DRAW, - STREAM_DRAW: ol.webgl.STREAM_DRAW, - DYNAMIC_DRAW: ol.webgl.DYNAMIC_DRAW - }; - -} - -goog.provide('ol.render.webgl.CircleReplay'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.color'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.render.webgl.circlereplay.defaultshader'); -goog.require('ol.render.webgl.Replay'); -goog.require('ol.render.webgl'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Buffer'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.render.webgl.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @struct - */ - ol.render.webgl.CircleReplay = function(tolerance, maxExtent) { - ol.render.webgl.Replay.call(this, tolerance, maxExtent); - - /** - * @private - * @type {ol.render.webgl.circlereplay.defaultshader.Locations} - */ - this.defaultLocations_ = null; - - /** - * @private - * @type {Array.<Array.<Array.<number>|number>>} - */ - this.styles_ = []; - - /** - * @private - * @type {Array.<number>} - */ - this.styleIndices_ = []; - - /** - * @private - * @type {number} - */ - this.radius_ = 0; - - /** - * @private - * @type {{fillColor: (Array.<number>|null), - * strokeColor: (Array.<number>|null), - * lineDash: Array.<number>, - * lineDashOffset: (number|undefined), - * lineWidth: (number|undefined), - * changed: boolean}|null} - */ - this.state_ = { - fillColor: null, - strokeColor: null, - lineDash: null, - lineDashOffset: undefined, - lineWidth: undefined, - changed: false - }; - - }; - ol.inherits(ol.render.webgl.CircleReplay, ol.render.webgl.Replay); - - - /** - * @private - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - */ - ol.render.webgl.CircleReplay.prototype.drawCoordinates_ = function( - flatCoordinates, offset, end, stride) { - var numVertices = this.vertices.length; - var numIndices = this.indices.length; - var n = numVertices / 4; - var i, ii; - for (i = offset, ii = end; i < ii; i += stride) { - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 0; - this.vertices[numVertices++] = this.radius_; - - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 1; - this.vertices[numVertices++] = this.radius_; - - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 2; - this.vertices[numVertices++] = this.radius_; - - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 3; - this.vertices[numVertices++] = this.radius_; - - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 2; - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n; - - n += 4; - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.drawCircle = function(circleGeometry, feature) { - var radius = circleGeometry.getRadius(); - var stride = circleGeometry.getStride(); - if (radius) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; - } - - this.radius_ = radius; - var flatCoordinates = circleGeometry.getFlatCoordinates(); - flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, 2, - stride, -this.origin[0], -this.origin[1]); - this.drawCoordinates_(flatCoordinates, 0, 2, stride); - } else { - if (this.state_.changed) { - this.styles_.pop(); - if (this.styles_.length) { - var lastState = this.styles_[this.styles_.length - 1]; - this.state_.fillColor = /** @type {Array.<number>} */ (lastState[0]); - this.state_.strokeColor = /** @type {Array.<number>} */ (lastState[1]); - this.state_.lineWidth = /** @type {number} */ (lastState[2]); - this.state_.changed = false; - } - } - } - }; - - - /** - * @inheritDoc - **/ - ol.render.webgl.CircleReplay.prototype.finish = function(context) { - // create, bind, and populate the vertices buffer - this.verticesBuffer = new ol.webgl.Buffer(this.vertices); - - // create, bind, and populate the indices buffer - this.indicesBuffer = new ol.webgl.Buffer(this.indices); - - this.startIndices.push(this.indices.length); - - //Clean up, if there is nothing to draw - if (this.styleIndices_.length === 0 && this.styles_.length > 0) { - this.styles_ = []; - } - - this.vertices = null; - this.indices = null; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.getDeleteResourcesFunction = function(context) { - // We only delete our stuff here. The shaders and the program may - // be used by other CircleReplay instances (for other layers). And - // they will be deleted when disposing of the ol.webgl.Context - // object. - var verticesBuffer = this.verticesBuffer; - var indicesBuffer = this.indicesBuffer; - return function() { - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - }; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - var fragmentShader, vertexShader; - fragmentShader = ol.render.webgl.circlereplay.defaultshader.fragment; - vertexShader = ol.render.webgl.circlereplay.defaultshader.vertex; - var program = context.getProgram(fragmentShader, vertexShader); - - // get the locations - var locations; - if (!this.defaultLocations_) { - // eslint-disable-next-line openlayers-internal/no-missing-requires - locations = new ol.render.webgl.circlereplay.defaultshader.Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT, - false, 16, 0); - - gl.enableVertexAttribArray(locations.a_instruction); - gl.vertexAttribPointer(locations.a_instruction, 1, ol.webgl.FLOAT, - false, 16, 8); - - gl.enableVertexAttribArray(locations.a_radius); - gl.vertexAttribPointer(locations.a_radius, 1, ol.webgl.FLOAT, - false, 16, 12); - - // Enable renderer specific uniforms. - gl.uniform2fv(locations.u_size, size); - gl.uniform1f(locations.u_pixelRatio, pixelRatio); - - return locations; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_position); - gl.disableVertexAttribArray(locations.a_instruction); - gl.disableVertexAttribArray(locations.a_radius); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - if (!ol.obj.isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_(gl, context, skippedFeaturesHash); - } else { - //Draw by style groups to minimize drawElements() calls. - var i, start, end, nextStyle; - end = this.startIndices[this.startIndices.length - 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - start = this.styleIndices_[i]; - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0])); - this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]), - /** @type {number} */ (nextStyle[2])); - this.drawElements(gl, context, start, end); - end = start; - } - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; - featureIndex = this.startIndices.length - 2; - end = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0])); - this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]), - /** @type {number} */ (nextStyle[2])); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || ol.extent.intersects( - /** @type {Array<number>} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - var result = featureCallback(feature); - - if (result) { - return result; - } - - } - featureIndex--; - end = start; - } - } - return undefined; - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object} skippedFeaturesHash Ids of features to skip. - */ - ol.render.webgl.CircleReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) { - var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; - featureIndex = this.startIndices.length - 2; - end = start = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, /** @type {Array.<number>} */ (nextStyle[0])); - this.setStrokeStyle_(gl, /** @type {Array.<number>} */ (nextStyle[1]), - /** @type {number} */ (nextStyle[2])); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - featureStart = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid]) { - if (start !== end) { - this.drawElements(gl, context, start, end); - } - end = featureStart; - } - featureIndex--; - start = featureStart; - } - if (start !== end) { - this.drawElements(gl, context, start, end); - } - start = end = groupStart; - } - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.<number>} color Color. - */ - ol.render.webgl.CircleReplay.prototype.setFillStyle_ = function(gl, color) { - gl.uniform4fv(this.defaultLocations_.u_fillColor, color); - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.<number>} color Color. - * @param {number} lineWidth Line width. - */ - ol.render.webgl.CircleReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth) { - gl.uniform4fv(this.defaultLocations_.u_strokeColor, color); - gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.CircleReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - var strokeStyleColor, strokeStyleWidth; - if (strokeStyle) { - var strokeStyleLineDash = strokeStyle.getLineDash(); - this.state_.lineDash = strokeStyleLineDash ? - strokeStyleLineDash : ol.render.webgl.defaultLineDash; - var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - this.state_.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : ol.render.webgl.defaultLineDashOffset; - strokeStyleColor = strokeStyle.getColor(); - if (!(strokeStyleColor instanceof CanvasGradient) && - !(strokeStyleColor instanceof CanvasPattern)) { - strokeStyleColor = ol.color.asArray(strokeStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || ol.render.webgl.defaultStrokeStyle; - } else { - strokeStyleColor = ol.render.webgl.defaultStrokeStyle; - } - strokeStyleWidth = strokeStyle.getWidth(); - strokeStyleWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : ol.render.webgl.defaultLineWidth; - } else { - strokeStyleColor = [0, 0, 0, 0]; - strokeStyleWidth = 0; - } - var fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0]; - if (!(fillStyleColor instanceof CanvasGradient) && - !(fillStyleColor instanceof CanvasPattern)) { - fillStyleColor = ol.color.asArray(fillStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || ol.render.webgl.defaultFillStyle; - } else { - fillStyleColor = ol.render.webgl.defaultFillStyle; - } - if (!this.state_.strokeColor || !ol.array.equals(this.state_.strokeColor, strokeStyleColor) || - !this.state_.fillColor || !ol.array.equals(this.state_.fillColor, fillStyleColor) || - this.state_.lineWidth !== strokeStyleWidth) { - this.state_.changed = true; - this.state_.fillColor = fillStyleColor; - this.state_.strokeColor = strokeStyleColor; - this.state_.lineWidth = strokeStyleWidth; - this.styles_.push([fillStyleColor, strokeStyleColor, strokeStyleWidth]); - } - }; - -} - -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.render.webgl.imagereplay.defaultshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.render.webgl.imagereplay.defaultshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.render.webgl.imagereplay.defaultshader.Fragment.SOURCE); - }; - ol.inherits(ol.render.webgl.imagereplay.defaultshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.imagereplay.defaultshader.Fragment.DEBUG_SOURCE : - ol.render.webgl.imagereplay.defaultshader.Fragment.OPTIMIZED_SOURCE; - - - ol.render.webgl.imagereplay.defaultshader.fragment = new ol.render.webgl.imagereplay.defaultshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.render.webgl.imagereplay.defaultshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.render.webgl.imagereplay.defaultshader.Vertex.SOURCE); - }; - ol.inherits(ol.render.webgl.imagereplay.defaultshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.0,0.0);gl_Position=h*vec4(c,0.0,1.0)+offsets;a=d;b=f;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.imagereplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.imagereplay.defaultshader.Vertex.DEBUG_SOURCE : - ol.render.webgl.imagereplay.defaultshader.Vertex.OPTIMIZED_SOURCE; - - - ol.render.webgl.imagereplay.defaultshader.vertex = new ol.render.webgl.imagereplay.defaultshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.render.webgl.imagereplay.defaultshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_image = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_image' : 'l'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'k'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - - /** - * @type {number} - */ - this.a_offsets = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_offsets' : 'e'); - - /** - * @type {number} - */ - this.a_opacity = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_opacity' : 'f'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'c'); - - /** - * @type {number} - */ - this.a_rotateWithView = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); - - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'd'); - }; - -} - -goog.provide('ol.webgl.ContextEventType'); - - -/** - * @enum {string} - */ -ol.webgl.ContextEventType = { - LOST: 'webglcontextlost', - RESTORED: 'webglcontextrestored' -}; - -goog.provide('ol.webgl.Context'); - -goog.require('ol'); -goog.require('ol.Disposable'); -goog.require('ol.array'); -goog.require('ol.events'); -goog.require('ol.obj'); -goog.require('ol.webgl'); -goog.require('ol.webgl.ContextEventType'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @classdesc - * A WebGL context for accessing low-level WebGL capabilities. - * - * @constructor - * @extends {ol.Disposable} - * @param {HTMLCanvasElement} canvas Canvas. - * @param {WebGLRenderingContext} gl GL. - */ - ol.webgl.Context = function(canvas, gl) { - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = canvas; - - /** - * @private - * @type {WebGLRenderingContext} - */ - this.gl_ = gl; - - /** - * @private - * @type {Object.<string, ol.WebglBufferCacheEntry>} - */ - this.bufferCache_ = {}; - - /** - * @private - * @type {Object.<string, WebGLShader>} - */ - this.shaderCache_ = {}; - - /** - * @private - * @type {Object.<string, WebGLProgram>} - */ - this.programCache_ = {}; - - /** - * @private - * @type {WebGLProgram} - */ - this.currentProgram_ = null; - - /** - * @private - * @type {WebGLFramebuffer} - */ - this.hitDetectionFramebuffer_ = null; - - /** - * @private - * @type {WebGLTexture} - */ - this.hitDetectionTexture_ = null; - - /** - * @private - * @type {WebGLRenderbuffer} - */ - this.hitDetectionRenderbuffer_ = null; - - /** - * @type {boolean} - */ - this.hasOESElementIndexUint = ol.array.includes( - ol.WEBGL_EXTENSIONS, 'OES_element_index_uint'); - - // use the OES_element_index_uint extension if available - if (this.hasOESElementIndexUint) { - gl.getExtension('OES_element_index_uint'); - } - - ol.events.listen(this.canvas_, ol.webgl.ContextEventType.LOST, - this.handleWebGLContextLost, this); - ol.events.listen(this.canvas_, ol.webgl.ContextEventType.RESTORED, - this.handleWebGLContextRestored, this); - - }; - ol.inherits(ol.webgl.Context, ol.Disposable); - - - /** - * Just bind the buffer if it's in the cache. Otherwise create - * the WebGL buffer, bind it, populate it, and add an entry to - * the cache. - * @param {number} target Target. - * @param {ol.webgl.Buffer} buf Buffer. - */ - ol.webgl.Context.prototype.bindBuffer = function(target, buf) { - var gl = this.getGL(); - var arr = buf.getArray(); - var bufferKey = String(ol.getUid(buf)); - if (bufferKey in this.bufferCache_) { - var bufferCacheEntry = this.bufferCache_[bufferKey]; - gl.bindBuffer(target, bufferCacheEntry.buffer); - } else { - var buffer = gl.createBuffer(); - gl.bindBuffer(target, buffer); - var /** @type {ArrayBufferView} */ arrayBuffer; - if (target == ol.webgl.ARRAY_BUFFER) { - arrayBuffer = new Float32Array(arr); - } else if (target == ol.webgl.ELEMENT_ARRAY_BUFFER) { - arrayBuffer = this.hasOESElementIndexUint ? - new Uint32Array(arr) : new Uint16Array(arr); - } - gl.bufferData(target, arrayBuffer, buf.getUsage()); - this.bufferCache_[bufferKey] = { - buf: buf, - buffer: buffer - }; - } - }; - - - /** - * @param {ol.webgl.Buffer} buf Buffer. - */ - ol.webgl.Context.prototype.deleteBuffer = function(buf) { - var gl = this.getGL(); - var bufferKey = String(ol.getUid(buf)); - var bufferCacheEntry = this.bufferCache_[bufferKey]; - if (!gl.isContextLost()) { - gl.deleteBuffer(bufferCacheEntry.buffer); - } - delete this.bufferCache_[bufferKey]; - }; - - - /** - * @inheritDoc - */ - ol.webgl.Context.prototype.disposeInternal = function() { - ol.events.unlistenAll(this.canvas_); - var gl = this.getGL(); - if (!gl.isContextLost()) { - var key; - for (key in this.bufferCache_) { - gl.deleteBuffer(this.bufferCache_[key].buffer); - } - for (key in this.programCache_) { - gl.deleteProgram(this.programCache_[key]); - } - for (key in this.shaderCache_) { - gl.deleteShader(this.shaderCache_[key]); - } - // delete objects for hit-detection - gl.deleteFramebuffer(this.hitDetectionFramebuffer_); - gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_); - gl.deleteTexture(this.hitDetectionTexture_); - } - }; - - - /** - * @return {HTMLCanvasElement} Canvas. - */ - ol.webgl.Context.prototype.getCanvas = function() { - return this.canvas_; - }; - - - /** - * Get the WebGL rendering context - * @return {WebGLRenderingContext} The rendering context. - * @api - */ - ol.webgl.Context.prototype.getGL = function() { - return this.gl_; - }; - - - /** - * Get the frame buffer for hit detection. - * @return {WebGLFramebuffer} The hit detection frame buffer. - */ - ol.webgl.Context.prototype.getHitDetectionFramebuffer = function() { - if (!this.hitDetectionFramebuffer_) { - this.initHitDetectionFramebuffer_(); - } - return this.hitDetectionFramebuffer_; - }; - - - /** - * Get shader from the cache if it's in the cache. Otherwise, create - * the WebGL shader, compile it, and add entry to cache. - * @param {ol.webgl.Shader} shaderObject Shader object. - * @return {WebGLShader} Shader. - */ - ol.webgl.Context.prototype.getShader = function(shaderObject) { - var shaderKey = String(ol.getUid(shaderObject)); - if (shaderKey in this.shaderCache_) { - return this.shaderCache_[shaderKey]; - } else { - var gl = this.getGL(); - var shader = gl.createShader(shaderObject.getType()); - gl.shaderSource(shader, shaderObject.getSource()); - gl.compileShader(shader); - this.shaderCache_[shaderKey] = shader; - return shader; - } - }; - - - /** - * Get the program from the cache if it's in the cache. Otherwise create - * the WebGL program, attach the shaders to it, and add an entry to the - * cache. - * @param {ol.webgl.Fragment} fragmentShaderObject Fragment shader. - * @param {ol.webgl.Vertex} vertexShaderObject Vertex shader. - * @return {WebGLProgram} Program. - */ - ol.webgl.Context.prototype.getProgram = function( - fragmentShaderObject, vertexShaderObject) { - var programKey = - ol.getUid(fragmentShaderObject) + '/' + ol.getUid(vertexShaderObject); - if (programKey in this.programCache_) { - return this.programCache_[programKey]; - } else { - var gl = this.getGL(); - var program = gl.createProgram(); - gl.attachShader(program, this.getShader(fragmentShaderObject)); - gl.attachShader(program, this.getShader(vertexShaderObject)); - gl.linkProgram(program); - this.programCache_[programKey] = program; - return program; - } - }; - - - /** - * FIXME empy description for jsdoc - */ - ol.webgl.Context.prototype.handleWebGLContextLost = function() { - ol.obj.clear(this.bufferCache_); - ol.obj.clear(this.shaderCache_); - ol.obj.clear(this.programCache_); - this.currentProgram_ = null; - this.hitDetectionFramebuffer_ = null; - this.hitDetectionTexture_ = null; - this.hitDetectionRenderbuffer_ = null; - }; - - - /** - * FIXME empy description for jsdoc - */ - ol.webgl.Context.prototype.handleWebGLContextRestored = function() { - }; - - - /** - * Creates a 1x1 pixel framebuffer for the hit-detection. - * @private - */ - ol.webgl.Context.prototype.initHitDetectionFramebuffer_ = function() { - var gl = this.gl_; - var framebuffer = gl.createFramebuffer(); - gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); - - var texture = ol.webgl.Context.createEmptyTexture(gl, 1, 1); - var renderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1); - gl.framebufferTexture2D( - gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, - gl.RENDERBUFFER, renderbuffer); - - gl.bindTexture(gl.TEXTURE_2D, null); - gl.bindRenderbuffer(gl.RENDERBUFFER, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - this.hitDetectionFramebuffer_ = framebuffer; - this.hitDetectionTexture_ = texture; - this.hitDetectionRenderbuffer_ = renderbuffer; - }; - - - /** - * Use a program. If the program is already in use, this will return `false`. - * @param {WebGLProgram} program Program. - * @return {boolean} Changed. - * @api - */ - ol.webgl.Context.prototype.useProgram = function(program) { - if (program == this.currentProgram_) { - return false; - } else { - var gl = this.getGL(); - gl.useProgram(program); - this.currentProgram_ = program; - return true; - } - }; - - - /** - * @param {WebGLRenderingContext} gl WebGL rendering context. - * @param {number=} opt_wrapS wrapS. - * @param {number=} opt_wrapT wrapT. - * @return {WebGLTexture} The texture. - * @private - */ - ol.webgl.Context.createTexture_ = function(gl, opt_wrapS, opt_wrapT) { - var texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - if (opt_wrapS !== undefined) { - gl.texParameteri( - ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_S, opt_wrapS); - } - if (opt_wrapT !== undefined) { - gl.texParameteri( - ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_T, opt_wrapT); - } - - return texture; - }; - - - /** - * @param {WebGLRenderingContext} gl WebGL rendering context. - * @param {number} width Width. - * @param {number} height Height. - * @param {number=} opt_wrapS wrapS. - * @param {number=} opt_wrapT wrapT. - * @return {WebGLTexture} The texture. - */ - ol.webgl.Context.createEmptyTexture = function( - gl, width, height, opt_wrapS, opt_wrapT) { - var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT); - gl.texImage2D( - gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, - null); - - return texture; - }; - - - /** - * @param {WebGLRenderingContext} gl WebGL rendering context. - * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. - * @param {number=} opt_wrapS wrapS. - * @param {number=} opt_wrapT wrapT. - * @return {WebGLTexture} The texture. - */ - ol.webgl.Context.createTexture = function(gl, image, opt_wrapS, opt_wrapT) { - var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT); - gl.texImage2D( - gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); - - return texture; - }; - -} - -goog.provide('ol.render.webgl.ImageReplay'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.render.webgl.imagereplay.defaultshader'); -goog.require('ol.render.webgl.Replay'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Buffer'); -goog.require('ol.webgl.Context'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.render.webgl.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @struct - */ - ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { - ol.render.webgl.Replay.call(this, tolerance, maxExtent); - - /** - * @type {number|undefined} - * @private - */ - this.anchorX_ = undefined; - - /** - * @type {number|undefined} - * @private - */ - this.anchorY_ = undefined; - - /** - * @type {Array.<number>} - * @private - */ - this.groupIndices_ = []; - - /** - * @type {Array.<number>} - * @private - */ - this.hitDetectionGroupIndices_ = []; - - /** - * @type {number|undefined} - * @private - */ - this.height_ = undefined; - - /** - * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} - * @private - */ - this.images_ = []; - - /** - * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} - * @private - */ - this.hitDetectionImages_ = []; - - /** - * @type {number|undefined} - * @private - */ - this.imageHeight_ = undefined; - - /** - * @type {number|undefined} - * @private - */ - this.imageWidth_ = undefined; - - /** - * @private - * @type {ol.render.webgl.imagereplay.defaultshader.Locations} - */ - this.defaultLocations_ = null; - - /** - * @private - * @type {number|undefined} - */ - this.opacity_ = undefined; - - /** - * @type {number|undefined} - * @private - */ - this.originX_ = undefined; - - /** - * @type {number|undefined} - * @private - */ - this.originY_ = undefined; - - /** - * @private - * @type {boolean|undefined} - */ - this.rotateWithView_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.scale_ = undefined; - - /** - * @type {Array.<WebGLTexture>} - * @private - */ - this.textures_ = []; - - /** - * @type {Array.<WebGLTexture>} - * @private - */ - this.hitDetectionTextures_ = []; - - /** - * @type {number|undefined} - * @private - */ - this.width_ = undefined; - }; - ol.inherits(ol.render.webgl.ImageReplay, ol.render.webgl.Replay); - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction = function(context) { - var verticesBuffer = this.verticesBuffer; - var indicesBuffer = this.indicesBuffer; - var textures = this.textures_; - var hitDetectionTextures = this.hitDetectionTextures_; - var gl = context.getGL(); - return function() { - if (!gl.isContextLost()) { - var i, ii; - for (i = 0, ii = textures.length; i < ii; ++i) { - gl.deleteTexture(textures[i]); - } - for (i = 0, ii = hitDetectionTextures.length; i < ii; ++i) { - gl.deleteTexture(hitDetectionTextures[i]); - } - } - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - }; - }; - - - /** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {number} My end. - * @private - */ - ol.render.webgl.ImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { - var anchorX = /** @type {number} */ (this.anchorX_); - var anchorY = /** @type {number} */ (this.anchorY_); - var height = /** @type {number} */ (this.height_); - var imageHeight = /** @type {number} */ (this.imageHeight_); - var imageWidth = /** @type {number} */ (this.imageWidth_); - var opacity = /** @type {number} */ (this.opacity_); - var originX = /** @type {number} */ (this.originX_); - var originY = /** @type {number} */ (this.originY_); - var rotateWithView = this.rotateWithView_ ? 1.0 : 0.0; - // this.rotation_ is anti-clockwise, but rotation is clockwise - var rotation = /** @type {number} */ (-this.rotation_); - var scale = /** @type {number} */ (this.scale_); - var width = /** @type {number} */ (this.width_); - var cos = Math.cos(rotation); - var sin = Math.sin(rotation); - var numIndices = this.indices.length; - var numVertices = this.vertices.length; - var i, n, offsetX, offsetY, x, y; - for (i = offset; i < end; i += stride) { - x = flatCoordinates[i] - this.origin[0]; - y = flatCoordinates[i + 1] - this.origin[1]; - - // There are 4 vertices per [x, y] point, one for each corner of the - // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if - // WebGL supported Geometry Shaders (which can emit new vertices), but that - // is not currently the case. - // - // And each vertex includes 8 values: the x and y coordinates, the x and - // y offsets used to calculate the position of the corner, the u and - // v texture coordinates for the corner, the opacity, and whether the - // the image should be rotated with the view (rotateWithView). - - n = numVertices / 8; - - // bottom-left corner - offsetX = -scale * anchorX; - offsetY = -scale * (height - anchorY); - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = originX / imageWidth; - this.vertices[numVertices++] = (originY + height) / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - // bottom-right corner - offsetX = scale * (width - anchorX); - offsetY = -scale * (height - anchorY); - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = (originX + width) / imageWidth; - this.vertices[numVertices++] = (originY + height) / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - // top-right corner - offsetX = scale * (width - anchorX); - offsetY = scale * anchorY; - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = (originX + width) / imageWidth; - this.vertices[numVertices++] = originY / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - // top-left corner - offsetX = -scale * anchorX; - offsetY = scale * anchorY; - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = originX / imageWidth; - this.vertices[numVertices++] = originY / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n + 3; - } - - return numVertices; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - var flatCoordinates = multiPointGeometry.getFlatCoordinates(); - var stride = multiPointGeometry.getStride(); - this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.drawPoint = function(pointGeometry, feature) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - var flatCoordinates = pointGeometry.getFlatCoordinates(); - var stride = pointGeometry.getStride(); - this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.finish = function(context) { - var gl = context.getGL(); - - this.groupIndices_.push(this.indices.length); - this.hitDetectionGroupIndices_.push(this.indices.length); - - // create, bind, and populate the vertices buffer - this.verticesBuffer = new ol.webgl.Buffer(this.vertices); - - var indices = this.indices; - - // create, bind, and populate the indices buffer - this.indicesBuffer = new ol.webgl.Buffer(indices); - - // create textures - /** @type {Object.<string, WebGLTexture>} */ - var texturePerImage = {}; - - this.createTextures_(this.textures_, this.images_, texturePerImage, gl); - - this.createTextures_(this.hitDetectionTextures_, this.hitDetectionImages_, - texturePerImage, gl); - - this.anchorX_ = undefined; - this.anchorY_ = undefined; - this.height_ = undefined; - this.images_ = null; - this.hitDetectionImages_ = null; - this.imageHeight_ = undefined; - this.imageWidth_ = undefined; - this.indices = null; - this.opacity_ = undefined; - this.originX_ = undefined; - this.originY_ = undefined; - this.rotateWithView_ = undefined; - this.rotation_ = undefined; - this.scale_ = undefined; - this.vertices = null; - this.width_ = undefined; - }; - - - /** - * @private - * @param {Array.<WebGLTexture>} textures Textures. - * @param {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} images - * Images. - * @param {Object.<string, WebGLTexture>} texturePerImage Texture cache. - * @param {WebGLRenderingContext} gl Gl. - */ - ol.render.webgl.ImageReplay.prototype.createTextures_ = function(textures, images, texturePerImage, gl) { - var texture, image, uid, i; - var ii = images.length; - for (i = 0; i < ii; ++i) { - image = images[i]; - - uid = ol.getUid(image).toString(); - if (uid in texturePerImage) { - texture = texturePerImage[uid]; - } else { - texture = ol.webgl.Context.createTexture( - gl, image, ol.webgl.CLAMP_TO_EDGE, ol.webgl.CLAMP_TO_EDGE); - texturePerImage[uid] = texture; - } - textures[i] = texture; - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - var fragmentShader = ol.render.webgl.imagereplay.defaultshader.fragment; - var vertexShader = ol.render.webgl.imagereplay.defaultshader.vertex; - var program = context.getProgram(fragmentShader, vertexShader); - - // get the locations - var locations; - if (!this.defaultLocations_) { - // eslint-disable-next-line openlayers-internal/no-missing-requires - locations = new ol.render.webgl.imagereplay.defaultshader.Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - // use the program (FIXME: use the return value) - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT, - false, 32, 0); - - gl.enableVertexAttribArray(locations.a_offsets); - gl.vertexAttribPointer(locations.a_offsets, 2, ol.webgl.FLOAT, - false, 32, 8); - - gl.enableVertexAttribArray(locations.a_texCoord); - gl.vertexAttribPointer(locations.a_texCoord, 2, ol.webgl.FLOAT, - false, 32, 16); - - gl.enableVertexAttribArray(locations.a_opacity); - gl.vertexAttribPointer(locations.a_opacity, 1, ol.webgl.FLOAT, - false, 32, 24); - - gl.enableVertexAttribArray(locations.a_rotateWithView); - gl.vertexAttribPointer(locations.a_rotateWithView, 1, ol.webgl.FLOAT, - false, 32, 28); - - return locations; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_position); - gl.disableVertexAttribArray(locations.a_offsets); - gl.disableVertexAttribArray(locations.a_texCoord); - gl.disableVertexAttribArray(locations.a_opacity); - gl.disableVertexAttribArray(locations.a_rotateWithView); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - var textures = hitDetection ? this.hitDetectionTextures_ : this.textures_; - var groupIndices = hitDetection ? this.hitDetectionGroupIndices_ : this.groupIndices_; - - if (!ol.obj.isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_( - gl, context, skippedFeaturesHash, textures, groupIndices); - } else { - var i, ii, start; - for (i = 0, ii = textures.length, start = 0; i < ii; ++i) { - gl.bindTexture(ol.webgl.TEXTURE_2D, textures[i]); - var end = groupIndices[i]; - this.drawElements(gl, context, start, end); - start = end; - } - } - }; - - - /** - * Draw the replay while paying attention to skipped features. - * - * This functions creates groups of features that can be drawn to together, - * so that the number of `drawElements` calls is minimized. - * - * For example given the following texture groups: - * - * Group 1: A B C - * Group 2: D [E] F G - * - * If feature E should be skipped, the following `drawElements` calls will be - * made: - * - * drawElements with feature A, B and C - * drawElements with feature D - * drawElements with feature F and G - * - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {Array.<WebGLTexture>} textures Textures. - * @param {Array.<number>} groupIndices Texture group indices. - */ - ol.render.webgl.ImageReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash, textures, - groupIndices) { - var featureIndex = 0; - - var i, ii; - for (i = 0, ii = textures.length; i < ii; ++i) { - gl.bindTexture(ol.webgl.TEXTURE_2D, textures[i]); - var groupStart = (i > 0) ? groupIndices[i - 1] : 0; - var groupEnd = groupIndices[i]; - - var start = groupStart; - var end = groupStart; - while (featureIndex < this.startIndices.length && - this.startIndices[featureIndex] <= groupEnd) { - var feature = this.startIndicesFeature[featureIndex]; - - var featureUid = ol.getUid(feature).toString(); - if (skippedFeaturesHash[featureUid] !== undefined) { - // feature should be skipped - if (start !== end) { - // draw the features so far - this.drawElements(gl, context, start, end); - } - // continue with the next feature - start = (featureIndex === this.startIndices.length - 1) ? - groupEnd : this.startIndices[featureIndex + 1]; - end = start; - } else { - // the feature is not skipped, augment the end index - end = (featureIndex === this.startIndices.length - 1) ? - groupEnd : this.startIndices[featureIndex + 1]; - } - featureIndex++; - } - - if (start !== end) { - // draw the remaining features (in case there was no skipped feature - // in this texture group, all features of a group are drawn together) - this.drawElements(gl, context, start, end); - } - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - var i, groupStart, start, end, feature, featureUid; - var featureIndex = this.startIndices.length - 1; - for (i = this.hitDetectionTextures_.length - 1; i >= 0; --i) { - gl.bindTexture(ol.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]); - groupStart = (i > 0) ? this.hitDetectionGroupIndices_[i - 1] : 0; - end = this.hitDetectionGroupIndices_[i]; - - // draw all features for this texture group - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || ol.extent.intersects( - /** @type {Array<number>} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - var result = featureCallback(feature); - if (result) { - return result; - } - } - - end = start; - featureIndex--; - } - } - return undefined; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) { - var anchor = imageStyle.getAnchor(); - var image = imageStyle.getImage(1); - var imageSize = imageStyle.getImageSize(); - var hitDetectionImage = imageStyle.getHitDetectionImage(1); - var opacity = imageStyle.getOpacity(); - var origin = imageStyle.getOrigin(); - var rotateWithView = imageStyle.getRotateWithView(); - var rotation = imageStyle.getRotation(); - var size = imageStyle.getSize(); - var scale = imageStyle.getScale(); - - var currentImage; - if (this.images_.length === 0) { - this.images_.push(image); - } else { - currentImage = this.images_[this.images_.length - 1]; - if (ol.getUid(currentImage) != ol.getUid(image)) { - this.groupIndices_.push(this.indices.length); - this.images_.push(image); - } - } - - if (this.hitDetectionImages_.length === 0) { - this.hitDetectionImages_.push(hitDetectionImage); - } else { - currentImage = - this.hitDetectionImages_[this.hitDetectionImages_.length - 1]; - if (ol.getUid(currentImage) != ol.getUid(hitDetectionImage)) { - this.hitDetectionGroupIndices_.push(this.indices.length); - this.hitDetectionImages_.push(hitDetectionImage); - } - } - - this.anchorX_ = anchor[0]; - this.anchorY_ = anchor[1]; - this.height_ = size[1]; - this.imageHeight_ = imageSize[1]; - this.imageWidth_ = imageSize[0]; - this.opacity_ = opacity; - this.originX_ = origin[0]; - this.originY_ = origin[1]; - this.rotation_ = rotation; - this.rotateWithView_ = rotateWithView; - this.scale_ = scale; - this.width_ = size[0]; - }; - -} - -goog.provide('ol.geom.flat.topology'); - -goog.require('ol.geom.flat.area'); - -/** - * Check if the linestring is a boundary. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {boolean} The linestring is a boundary. - */ -ol.geom.flat.topology.lineStringIsClosed = function(flatCoordinates, offset, end, stride) { - var lastCoord = end - stride; - if (flatCoordinates[offset] === flatCoordinates[lastCoord] && - flatCoordinates[offset + 1] === flatCoordinates[lastCoord + 1] && (end - offset) / stride > 3) { - return !!ol.geom.flat.area.linearRing(flatCoordinates, offset, end, stride); - } - return false; -}; - -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.render.webgl.linestringreplay.defaultshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.render.webgl.linestringreplay.defaultshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.render.webgl.linestringreplay.defaultshader.Fragment.SOURCE); - }; - ol.inherits(ol.render.webgl.linestringreplay.defaultshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.linestringreplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying float v_round;\nvarying vec2 v_roundVertex;\nvarying float v_halfWidth;\n\n\n\nuniform float u_opacity;\nuniform vec4 u_color;\nuniform vec2 u_size;\nuniform float u_pixelRatio;\n\nvoid main(void) {\n if (v_round > 0.0) {\n vec2 windowCoords = vec2((v_roundVertex.x + 1.0) / 2.0 * u_size.x * u_pixelRatio,\n (v_roundVertex.y + 1.0) / 2.0 * u_size.y * u_pixelRatio);\n if (length(windowCoords - gl_FragCoord.xy) > v_halfWidth * u_pixelRatio) {\n discard;\n }\n }\n gl_FragColor = u_color;\n float alpha = u_color.a * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.linestringreplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying float a;varying vec2 b;varying float c;uniform float m;uniform vec4 n;uniform vec2 o;uniform float p;void main(void){if(a>0.0){vec2 windowCoords=vec2((b.x+1.0)/2.0*o.x*p,(b.y+1.0)/2.0*o.y*p);if(length(windowCoords-gl_FragCoord.xy)>c*p){discard;}} gl_FragColor=n;float alpha=n.a*m;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.linestringreplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.linestringreplay.defaultshader.Fragment.DEBUG_SOURCE : - ol.render.webgl.linestringreplay.defaultshader.Fragment.OPTIMIZED_SOURCE; - - - ol.render.webgl.linestringreplay.defaultshader.fragment = new ol.render.webgl.linestringreplay.defaultshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.render.webgl.linestringreplay.defaultshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.render.webgl.linestringreplay.defaultshader.Vertex.SOURCE); - }; - ol.inherits(ol.render.webgl.linestringreplay.defaultshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.linestringreplay.defaultshader.Vertex.DEBUG_SOURCE = 'varying float v_round;\nvarying vec2 v_roundVertex;\nvarying float v_halfWidth;\n\n\nattribute vec2 a_lastPos;\nattribute vec2 a_position;\nattribute vec2 a_nextPos;\nattribute float a_direction;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\nuniform float u_lineWidth;\nuniform float u_miterLimit;\n\nbool nearlyEquals(in float value, in float ref) {\n float epsilon = 0.000000000001;\n return value >= ref - epsilon && value <= ref + epsilon;\n}\n\nvoid alongNormal(out vec2 offset, in vec2 nextP, in float turnDir, in float direction) {\n vec2 dirVect = nextP - a_position;\n vec2 normal = normalize(vec2(-turnDir * dirVect.y, turnDir * dirVect.x));\n offset = u_lineWidth / 2.0 * normal * direction;\n}\n\nvoid miterUp(out vec2 offset, out float round, in bool isRound, in float direction) {\n float halfWidth = u_lineWidth / 2.0;\n vec2 tangent = normalize(normalize(a_nextPos - a_position) + normalize(a_position - a_lastPos));\n vec2 normal = vec2(-tangent.y, tangent.x);\n vec2 dirVect = a_nextPos - a_position;\n vec2 tmpNormal = normalize(vec2(-dirVect.y, dirVect.x));\n float miterLength = abs(halfWidth / dot(normal, tmpNormal));\n offset = normal * direction * miterLength;\n round = 0.0;\n if (isRound) {\n round = 1.0;\n } else if (miterLength > u_miterLimit + u_lineWidth) {\n offset = halfWidth * tmpNormal * direction;\n }\n}\n\nbool miterDown(out vec2 offset, in vec4 projPos, in mat4 offsetMatrix, in float direction) {\n bool degenerate = false;\n vec2 tangent = normalize(normalize(a_nextPos - a_position) + normalize(a_position - a_lastPos));\n vec2 normal = vec2(-tangent.y, tangent.x);\n vec2 dirVect = a_lastPos - a_position;\n vec2 tmpNormal = normalize(vec2(-dirVect.y, dirVect.x));\n vec2 longOffset, shortOffset, longVertex;\n vec4 shortProjVertex;\n float halfWidth = u_lineWidth / 2.0;\n if (length(a_nextPos - a_position) > length(a_lastPos - a_position)) {\n longOffset = tmpNormal * direction * halfWidth;\n shortOffset = normalize(vec2(dirVect.y, -dirVect.x)) * direction * halfWidth;\n longVertex = a_nextPos;\n shortProjVertex = u_projectionMatrix * vec4(a_lastPos, 0.0, 1.0);\n } else {\n shortOffset = tmpNormal * direction * halfWidth;\n longOffset = normalize(vec2(dirVect.y, -dirVect.x)) * direction * halfWidth;\n longVertex = a_lastPos;\n shortProjVertex = u_projectionMatrix * vec4(a_nextPos, 0.0, 1.0);\n }\n //Intersection algorithm based on theory by Paul Bourke (http://paulbourke.net/geometry/pointlineplane/).\n vec4 p1 = u_projectionMatrix * vec4(longVertex, 0.0, 1.0) + offsetMatrix * vec4(longOffset, 0.0, 0.0);\n vec4 p2 = projPos + offsetMatrix * vec4(longOffset, 0.0, 0.0);\n vec4 p3 = shortProjVertex + offsetMatrix * vec4(-shortOffset, 0.0, 0.0);\n vec4 p4 = shortProjVertex + offsetMatrix * vec4(shortOffset, 0.0, 0.0);\n float denom = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);\n float firstU = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;\n float secondU = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;\n float epsilon = 0.000000000001;\n if (firstU > epsilon && firstU < 1.0 - epsilon && secondU > epsilon && secondU < 1.0 - epsilon) {\n shortProjVertex.x = p1.x + firstU * (p2.x - p1.x);\n shortProjVertex.y = p1.y + firstU * (p2.y - p1.y);\n offset = shortProjVertex.xy;\n degenerate = true;\n } else {\n float miterLength = abs(halfWidth / dot(normal, tmpNormal));\n offset = normal * direction * miterLength;\n }\n return degenerate;\n}\n\nvoid squareCap(out vec2 offset, out float round, in bool isRound, in vec2 nextP,\n in float turnDir, in float direction) {\n round = 0.0;\n vec2 dirVect = a_position - nextP;\n vec2 firstNormal = normalize(dirVect);\n vec2 secondNormal = vec2(turnDir * firstNormal.y * direction, -turnDir * firstNormal.x * direction);\n vec2 hypotenuse = normalize(firstNormal - secondNormal);\n vec2 normal = vec2(turnDir * hypotenuse.y * direction, -turnDir * hypotenuse.x * direction);\n float length = sqrt(v_halfWidth * v_halfWidth * 2.0);\n offset = normal * length;\n if (isRound) {\n round = 1.0;\n }\n}\n\nvoid main(void) {\n bool degenerate = false;\n float direction = float(sign(a_direction));\n mat4 offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n vec2 offset;\n vec4 projPos = u_projectionMatrix * vec4(a_position, 0.0, 1.0);\n bool round = nearlyEquals(mod(a_direction, 2.0), 0.0);\n\n v_round = 0.0;\n v_halfWidth = u_lineWidth / 2.0;\n v_roundVertex = projPos.xy;\n\n if (nearlyEquals(mod(a_direction, 3.0), 0.0) || nearlyEquals(mod(a_direction, 17.0), 0.0)) {\n alongNormal(offset, a_nextPos, 1.0, direction);\n } else if (nearlyEquals(mod(a_direction, 5.0), 0.0) || nearlyEquals(mod(a_direction, 13.0), 0.0)) {\n alongNormal(offset, a_lastPos, -1.0, direction);\n } else if (nearlyEquals(mod(a_direction, 23.0), 0.0)) {\n miterUp(offset, v_round, round, direction);\n } else if (nearlyEquals(mod(a_direction, 19.0), 0.0)) {\n degenerate = miterDown(offset, projPos, offsetMatrix, direction);\n } else if (nearlyEquals(mod(a_direction, 7.0), 0.0)) {\n squareCap(offset, v_round, round, a_nextPos, 1.0, direction);\n } else if (nearlyEquals(mod(a_direction, 11.0), 0.0)) {\n squareCap(offset, v_round, round, a_lastPos, -1.0, direction);\n }\n if (!degenerate) {\n vec4 offsets = offsetMatrix * vec4(offset, 0.0, 0.0);\n gl_Position = projPos + offsets;\n } else {\n gl_Position = vec4(offset, 0.0, 1.0);\n }\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.linestringreplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'varying float a;varying vec2 b;varying float c;attribute vec2 d;attribute vec2 e;attribute vec2 f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;bool nearlyEquals(in float value,in float ref){float epsilon=0.000000000001;return value>=ref-epsilon&&value<=ref+epsilon;}void alongNormal(out vec2 offset,in vec2 nextP,in float turnDir,in float direction){vec2 dirVect=nextP-e;vec2 normal=normalize(vec2(-turnDir*dirVect.y,turnDir*dirVect.x));offset=k/2.0*normal*direction;}void miterUp(out vec2 offset,out float round,in bool isRound,in float direction){float halfWidth=k/2.0;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=f-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;round=0.0;if(isRound){round=1.0;}else if(miterLength>l+k){offset=halfWidth*tmpNormal*direction;}} bool miterDown(out vec2 offset,in vec4 projPos,in mat4 offsetMatrix,in float direction){bool degenerate=false;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=d-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));vec2 longOffset,shortOffset,longVertex;vec4 shortProjVertex;float halfWidth=k/2.0;if(length(f-e)>length(d-e)){longOffset=tmpNormal*direction*halfWidth;shortOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=f;shortProjVertex=h*vec4(d,0.0,1.0);}else{shortOffset=tmpNormal*direction*halfWidth;longOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=d;shortProjVertex=h*vec4(f,0.0,1.0);}vec4 p1=h*vec4(longVertex,0.0,1.0)+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p2=projPos+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p3=shortProjVertex+offsetMatrix*vec4(-shortOffset,0.0,0.0);vec4 p4=shortProjVertex+offsetMatrix*vec4(shortOffset,0.0,0.0);float denom=(p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y);float firstU=((p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x))/denom;float secondU=((p2.x-p1.x)*(p1.y-p3.y)-(p2.y-p1.y)*(p1.x-p3.x))/denom;float epsilon=0.000000000001;if(firstU>epsilon&&firstU<1.0-epsilon&&secondU>epsilon&&secondU<1.0-epsilon){shortProjVertex.x=p1.x+firstU*(p2.x-p1.x);shortProjVertex.y=p1.y+firstU*(p2.y-p1.y);offset=shortProjVertex.xy;degenerate=true;}else{float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;}return degenerate;}void squareCap(out vec2 offset,out float round,in bool isRound,in vec2 nextP,in float turnDir,in float direction){round=0.0;vec2 dirVect=e-nextP;vec2 firstNormal=normalize(dirVect);vec2 secondNormal=vec2(turnDir*firstNormal.y*direction,-turnDir*firstNormal.x*direction);vec2 hypotenuse=normalize(firstNormal-secondNormal);vec2 normal=vec2(turnDir*hypotenuse.y*direction,-turnDir*hypotenuse.x*direction);float length=sqrt(c*c*2.0);offset=normal*length;if(isRound){round=1.0;}} void main(void){bool degenerate=false;float direction=float(sign(g));mat4 offsetMatrix=i*j;vec2 offset;vec4 projPos=h*vec4(e,0.0,1.0);bool round=nearlyEquals(mod(g,2.0),0.0);a=0.0;c=k/2.0;b=projPos.xy;if(nearlyEquals(mod(g,3.0),0.0)||nearlyEquals(mod(g,17.0),0.0)){alongNormal(offset,f,1.0,direction);}else if(nearlyEquals(mod(g,5.0),0.0)||nearlyEquals(mod(g,13.0),0.0)){alongNormal(offset,d,-1.0,direction);}else if(nearlyEquals(mod(g,23.0),0.0)){miterUp(offset,a,round,direction);}else if(nearlyEquals(mod(g,19.0),0.0)){degenerate=miterDown(offset,projPos,offsetMatrix,direction);}else if(nearlyEquals(mod(g,7.0),0.0)){squareCap(offset,a,round,f,1.0,direction);}else if(nearlyEquals(mod(g,11.0),0.0)){squareCap(offset,a,round,d,-1.0,direction);}if(!degenerate){vec4 offsets=offsetMatrix*vec4(offset,0.0,0.0);gl_Position=projPos+offsets;}else{gl_Position=vec4(offset,0.0,1.0);}}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.linestringreplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.linestringreplay.defaultshader.Vertex.DEBUG_SOURCE : - ol.render.webgl.linestringreplay.defaultshader.Vertex.OPTIMIZED_SOURCE; - - - ol.render.webgl.linestringreplay.defaultshader.vertex = new ol.render.webgl.linestringreplay.defaultshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.render.webgl.linestringreplay.defaultshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_color = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_color' : 'n'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_lineWidth = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_lineWidth' : 'k'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_miterLimit = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_miterLimit' : 'l'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'm'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_pixelRatio = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_pixelRatio' : 'p'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_size = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_size' : 'o'); - - /** - * @type {number} - */ - this.a_direction = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_direction' : 'g'); - - /** - * @type {number} - */ - this.a_lastPos = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_lastPos' : 'd'); - - /** - * @type {number} - */ - this.a_nextPos = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_nextPos' : 'f'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'e'); - }; - -} - -goog.provide('ol.render.webgl.LineStringReplay'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.color'); -goog.require('ol.extent'); -goog.require('ol.geom.flat.orient'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.geom.flat.topology'); -goog.require('ol.obj'); -goog.require('ol.render.webgl'); -goog.require('ol.render.webgl.Replay'); -goog.require('ol.render.webgl.linestringreplay.defaultshader'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Buffer'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.render.webgl.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @struct - */ - ol.render.webgl.LineStringReplay = function(tolerance, maxExtent) { - ol.render.webgl.Replay.call(this, tolerance, maxExtent); - - /** - * @private - * @type {ol.render.webgl.linestringreplay.defaultshader.Locations} - */ - this.defaultLocations_ = null; - - /** - * @private - * @type {Array.<Array.<?>>} - */ - this.styles_ = []; - - /** - * @private - * @type {Array.<number>} - */ - this.styleIndices_ = []; - - /** - * @private - * @type {{strokeColor: (Array.<number>|null), - * lineCap: (string|undefined), - * lineDash: Array.<number>, - * lineDashOffset: (number|undefined), - * lineJoin: (string|undefined), - * lineWidth: (number|undefined), - * miterLimit: (number|undefined), - * changed: boolean}|null} - */ - this.state_ = { - strokeColor: null, - lineCap: undefined, - lineDash: null, - lineDashOffset: undefined, - lineJoin: undefined, - lineWidth: undefined, - miterLimit: undefined, - changed: false - }; - - }; - ol.inherits(ol.render.webgl.LineStringReplay, ol.render.webgl.Replay); - - - /** - * Draw one segment. - * @private - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - */ - ol.render.webgl.LineStringReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { - - var i, ii; - var numVertices = this.vertices.length; - var numIndices = this.indices.length; - //To save a vertex, the direction of a point is a product of the sign (1 or -1), a prime from - //ol.render.webgl.LineStringReplay.Instruction_, and a rounding factor (1 or 2). If the product is even, - //we round it. If it is odd, we don't. - var lineJoin = this.state_.lineJoin === 'bevel' ? 0 : - this.state_.lineJoin === 'miter' ? 1 : 2; - var lineCap = this.state_.lineCap === 'butt' ? 0 : - this.state_.lineCap === 'square' ? 1 : 2; - var closed = ol.geom.flat.topology.lineStringIsClosed(flatCoordinates, offset, end, stride); - var startCoords, sign, n; - var lastIndex = numIndices; - var lastSign = 1; - //We need the adjacent vertices to define normals in joins. p0 = last, p1 = current, p2 = next. - var p0, p1, p2; - - for (i = offset, ii = end; i < ii; i += stride) { - - n = numVertices / 7; - - p0 = p1; - p1 = p2 || [flatCoordinates[i], flatCoordinates[i + 1]]; - //First vertex. - if (i === offset) { - p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]]; - if (end - offset === stride * 2 && ol.array.equals(p1, p2)) { - break; - } - if (closed) { - //A closed line! Complete the circle. - p0 = [flatCoordinates[end - stride * 2], - flatCoordinates[end - stride * 2 + 1]]; - - startCoords = p2; - } else { - //Add the first two/four vertices. - - if (lineCap) { - numVertices = this.addVertices_([0, 0], p1, p2, - lastSign * ol.render.webgl.LineStringReplay.Instruction_.BEGIN_LINE_CAP * lineCap, numVertices); - - numVertices = this.addVertices_([0, 0], p1, p2, - -lastSign * ol.render.webgl.LineStringReplay.Instruction_.BEGIN_LINE_CAP * lineCap, numVertices); - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n + 2; - - } - - numVertices = this.addVertices_([0, 0], p1, p2, - lastSign * ol.render.webgl.LineStringReplay.Instruction_.BEGIN_LINE * (lineCap || 1), numVertices); - - numVertices = this.addVertices_([0, 0], p1, p2, - -lastSign * ol.render.webgl.LineStringReplay.Instruction_.BEGIN_LINE * (lineCap || 1), numVertices); - - lastIndex = numVertices / 7 - 1; - - continue; - } - } else if (i === end - stride) { - //Last vertex. - if (closed) { - //Same as the first vertex. - p2 = startCoords; - break; - } else { - p0 = p0 || [0, 0]; - - numVertices = this.addVertices_(p0, p1, [0, 0], - lastSign * ol.render.webgl.LineStringReplay.Instruction_.END_LINE * (lineCap || 1), numVertices); - - numVertices = this.addVertices_(p0, p1, [0, 0], - -lastSign * ol.render.webgl.LineStringReplay.Instruction_.END_LINE * (lineCap || 1), numVertices); - - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastIndex - 1; - this.indices[numIndices++] = lastIndex; - - this.indices[numIndices++] = lastIndex; - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n; - - if (lineCap) { - numVertices = this.addVertices_(p0, p1, [0, 0], - lastSign * ol.render.webgl.LineStringReplay.Instruction_.END_LINE_CAP * lineCap, numVertices); - - numVertices = this.addVertices_(p0, p1, [0, 0], - -lastSign * ol.render.webgl.LineStringReplay.Instruction_.END_LINE_CAP * lineCap, numVertices); - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n + 2; - - } - - break; - } - } else { - p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]]; - } - - // We group CW and straight lines, thus the not so inituitive CCW checking function. - sign = ol.render.webgl.triangleIsCounterClockwise(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]) - ? -1 : 1; - - numVertices = this.addVertices_(p0, p1, p2, - sign * ol.render.webgl.LineStringReplay.Instruction_.BEVEL_FIRST * (lineJoin || 1), numVertices); - - numVertices = this.addVertices_(p0, p1, p2, - sign * ol.render.webgl.LineStringReplay.Instruction_.BEVEL_SECOND * (lineJoin || 1), numVertices); - - numVertices = this.addVertices_(p0, p1, p2, - -sign * ol.render.webgl.LineStringReplay.Instruction_.MITER_BOTTOM * (lineJoin || 1), numVertices); - - if (i > offset) { - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastIndex - 1; - this.indices[numIndices++] = lastIndex; - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1; - } - - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n + 1; - - lastIndex = n + 2; - lastSign = sign; - - //Add miter - if (lineJoin) { - numVertices = this.addVertices_(p0, p1, p2, - sign * ol.render.webgl.LineStringReplay.Instruction_.MITER_TOP * lineJoin, numVertices); - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n; - } - } - - if (closed) { - n = n || numVertices / 7; - sign = ol.geom.flat.orient.linearRingIsClockwise([p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]], 0, 6, 2) - ? 1 : -1; - - numVertices = this.addVertices_(p0, p1, p2, - sign * ol.render.webgl.LineStringReplay.Instruction_.BEVEL_FIRST * (lineJoin || 1), numVertices); - - numVertices = this.addVertices_(p0, p1, p2, - -sign * ol.render.webgl.LineStringReplay.Instruction_.MITER_BOTTOM * (lineJoin || 1), numVertices); - - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastIndex - 1; - this.indices[numIndices++] = lastIndex; - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1; - } - }; - - /** - * @param {Array.<number>} p0 Last coordinates. - * @param {Array.<number>} p1 Current coordinates. - * @param {Array.<number>} p2 Next coordinates. - * @param {number} product Sign, instruction, and rounding product. - * @param {number} numVertices Vertex counter. - * @return {number} Vertex counter. - * @private - */ - ol.render.webgl.LineStringReplay.prototype.addVertices_ = function(p0, p1, p2, product, numVertices) { - this.vertices[numVertices++] = p0[0]; - this.vertices[numVertices++] = p0[1]; - this.vertices[numVertices++] = p1[0]; - this.vertices[numVertices++] = p1[1]; - this.vertices[numVertices++] = p2[0]; - this.vertices[numVertices++] = p2[1]; - this.vertices[numVertices++] = product; - - return numVertices; - }; - - /** - * Check if the linestring can be drawn (i. e. valid). - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {boolean} The linestring can be drawn. - * @private - */ - ol.render.webgl.LineStringReplay.prototype.isValid_ = function(flatCoordinates, offset, end, stride) { - var range = end - offset; - if (range < stride * 2) { - return false; - } else if (range === stride * 2) { - var firstP = [flatCoordinates[offset], flatCoordinates[offset + 1]]; - var lastP = [flatCoordinates[offset + stride], flatCoordinates[offset + stride + 1]]; - return !ol.array.equals(firstP, lastP); - } - - return true; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) { - var flatCoordinates = lineStringGeometry.getFlatCoordinates(); - var stride = lineStringGeometry.getStride(); - if (this.isValid_(flatCoordinates, 0, flatCoordinates.length, stride)) { - flatCoordinates = ol.geom.flat.transform.translate(flatCoordinates, 0, flatCoordinates.length, - stride, -this.origin[0], -this.origin[1]); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; - } - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) { - var indexCount = this.indices.length; - var ends = multiLineStringGeometry.getEnds(); - ends.unshift(0); - var flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); - var stride = multiLineStringGeometry.getStride(); - var i, ii; - if (ends.length > 1) { - for (i = 1, ii = ends.length; i < ii; ++i) { - if (this.isValid_(flatCoordinates, ends[i - 1], ends[i], stride)) { - var lineString = ol.geom.flat.transform.translate(flatCoordinates, ends[i - 1], ends[i], - stride, -this.origin[0], -this.origin[1]); - this.drawCoordinates_( - lineString, 0, lineString.length, stride); - } - } - } - if (this.indices.length > indexCount) { - this.startIndices.push(indexCount); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(indexCount); - this.state_.changed = false; - } - } - }; - - - /** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {Array.<Array.<number>>} holeFlatCoordinates Hole flat coordinates. - * @param {number} stride Stride. - */ - ol.render.webgl.LineStringReplay.prototype.drawPolygonCoordinates = function( - flatCoordinates, holeFlatCoordinates, stride) { - if (!ol.geom.flat.topology.lineStringIsClosed(flatCoordinates, 0, - flatCoordinates.length, stride)) { - flatCoordinates.push(flatCoordinates[0]); - flatCoordinates.push(flatCoordinates[1]); - } - this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); - if (holeFlatCoordinates.length) { - var i, ii; - for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { - if (!ol.geom.flat.topology.lineStringIsClosed(holeFlatCoordinates[i], 0, - holeFlatCoordinates[i].length, stride)) { - holeFlatCoordinates[i].push(holeFlatCoordinates[i][0]); - holeFlatCoordinates[i].push(holeFlatCoordinates[i][1]); - } - this.drawCoordinates_(holeFlatCoordinates[i], 0, - holeFlatCoordinates[i].length, stride); - } - } - }; - - - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {number=} opt_index Index count. - */ - ol.render.webgl.LineStringReplay.prototype.setPolygonStyle = function(feature, opt_index) { - var index = opt_index === undefined ? this.indices.length : opt_index; - this.startIndices.push(index); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(index); - this.state_.changed = false; - } - }; - - - /** - * @return {number} Current index. - */ - ol.render.webgl.LineStringReplay.prototype.getCurrentIndex = function() { - return this.indices.length; - }; - - - /** - * @inheritDoc - **/ - ol.render.webgl.LineStringReplay.prototype.finish = function(context) { - // create, bind, and populate the vertices buffer - this.verticesBuffer = new ol.webgl.Buffer(this.vertices); - - // create, bind, and populate the indices buffer - this.indicesBuffer = new ol.webgl.Buffer(this.indices); - - this.startIndices.push(this.indices.length); - - //Clean up, if there is nothing to draw - if (this.styleIndices_.length === 0 && this.styles_.length > 0) { - this.styles_ = []; - } - - this.vertices = null; - this.indices = null; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.getDeleteResourcesFunction = function(context) { - var verticesBuffer = this.verticesBuffer; - var indicesBuffer = this.indicesBuffer; - return function() { - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - }; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - var fragmentShader, vertexShader; - fragmentShader = ol.render.webgl.linestringreplay.defaultshader.fragment; - vertexShader = ol.render.webgl.linestringreplay.defaultshader.vertex; - var program = context.getProgram(fragmentShader, vertexShader); - - // get the locations - var locations; - if (!this.defaultLocations_) { - // eslint-disable-next-line openlayers-internal/no-missing-requires - locations = new ol.render.webgl.linestringreplay.defaultshader.Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_lastPos); - gl.vertexAttribPointer(locations.a_lastPos, 2, ol.webgl.FLOAT, - false, 28, 0); - - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT, - false, 28, 8); - - gl.enableVertexAttribArray(locations.a_nextPos); - gl.vertexAttribPointer(locations.a_nextPos, 2, ol.webgl.FLOAT, - false, 28, 16); - - gl.enableVertexAttribArray(locations.a_direction); - gl.vertexAttribPointer(locations.a_direction, 1, ol.webgl.FLOAT, - false, 28, 24); - - // Enable renderer specific uniforms. - gl.uniform2fv(locations.u_size, size); - gl.uniform1f(locations.u_pixelRatio, pixelRatio); - - return locations; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_lastPos); - gl.disableVertexAttribArray(locations.a_position); - gl.disableVertexAttribArray(locations.a_nextPos); - gl.disableVertexAttribArray(locations.a_direction); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - //Save GL parameters. - var tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC)); - var tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK)); - - if (!hitDetection) { - gl.enable(gl.DEPTH_TEST); - gl.depthMask(true); - gl.depthFunc(gl.NOTEQUAL); - } - - if (!ol.obj.isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_(gl, context, skippedFeaturesHash); - } else { - //Draw by style groups to minimize drawElements() calls. - var i, start, end, nextStyle; - end = this.startIndices[this.startIndices.length - 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - start = this.styleIndices_[i]; - nextStyle = this.styles_[i]; - this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - end = start; - } - } - if (!hitDetection) { - gl.disable(gl.DEPTH_TEST); - gl.clear(gl.DEPTH_BUFFER_BIT); - //Restore GL parameters. - gl.depthMask(tmpDepthMask); - gl.depthFunc(tmpDepthFunc); - } - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object} skippedFeaturesHash Ids of features to skip. - */ - ol.render.webgl.LineStringReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) { - var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; - featureIndex = this.startIndices.length - 2; - end = start = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - featureStart = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid]) { - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - } - end = featureStart; - } - featureIndex--; - start = featureStart; - } - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - } - start = end = groupStart; - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; - featureIndex = this.startIndices.length - 2; - end = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || ol.extent.intersects( - /** @type {Array<number>} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - var result = featureCallback(feature); - - if (result) { - return result; - } - - } - featureIndex--; - end = start; - } - } - return undefined; - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.<number>} color Color. - * @param {number} lineWidth Line width. - * @param {number} miterLimit Miter limit. - */ - ol.render.webgl.LineStringReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth, miterLimit) { - gl.uniform4fv(this.defaultLocations_.u_color, color); - gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth); - gl.uniform1f(this.defaultLocations_.u_miterLimit, miterLimit); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.LineStringReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - var strokeStyleLineCap = strokeStyle.getLineCap(); - this.state_.lineCap = strokeStyleLineCap !== undefined ? - strokeStyleLineCap : ol.render.webgl.defaultLineCap; - var strokeStyleLineDash = strokeStyle.getLineDash(); - this.state_.lineDash = strokeStyleLineDash ? - strokeStyleLineDash : ol.render.webgl.defaultLineDash; - var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - this.state_.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : ol.render.webgl.defaultLineDashOffset; - var strokeStyleLineJoin = strokeStyle.getLineJoin(); - this.state_.lineJoin = strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : ol.render.webgl.defaultLineJoin; - var strokeStyleColor = strokeStyle.getColor(); - if (!(strokeStyleColor instanceof CanvasGradient) && - !(strokeStyleColor instanceof CanvasPattern)) { - strokeStyleColor = ol.color.asArray(strokeStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || ol.render.webgl.defaultStrokeStyle; - } else { - strokeStyleColor = ol.render.webgl.defaultStrokeStyle; - } - var strokeStyleWidth = strokeStyle.getWidth(); - strokeStyleWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : ol.render.webgl.defaultLineWidth; - var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - strokeStyleMiterLimit = strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : ol.render.webgl.defaultMiterLimit; - if (!this.state_.strokeColor || !ol.array.equals(this.state_.strokeColor, strokeStyleColor) || - this.state_.lineWidth !== strokeStyleWidth || this.state_.miterLimit !== strokeStyleMiterLimit) { - this.state_.changed = true; - this.state_.strokeColor = strokeStyleColor; - this.state_.lineWidth = strokeStyleWidth; - this.state_.miterLimit = strokeStyleMiterLimit; - this.styles_.push([strokeStyleColor, strokeStyleWidth, strokeStyleMiterLimit]); - } - }; - - /** - * @enum {number} - * @private - */ - ol.render.webgl.LineStringReplay.Instruction_ = { - ROUND: 2, - BEGIN_LINE: 3, - END_LINE: 5, - BEGIN_LINE_CAP: 7, - END_LINE_CAP: 11, - BEVEL_FIRST: 13, - BEVEL_SECOND: 17, - MITER_BOTTOM: 19, - MITER_TOP: 23 - }; - -} - -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.render.webgl.polygonreplay.defaultshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.render.webgl.polygonreplay.defaultshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.render.webgl.polygonreplay.defaultshader.Fragment.SOURCE); - }; - ol.inherits(ol.render.webgl.polygonreplay.defaultshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.polygonreplay.defaultshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\n\n\n\nuniform vec4 u_color;\nuniform float u_opacity;\n\nvoid main(void) {\n gl_FragColor = u_color;\n float alpha = u_color.a * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.polygonreplay.defaultshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;uniform vec4 e;uniform float f;void main(void){gl_FragColor=e;float alpha=e.a*f;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.polygonreplay.defaultshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.polygonreplay.defaultshader.Fragment.DEBUG_SOURCE : - ol.render.webgl.polygonreplay.defaultshader.Fragment.OPTIMIZED_SOURCE; - - - ol.render.webgl.polygonreplay.defaultshader.fragment = new ol.render.webgl.polygonreplay.defaultshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.render.webgl.polygonreplay.defaultshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.render.webgl.polygonreplay.defaultshader.Vertex.SOURCE); - }; - ol.inherits(ol.render.webgl.polygonreplay.defaultshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.render.webgl.polygonreplay.defaultshader.Vertex.DEBUG_SOURCE = '\n\nattribute vec2 a_position;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0);\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.polygonreplay.defaultshader.Vertex.OPTIMIZED_SOURCE = 'attribute vec2 a;uniform mat4 b;uniform mat4 c;uniform mat4 d;void main(void){gl_Position=b*vec4(a,0.0,1.0);}'; - - - /** - * @const - * @type {string} - */ - ol.render.webgl.polygonreplay.defaultshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.render.webgl.polygonreplay.defaultshader.Vertex.DEBUG_SOURCE : - ol.render.webgl.polygonreplay.defaultshader.Vertex.OPTIMIZED_SOURCE; - - - ol.render.webgl.polygonreplay.defaultshader.vertex = new ol.render.webgl.polygonreplay.defaultshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.render.webgl.polygonreplay.defaultshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_color = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_color' : 'e'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'd'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'c'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'f'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'b'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'a'); - }; - -} - -goog.provide('ol.style.Stroke'); - -goog.require('ol'); - - -/** - * @classdesc - * Set stroke style for vector features. - * Note that the defaults given are the Canvas defaults, which will be used if - * option is not defined. The `get` functions return whatever was entered in - * the options; they will not return the default. - * - * @constructor - * @param {olx.style.StrokeOptions=} opt_options Options. - * @api - */ -ol.style.Stroke = function(opt_options) { - - var options = opt_options || {}; - - /** - * @private - * @type {ol.Color|ol.ColorLike} - */ - this.color_ = options.color !== undefined ? options.color : null; - - /** - * @private - * @type {string|undefined} - */ - this.lineCap_ = options.lineCap; - - /** - * @private - * @type {Array.<number>} - */ - this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null; - - /** - * @private - * @type {number|undefined} - */ - this.lineDashOffset_ = options.lineDashOffset; - - /** - * @private - * @type {string|undefined} - */ - this.lineJoin_ = options.lineJoin; - - /** - * @private - * @type {number|undefined} - */ - this.miterLimit_ = options.miterLimit; - - /** - * @private - * @type {number|undefined} - */ - this.width_ = options.width; - - /** - * @private - * @type {string|undefined} - */ - this.checksum_ = undefined; -}; - - -/** - * Clones the style. - * @return {ol.style.Stroke} The cloned style. - * @api - */ -ol.style.Stroke.prototype.clone = function() { - var color = this.getColor(); - return new ol.style.Stroke({ - color: (color && color.slice) ? color.slice() : color || undefined, - lineCap: this.getLineCap(), - lineDash: this.getLineDash() ? this.getLineDash().slice() : undefined, - lineDashOffset: this.getLineDashOffset(), - lineJoin: this.getLineJoin(), - miterLimit: this.getMiterLimit(), - width: this.getWidth() - }); -}; - - -/** - * Get the stroke color. - * @return {ol.Color|ol.ColorLike} Color. - * @api - */ -ol.style.Stroke.prototype.getColor = function() { - return this.color_; -}; - - -/** - * Get the line cap type for the stroke. - * @return {string|undefined} Line cap. - * @api - */ -ol.style.Stroke.prototype.getLineCap = function() { - return this.lineCap_; -}; - - -/** - * Get the line dash style for the stroke. - * @return {Array.<number>} Line dash. - * @api - */ -ol.style.Stroke.prototype.getLineDash = function() { - return this.lineDash_; -}; - - -/** - * Get the line dash offset for the stroke. - * @return {number|undefined} Line dash offset. - * @api - */ -ol.style.Stroke.prototype.getLineDashOffset = function() { - return this.lineDashOffset_; -}; - - -/** - * Get the line join type for the stroke. - * @return {string|undefined} Line join. - * @api - */ -ol.style.Stroke.prototype.getLineJoin = function() { - return this.lineJoin_; -}; - - -/** - * Get the miter limit for the stroke. - * @return {number|undefined} Miter limit. - * @api - */ -ol.style.Stroke.prototype.getMiterLimit = function() { - return this.miterLimit_; -}; - - -/** - * Get the stroke width. - * @return {number|undefined} Width. - * @api - */ -ol.style.Stroke.prototype.getWidth = function() { - return this.width_; -}; - - -/** - * Set the color. - * - * @param {ol.Color|ol.ColorLike} color Color. - * @api - */ -ol.style.Stroke.prototype.setColor = function(color) { - this.color_ = color; - this.checksum_ = undefined; -}; - - -/** - * Set the line cap. - * - * @param {string|undefined} lineCap Line cap. - * @api - */ -ol.style.Stroke.prototype.setLineCap = function(lineCap) { - this.lineCap_ = lineCap; - this.checksum_ = undefined; -}; - - -/** - * Set the line dash. - * - * Please note that Internet Explorer 10 and lower [do not support][mdn] the - * `setLineDash` method on the `CanvasRenderingContext2D` and therefore this - * property will have no visual effect in these browsers. - * - * [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility - * - * @param {Array.<number>} lineDash Line dash. - * @api - */ -ol.style.Stroke.prototype.setLineDash = function(lineDash) { - this.lineDash_ = lineDash; - this.checksum_ = undefined; -}; - - -/** - * Set the line dash offset. - * - * @param {number|undefined} lineDashOffset Line dash offset. - * @api - */ -ol.style.Stroke.prototype.setLineDashOffset = function(lineDashOffset) { - this.lineDashOffset_ = lineDashOffset; - this.checksum_ = undefined; -}; - - -/** - * Set the line join. - * - * @param {string|undefined} lineJoin Line join. - * @api - */ -ol.style.Stroke.prototype.setLineJoin = function(lineJoin) { - this.lineJoin_ = lineJoin; - this.checksum_ = undefined; -}; - - -/** - * Set the miter limit. - * - * @param {number|undefined} miterLimit Miter limit. - * @api - */ -ol.style.Stroke.prototype.setMiterLimit = function(miterLimit) { - this.miterLimit_ = miterLimit; - this.checksum_ = undefined; -}; - - -/** - * Set the width. - * - * @param {number|undefined} width Width. - * @api - */ -ol.style.Stroke.prototype.setWidth = function(width) { - this.width_ = width; - this.checksum_ = undefined; -}; - - -/** - * @return {string} The checksum. - */ -ol.style.Stroke.prototype.getChecksum = function() { - if (this.checksum_ === undefined) { - this.checksum_ = 's'; - if (this.color_) { - if (typeof this.color_ === 'string') { - this.checksum_ += this.color_; - } else { - this.checksum_ += ol.getUid(this.color_).toString(); - } - } else { - this.checksum_ += '-'; - } - this.checksum_ += ',' + - (this.lineCap_ !== undefined ? - this.lineCap_.toString() : '-') + ',' + - (this.lineDash_ ? - this.lineDash_.toString() : '-') + ',' + - (this.lineDashOffset_ !== undefined ? - this.lineDashOffset_ : '-') + ',' + - (this.lineJoin_ !== undefined ? - this.lineJoin_ : '-') + ',' + - (this.miterLimit_ !== undefined ? - this.miterLimit_.toString() : '-') + ',' + - (this.width_ !== undefined ? - this.width_.toString() : '-'); - } - - return this.checksum_; -}; - -goog.provide('ol.structs.LinkedList'); - -/** - * Creates an empty linked list structure. - * - * @constructor - * @struct - * @param {boolean=} opt_circular The last item is connected to the first one, - * and the first item to the last one. Default is true. - */ -ol.structs.LinkedList = function(opt_circular) { - - /** - * @private - * @type {ol.LinkedListItem|undefined} - */ - this.first_ = undefined; - - /** - * @private - * @type {ol.LinkedListItem|undefined} - */ - this.last_ = undefined; - - /** - * @private - * @type {ol.LinkedListItem|undefined} - */ - this.head_ = undefined; - - /** - * @private - * @type {boolean} - */ - this.circular_ = opt_circular === undefined ? true : opt_circular; - - /** - * @private - * @type {number} - */ - this.length_ = 0; -}; - -/** - * Inserts an item into the linked list right after the current one. - * - * @param {?} data Item data. - */ -ol.structs.LinkedList.prototype.insertItem = function(data) { - - /** @type {ol.LinkedListItem} */ - var item = { - prev: undefined, - next: undefined, - data: data - }; - - var head = this.head_; - - //Initialize the list. - if (!head) { - this.first_ = item; - this.last_ = item; - if (this.circular_) { - item.next = item; - item.prev = item; - } - } else { - //Link the new item to the adjacent ones. - var next = head.next; - item.prev = head; - item.next = next; - head.next = item; - if (next) { - next.prev = item; - } - - if (head === this.last_) { - this.last_ = item; - } - } - this.head_ = item; - this.length_++; -}; - -/** - * Removes the current item from the list. Sets the cursor to the next item, - * if possible. - */ -ol.structs.LinkedList.prototype.removeItem = function() { - var head = this.head_; - if (head) { - var next = head.next; - var prev = head.prev; - if (next) { - next.prev = prev; - } - if (prev) { - prev.next = next; - } - this.head_ = next || prev; - - if (this.first_ === this.last_) { - this.head_ = undefined; - this.first_ = undefined; - this.last_ = undefined; - } else if (this.first_ === head) { - this.first_ = this.head_; - } else if (this.last_ === head) { - this.last_ = prev ? this.head_.prev : this.head_; - } - this.length_--; - } -}; - -/** - * Sets the cursor to the first item, and returns the associated data. - * - * @return {?} Item data. - */ -ol.structs.LinkedList.prototype.firstItem = function() { - this.head_ = this.first_; - if (this.head_) { - return this.head_.data; - } - return undefined; -}; - -/** -* Sets the cursor to the last item, and returns the associated data. -* -* @return {?} Item data. -*/ -ol.structs.LinkedList.prototype.lastItem = function() { - this.head_ = this.last_; - if (this.head_) { - return this.head_.data; - } - return undefined; -}; - -/** - * Sets the cursor to the next item, and returns the associated data. - * - * @return {?} Item data. - */ -ol.structs.LinkedList.prototype.nextItem = function() { - if (this.head_ && this.head_.next) { - this.head_ = this.head_.next; - return this.head_.data; - } - return undefined; -}; - -/** - * Returns the next item's data without moving the cursor. - * - * @return {?} Item data. - */ -ol.structs.LinkedList.prototype.getNextItem = function() { - if (this.head_ && this.head_.next) { - return this.head_.next.data; - } - return undefined; -}; - -/** - * Sets the cursor to the previous item, and returns the associated data. - * - * @return {?} Item data. - */ -ol.structs.LinkedList.prototype.prevItem = function() { - if (this.head_ && this.head_.prev) { - this.head_ = this.head_.prev; - return this.head_.data; - } - return undefined; -}; - -/** - * Returns the previous item's data without moving the cursor. - * - * @return {?} Item data. - */ -ol.structs.LinkedList.prototype.getPrevItem = function() { - if (this.head_ && this.head_.prev) { - return this.head_.prev.data; - } - return undefined; -}; - -/** - * Returns the current item's data. - * - * @return {?} Item data. - */ -ol.structs.LinkedList.prototype.getCurrItem = function() { - if (this.head_) { - return this.head_.data; - } - return undefined; -}; - -/** - * Sets the first item of the list. This only works for circular lists, and sets - * the last item accordingly. - */ -ol.structs.LinkedList.prototype.setFirstItem = function() { - if (this.circular_ && this.head_) { - this.first_ = this.head_; - this.last_ = this.head_.prev; - } -}; - -/** - * Concatenates two lists. - * @param {ol.structs.LinkedList} list List to merge into the current list. - */ -ol.structs.LinkedList.prototype.concat = function(list) { - if (list.head_) { - if (this.head_) { - var end = this.head_.next; - this.head_.next = list.first_; - list.first_.prev = this.head_; - end.prev = list.last_; - list.last_.next = end; - this.length_ += list.length_; - } else { - this.head_ = list.head_; - this.first_ = list.first_; - this.last_ = list.last_; - this.length_ = list.length_; - } - list.head_ = undefined; - list.first_ = undefined; - list.last_ = undefined; - list.length_ = 0; - } -}; - -/** - * Returns the current length of the list. - * - * @return {number} Length. - */ -ol.structs.LinkedList.prototype.getLength = function() { - return this.length_; -}; - - -/** - * @fileoverview - * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} - */ -goog.provide('ol.ext.rbush'); - -/** @typedef {function(*)} */ -ol.ext.rbush = function() {}; - -(function() {(function (exports) { -'use strict'; - -var index$2 = partialSort; -function partialSort(arr, k, left, right, compare) { - left = left || 0; - right = right || (arr.length - 1); - compare = compare || defaultCompare; - while (right > left) { - if (right - left > 600) { - var n = right - left + 1; - var m = k - left + 1; - var z = Math.log(n); - var s = 0.5 * Math.exp(2 * z / 3); - var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); - var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); - var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); - partialSort(arr, k, newLeft, newRight, compare); - } - var t = arr[k]; - var i = left; - var j = right; - swap(arr, left, k); - if (compare(arr[right], t) > 0) swap(arr, left, right); - while (i < j) { - swap(arr, i, j); - i++; - j--; - while (compare(arr[i], t) < 0) i++; - while (compare(arr[j], t) > 0) j--; - } - if (compare(arr[left], t) === 0) swap(arr, left, j); - else { - j++; - swap(arr, j, right); - } - if (j <= k) left = j + 1; - if (k <= j) right = j - 1; - } -} -function swap(arr, i, j) { - var tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; -} -function defaultCompare(a, b) { - return a < b ? -1 : a > b ? 1 : 0; -} - -var index = rbush; -function rbush(maxEntries, format) { - if (!(this instanceof rbush)) return new rbush(maxEntries, format); - this._maxEntries = Math.max(4, maxEntries || 9); - this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); - if (format) { - this._initFormat(format); - } - this.clear(); -} -rbush.prototype = { - all: function () { - return this._all(this.data, []); - }, - search: function (bbox) { - var node = this.data, - result = [], - toBBox = this.toBBox; - if (!intersects(bbox, node)) return result; - var nodesToSearch = [], - i, len, child, childBBox; - while (node) { - for (i = 0, len = node.children.length; i < len; i++) { - child = node.children[i]; - childBBox = node.leaf ? toBBox(child) : child; - if (intersects(bbox, childBBox)) { - if (node.leaf) result.push(child); - else if (contains(bbox, childBBox)) this._all(child, result); - else nodesToSearch.push(child); - } - } - node = nodesToSearch.pop(); - } - return result; - }, - collides: function (bbox) { - var node = this.data, - toBBox = this.toBBox; - if (!intersects(bbox, node)) return false; - var nodesToSearch = [], - i, len, child, childBBox; - while (node) { - for (i = 0, len = node.children.length; i < len; i++) { - child = node.children[i]; - childBBox = node.leaf ? toBBox(child) : child; - if (intersects(bbox, childBBox)) { - if (node.leaf || contains(bbox, childBBox)) return true; - nodesToSearch.push(child); - } - } - node = nodesToSearch.pop(); - } - return false; - }, - load: function (data) { - if (!(data && data.length)) return this; - if (data.length < this._minEntries) { - for (var i = 0, len = data.length; i < len; i++) { - this.insert(data[i]); - } - return this; - } - var node = this._build(data.slice(), 0, data.length - 1, 0); - if (!this.data.children.length) { - this.data = node; - } else if (this.data.height === node.height) { - this._splitRoot(this.data, node); - } else { - if (this.data.height < node.height) { - var tmpNode = this.data; - this.data = node; - node = tmpNode; - } - this._insert(node, this.data.height - node.height - 1, true); - } - return this; - }, - insert: function (item) { - if (item) this._insert(item, this.data.height - 1); - return this; - }, - clear: function () { - this.data = createNode([]); - return this; - }, - remove: function (item, equalsFn) { - if (!item) return this; - var node = this.data, - bbox = this.toBBox(item), - path = [], - indexes = [], - i, parent, index, goingUp; - while (node || path.length) { - if (!node) { - node = path.pop(); - parent = path[path.length - 1]; - i = indexes.pop(); - goingUp = true; - } - if (node.leaf) { - index = findItem(item, node.children, equalsFn); - if (index !== -1) { - node.children.splice(index, 1); - path.push(node); - this._condense(path); - return this; - } - } - if (!goingUp && !node.leaf && contains(node, bbox)) { - path.push(node); - indexes.push(i); - i = 0; - parent = node; - node = node.children[0]; - } else if (parent) { - i++; - node = parent.children[i]; - goingUp = false; - } else node = null; - } - return this; - }, - toBBox: function (item) { return item; }, - compareMinX: compareNodeMinX, - compareMinY: compareNodeMinY, - toJSON: function () { return this.data; }, - fromJSON: function (data) { - this.data = data; - return this; - }, - _all: function (node, result) { - var nodesToSearch = []; - while (node) { - if (node.leaf) result.push.apply(result, node.children); - else nodesToSearch.push.apply(nodesToSearch, node.children); - node = nodesToSearch.pop(); - } - return result; - }, - _build: function (items, left, right, height) { - var N = right - left + 1, - M = this._maxEntries, - node; - if (N <= M) { - node = createNode(items.slice(left, right + 1)); - calcBBox(node, this.toBBox); - return node; - } - if (!height) { - height = Math.ceil(Math.log(N) / Math.log(M)); - M = Math.ceil(N / Math.pow(M, height - 1)); - } - node = createNode([]); - node.leaf = false; - node.height = height; - var N2 = Math.ceil(N / M), - N1 = N2 * Math.ceil(Math.sqrt(M)), - i, j, right2, right3; - multiSelect(items, left, right, N1, this.compareMinX); - for (i = left; i <= right; i += N1) { - right2 = Math.min(i + N1 - 1, right); - multiSelect(items, i, right2, N2, this.compareMinY); - for (j = i; j <= right2; j += N2) { - right3 = Math.min(j + N2 - 1, right2); - node.children.push(this._build(items, j, right3, height - 1)); - } - } - calcBBox(node, this.toBBox); - return node; - }, - _chooseSubtree: function (bbox, node, level, path) { - var i, len, child, targetNode, area, enlargement, minArea, minEnlargement; - while (true) { - path.push(node); - if (node.leaf || path.length - 1 === level) break; - minArea = minEnlargement = Infinity; - for (i = 0, len = node.children.length; i < len; i++) { - child = node.children[i]; - area = bboxArea(child); - enlargement = enlargedArea(bbox, child) - area; - if (enlargement < minEnlargement) { - minEnlargement = enlargement; - minArea = area < minArea ? area : minArea; - targetNode = child; - } else if (enlargement === minEnlargement) { - if (area < minArea) { - minArea = area; - targetNode = child; - } - } - } - node = targetNode || node.children[0]; - } - return node; - }, - _insert: function (item, level, isNode) { - var toBBox = this.toBBox, - bbox = isNode ? item : toBBox(item), - insertPath = []; - var node = this._chooseSubtree(bbox, this.data, level, insertPath); - node.children.push(item); - extend(node, bbox); - while (level >= 0) { - if (insertPath[level].children.length > this._maxEntries) { - this._split(insertPath, level); - level--; - } else break; - } - this._adjustParentBBoxes(bbox, insertPath, level); - }, - _split: function (insertPath, level) { - var node = insertPath[level], - M = node.children.length, - m = this._minEntries; - this._chooseSplitAxis(node, m, M); - var splitIndex = this._chooseSplitIndex(node, m, M); - var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); - newNode.height = node.height; - newNode.leaf = node.leaf; - calcBBox(node, this.toBBox); - calcBBox(newNode, this.toBBox); - if (level) insertPath[level - 1].children.push(newNode); - else this._splitRoot(node, newNode); - }, - _splitRoot: function (node, newNode) { - this.data = createNode([node, newNode]); - this.data.height = node.height + 1; - this.data.leaf = false; - calcBBox(this.data, this.toBBox); - }, - _chooseSplitIndex: function (node, m, M) { - var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index; - minOverlap = minArea = Infinity; - for (i = m; i <= M - m; i++) { - bbox1 = distBBox(node, 0, i, this.toBBox); - bbox2 = distBBox(node, i, M, this.toBBox); - overlap = intersectionArea(bbox1, bbox2); - area = bboxArea(bbox1) + bboxArea(bbox2); - if (overlap < minOverlap) { - minOverlap = overlap; - index = i; - minArea = area < minArea ? area : minArea; - } else if (overlap === minOverlap) { - if (area < minArea) { - minArea = area; - index = i; - } - } - } - return index; - }, - _chooseSplitAxis: function (node, m, M) { - var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX, - compareMinY = node.leaf ? this.compareMinY : compareNodeMinY, - xMargin = this._allDistMargin(node, m, M, compareMinX), - yMargin = this._allDistMargin(node, m, M, compareMinY); - if (xMargin < yMargin) node.children.sort(compareMinX); - }, - _allDistMargin: function (node, m, M, compare) { - node.children.sort(compare); - var toBBox = this.toBBox, - leftBBox = distBBox(node, 0, m, toBBox), - rightBBox = distBBox(node, M - m, M, toBBox), - margin = bboxMargin(leftBBox) + bboxMargin(rightBBox), - i, child; - for (i = m; i < M - m; i++) { - child = node.children[i]; - extend(leftBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin(leftBBox); - } - for (i = M - m - 1; i >= m; i--) { - child = node.children[i]; - extend(rightBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin(rightBBox); - } - return margin; - }, - _adjustParentBBoxes: function (bbox, path, level) { - for (var i = level; i >= 0; i--) { - extend(path[i], bbox); - } - }, - _condense: function (path) { - for (var i = path.length - 1, siblings; i >= 0; i--) { - if (path[i].children.length === 0) { - if (i > 0) { - siblings = path[i - 1].children; - siblings.splice(siblings.indexOf(path[i]), 1); - } else this.clear(); - } else calcBBox(path[i], this.toBBox); - } - }, - _initFormat: function (format) { - var compareArr = ['return a', ' - b', ';']; - this.compareMinX = new Function('a', 'b', compareArr.join(format[0])); - this.compareMinY = new Function('a', 'b', compareArr.join(format[1])); - this.toBBox = new Function('a', - 'return {minX: a' + format[0] + - ', minY: a' + format[1] + - ', maxX: a' + format[2] + - ', maxY: a' + format[3] + '};'); - } -}; -function findItem(item, items, equalsFn) { - if (!equalsFn) return items.indexOf(item); - for (var i = 0; i < items.length; i++) { - if (equalsFn(item, items[i])) return i; - } - return -1; -} -function calcBBox(node, toBBox) { - distBBox(node, 0, node.children.length, toBBox, node); -} -function distBBox(node, k, p, toBBox, destNode) { - if (!destNode) destNode = createNode(null); - destNode.minX = Infinity; - destNode.minY = Infinity; - destNode.maxX = -Infinity; - destNode.maxY = -Infinity; - for (var i = k, child; i < p; i++) { - child = node.children[i]; - extend(destNode, node.leaf ? toBBox(child) : child); - } - return destNode; -} -function extend(a, b) { - a.minX = Math.min(a.minX, b.minX); - a.minY = Math.min(a.minY, b.minY); - a.maxX = Math.max(a.maxX, b.maxX); - a.maxY = Math.max(a.maxY, b.maxY); - return a; -} -function compareNodeMinX(a, b) { return a.minX - b.minX; } -function compareNodeMinY(a, b) { return a.minY - b.minY; } -function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } -function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } -function enlargedArea(a, b) { - return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * - (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); -} -function intersectionArea(a, b) { - var minX = Math.max(a.minX, b.minX), - minY = Math.max(a.minY, b.minY), - maxX = Math.min(a.maxX, b.maxX), - maxY = Math.min(a.maxY, b.maxY); - return Math.max(0, maxX - minX) * - Math.max(0, maxY - minY); -} -function contains(a, b) { - return a.minX <= b.minX && - a.minY <= b.minY && - b.maxX <= a.maxX && - b.maxY <= a.maxY; -} -function intersects(a, b) { - return b.minX <= a.maxX && - b.minY <= a.maxY && - b.maxX >= a.minX && - b.maxY >= a.minY; -} -function createNode(children) { - return { - children: children, - height: 1, - leaf: true, - minX: Infinity, - minY: Infinity, - maxX: -Infinity, - maxY: -Infinity - }; -} -function multiSelect(arr, left, right, n, compare) { - var stack = [left, right], - mid; - while (stack.length) { - right = stack.pop(); - left = stack.pop(); - if (right - left <= n) continue; - mid = left + Math.ceil((right - left) / n / 2) * n; - index$2(arr, mid, left, right, compare); - stack.push(left, mid, mid, right); - } -} - -exports['default'] = index; - -}((this.rbush = this.rbush || {})));}).call(ol.ext); -ol.ext.rbush = ol.ext.rbush.default; - -goog.provide('ol.structs.RBush'); - -goog.require('ol'); -goog.require('ol.ext.rbush'); -goog.require('ol.extent'); -goog.require('ol.obj'); - - -/** - * Wrapper around the RBush by Vladimir Agafonkin. - * - * @constructor - * @param {number=} opt_maxEntries Max entries. - * @see https://github.com/mourner/rbush - * @struct - * @template T - */ -ol.structs.RBush = function(opt_maxEntries) { - - /** - * @private - */ - this.rbush_ = ol.ext.rbush(opt_maxEntries); - - /** - * A mapping between the objects added to this rbush wrapper - * and the objects that are actually added to the internal rbush. - * @private - * @type {Object.<number, ol.RBushEntry>} - */ - this.items_ = {}; - -}; - - -/** - * Insert a value into the RBush. - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - */ -ol.structs.RBush.prototype.insert = function(extent, value) { - /** @type {ol.RBushEntry} */ - var item = { - minX: extent[0], - minY: extent[1], - maxX: extent[2], - maxY: extent[3], - value: value - }; - - this.rbush_.insert(item); - this.items_[ol.getUid(value)] = item; -}; - - -/** - * Bulk-insert values into the RBush. - * @param {Array.<ol.Extent>} extents Extents. - * @param {Array.<T>} values Values. - */ -ol.structs.RBush.prototype.load = function(extents, values) { - var items = new Array(values.length); - for (var i = 0, l = values.length; i < l; i++) { - var extent = extents[i]; - var value = values[i]; - - /** @type {ol.RBushEntry} */ - var item = { - minX: extent[0], - minY: extent[1], - maxX: extent[2], - maxY: extent[3], - value: value - }; - items[i] = item; - this.items_[ol.getUid(value)] = item; - } - this.rbush_.load(items); -}; - - -/** - * Remove a value from the RBush. - * @param {T} value Value. - * @return {boolean} Removed. - */ -ol.structs.RBush.prototype.remove = function(value) { - var uid = ol.getUid(value); - - // get the object in which the value was wrapped when adding to the - // internal rbush. then use that object to do the removal. - var item = this.items_[uid]; - delete this.items_[uid]; - return this.rbush_.remove(item) !== null; -}; - - -/** - * Update the extent of a value in the RBush. - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - */ -ol.structs.RBush.prototype.update = function(extent, value) { - var item = this.items_[ol.getUid(value)]; - var bbox = [item.minX, item.minY, item.maxX, item.maxY]; - if (!ol.extent.equals(bbox, extent)) { - this.remove(value); - this.insert(extent, value); - } -}; - - -/** - * Return all values in the RBush. - * @return {Array.<T>} All. - */ -ol.structs.RBush.prototype.getAll = function() { - var items = this.rbush_.all(); - return items.map(function(item) { - return item.value; - }); -}; - - -/** - * Return all values in the given extent. - * @param {ol.Extent} extent Extent. - * @return {Array.<T>} All in extent. - */ -ol.structs.RBush.prototype.getInExtent = function(extent) { - /** @type {ol.RBushEntry} */ - var bbox = { - minX: extent[0], - minY: extent[1], - maxX: extent[2], - maxY: extent[3] - }; - var items = this.rbush_.search(bbox); - return items.map(function(item) { - return item.value; - }); -}; - - -/** - * Calls a callback function with each value in the tree. - * If the callback returns a truthy value, this value is returned without - * checking the rest of the tree. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @return {*} Callback return value. - * @template S - */ -ol.structs.RBush.prototype.forEach = function(callback, opt_this) { - return this.forEach_(this.getAll(), callback, opt_this); -}; - - -/** - * Calls a callback function with each value in the provided extent. - * @param {ol.Extent} extent Extent. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @return {*} Callback return value. - * @template S - */ -ol.structs.RBush.prototype.forEachInExtent = function(extent, callback, opt_this) { - return this.forEach_(this.getInExtent(extent), callback, opt_this); -}; - - -/** - * @param {Array.<T>} values Values. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @private - * @return {*} Callback return value. - * @template S - */ -ol.structs.RBush.prototype.forEach_ = function(values, callback, opt_this) { - var result; - for (var i = 0, l = values.length; i < l; i++) { - result = callback.call(opt_this, values[i]); - if (result) { - return result; - } - } - return result; -}; - - -/** - * @return {boolean} Is empty. - */ -ol.structs.RBush.prototype.isEmpty = function() { - return ol.obj.isEmpty(this.items_); -}; - - -/** - * Remove all values from the RBush. - */ -ol.structs.RBush.prototype.clear = function() { - this.rbush_.clear(); - this.items_ = {}; -}; - - -/** - * @param {ol.Extent=} opt_extent Extent. - * @return {!ol.Extent} Extent. - */ -ol.structs.RBush.prototype.getExtent = function(opt_extent) { - // FIXME add getExtent() to rbush - var data = this.rbush_.data; - return ol.extent.createOrUpdate(data.minX, data.minY, data.maxX, data.maxY, opt_extent); -}; - - -/** - * @param {ol.structs.RBush} rbush R-Tree. - */ -ol.structs.RBush.prototype.concat = function(rbush) { - this.rbush_.load(rbush.rbush_.all()); - for (var i in rbush.items_) { - this.items_[i | 0] = rbush.items_[i | 0]; - } -}; - -goog.provide('ol.render.webgl.PolygonReplay'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.color'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.geom.flat.contains'); -goog.require('ol.geom.flat.orient'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.render.webgl.polygonreplay.defaultshader'); -goog.require('ol.render.webgl.LineStringReplay'); -goog.require('ol.render.webgl.Replay'); -goog.require('ol.render.webgl'); -goog.require('ol.style.Stroke'); -goog.require('ol.structs.LinkedList'); -goog.require('ol.structs.RBush'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Buffer'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.render.webgl.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @struct - */ - ol.render.webgl.PolygonReplay = function(tolerance, maxExtent) { - ol.render.webgl.Replay.call(this, tolerance, maxExtent); - - this.lineStringReplay = new ol.render.webgl.LineStringReplay( - tolerance, maxExtent); - - /** - * @private - * @type {ol.render.webgl.polygonreplay.defaultshader.Locations} - */ - this.defaultLocations_ = null; - - /** - * @private - * @type {Array.<Array.<number>>} - */ - this.styles_ = []; - - /** - * @private - * @type {Array.<number>} - */ - this.styleIndices_ = []; - - /** - * @private - * @type {{fillColor: (Array.<number>|null), - * changed: boolean}|null} - */ - this.state_ = { - fillColor: null, - changed: false - }; - - }; - ol.inherits(ol.render.webgl.PolygonReplay, ol.render.webgl.Replay); - - - /** - * Draw one polygon. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {Array.<Array.<number>>} holeFlatCoordinates Hole flat coordinates. - * @param {number} stride Stride. - * @private - */ - ol.render.webgl.PolygonReplay.prototype.drawCoordinates_ = function( - flatCoordinates, holeFlatCoordinates, stride) { - // Triangulate the polygon - var outerRing = new ol.structs.LinkedList(); - var rtree = new ol.structs.RBush(); - // Initialize the outer ring - var maxX = this.processFlatCoordinates_(flatCoordinates, stride, outerRing, rtree, true); - - // Eliminate holes, if there are any - if (holeFlatCoordinates.length) { - var i, ii; - var holeLists = []; - for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { - var holeList = { - list: new ol.structs.LinkedList(), - maxX: undefined, - rtree: new ol.structs.RBush() - }; - holeLists.push(holeList); - holeList.maxX = this.processFlatCoordinates_(holeFlatCoordinates[i], - stride, holeList.list, holeList.rtree, false); - } - holeLists.sort(function(a, b) { - return b.maxX[0] === a.maxX[0] ? a.maxX[1] - b.maxX[1] : b.maxX[0] - a.maxX[0]; - }); - for (i = 0; i < holeLists.length; ++i) { - var currList = holeLists[i].list; - var start = currList.firstItem(); - var currItem = start; - var intersection; - do { - if (this.getIntersections_(currItem, rtree).length) { - intersection = true; - break; - } - currItem = currList.nextItem(); - } while (start !== currItem); - if (!intersection) { - this.classifyPoints_(currList, holeLists[i].rtree, true); - if (this.bridgeHole_(currList, holeLists[i].maxX[0], outerRing, maxX[0], rtree)) { - rtree.concat(holeLists[i].rtree); - this.classifyPoints_(outerRing, rtree, false); - } - } - } - } else { - this.classifyPoints_(outerRing, rtree, false); - } - this.triangulate_(outerRing, rtree); - }; - - - /** - * Inserts flat coordinates in a linked list and adds them to the vertex buffer. - * @private - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} stride Stride. - * @param {ol.structs.LinkedList} list Linked list. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @param {boolean} clockwise Coordinate order should be clockwise. - * @return {Array.<number>} X and Y coords of maximum X value. - */ - ol.render.webgl.PolygonReplay.prototype.processFlatCoordinates_ = function( - flatCoordinates, stride, list, rtree, clockwise) { - var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates, - 0, flatCoordinates.length, stride); - var i, ii, maxXX, maxXY; - var n = this.vertices.length / 2; - /** @type {ol.WebglPolygonVertex} */ - var start; - /** @type {ol.WebglPolygonVertex} */ - var p0; - /** @type {ol.WebglPolygonVertex} */ - var p1; - var extents = []; - var segments = []; - if (clockwise === isClockwise) { - start = this.createPoint_(flatCoordinates[0], flatCoordinates[1], n++); - p0 = start; - maxXX = flatCoordinates[0]; - maxXY = flatCoordinates[1]; - for (i = stride, ii = flatCoordinates.length; i < ii; i += stride) { - p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); - segments.push(this.insertItem_(p0, p1, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - if (flatCoordinates[i] > maxXX) { - maxXX = flatCoordinates[i]; - maxXY = flatCoordinates[i + 1]; - } - p0 = p1; - } - segments.push(this.insertItem_(p1, start, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - } else { - var end = flatCoordinates.length - stride; - start = this.createPoint_(flatCoordinates[end], flatCoordinates[end + 1], n++); - p0 = start; - maxXX = flatCoordinates[end]; - maxXY = flatCoordinates[end + 1]; - for (i = end - stride, ii = 0; i >= ii; i -= stride) { - p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); - segments.push(this.insertItem_(p0, p1, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - if (flatCoordinates[i] > maxXX) { - maxXX = flatCoordinates[i]; - maxXY = flatCoordinates[i + 1]; - } - p0 = p1; - } - segments.push(this.insertItem_(p1, start, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - } - rtree.load(extents, segments); - - return [maxXX, maxXY]; - }; - - - /** - * Classifies the points of a polygon list as convex, reflex. Removes collinear vertices. - * @private - * @param {ol.structs.LinkedList} list Polygon ring. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @param {boolean} ccw The orientation of the polygon is counter-clockwise. - * @return {boolean} There were reclassified points. - */ - ol.render.webgl.PolygonReplay.prototype.classifyPoints_ = function(list, rtree, ccw) { - var start = list.firstItem(); - var s0 = start; - var s1 = list.nextItem(); - var pointsReclassified = false; - do { - var reflex = ccw ? ol.render.webgl.triangleIsCounterClockwise(s1.p1.x, - s1.p1.y, s0.p1.x, s0.p1.y, s0.p0.x, s0.p0.y) : - ol.render.webgl.triangleIsCounterClockwise(s0.p0.x, s0.p0.y, s0.p1.x, - s0.p1.y, s1.p1.x, s1.p1.y); - if (reflex === undefined) { - this.removeItem_(s0, s1, list, rtree); - pointsReclassified = true; - if (s1 === start) { - start = list.getNextItem(); - } - s1 = s0; - list.prevItem(); - } else if (s0.p1.reflex !== reflex) { - s0.p1.reflex = reflex; - pointsReclassified = true; - } - s0 = s1; - s1 = list.nextItem(); - } while (s0 !== start); - return pointsReclassified; - }; - - - /** - * @private - * @param {ol.structs.LinkedList} hole Linked list of the hole. - * @param {number} holeMaxX Maximum X value of the hole. - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @param {number} listMaxX Maximum X value of the polygon. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @return {boolean} Bridging was successful. - */ - ol.render.webgl.PolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, - list, listMaxX, rtree) { - var seg = hole.firstItem(); - while (seg.p1.x !== holeMaxX) { - seg = hole.nextItem(); - } - - var p1 = seg.p1; - /** @type {ol.WebglPolygonVertex} */ - var p2 = {x: listMaxX, y: p1.y, i: -1}; - var minDist = Infinity; - var i, ii, bestPoint; - /** @type {ol.WebglPolygonVertex} */ - var p5; - - var intersectingSegments = this.getIntersections_({p0: p1, p1: p2}, rtree, true); - for (i = 0, ii = intersectingSegments.length; i < ii; ++i) { - var currSeg = intersectingSegments[i]; - var intersection = this.calculateIntersection_(p1, p2, currSeg.p0, - currSeg.p1, true); - var dist = Math.abs(p1.x - intersection[0]); - if (dist < minDist && ol.render.webgl.triangleIsCounterClockwise(p1.x, p1.y, - currSeg.p0.x, currSeg.p0.y, currSeg.p1.x, currSeg.p1.y) !== undefined) { - minDist = dist; - p5 = {x: intersection[0], y: intersection[1], i: -1}; - seg = currSeg; - } - } - if (minDist === Infinity) { - return false; - } - bestPoint = seg.p1; - - if (minDist > 0) { - var pointsInTriangle = this.getPointsInTriangle_(p1, p5, seg.p1, rtree); - if (pointsInTriangle.length) { - var theta = Infinity; - for (i = 0, ii = pointsInTriangle.length; i < ii; ++i) { - var currPoint = pointsInTriangle[i]; - var currTheta = Math.atan2(p1.y - currPoint.y, p2.x - currPoint.x); - if (currTheta < theta || (currTheta === theta && currPoint.x < bestPoint.x)) { - theta = currTheta; - bestPoint = currPoint; - } - } - } - } - - seg = list.firstItem(); - while (seg.p1.x !== bestPoint.x || seg.p1.y !== bestPoint.y) { - seg = list.nextItem(); - } - - //We clone the bridge points as they can have different convexity. - var p0Bridge = {x: p1.x, y: p1.y, i: p1.i, reflex: undefined}; - var p1Bridge = {x: seg.p1.x, y: seg.p1.y, i: seg.p1.i, reflex: undefined}; - - hole.getNextItem().p0 = p0Bridge; - this.insertItem_(p1, seg.p1, hole, rtree); - this.insertItem_(p1Bridge, p0Bridge, hole, rtree); - seg.p1 = p1Bridge; - hole.setFirstItem(); - list.concat(hole); - - return true; - }; - - - /** - * @private - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - */ - ol.render.webgl.PolygonReplay.prototype.triangulate_ = function(list, rtree) { - var ccw = false; - var simple = this.isSimple_(list, rtree); - - // Start clipping ears - while (list.getLength() > 3) { - if (simple) { - if (!this.clipEars_(list, rtree, simple, ccw)) { - if (!this.classifyPoints_(list, rtree, ccw)) { - // Due to the behavior of OL's PIP algorithm, the ear clipping cannot - // introduce touching segments. However, the original data may have some. - if (!this.resolveLocalSelfIntersections_(list, rtree, true)) { - break; - } - } - } - } else { - if (!this.clipEars_(list, rtree, simple, ccw)) { - // We ran out of ears, try to reclassify. - if (!this.classifyPoints_(list, rtree, ccw)) { - // We have a bad polygon, try to resolve local self-intersections. - if (!this.resolveLocalSelfIntersections_(list, rtree)) { - simple = this.isSimple_(list, rtree); - if (!simple) { - // We have a really bad polygon, try more time consuming methods. - this.splitPolygon_(list, rtree); - break; - } else { - ccw = !this.isClockwise_(list); - this.classifyPoints_(list, rtree, ccw); - } - } - } - } - } - } - if (list.getLength() === 3) { - var numIndices = this.indices.length; - this.indices[numIndices++] = list.getPrevItem().p0.i; - this.indices[numIndices++] = list.getCurrItem().p0.i; - this.indices[numIndices++] = list.getNextItem().p0.i; - } - }; - - - /** - * @private - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @param {boolean} simple The polygon is simple. - * @param {boolean} ccw Orientation of the polygon is counter-clockwise. - * @return {boolean} There were processed ears. - */ - ol.render.webgl.PolygonReplay.prototype.clipEars_ = function(list, rtree, simple, ccw) { - var numIndices = this.indices.length; - var start = list.firstItem(); - var s0 = list.getPrevItem(); - var s1 = start; - var s2 = list.nextItem(); - var s3 = list.getNextItem(); - var p0, p1, p2; - var processedEars = false; - do { - p0 = s1.p0; - p1 = s1.p1; - p2 = s2.p1; - if (p1.reflex === false) { - // We might have a valid ear - var diagonalIsInside = ccw ? this.diagonalIsInside_(s3.p1, p2, p1, p0, - s0.p0) : this.diagonalIsInside_(s0.p0, p0, p1, p2, s3.p1); - if ((simple || this.getIntersections_({p0: p0, p1: p2}, rtree).length === 0) && - diagonalIsInside && this.getPointsInTriangle_(p0, p1, p2, rtree, true).length === 0) { - //The diagonal is completely inside the polygon - if (simple || p0.reflex === false || p2.reflex === false || - ol.geom.flat.orient.linearRingIsClockwise([s0.p0.x, s0.p0.y, p0.x, - p0.y, p1.x, p1.y, p2.x, p2.y, s3.p1.x, s3.p1.y], 0, 10, 2) === !ccw) { - //The diagonal is persumably valid, we have an ear - this.indices[numIndices++] = p0.i; - this.indices[numIndices++] = p1.i; - this.indices[numIndices++] = p2.i; - this.removeItem_(s1, s2, list, rtree); - if (s2 === start) { - start = s3; - } - processedEars = true; - } - } - } - // Else we have a reflex point. - s0 = list.getPrevItem(); - s1 = list.getCurrItem(); - s2 = list.nextItem(); - s3 = list.getNextItem(); - } while (s1 !== start && list.getLength() > 3); - - return processedEars; - }; - - - /** - * @private - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @param {boolean=} opt_touch Resolve touching segments. - * @return {boolean} There were resolved intersections. - */ - ol.render.webgl.PolygonReplay.prototype.resolveLocalSelfIntersections_ = function( - list, rtree, opt_touch) { - var start = list.firstItem(); - list.nextItem(); - var s0 = start; - var s1 = list.nextItem(); - var resolvedIntersections = false; - - do { - var intersection = this.calculateIntersection_(s0.p0, s0.p1, s1.p0, s1.p1, - opt_touch); - if (intersection) { - var breakCond = false; - var numVertices = this.vertices.length; - var numIndices = this.indices.length; - var n = numVertices / 2; - var seg = list.prevItem(); - list.removeItem(); - rtree.remove(seg); - breakCond = (seg === start); - var p; - if (opt_touch) { - if (intersection[0] === s0.p0.x && intersection[1] === s0.p0.y) { - list.prevItem(); - p = s0.p0; - s1.p0 = p; - rtree.remove(s0); - breakCond = breakCond || (s0 === start); - } else { - p = s1.p1; - s0.p1 = p; - rtree.remove(s1); - breakCond = breakCond || (s1 === start); - } - list.removeItem(); - } else { - p = this.createPoint_(intersection[0], intersection[1], n); - s0.p1 = p; - s1.p0 = p; - rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), - Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); - rtree.update([Math.min(s1.p0.x, s1.p1.x), Math.min(s1.p0.y, s1.p1.y), - Math.max(s1.p0.x, s1.p1.x), Math.max(s1.p0.y, s1.p1.y)], s1); - } - - this.indices[numIndices++] = seg.p0.i; - this.indices[numIndices++] = seg.p1.i; - this.indices[numIndices++] = p.i; - - resolvedIntersections = true; - if (breakCond) { - break; - } - } - - s0 = list.getPrevItem(); - s1 = list.nextItem(); - } while (s0 !== start); - return resolvedIntersections; - }; - - - /** - * @private - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @return {boolean} The polygon is simple. - */ - ol.render.webgl.PolygonReplay.prototype.isSimple_ = function(list, rtree) { - var start = list.firstItem(); - var seg = start; - do { - if (this.getIntersections_(seg, rtree).length) { - return false; - } - seg = list.nextItem(); - } while (seg !== start); - return true; - }; - - - /** - * @private - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @return {boolean} Orientation is clockwise. - */ - ol.render.webgl.PolygonReplay.prototype.isClockwise_ = function(list) { - var length = list.getLength() * 2; - var flatCoordinates = new Array(length); - var start = list.firstItem(); - var seg = start; - var i = 0; - do { - flatCoordinates[i++] = seg.p0.x; - flatCoordinates[i++] = seg.p0.y; - seg = list.nextItem(); - } while (seg !== start); - return ol.geom.flat.orient.linearRingIsClockwise(flatCoordinates, 0, length, 2); - }; - - - /** - * @private - * @param {ol.structs.LinkedList} list Linked list of the polygon. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - */ - ol.render.webgl.PolygonReplay.prototype.splitPolygon_ = function(list, rtree) { - var start = list.firstItem(); - var s0 = start; - do { - var intersections = this.getIntersections_(s0, rtree); - if (intersections.length) { - var s1 = intersections[0]; - var n = this.vertices.length / 2; - var intersection = this.calculateIntersection_(s0.p0, - s0.p1, s1.p0, s1.p1); - var p = this.createPoint_(intersection[0], intersection[1], n); - var newPolygon = new ol.structs.LinkedList(); - var newRtree = new ol.structs.RBush(); - this.insertItem_(p, s0.p1, newPolygon, newRtree); - s0.p1 = p; - rtree.update([Math.min(s0.p0.x, p.x), Math.min(s0.p0.y, p.y), - Math.max(s0.p0.x, p.x), Math.max(s0.p0.y, p.y)], s0); - var currItem = list.nextItem(); - while (currItem !== s1) { - this.insertItem_(currItem.p0, currItem.p1, newPolygon, newRtree); - rtree.remove(currItem); - list.removeItem(); - currItem = list.getCurrItem(); - } - this.insertItem_(s1.p0, p, newPolygon, newRtree); - s1.p0 = p; - rtree.update([Math.min(s1.p1.x, p.x), Math.min(s1.p1.y, p.y), - Math.max(s1.p1.x, p.x), Math.max(s1.p1.y, p.y)], s1); - this.classifyPoints_(list, rtree, false); - this.triangulate_(list, rtree); - this.classifyPoints_(newPolygon, newRtree, false); - this.triangulate_(newPolygon, newRtree); - break; - } - s0 = list.nextItem(); - } while (s0 !== start); - }; - - - /** - * @private - * @param {number} x X coordinate. - * @param {number} y Y coordinate. - * @param {number} i Index. - * @return {ol.WebglPolygonVertex} List item. - */ - ol.render.webgl.PolygonReplay.prototype.createPoint_ = function(x, y, i) { - var numVertices = this.vertices.length; - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - /** @type {ol.WebglPolygonVertex} */ - var p = { - x: x, - y: y, - i: i, - reflex: undefined - }; - return p; - }; - - - /** - * @private - * @param {ol.WebglPolygonVertex} p0 First point of segment. - * @param {ol.WebglPolygonVertex} p1 Second point of segment. - * @param {ol.structs.LinkedList} list Polygon ring. - * @param {ol.structs.RBush=} opt_rtree Insert the segment into the R-Tree. - * @return {ol.WebglPolygonSegment} segment. - */ - ol.render.webgl.PolygonReplay.prototype.insertItem_ = function(p0, p1, list, opt_rtree) { - var seg = { - p0: p0, - p1: p1 - }; - list.insertItem(seg); - if (opt_rtree) { - opt_rtree.insert([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), - Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)], seg); - } - return seg; - }; - - - /** - * @private - * @param {ol.WebglPolygonSegment} s0 Segment before the remove candidate. - * @param {ol.WebglPolygonSegment} s1 Remove candidate segment. - * @param {ol.structs.LinkedList} list Polygon ring. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - */ - ol.render.webgl.PolygonReplay.prototype.removeItem_ = function(s0, s1, list, rtree) { - if (list.getCurrItem() === s1) { - list.removeItem(); - s0.p1 = s1.p1; - rtree.remove(s1); - rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), - Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); - } - }; - - - /** - * @private - * @param {ol.WebglPolygonVertex} p0 First point. - * @param {ol.WebglPolygonVertex} p1 Second point. - * @param {ol.WebglPolygonVertex} p2 Third point. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @param {boolean=} opt_reflex Only include reflex points. - * @return {Array.<ol.WebglPolygonVertex>} Points in the triangle. - */ - ol.render.webgl.PolygonReplay.prototype.getPointsInTriangle_ = function(p0, p1, - p2, rtree, opt_reflex) { - var i, ii, j, p; - var result = []; - var segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x, p2.x), - Math.min(p0.y, p1.y, p2.y), Math.max(p0.x, p1.x, p2.x), Math.max(p0.y, - p1.y, p2.y)]); - for (i = 0, ii = segmentsInExtent.length; i < ii; ++i) { - for (j in segmentsInExtent[i]) { - p = segmentsInExtent[i][j]; - if (typeof p === 'object' && (!opt_reflex || p.reflex)) { - if ((p.x !== p0.x || p.y !== p0.y) && (p.x !== p1.x || p.y !== p1.y) && - (p.x !== p2.x || p.y !== p2.y) && result.indexOf(p) === -1 && - ol.geom.flat.contains.linearRingContainsXY([p0.x, p0.y, p1.x, p1.y, - p2.x, p2.y], 0, 6, 2, p.x, p.y)) { - result.push(p); - } - } - } - } - return result; - }; - - - /** - * @private - * @param {ol.WebglPolygonSegment} segment Segment. - * @param {ol.structs.RBush} rtree R-Tree of the polygon. - * @param {boolean=} opt_touch Touching segments should be considered an intersection. - * @return {Array.<ol.WebglPolygonSegment>} Intersecting segments. - */ - ol.render.webgl.PolygonReplay.prototype.getIntersections_ = function(segment, rtree, opt_touch) { - var p0 = segment.p0; - var p1 = segment.p1; - var segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x), - Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)]); - var result = []; - var i, ii; - for (i = 0, ii = segmentsInExtent.length; i < ii; ++i) { - var currSeg = segmentsInExtent[i]; - if (segment !== currSeg && (opt_touch || currSeg.p0 !== p1 || currSeg.p1 !== p0) && - this.calculateIntersection_(p0, p1, currSeg.p0, currSeg.p1, opt_touch)) { - result.push(currSeg); - } - } - return result; - }; - - - /** - * Line intersection algorithm by Paul Bourke. - * @see http://paulbourke.net/geometry/pointlineplane/ - * - * @private - * @param {ol.WebglPolygonVertex} p0 First point. - * @param {ol.WebglPolygonVertex} p1 Second point. - * @param {ol.WebglPolygonVertex} p2 Third point. - * @param {ol.WebglPolygonVertex} p3 Fourth point. - * @param {boolean=} opt_touch Touching segments should be considered an intersection. - * @return {Array.<number>|undefined} Intersection coordinates. - */ - ol.render.webgl.PolygonReplay.prototype.calculateIntersection_ = function(p0, - p1, p2, p3, opt_touch) { - var denom = (p3.y - p2.y) * (p1.x - p0.x) - (p3.x - p2.x) * (p1.y - p0.y); - if (denom !== 0) { - var ua = ((p3.x - p2.x) * (p0.y - p2.y) - (p3.y - p2.y) * (p0.x - p2.x)) / denom; - var ub = ((p1.x - p0.x) * (p0.y - p2.y) - (p1.y - p0.y) * (p0.x - p2.x)) / denom; - if ((!opt_touch && ua > ol.render.webgl.EPSILON && ua < 1 - ol.render.webgl.EPSILON && - ub > ol.render.webgl.EPSILON && ub < 1 - ol.render.webgl.EPSILON) || (opt_touch && - ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)) { - return [p0.x + ua * (p1.x - p0.x), p0.y + ua * (p1.y - p0.y)]; - } - } - return undefined; - }; - - - /** - * @private - * @param {ol.WebglPolygonVertex} p0 Point before the start of the diagonal. - * @param {ol.WebglPolygonVertex} p1 Start point of the diagonal. - * @param {ol.WebglPolygonVertex} p2 Ear candidate. - * @param {ol.WebglPolygonVertex} p3 End point of the diagonal. - * @param {ol.WebglPolygonVertex} p4 Point after the end of the diagonal. - * @return {boolean} Diagonal is inside the polygon. - */ - ol.render.webgl.PolygonReplay.prototype.diagonalIsInside_ = function(p0, p1, p2, p3, p4) { - if (p1.reflex === undefined || p3.reflex === undefined) { - return false; - } - var p1IsLeftOf = (p2.x - p3.x) * (p1.y - p3.y) > (p2.y - p3.y) * (p1.x - p3.x); - var p1IsRightOf = (p4.x - p3.x) * (p1.y - p3.y) < (p4.y - p3.y) * (p1.x - p3.x); - var p3IsLeftOf = (p0.x - p1.x) * (p3.y - p1.y) > (p0.y - p1.y) * (p3.x - p1.x); - var p3IsRightOf = (p2.x - p1.x) * (p3.y - p1.y) < (p2.y - p1.y) * (p3.x - p1.x); - var p1InCone = p3.reflex ? p1IsRightOf || p1IsLeftOf : p1IsRightOf && p1IsLeftOf; - var p3InCone = p1.reflex ? p3IsRightOf || p3IsLeftOf : p3IsRightOf && p3IsLeftOf; - return p1InCone && p3InCone; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) { - var endss = multiPolygonGeometry.getEndss(); - var stride = multiPolygonGeometry.getStride(); - var currIndex = this.indices.length; - var currLineIndex = this.lineStringReplay.getCurrentIndex(); - var flatCoordinates = multiPolygonGeometry.getFlatCoordinates(); - var i, ii, j, jj; - var start = 0; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - if (ends.length > 0) { - var outerRing = ol.geom.flat.transform.translate(flatCoordinates, start, ends[0], - stride, -this.origin[0], -this.origin[1]); - if (outerRing.length) { - var holes = []; - var holeFlatCoords; - for (j = 1, jj = ends.length; j < jj; ++j) { - if (ends[j] !== ends[j - 1]) { - holeFlatCoords = ol.geom.flat.transform.translate(flatCoordinates, ends[j - 1], - ends[j], stride, -this.origin[0], -this.origin[1]); - holes.push(holeFlatCoords); - } - } - this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); - this.drawCoordinates_(outerRing, holes, stride); - } - } - start = ends[ends.length - 1]; - } - if (this.indices.length > currIndex) { - this.startIndices.push(currIndex); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(currIndex); - this.state_.changed = false; - } - } - if (this.lineStringReplay.getCurrentIndex() > currLineIndex) { - this.lineStringReplay.setPolygonStyle(feature, currLineIndex); - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) { - var ends = polygonGeometry.getEnds(); - var stride = polygonGeometry.getStride(); - if (ends.length > 0) { - var flatCoordinates = polygonGeometry.getFlatCoordinates().map(Number); - var outerRing = ol.geom.flat.transform.translate(flatCoordinates, 0, ends[0], - stride, -this.origin[0], -this.origin[1]); - if (outerRing.length) { - var holes = []; - var i, ii, holeFlatCoords; - for (i = 1, ii = ends.length; i < ii; ++i) { - if (ends[i] !== ends[i - 1]) { - holeFlatCoords = ol.geom.flat.transform.translate(flatCoordinates, ends[i - 1], - ends[i], stride, -this.origin[0], -this.origin[1]); - holes.push(holeFlatCoords); - } - } - - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; - } - this.lineStringReplay.setPolygonStyle(feature); - - this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); - this.drawCoordinates_(outerRing, holes, stride); - } - } - }; - - - /** - * @inheritDoc - **/ - ol.render.webgl.PolygonReplay.prototype.finish = function(context) { - // create, bind, and populate the vertices buffer - this.verticesBuffer = new ol.webgl.Buffer(this.vertices); - - // create, bind, and populate the indices buffer - this.indicesBuffer = new ol.webgl.Buffer(this.indices); - - this.startIndices.push(this.indices.length); - - this.lineStringReplay.finish(context); - - //Clean up, if there is nothing to draw - if (this.styleIndices_.length === 0 && this.styles_.length > 0) { - this.styles_ = []; - } - - this.vertices = null; - this.indices = null; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.getDeleteResourcesFunction = function(context) { - var verticesBuffer = this.verticesBuffer; - var indicesBuffer = this.indicesBuffer; - var lineDeleter = this.lineStringReplay.getDeleteResourcesFunction(context); - return function() { - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - lineDeleter(); - }; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - var fragmentShader, vertexShader; - fragmentShader = ol.render.webgl.polygonreplay.defaultshader.fragment; - vertexShader = ol.render.webgl.polygonreplay.defaultshader.vertex; - var program = context.getProgram(fragmentShader, vertexShader); - - // get the locations - var locations; - if (!this.defaultLocations_) { - // eslint-disable-next-line openlayers-internal/no-missing-requires - locations = new ol.render.webgl.polygonreplay.defaultshader.Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, ol.webgl.FLOAT, - false, 8, 0); - - return locations; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_position); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - //Save GL parameters. - var tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC)); - var tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK)); - - if (!hitDetection) { - gl.enable(gl.DEPTH_TEST); - gl.depthMask(true); - gl.depthFunc(gl.NOTEQUAL); - } - - if (!ol.obj.isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_(gl, context, skippedFeaturesHash); - } else { - //Draw by style groups to minimize drawElements() calls. - var i, start, end, nextStyle; - end = this.startIndices[this.startIndices.length - 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - start = this.styleIndices_[i]; - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, nextStyle); - this.drawElements(gl, context, start, end); - end = start; - } - } - if (!hitDetection) { - gl.disable(gl.DEPTH_TEST); - gl.clear(gl.DEPTH_BUFFER_BIT); - //Restore GL parameters. - gl.depthMask(tmpDepthMask); - gl.depthFunc(tmpDepthFunc); - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; - featureIndex = this.startIndices.length - 2; - end = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, nextStyle); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || ol.extent.intersects( - /** @type {Array<number>} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - var result = featureCallback(feature); - - if (result) { - return result; - } - - } - featureIndex--; - end = start; - } - } - return undefined; - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {ol.webgl.Context} context Context. - * @param {Object} skippedFeaturesHash Ids of features to skip. - */ - ol.render.webgl.PolygonReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) { - var i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; - featureIndex = this.startIndices.length - 2; - end = start = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, nextStyle); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - featureStart = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = ol.getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid]) { - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - } - end = featureStart; - } - featureIndex--; - start = featureStart; - } - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - } - start = end = groupStart; - } - }; - - - /** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.<number>} color Color. - */ - ol.render.webgl.PolygonReplay.prototype.setFillStyle_ = function(gl, color) { - gl.uniform4fv(this.defaultLocations_.u_color, color); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.PolygonReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - var fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0]; - if (!(fillStyleColor instanceof CanvasGradient) && - !(fillStyleColor instanceof CanvasPattern)) { - fillStyleColor = ol.color.asArray(fillStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || ol.render.webgl.defaultFillStyle; - } else { - fillStyleColor = ol.render.webgl.defaultFillStyle; - } - if (!this.state_.fillColor || !ol.array.equals(fillStyleColor, this.state_.fillColor)) { - this.state_.fillColor = fillStyleColor; - this.state_.changed = true; - this.styles_.push(fillStyleColor); - } - //Provide a null stroke style, if no strokeStyle is provided. Required for the draw interaction to work. - if (strokeStyle) { - this.lineStringReplay.setFillStrokeStyle(null, strokeStyle); - } else { - var nullStrokeStyle = new ol.style.Stroke({ - color: [0, 0, 0, 0], - lineWidth: 0 - }); - this.lineStringReplay.setFillStrokeStyle(null, nullStrokeStyle); - } - }; - -} - -goog.provide('ol.render.webgl.TextReplay'); - -goog.require('ol'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @abstract - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @struct - */ - ol.render.webgl.TextReplay = function(tolerance, maxExtent) {}; - - /** - * @param {ol.style.Text} textStyle Text style. - */ - ol.render.webgl.TextReplay.prototype.setTextStyle = function(textStyle) {}; - - /** - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.TextReplay.prototype.replay = function(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent) { - return undefined; - }; - - /** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ - ol.render.webgl.TextReplay.prototype.drawText = function(flatCoordinates, offset, - end, stride, geometry, feature) {}; - - /** - * @abstract - * @param {ol.webgl.Context} context Context. - */ - ol.render.webgl.TextReplay.prototype.finish = function(context) {}; - - /** - * @param {ol.webgl.Context} context WebGL context. - * @return {function()} Delete resources function. - */ - ol.render.webgl.TextReplay.prototype.getDeleteResourcesFunction = function(context) { - return ol.nullFunction; - }; - -} - -goog.provide('ol.render.webgl.ReplayGroup'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.render.replay'); -goog.require('ol.render.ReplayGroup'); -goog.require('ol.render.webgl.CircleReplay'); -goog.require('ol.render.webgl.ImageReplay'); -goog.require('ol.render.webgl.LineStringReplay'); -goog.require('ol.render.webgl.PolygonReplay'); -goog.require('ol.render.webgl.TextReplay'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.render.ReplayGroup} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @param {number=} opt_renderBuffer Render buffer. - * @struct - */ - ol.render.webgl.ReplayGroup = function(tolerance, maxExtent, opt_renderBuffer) { - ol.render.ReplayGroup.call(this); - - /** - * @type {ol.Extent} - * @private - */ - this.maxExtent_ = maxExtent; - - /** - * @type {number} - * @private - */ - this.tolerance_ = tolerance; - - /** - * @type {number|undefined} - * @private - */ - this.renderBuffer_ = opt_renderBuffer; - - /** - * @private - * @type {!Object.<string, - * Object.<ol.render.ReplayType, ol.render.webgl.Replay>>} - */ - this.replaysByZIndex_ = {}; - - }; - ol.inherits(ol.render.webgl.ReplayGroup, ol.render.ReplayGroup); - - - /** - * @param {ol.webgl.Context} context WebGL context. - * @return {function()} Delete resources function. - */ - ol.render.webgl.ReplayGroup.prototype.getDeleteResourcesFunction = function(context) { - var functions = []; - var zKey; - for (zKey in this.replaysByZIndex_) { - var replays = this.replaysByZIndex_[zKey]; - var replayKey; - for (replayKey in replays) { - functions.push( - replays[replayKey].getDeleteResourcesFunction(context)); - } - } - return function() { - var length = functions.length; - var result; - for (var i = 0; i < length; i++) { - result = functions[i].apply(this, arguments); - } - return result; - }; - }; - - - /** - * @param {ol.webgl.Context} context Context. - */ - ol.render.webgl.ReplayGroup.prototype.finish = function(context) { - var zKey; - for (zKey in this.replaysByZIndex_) { - var replays = this.replaysByZIndex_[zKey]; - var replayKey; - for (replayKey in replays) { - replays[replayKey].finish(context); - } - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ReplayGroup.prototype.getReplay = function(zIndex, replayType) { - var zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; - var replays = this.replaysByZIndex_[zIndexKey]; - if (replays === undefined) { - replays = {}; - this.replaysByZIndex_[zIndexKey] = replays; - } - var replay = replays[replayType]; - if (replay === undefined) { - /** - * @type {Function} - */ - var Constructor = ol.render.webgl.ReplayGroup.BATCH_CONSTRUCTORS_[replayType]; - replay = new Constructor(this.tolerance_, this.maxExtent_); - replays[replayType] = replay; - } - return replay; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.ReplayGroup.prototype.isEmpty = function() { - return ol.obj.isEmpty(this.replaysByZIndex_); - }; - - - /** - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - */ - ol.render.webgl.ReplayGroup.prototype.replay = function(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash) { - /** @type {Array.<number>} */ - var zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(ol.array.numberSafeCompareFunction); - - var i, ii, j, jj, replays, replay; - for (i = 0, ii = zs.length; i < ii; ++i) { - replays = this.replaysByZIndex_[zs[i].toString()]; - for (j = 0, jj = ol.render.replay.ORDER.length; j < jj; ++j) { - replay = replays[ol.render.replay.ORDER[j]]; - if (replay !== undefined) { - replay.replay(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - undefined, false); - } - } - } - }; - - - /** - * @private - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.ReplayGroup.prototype.replayHitDetection_ = function(context, - center, resolution, rotation, size, pixelRatio, opacity, - skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { - /** @type {Array.<number>} */ - var zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(function(a, b) { - return b - a; - }); - - var i, ii, j, replays, replay, result; - for (i = 0, ii = zs.length; i < ii; ++i) { - replays = this.replaysByZIndex_[zs[i].toString()]; - for (j = ol.render.replay.ORDER.length - 1; j >= 0; --j) { - replay = replays[ol.render.replay.ORDER[j]]; - if (replay !== undefined) { - result = replay.replay(context, - center, resolution, rotation, size, pixelRatio, opacity, - skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent); - if (result) { - return result; - } - } - } - } - return undefined; - }; - - - /** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} callback Feature callback. - * @return {T|undefined} Callback result. - * @template T - */ - ol.render.webgl.ReplayGroup.prototype.forEachFeatureAtCoordinate = function( - coordinate, context, center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - callback) { - var gl = context.getGL(); - gl.bindFramebuffer( - gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); - - - /** - * @type {ol.Extent} - */ - var hitExtent; - if (this.renderBuffer_ !== undefined) { - // build an extent around the coordinate, so that only features that - // intersect this extent are checked - hitExtent = ol.extent.buffer( - ol.extent.createOrUpdateFromCoordinate(coordinate), - resolution * this.renderBuffer_); - } - - return this.replayHitDetection_(context, - coordinate, resolution, rotation, ol.render.webgl.ReplayGroup.HIT_DETECTION_SIZE_, - pixelRatio, opacity, skippedFeaturesHash, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - var imageData = new Uint8Array(4); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); - - if (imageData[3] > 0) { - var result = callback(feature); - if (result) { - return result; - } - } - }, true, hitExtent); - }; - - - /** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @return {boolean} Is there a feature at the given coordinate? - */ - ol.render.webgl.ReplayGroup.prototype.hasFeatureAtCoordinate = function( - coordinate, context, center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash) { - var gl = context.getGL(); - gl.bindFramebuffer( - gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); - - var hasFeature = this.replayHitDetection_(context, - coordinate, resolution, rotation, ol.render.webgl.ReplayGroup.HIT_DETECTION_SIZE_, - pixelRatio, opacity, skippedFeaturesHash, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {boolean} Is there a feature? - */ - function(feature) { - var imageData = new Uint8Array(4); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); - return imageData[3] > 0; - }, false); - - return hasFeature !== undefined; - }; - - /** - * @const - * @private - * @type {Array.<number>} - */ - ol.render.webgl.ReplayGroup.HIT_DETECTION_SIZE_ = [1, 1]; - - /** - * @const - * @private - * @type {Object.<ol.render.ReplayType, - * function(new: ol.render.webgl.Replay, number, - * ol.Extent)>} - */ - ol.render.webgl.ReplayGroup.BATCH_CONSTRUCTORS_ = { - 'Circle': ol.render.webgl.CircleReplay, - 'Image': ol.render.webgl.ImageReplay, - 'LineString': ol.render.webgl.LineStringReplay, - 'Polygon': ol.render.webgl.PolygonReplay, - 'Text': ol.render.webgl.TextReplay - }; - -} - -goog.provide('ol.render.webgl.Immediate'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.render.ReplayType'); -goog.require('ol.render.VectorContext'); -goog.require('ol.render.webgl.ReplayGroup'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.render.VectorContext} - * @param {ol.webgl.Context} context Context. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {ol.Size} size Size. - * @param {ol.Extent} extent Extent. - * @param {number} pixelRatio Pixel ratio. - * @struct - */ - ol.render.webgl.Immediate = function(context, center, resolution, rotation, size, extent, pixelRatio) { - ol.render.VectorContext.call(this); - - /** - * @private - */ - this.context_ = context; - - /** - * @private - */ - this.center_ = center; - - /** - * @private - */ - this.extent_ = extent; - - /** - * @private - */ - this.pixelRatio_ = pixelRatio; - - /** - * @private - */ - this.size_ = size; - - /** - * @private - */ - this.rotation_ = rotation; - - /** - * @private - */ - this.resolution_ = resolution; - - /** - * @private - * @type {ol.style.Image} - */ - this.imageStyle_ = null; - - /** - * @private - * @type {ol.style.Fill} - */ - this.fillStyle_ = null; - - /** - * @private - * @type {ol.style.Stroke} - */ - this.strokeStyle_ = null; - - }; - ol.inherits(ol.render.webgl.Immediate, ol.render.VectorContext); - - - /** - * Set the rendering style. Note that since this is an immediate rendering API, - * any `zIndex` on the provided style will be ignored. - * - * @param {ol.style.Style} style The rendering style. - * @override - * @api - */ - ol.render.webgl.Immediate.prototype.setStyle = function(style) { - this.setFillStrokeStyle(style.getFill(), style.getStroke()); - this.setImageStyle(style.getImage()); - }; - - - /** - * Render a geometry into the canvas. Call - * {@link ol.render.webgl.Immediate#setStyle} first to set the rendering style. - * - * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render. - * @override - * @api - */ - ol.render.webgl.Immediate.prototype.drawGeometry = function(geometry) { - var type = geometry.getType(); - switch (type) { - case ol.geom.GeometryType.POINT: - this.drawPoint(/** @type {ol.geom.Point} */ (geometry), null); - break; - case ol.geom.GeometryType.LINE_STRING: - this.drawLineString(/** @type {ol.geom.LineString} */ (geometry), null); - break; - case ol.geom.GeometryType.POLYGON: - this.drawPolygon(/** @type {ol.geom.Polygon} */ (geometry), null); - break; - case ol.geom.GeometryType.MULTI_POINT: - this.drawMultiPoint(/** @type {ol.geom.MultiPoint} */ (geometry), null); - break; - case ol.geom.GeometryType.MULTI_LINE_STRING: - this.drawMultiLineString(/** @type {ol.geom.MultiLineString} */ (geometry), null); - break; - case ol.geom.GeometryType.MULTI_POLYGON: - this.drawMultiPolygon(/** @type {ol.geom.MultiPolygon} */ (geometry), null); - break; - case ol.geom.GeometryType.GEOMETRY_COLLECTION: - this.drawGeometryCollection(/** @type {ol.geom.GeometryCollection} */ (geometry), null); - break; - case ol.geom.GeometryType.CIRCLE: - this.drawCircle(/** @type {ol.geom.Circle} */ (geometry), null); - break; - default: - // pass - } - }; - - - /** - * @inheritDoc - * @api - */ - ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) { - var geometry = style.getGeometryFunction()(feature); - if (!geometry || - !ol.extent.intersects(this.extent_, geometry.getExtent())) { - return; - } - this.setStyle(style); - this.drawGeometry(geometry); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawGeometryCollection = function(geometry, data) { - var geometries = geometry.getGeometriesArray(); - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - this.drawGeometry(geometries[i]); - } - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawPoint = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.ImageReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.IMAGE)); - replay.setImageStyle(this.imageStyle_); - replay.drawPoint(geometry, data); - replay.finish(context); - // default colors - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawMultiPoint = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.ImageReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.IMAGE)); - replay.setImageStyle(this.imageStyle_); - replay.drawMultiPoint(geometry, data); - replay.finish(context); - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawLineString = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.LineStringReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.LINE_STRING)); - replay.setFillStrokeStyle(null, this.strokeStyle_); - replay.drawLineString(geometry, data); - replay.finish(context); - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawMultiLineString = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.LineStringReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.LINE_STRING)); - replay.setFillStrokeStyle(null, this.strokeStyle_); - replay.drawMultiLineString(geometry, data); - replay.finish(context); - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawPolygon = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.PolygonReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.POLYGON)); - replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); - replay.drawPolygon(geometry, data); - replay.finish(context); - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawMultiPolygon = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.PolygonReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.POLYGON)); - replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); - replay.drawMultiPolygon(geometry, data); - replay.finish(context); - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.drawCircle = function(geometry, data) { - var context = this.context_; - var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); - var replay = /** @type {ol.render.webgl.CircleReplay} */ ( - replayGroup.getReplay(0, ol.render.ReplayType.CIRCLE)); - replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); - replay.drawCircle(geometry, data); - replay.finish(context); - var opacity = 1; - var skippedFeatures = {}; - var featureCallback; - var oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) { - this.imageStyle_ = imageStyle; - }; - - - /** - * @inheritDoc - */ - ol.render.webgl.Immediate.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - this.fillStyle_ = fillStyle; - this.strokeStyle_ = strokeStyle; - }; - -} - -goog.provide('ol.structs.LRUCache'); - -goog.require('ol.asserts'); - - -/** - * Implements a Least-Recently-Used cache where the keys do not conflict with - * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring - * items from the cache is the responsibility of the user. - * @constructor - * @struct - * @template T - */ -ol.structs.LRUCache = function() { - - /** - * @private - * @type {number} - */ - this.count_ = 0; - - /** - * @private - * @type {!Object.<string, ol.LRUCacheEntry>} - */ - this.entries_ = {}; - - /** - * @private - * @type {?ol.LRUCacheEntry} - */ - this.oldest_ = null; - - /** - * @private - * @type {?ol.LRUCacheEntry} - */ - this.newest_ = null; - -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.structs.LRUCache.prototype.clear = function() { - this.count_ = 0; - this.entries_ = {}; - this.oldest_ = null; - this.newest_ = null; -}; - - -/** - * @param {string} key Key. - * @return {boolean} Contains key. - */ -ol.structs.LRUCache.prototype.containsKey = function(key) { - return this.entries_.hasOwnProperty(key); -}; - - -/** - * @param {function(this: S, T, string, ol.structs.LRUCache): ?} f The function - * to call for every entry from the oldest to the newer. This function takes - * 3 arguments (the entry value, the entry key and the LRUCache object). - * The return value is ignored. - * @param {S=} opt_this The object to use as `this` in `f`. - * @template S - */ -ol.structs.LRUCache.prototype.forEach = function(f, opt_this) { - var entry = this.oldest_; - while (entry) { - f.call(opt_this, entry.value_, entry.key_, this); - entry = entry.newer; - } -}; - - -/** - * @param {string} key Key. - * @return {T} Value. - */ -ol.structs.LRUCache.prototype.get = function(key) { - var entry = this.entries_[key]; - ol.asserts.assert(entry !== undefined, - 15); // Tried to get a value for a key that does not exist in the cache - if (entry === this.newest_) { - return entry.value_; - } else if (entry === this.oldest_) { - this.oldest_ = /** @type {ol.LRUCacheEntry} */ (this.oldest_.newer); - this.oldest_.older = null; - } else { - entry.newer.older = entry.older; - entry.older.newer = entry.newer; - } - entry.newer = null; - entry.older = this.newest_; - this.newest_.newer = entry; - this.newest_ = entry; - return entry.value_; -}; - - -/** - * @return {number} Count. - */ -ol.structs.LRUCache.prototype.getCount = function() { - return this.count_; -}; - - -/** - * @return {Array.<string>} Keys. - */ -ol.structs.LRUCache.prototype.getKeys = function() { - var keys = new Array(this.count_); - var i = 0; - var entry; - for (entry = this.newest_; entry; entry = entry.older) { - keys[i++] = entry.key_; - } - return keys; -}; - - -/** - * @return {Array.<T>} Values. - */ -ol.structs.LRUCache.prototype.getValues = function() { - var values = new Array(this.count_); - var i = 0; - var entry; - for (entry = this.newest_; entry; entry = entry.older) { - values[i++] = entry.value_; - } - return values; -}; - - -/** - * @return {T} Last value. - */ -ol.structs.LRUCache.prototype.peekLast = function() { - return this.oldest_.value_; -}; - - -/** - * @return {string} Last key. - */ -ol.structs.LRUCache.prototype.peekLastKey = function() { - return this.oldest_.key_; -}; - - -/** - * @return {T} value Value. - */ -ol.structs.LRUCache.prototype.pop = function() { - var entry = this.oldest_; - delete this.entries_[entry.key_]; - if (entry.newer) { - entry.newer.older = null; - } - this.oldest_ = /** @type {ol.LRUCacheEntry} */ (entry.newer); - if (!this.oldest_) { - this.newest_ = null; - } - --this.count_; - return entry.value_; -}; - - -/** - * @param {string} key Key. - * @param {T} value Value. - */ -ol.structs.LRUCache.prototype.replace = function(key, value) { - this.get(key); // update `newest_` - this.entries_[key].value_ = value; -}; - - -/** - * @param {string} key Key. - * @param {T} value Value. - */ -ol.structs.LRUCache.prototype.set = function(key, value) { - ol.asserts.assert(!(key in this.entries_), - 16); // Tried to set a value for a key that is used already - var entry = /** @type {ol.LRUCacheEntry} */ ({ - key_: key, - newer: null, - older: this.newest_, - value_: value - }); - if (!this.newest_) { - this.oldest_ = entry; - } else { - this.newest_.newer = entry; - } - this.newest_ = entry; - this.entries_[key] = entry; - ++this.count_; -}; - -// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE) - -goog.provide('ol.renderer.webgl.Map'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.css'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.layer.Layer'); -goog.require('ol.render.Event'); -goog.require('ol.render.EventType'); -goog.require('ol.render.webgl.Immediate'); -goog.require('ol.renderer.Map'); -goog.require('ol.renderer.Type'); -goog.require('ol.source.State'); -goog.require('ol.structs.LRUCache'); -goog.require('ol.structs.PriorityQueue'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Context'); -goog.require('ol.webgl.ContextEventType'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.renderer.Map} - * @param {Element} container Container. - * @param {ol.Map} map Map. - */ - ol.renderer.webgl.Map = function(container, map) { - ol.renderer.Map.call(this, container, map); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = /** @type {HTMLCanvasElement} */ - (document.createElement('CANVAS')); - this.canvas_.style.width = '100%'; - this.canvas_.style.height = '100%'; - this.canvas_.style.display = 'block'; - this.canvas_.className = ol.css.CLASS_UNSELECTABLE; - container.insertBefore(this.canvas_, container.childNodes[0] || null); - - /** - * @private - * @type {number} - */ - this.clipTileCanvasWidth_ = 0; - - /** - * @private - * @type {number} - */ - this.clipTileCanvasHeight_ = 0; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.clipTileContext_ = ol.dom.createCanvasContext2D(); - - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = true; - - /** - * @private - * @type {WebGLRenderingContext} - */ - this.gl_ = ol.webgl.getContext(this.canvas_, { - antialias: true, - depth: true, - failIfMajorPerformanceCaveat: true, - preserveDrawingBuffer: false, - stencil: true - }); - - /** - * @private - * @type {ol.webgl.Context} - */ - this.context_ = new ol.webgl.Context(this.canvas_, this.gl_); - - ol.events.listen(this.canvas_, ol.webgl.ContextEventType.LOST, - this.handleWebGLContextLost, this); - ol.events.listen(this.canvas_, ol.webgl.ContextEventType.RESTORED, - this.handleWebGLContextRestored, this); - - /** - * @private - * @type {ol.structs.LRUCache.<ol.WebglTextureCacheEntry|null>} - */ - this.textureCache_ = new ol.structs.LRUCache(); - - /** - * @private - * @type {ol.Coordinate} - */ - this.focus_ = null; - - /** - * @private - * @type {ol.structs.PriorityQueue.<Array>} - */ - this.tileTextureQueue_ = new ol.structs.PriorityQueue( - /** - * @param {Array.<*>} element Element. - * @return {number} Priority. - * @this {ol.renderer.webgl.Map} - */ - (function(element) { - var tileCenter = /** @type {ol.Coordinate} */ (element[1]); - var tileResolution = /** @type {number} */ (element[2]); - var deltaX = tileCenter[0] - this.focus_[0]; - var deltaY = tileCenter[1] - this.focus_[1]; - return 65536 * Math.log(tileResolution) + - Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; - }).bind(this), - /** - * @param {Array.<*>} element Element. - * @return {string} Key. - */ - function(element) { - return /** @type {ol.Tile} */ (element[0]).getKey(); - }); - - - /** - * @param {ol.Map} map Map. - * @param {?olx.FrameState} frameState Frame state. - * @return {boolean} false. - * @this {ol.renderer.webgl.Map} - */ - this.loadNextTileTexture_ = - function(map, frameState) { - if (!this.tileTextureQueue_.isEmpty()) { - this.tileTextureQueue_.reprioritize(); - var element = this.tileTextureQueue_.dequeue(); - var tile = /** @type {ol.Tile} */ (element[0]); - var tileSize = /** @type {ol.Size} */ (element[3]); - var tileGutter = /** @type {number} */ (element[4]); - this.bindTileTexture( - tile, tileSize, tileGutter, ol.webgl.LINEAR, ol.webgl.LINEAR); - } - return false; - }.bind(this); - - - /** - * @private - * @type {number} - */ - this.textureCacheFrameMarkerCount_ = 0; - - this.initializeGL_(); - }; - ol.inherits(ol.renderer.webgl.Map, ol.renderer.Map); - - - /** - * @param {ol.Tile} tile Tile. - * @param {ol.Size} tileSize Tile size. - * @param {number} tileGutter Tile gutter. - * @param {number} magFilter Mag filter. - * @param {number} minFilter Min filter. - */ - ol.renderer.webgl.Map.prototype.bindTileTexture = function(tile, tileSize, tileGutter, magFilter, minFilter) { - var gl = this.getGL(); - var tileKey = tile.getKey(); - if (this.textureCache_.containsKey(tileKey)) { - var textureCacheEntry = this.textureCache_.get(tileKey); - gl.bindTexture(ol.webgl.TEXTURE_2D, textureCacheEntry.texture); - if (textureCacheEntry.magFilter != magFilter) { - gl.texParameteri( - ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MAG_FILTER, magFilter); - textureCacheEntry.magFilter = magFilter; - } - if (textureCacheEntry.minFilter != minFilter) { - gl.texParameteri( - ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MIN_FILTER, minFilter); - textureCacheEntry.minFilter = minFilter; - } - } else { - var texture = gl.createTexture(); - gl.bindTexture(ol.webgl.TEXTURE_2D, texture); - if (tileGutter > 0) { - var clipTileCanvas = this.clipTileContext_.canvas; - var clipTileContext = this.clipTileContext_; - if (this.clipTileCanvasWidth_ !== tileSize[0] || - this.clipTileCanvasHeight_ !== tileSize[1]) { - clipTileCanvas.width = tileSize[0]; - clipTileCanvas.height = tileSize[1]; - this.clipTileCanvasWidth_ = tileSize[0]; - this.clipTileCanvasHeight_ = tileSize[1]; - } else { - clipTileContext.clearRect(0, 0, tileSize[0], tileSize[1]); - } - clipTileContext.drawImage(tile.getImage(), tileGutter, tileGutter, - tileSize[0], tileSize[1], 0, 0, tileSize[0], tileSize[1]); - gl.texImage2D(ol.webgl.TEXTURE_2D, 0, - ol.webgl.RGBA, ol.webgl.RGBA, - ol.webgl.UNSIGNED_BYTE, clipTileCanvas); - } else { - gl.texImage2D(ol.webgl.TEXTURE_2D, 0, - ol.webgl.RGBA, ol.webgl.RGBA, - ol.webgl.UNSIGNED_BYTE, tile.getImage()); - } - gl.texParameteri( - ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri( - ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_S, - ol.webgl.CLAMP_TO_EDGE); - gl.texParameteri(ol.webgl.TEXTURE_2D, ol.webgl.TEXTURE_WRAP_T, - ol.webgl.CLAMP_TO_EDGE); - this.textureCache_.set(tileKey, { - texture: texture, - magFilter: magFilter, - minFilter: minFilter - }); - } - }; - - - /** - * @param {ol.render.EventType} type Event type. - * @param {olx.FrameState} frameState Frame state. - * @private - */ - ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ = function(type, frameState) { - var map = this.getMap(); - if (map.hasListener(type)) { - var context = this.context_; - - var extent = frameState.extent; - var size = frameState.size; - var viewState = frameState.viewState; - var pixelRatio = frameState.pixelRatio; - - var resolution = viewState.resolution; - var center = viewState.center; - var rotation = viewState.rotation; - - var vectorContext = new ol.render.webgl.Immediate(context, - center, resolution, rotation, size, extent, pixelRatio); - var composeEvent = new ol.render.Event(type, vectorContext, - frameState, null, context); - map.dispatchEvent(composeEvent); - } - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.Map.prototype.disposeInternal = function() { - var gl = this.getGL(); - if (!gl.isContextLost()) { - this.textureCache_.forEach( - /** - * @param {?ol.WebglTextureCacheEntry} textureCacheEntry - * Texture cache entry. - */ - function(textureCacheEntry) { - if (textureCacheEntry) { - gl.deleteTexture(textureCacheEntry.texture); - } - }); - } - this.context_.dispose(); - ol.renderer.Map.prototype.disposeInternal.call(this); - }; - - - /** - * @param {ol.Map} map Map. - * @param {olx.FrameState} frameState Frame state. - * @private - */ - ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) { - var gl = this.getGL(); - var textureCacheEntry; - while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > - ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { - textureCacheEntry = this.textureCache_.peekLast(); - if (!textureCacheEntry) { - if (+this.textureCache_.peekLastKey() == frameState.index) { - break; - } else { - --this.textureCacheFrameMarkerCount_; - } - } else { - gl.deleteTexture(textureCacheEntry.texture); - } - this.textureCache_.pop(); - } - }; - - - /** - * @return {ol.webgl.Context} The context. - */ - ol.renderer.webgl.Map.prototype.getContext = function() { - return this.context_; - }; - - - /** - * @return {WebGLRenderingContext} GL. - */ - ol.renderer.webgl.Map.prototype.getGL = function() { - return this.gl_; - }; - - - /** - * @return {ol.structs.PriorityQueue.<Array>} Tile texture queue. - */ - ol.renderer.webgl.Map.prototype.getTileTextureQueue = function() { - return this.tileTextureQueue_; - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.Map.prototype.getType = function() { - return ol.renderer.Type.WEBGL; - }; - - - /** - * @param {ol.events.Event} event Event. - * @protected - */ - ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) { - event.preventDefault(); - this.textureCache_.clear(); - this.textureCacheFrameMarkerCount_ = 0; - - var renderers = this.getLayerRenderers(); - for (var id in renderers) { - var renderer = /** @type {ol.renderer.webgl.Layer} */ (renderers[id]); - renderer.handleWebGLContextLost(); - } - }; - - - /** - * @protected - */ - ol.renderer.webgl.Map.prototype.handleWebGLContextRestored = function() { - this.initializeGL_(); - this.getMap().render(); - }; - - - /** - * @private - */ - ol.renderer.webgl.Map.prototype.initializeGL_ = function() { - var gl = this.gl_; - gl.activeTexture(ol.webgl.TEXTURE0); - gl.blendFuncSeparate( - ol.webgl.SRC_ALPHA, ol.webgl.ONE_MINUS_SRC_ALPHA, - ol.webgl.ONE, ol.webgl.ONE_MINUS_SRC_ALPHA); - gl.disable(ol.webgl.CULL_FACE); - gl.disable(ol.webgl.DEPTH_TEST); - gl.disable(ol.webgl.SCISSOR_TEST); - gl.disable(ol.webgl.STENCIL_TEST); - }; - - - /** - * @param {ol.Tile} tile Tile. - * @return {boolean} Is tile texture loaded. - */ - ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) { - return this.textureCache_.containsKey(tile.getKey()); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { - - var context = this.getContext(); - var gl = this.getGL(); - - if (gl.isContextLost()) { - return false; - } - - if (!frameState) { - if (this.renderedVisible_) { - this.canvas_.style.display = 'none'; - this.renderedVisible_ = false; - } - return false; - } - - this.focus_ = frameState.focus; - - this.textureCache_.set((-frameState.index).toString(), null); - ++this.textureCacheFrameMarkerCount_; - - this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState); - - /** @type {Array.<ol.LayerState>} */ - var layerStatesToDraw = []; - var layerStatesArray = frameState.layerStatesArray; - ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); - - var viewResolution = frameState.viewState.resolution; - var i, ii, layerRenderer, layerState; - for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerState = layerStatesArray[i]; - if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && - layerState.sourceState == ol.source.State.READY) { - layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layerState.layer)); - if (layerRenderer.prepareFrame(frameState, layerState, context)) { - layerStatesToDraw.push(layerState); - } - } - } - - var width = frameState.size[0] * frameState.pixelRatio; - var height = frameState.size[1] * frameState.pixelRatio; - if (this.canvas_.width != width || this.canvas_.height != height) { - this.canvas_.width = width; - this.canvas_.height = height; - } - - gl.bindFramebuffer(ol.webgl.FRAMEBUFFER, null); - - gl.clearColor(0, 0, 0, 0); - gl.clear(ol.webgl.COLOR_BUFFER_BIT); - gl.enable(ol.webgl.BLEND); - gl.viewport(0, 0, this.canvas_.width, this.canvas_.height); - - for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) { - layerState = layerStatesToDraw[i]; - layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layerState.layer)); - layerRenderer.composeFrame(frameState, layerState, context); - } - - if (!this.renderedVisible_) { - this.canvas_.style.display = ''; - this.renderedVisible_ = true; - } - - this.calculateMatrices2D(frameState); - - if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > - ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { - frameState.postRenderFunctions.push( - /** @type {ol.PostRenderFunction} */ (this.expireCache_.bind(this)) - ); - } - - if (!this.tileTextureQueue_.isEmpty()) { - frameState.postRenderFunctions.push(this.loadNextTileTexture_); - frameState.animate = true; - } - - this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState); - - this.scheduleRemoveUnusedLayerRenderers(frameState); - this.scheduleExpireIconCache(frameState); - - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) { - var result; - - if (this.getGL().isContextLost()) { - return false; - } - - var viewState = frameState.viewState; - - var layerStates = frameState.layerStatesArray; - var numLayers = layerStates.length; - var i; - for (i = numLayers - 1; i >= 0; --i) { - var layerState = layerStates[i]; - var layer = layerState.layer; - if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && - layerFilter.call(thisArg2, layer)) { - var layerRenderer = this.getLayerRenderer(layer); - result = layerRenderer.forEachFeatureAtCoordinate( - coordinate, frameState, hitTolerance, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.Map.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, layerFilter, thisArg) { - var hasFeature = false; - - if (this.getGL().isContextLost()) { - return false; - } - - var viewState = frameState.viewState; - - var layerStates = frameState.layerStatesArray; - var numLayers = layerStates.length; - var i; - for (i = numLayers - 1; i >= 0; --i) { - var layerState = layerStates[i]; - var layer = layerState.layer; - if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && - layerFilter.call(thisArg, layer)) { - var layerRenderer = this.getLayerRenderer(layer); - hasFeature = - layerRenderer.hasFeatureAtCoordinate(coordinate, frameState); - if (hasFeature) { - return true; - } - } - } - return hasFeature; - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, - layerFilter, thisArg2) { - if (this.getGL().isContextLost()) { - return false; - } - - var viewState = frameState.viewState; - var result; - - var layerStates = frameState.layerStatesArray; - var numLayers = layerStates.length; - var i; - for (i = numLayers - 1; i >= 0; --i) { - var layerState = layerStates[i]; - var layer = layerState.layer; - if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && - layerFilter.call(thisArg, layer)) { - var layerRenderer = /** @type {ol.renderer.webgl.Layer} */ (this.getLayerRenderer(layer)); - result = layerRenderer.forEachLayerAtPixel( - pixel, frameState, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; - }; - -} - -// FIXME recheck layer/map projection compatibility when projection changes -// FIXME layer renderers should skip when they can't reproject -// FIXME add tilt and height? - -goog.provide('ol.Map'); - -goog.require('ol'); -goog.require('ol.Collection'); -goog.require('ol.CollectionEventType'); -goog.require('ol.MapBrowserEvent'); -goog.require('ol.MapBrowserEventHandler'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.MapEvent'); -goog.require('ol.MapEventType'); -goog.require('ol.MapProperty'); -goog.require('ol.Object'); -goog.require('ol.ObjectEventType'); -goog.require('ol.TileQueue'); -goog.require('ol.View'); -goog.require('ol.ViewHint'); -goog.require('ol.asserts'); -goog.require('ol.control'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.has'); -goog.require('ol.interaction'); -goog.require('ol.layer.Group'); -goog.require('ol.obj'); -goog.require('ol.renderer.Map'); -goog.require('ol.renderer.Type'); -goog.require('ol.renderer.canvas.Map'); -goog.require('ol.renderer.webgl.Map'); -goog.require('ol.size'); -goog.require('ol.structs.PriorityQueue'); -goog.require('ol.transform'); - - -/** - * @const - * @type {string} - */ -ol.OL_URL = 'https://openlayers.org/'; - - -/** - * @const - * @type {string} - */ -ol.OL_LOGO_URL = 'data:image/png;base64,' + - 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBI' + - 'WXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAA' + - 'AhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszW' + - 'WMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvY' + - 'asvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvX' + - 'H1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1Vk' + - 'bMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW' + - '2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLP' + - 'VcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqT' + - 'acrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaar' + - 'ldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+Hi' + - 'zeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDn' + - 'BAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSF' + - 'hYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJ' + - 'REFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxC' + - 'Brb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe' + - '0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8' + - 'a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7a' + - 'hgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCn' + - 'B3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDg' + - 'q82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC'; - - -/** - * @type {Array.<ol.renderer.Type>} - * @const - */ -ol.DEFAULT_RENDERER_TYPES = [ - ol.renderer.Type.CANVAS, - ol.renderer.Type.WEBGL -]; - - -/** - * @classdesc - * The map is the core component of OpenLayers. For a map to render, a view, - * one or more layers, and a target container are needed: - * - * var map = new ol.Map({ - * view: new ol.View({ - * center: [0, 0], - * zoom: 1 - * }), - * layers: [ - * new ol.layer.Tile({ - * source: new ol.source.OSM() - * }) - * ], - * target: 'map' - * }); - * - * The above snippet creates a map using a {@link ol.layer.Tile} to display - * {@link ol.source.OSM} OSM data and render it to a DOM element with the - * id `map`. - * - * The constructor places a viewport container (with CSS class name - * `ol-viewport`) in the target element (see `getViewport()`), and then two - * further elements within the viewport: one with CSS class name - * `ol-overlaycontainer-stopevent` for controls and some overlays, and one with - * CSS class name `ol-overlaycontainer` for other overlays (see the `stopEvent` - * option of {@link ol.Overlay} for the difference). The map itself is placed in - * a further element within the viewport. - * - * Layers are stored as a `ol.Collection` in layerGroups. A top-level group is - * provided by the library. This is what is accessed by `getLayerGroup` and - * `setLayerGroup`. Layers entered in the options are added to this group, and - * `addLayer` and `removeLayer` change the layer collection in the group. - * `getLayers` is a convenience function for `getLayerGroup().getLayers()`. - * Note that `ol.layer.Group` is a subclass of `ol.layer.Base`, so layers - * entered in the options or added with `addLayer` can be groups, which can - * contain further groups, and so on. - * - * @constructor - * @extends {ol.Object} - * @param {olx.MapOptions} options Map options. - * @fires ol.MapBrowserEvent - * @fires ol.MapEvent - * @fires ol.render.Event#postcompose - * @fires ol.render.Event#precompose - * @api - */ -ol.Map = function(options) { - - ol.Object.call(this); - - var optionsInternal = ol.Map.createOptionsInternal(options); - - /** - * @type {boolean} - * @private - */ - this.loadTilesWhileAnimating_ = - options.loadTilesWhileAnimating !== undefined ? - options.loadTilesWhileAnimating : false; - - /** - * @type {boolean} - * @private - */ - this.loadTilesWhileInteracting_ = - options.loadTilesWhileInteracting !== undefined ? - options.loadTilesWhileInteracting : false; - - /** - * @private - * @type {number} - */ - this.pixelRatio_ = options.pixelRatio !== undefined ? - options.pixelRatio : ol.has.DEVICE_PIXEL_RATIO; - - /** - * @private - * @type {Object.<string, string>} - */ - this.logos_ = optionsInternal.logos; - - /** - * @private - * @type {number|undefined} - */ - this.animationDelayKey_; - - /** - * @private - */ - this.animationDelay_ = function() { - this.animationDelayKey_ = undefined; - this.renderFrame_.call(this, Date.now()); - }.bind(this); - - /** - * @private - * @type {ol.Transform} - */ - this.coordinateToPixelTransform_ = ol.transform.create(); - - /** - * @private - * @type {ol.Transform} - */ - this.pixelToCoordinateTransform_ = ol.transform.create(); - - /** - * @private - * @type {number} - */ - this.frameIndex_ = 0; - - /** - * @private - * @type {?olx.FrameState} - */ - this.frameState_ = null; - - /** - * The extent at the previous 'moveend' event. - * @private - * @type {ol.Extent} - */ - this.previousExtent_ = null; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.viewPropertyListenerKey_ = null; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.viewChangeListenerKey_ = null; - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.layerGroupPropertyListenerKeys_ = null; - - /** - * @private - * @type {Element} - */ - this.viewport_ = document.createElement('DIV'); - this.viewport_.className = 'ol-viewport' + (ol.has.TOUCH ? ' ol-touch' : ''); - this.viewport_.style.position = 'relative'; - this.viewport_.style.overflow = 'hidden'; - this.viewport_.style.width = '100%'; - this.viewport_.style.height = '100%'; - // prevent page zoom on IE >= 10 browsers - this.viewport_.style.msTouchAction = 'none'; - this.viewport_.style.touchAction = 'none'; - - /** - * @private - * @type {!Element} - */ - this.overlayContainer_ = document.createElement('DIV'); - this.overlayContainer_.className = 'ol-overlaycontainer'; - this.viewport_.appendChild(this.overlayContainer_); - - /** - * @private - * @type {!Element} - */ - this.overlayContainerStopEvent_ = document.createElement('DIV'); - this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent'; - var overlayEvents = [ - ol.events.EventType.CLICK, - ol.events.EventType.DBLCLICK, - ol.events.EventType.MOUSEDOWN, - ol.events.EventType.TOUCHSTART, - ol.events.EventType.MSPOINTERDOWN, - ol.MapBrowserEventType.POINTERDOWN, - ol.events.EventType.MOUSEWHEEL, - ol.events.EventType.WHEEL - ]; - for (var i = 0, ii = overlayEvents.length; i < ii; ++i) { - ol.events.listen(this.overlayContainerStopEvent_, overlayEvents[i], - ol.events.Event.stopPropagation); - } - this.viewport_.appendChild(this.overlayContainerStopEvent_); - - /** - * @private - * @type {ol.MapBrowserEventHandler} - */ - this.mapBrowserEventHandler_ = new ol.MapBrowserEventHandler(this, options.moveTolerance); - for (var key in ol.MapBrowserEventType) { - ol.events.listen(this.mapBrowserEventHandler_, ol.MapBrowserEventType[key], - this.handleMapBrowserEvent, this); - } - - /** - * @private - * @type {Element|Document} - */ - this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget; - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.keyHandlerKeys_ = null; - - ol.events.listen(this.viewport_, ol.events.EventType.WHEEL, - this.handleBrowserEvent, this); - ol.events.listen(this.viewport_, ol.events.EventType.MOUSEWHEEL, - this.handleBrowserEvent, this); - - /** - * @type {ol.Collection.<ol.control.Control>} - * @private - */ - this.controls_ = optionsInternal.controls; - - /** - * @type {ol.Collection.<ol.interaction.Interaction>} - * @private - */ - this.interactions_ = optionsInternal.interactions; - - /** - * @type {ol.Collection.<ol.Overlay>} - * @private - */ - this.overlays_ = optionsInternal.overlays; - - /** - * A lookup of overlays by id. - * @private - * @type {Object.<string, ol.Overlay>} - */ - this.overlayIdIndex_ = {}; - - /** - * @type {ol.renderer.Map} - * @private - */ - this.renderer_ = new /** @type {Function} */ (optionsInternal.rendererConstructor)(this.viewport_, this); - - /** - * @type {function(Event)|undefined} - * @private - */ - this.handleResize_; - - /** - * @private - * @type {ol.Coordinate} - */ - this.focus_ = null; - - /** - * @private - * @type {Array.<ol.PostRenderFunction>} - */ - this.postRenderFunctions_ = []; - - /** - * @private - * @type {ol.TileQueue} - */ - this.tileQueue_ = new ol.TileQueue( - this.getTilePriority.bind(this), - this.handleTileChange_.bind(this)); - - /** - * Uids of features to skip at rendering time. - * @type {Object.<string, boolean>} - * @private - */ - this.skippedFeatureUids_ = {}; - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.MapProperty.LAYERGROUP), - this.handleLayerGroupChanged_, this); - ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.VIEW), - this.handleViewChanged_, this); - ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.SIZE), - this.handleSizeChanged_, this); - ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.TARGET), - this.handleTargetChanged_, this); - - // setProperties will trigger the rendering of the map if the map - // is "defined" already. - this.setProperties(optionsInternal.values); - - this.controls_.forEach( - /** - * @param {ol.control.Control} control Control. - * @this {ol.Map} - */ - function(control) { - control.setMap(this); - }, this); - - ol.events.listen(this.controls_, ol.CollectionEventType.ADD, - /** - * @param {ol.Collection.Event} event Collection event. - */ - function(event) { - event.element.setMap(this); - }, this); - - ol.events.listen(this.controls_, ol.CollectionEventType.REMOVE, - /** - * @param {ol.Collection.Event} event Collection event. - */ - function(event) { - event.element.setMap(null); - }, this); - - this.interactions_.forEach( - /** - * @param {ol.interaction.Interaction} interaction Interaction. - * @this {ol.Map} - */ - function(interaction) { - interaction.setMap(this); - }, this); - - ol.events.listen(this.interactions_, ol.CollectionEventType.ADD, - /** - * @param {ol.Collection.Event} event Collection event. - */ - function(event) { - event.element.setMap(this); - }, this); - - ol.events.listen(this.interactions_, ol.CollectionEventType.REMOVE, - /** - * @param {ol.Collection.Event} event Collection event. - */ - function(event) { - event.element.setMap(null); - }, this); - - this.overlays_.forEach(this.addOverlayInternal_, this); - - ol.events.listen(this.overlays_, ol.CollectionEventType.ADD, - /** - * @param {ol.Collection.Event} event Collection event. - */ - function(event) { - this.addOverlayInternal_(/** @type {ol.Overlay} */ (event.element)); - }, this); - - ol.events.listen(this.overlays_, ol.CollectionEventType.REMOVE, - /** - * @param {ol.Collection.Event} event Collection event. - */ - function(event) { - var overlay = /** @type {ol.Overlay} */ (event.element); - var id = overlay.getId(); - if (id !== undefined) { - delete this.overlayIdIndex_[id.toString()]; - } - event.element.setMap(null); - }, this); - -}; -ol.inherits(ol.Map, ol.Object); - - -/** - * Add the given control to the map. - * @param {ol.control.Control} control Control. - * @api - */ -ol.Map.prototype.addControl = function(control) { - this.getControls().push(control); -}; - - -/** - * Add the given interaction to the map. - * @param {ol.interaction.Interaction} interaction Interaction to add. - * @api - */ -ol.Map.prototype.addInteraction = function(interaction) { - this.getInteractions().push(interaction); -}; - - -/** - * Adds the given layer to the top of this map. If you want to add a layer - * elsewhere in the stack, use `getLayers()` and the methods available on - * {@link ol.Collection}. - * @param {ol.layer.Base} layer Layer. - * @api - */ -ol.Map.prototype.addLayer = function(layer) { - var layers = this.getLayerGroup().getLayers(); - layers.push(layer); -}; - - -/** - * Add the given overlay to the map. - * @param {ol.Overlay} overlay Overlay. - * @api - */ -ol.Map.prototype.addOverlay = function(overlay) { - this.getOverlays().push(overlay); -}; - - -/** - * This deals with map's overlay collection changes. - * @param {ol.Overlay} overlay Overlay. - * @private - */ -ol.Map.prototype.addOverlayInternal_ = function(overlay) { - var id = overlay.getId(); - if (id !== undefined) { - this.overlayIdIndex_[id.toString()] = overlay; - } - overlay.setMap(this); -}; - - -/** - * - * @inheritDoc - */ -ol.Map.prototype.disposeInternal = function() { - this.mapBrowserEventHandler_.dispose(); - this.renderer_.dispose(); - ol.events.unlisten(this.viewport_, ol.events.EventType.WHEEL, - this.handleBrowserEvent, this); - ol.events.unlisten(this.viewport_, ol.events.EventType.MOUSEWHEEL, - this.handleBrowserEvent, this); - if (this.handleResize_ !== undefined) { - window.removeEventListener(ol.events.EventType.RESIZE, - this.handleResize_, false); - this.handleResize_ = undefined; - } - if (this.animationDelayKey_) { - cancelAnimationFrame(this.animationDelayKey_); - this.animationDelayKey_ = undefined; - } - this.setTarget(null); - ol.Object.prototype.disposeInternal.call(this); -}; - - -/** - * Detect features that intersect a pixel on the viewport, and execute a - * callback with each intersecting feature. Layers included in the detection can - * be configured through `opt_layerFilter`. - * @param {ol.Pixel} pixel Pixel. - * @param {function(this: S, (ol.Feature|ol.render.Feature), - * ol.layer.Layer): T} callback Feature callback. The callback will be - * called with two arguments. The first argument is one - * {@link ol.Feature feature} or - * {@link ol.render.Feature render feature} at the pixel, the second is - * the {@link ol.layer.Layer layer} of the feature and will be null for - * unmanaged layers. To stop detection, callback functions can return a - * truthy value. - * @param {olx.AtPixelOptions=} opt_options Optional options. - * @return {T|undefined} Callback result, i.e. the return value of last - * callback execution, or the first truthy callback return value. - * @template S,T - * @api - */ -ol.Map.prototype.forEachFeatureAtPixel = function(pixel, callback, opt_options) { - if (!this.frameState_) { - return; - } - var coordinate = this.getCoordinateFromPixel(pixel); - opt_options = opt_options !== undefined ? opt_options : {}; - var hitTolerance = opt_options.hitTolerance !== undefined ? - opt_options.hitTolerance * this.frameState_.pixelRatio : 0; - var layerFilter = opt_options.layerFilter !== undefined ? - opt_options.layerFilter : ol.functions.TRUE; - return this.renderer_.forEachFeatureAtCoordinate( - coordinate, this.frameState_, hitTolerance, callback, null, - layerFilter, null); -}; - - -/** - * Detect layers that have a color value at a pixel on the viewport, and - * execute a callback with each matching layer. Layers included in the - * detection can be configured through `opt_layerFilter`. - * @param {ol.Pixel} pixel Pixel. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback - * Layer callback. This callback will receive two arguments: first is the - * {@link ol.layer.Layer layer}, second argument is an array representing - * [R, G, B, A] pixel values (0 - 255) and will be `null` for layer types - * that do not currently support this argument. To stop detection, callback - * functions can return a truthy value. - * @param {S=} opt_this Value to use as `this` when executing `callback`. - * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer - * filter function. The filter function will receive one argument, the - * {@link ol.layer.Layer layer-candidate} and it should return a boolean - * value. Only layers which are visible and for which this function returns - * `true` will be tested for features. By default, all visible layers will - * be tested. - * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`. - * @return {T|undefined} Callback result, i.e. the return value of last - * callback execution, or the first truthy callback return value. - * @template S,T,U - * @api - */ -ol.Map.prototype.forEachLayerAtPixel = function(pixel, callback, opt_this, opt_layerFilter, opt_this2) { - if (!this.frameState_) { - return; - } - var thisArg = opt_this !== undefined ? opt_this : null; - var layerFilter = opt_layerFilter !== undefined ? - opt_layerFilter : ol.functions.TRUE; - var thisArg2 = opt_this2 !== undefined ? opt_this2 : null; - return this.renderer_.forEachLayerAtPixel( - pixel, this.frameState_, callback, thisArg, - layerFilter, thisArg2); -}; - - -/** - * Detect if features intersect a pixel on the viewport. Layers included in the - * detection can be configured through `opt_layerFilter`. - * @param {ol.Pixel} pixel Pixel. - * @param {olx.AtPixelOptions=} opt_options Optional options. - * @return {boolean} Is there a feature at the given pixel? - * @template U - * @api - */ -ol.Map.prototype.hasFeatureAtPixel = function(pixel, opt_options) { - if (!this.frameState_) { - return false; - } - var coordinate = this.getCoordinateFromPixel(pixel); - opt_options = opt_options !== undefined ? opt_options : {}; - var layerFilter = opt_options.layerFilter !== undefined ? - opt_options.layerFilter : ol.functions.TRUE; - var hitTolerance = opt_options.hitTolerance !== undefined ? - opt_options.hitTolerance * this.frameState_.pixelRatio : 0; - return this.renderer_.hasFeatureAtCoordinate( - coordinate, this.frameState_, hitTolerance, layerFilter, null); -}; - - -/** - * Returns the coordinate in view projection for a browser event. - * @param {Event} event Event. - * @return {ol.Coordinate} Coordinate. - * @api - */ -ol.Map.prototype.getEventCoordinate = function(event) { - return this.getCoordinateFromPixel(this.getEventPixel(event)); -}; - - -/** - * Returns the map pixel position for a browser event relative to the viewport. - * @param {Event} event Event. - * @return {ol.Pixel} Pixel. - * @api - */ -ol.Map.prototype.getEventPixel = function(event) { - var viewportPosition = this.viewport_.getBoundingClientRect(); - var eventPosition = event.changedTouches ? event.changedTouches[0] : event; - return [ - eventPosition.clientX - viewportPosition.left, - eventPosition.clientY - viewportPosition.top - ]; -}; - - -/** - * Get the target in which this map is rendered. - * Note that this returns what is entered as an option or in setTarget: - * if that was an element, it returns an element; if a string, it returns that. - * @return {Element|string|undefined} The Element or id of the Element that the - * map is rendered in. - * @observable - * @api - */ -ol.Map.prototype.getTarget = function() { - return /** @type {Element|string|undefined} */ ( - this.get(ol.MapProperty.TARGET)); -}; - - -/** - * Get the DOM element into which this map is rendered. In contrast to - * `getTarget` this method always return an `Element`, or `null` if the - * map has no target. - * @return {Element} The element that the map is rendered in. - * @api - */ -ol.Map.prototype.getTargetElement = function() { - var target = this.getTarget(); - if (target !== undefined) { - return typeof target === 'string' ? - document.getElementById(target) : - target; - } else { - return null; - } -}; - - -/** - * Get the coordinate for a given pixel. This returns a coordinate in the - * map view projection. - * @param {ol.Pixel} pixel Pixel position in the map viewport. - * @return {ol.Coordinate} The coordinate for the pixel position. - * @api - */ -ol.Map.prototype.getCoordinateFromPixel = function(pixel) { - var frameState = this.frameState_; - if (!frameState) { - return null; - } else { - return ol.transform.apply(frameState.pixelToCoordinateTransform, pixel.slice()); - } -}; - - -/** - * Get the map controls. Modifying this collection changes the controls - * associated with the map. - * @return {ol.Collection.<ol.control.Control>} Controls. - * @api - */ -ol.Map.prototype.getControls = function() { - return this.controls_; -}; - - -/** - * Get the map overlays. Modifying this collection changes the overlays - * associated with the map. - * @return {ol.Collection.<ol.Overlay>} Overlays. - * @api - */ -ol.Map.prototype.getOverlays = function() { - return this.overlays_; -}; - - -/** - * Get an overlay by its identifier (the value returned by overlay.getId()). - * Note that the index treats string and numeric identifiers as the same. So - * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`. - * @param {string|number} id Overlay identifier. - * @return {ol.Overlay} Overlay. - * @api - */ -ol.Map.prototype.getOverlayById = function(id) { - var overlay = this.overlayIdIndex_[id.toString()]; - return overlay !== undefined ? overlay : null; -}; - - -/** - * Get the map interactions. Modifying this collection changes the interactions - * associated with the map. - * - * Interactions are used for e.g. pan, zoom and rotate. - * @return {ol.Collection.<ol.interaction.Interaction>} Interactions. - * @api - */ -ol.Map.prototype.getInteractions = function() { - return this.interactions_; -}; - - -/** - * Get the layergroup associated with this map. - * @return {ol.layer.Group} A layer group containing the layers in this map. - * @observable - * @api - */ -ol.Map.prototype.getLayerGroup = function() { - return /** @type {ol.layer.Group} */ (this.get(ol.MapProperty.LAYERGROUP)); -}; - - -/** - * Get the collection of layers associated with this map. - * @return {!ol.Collection.<ol.layer.Base>} Layers. - * @api - */ -ol.Map.prototype.getLayers = function() { - var layers = this.getLayerGroup().getLayers(); - return layers; -}; - - -/** - * Get the pixel for a coordinate. This takes a coordinate in the map view - * projection and returns the corresponding pixel. - * @param {ol.Coordinate} coordinate A map coordinate. - * @return {ol.Pixel} A pixel position in the map viewport. - * @api - */ -ol.Map.prototype.getPixelFromCoordinate = function(coordinate) { - var frameState = this.frameState_; - if (!frameState) { - return null; - } else { - return ol.transform.apply(frameState.coordinateToPixelTransform, - coordinate.slice(0, 2)); - } -}; - - -/** - * Get the map renderer. - * @return {ol.renderer.Map} Renderer - */ -ol.Map.prototype.getRenderer = function() { - return this.renderer_; -}; - - -/** - * Get the size of this map. - * @return {ol.Size|undefined} The size in pixels of the map in the DOM. - * @observable - * @api - */ -ol.Map.prototype.getSize = function() { - return /** @type {ol.Size|undefined} */ (this.get(ol.MapProperty.SIZE)); -}; - - -/** - * Get the view associated with this map. A view manages properties such as - * center and resolution. - * @return {ol.View} The view that controls this map. - * @observable - * @api - */ -ol.Map.prototype.getView = function() { - return /** @type {ol.View} */ (this.get(ol.MapProperty.VIEW)); -}; - - -/** - * Get the element that serves as the map viewport. - * @return {Element} Viewport. - * @api - */ -ol.Map.prototype.getViewport = function() { - return this.viewport_; -}; - - -/** - * Get the element that serves as the container for overlays. Elements added to - * this container will let mousedown and touchstart events through to the map, - * so clicks and gestures on an overlay will trigger {@link ol.MapBrowserEvent} - * events. - * @return {!Element} The map's overlay container. - */ -ol.Map.prototype.getOverlayContainer = function() { - return this.overlayContainer_; -}; - - -/** - * Get the element that serves as a container for overlays that don't allow - * event propagation. Elements added to this container won't let mousedown and - * touchstart events through to the map, so clicks and gestures on an overlay - * don't trigger any {@link ol.MapBrowserEvent}. - * @return {!Element} The map's overlay container that stops events. - */ -ol.Map.prototype.getOverlayContainerStopEvent = function() { - return this.overlayContainerStopEvent_; -}; - - -/** - * @param {ol.Tile} tile Tile. - * @param {string} tileSourceKey Tile source key. - * @param {ol.Coordinate} tileCenter Tile center. - * @param {number} tileResolution Tile resolution. - * @return {number} Tile priority. - */ -ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter, tileResolution) { - // Filter out tiles at higher zoom levels than the current zoom level, or that - // are outside the visible extent. - var frameState = this.frameState_; - if (!frameState || !(tileSourceKey in frameState.wantedTiles)) { - return ol.structs.PriorityQueue.DROP; - } - if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) { - return ol.structs.PriorityQueue.DROP; - } - // Prioritize the highest zoom level tiles closest to the focus. - // Tiles at higher zoom levels are prioritized using Math.log(tileResolution). - // Within a zoom level, tiles are prioritized by the distance in pixels - // between the center of the tile and the focus. The factor of 65536 means - // that the prioritization should behave as desired for tiles up to - // 65536 * Math.log(2) = 45426 pixels from the focus. - var deltaX = tileCenter[0] - frameState.focus[0]; - var deltaY = tileCenter[1] - frameState.focus[1]; - return 65536 * Math.log(tileResolution) + - Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; -}; - - -/** - * @param {Event} browserEvent Browser event. - * @param {string=} opt_type Type. - */ -ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) { - var type = opt_type || browserEvent.type; - var mapBrowserEvent = new ol.MapBrowserEvent(type, this, browserEvent); - this.handleMapBrowserEvent(mapBrowserEvent); -}; - - -/** - * @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle. - */ -ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) { - if (!this.frameState_) { - // With no view defined, we cannot translate pixels into geographical - // coordinates so interactions cannot be used. - return; - } - this.focus_ = mapBrowserEvent.coordinate; - mapBrowserEvent.frameState = this.frameState_; - var interactionsArray = this.getInteractions().getArray(); - var i; - if (this.dispatchEvent(mapBrowserEvent) !== false) { - for (i = interactionsArray.length - 1; i >= 0; i--) { - var interaction = interactionsArray[i]; - if (!interaction.getActive()) { - continue; - } - var cont = interaction.handleEvent(mapBrowserEvent); - if (!cont) { - break; - } - } - } -}; - - -/** - * @protected - */ -ol.Map.prototype.handlePostRender = function() { - - var frameState = this.frameState_; - - // Manage the tile queue - // Image loads are expensive and a limited resource, so try to use them - // efficiently: - // * When the view is static we allow a large number of parallel tile loads - // to complete the frame as quickly as possible. - // * When animating or interacting, image loads can cause janks, so we reduce - // the maximum number of loads per frame and limit the number of parallel - // tile loads to remain reactive to view changes and to reduce the chance of - // loading tiles that will quickly disappear from view. - var tileQueue = this.tileQueue_; - if (!tileQueue.isEmpty()) { - var maxTotalLoading = 16; - var maxNewLoads = maxTotalLoading; - if (frameState) { - var hints = frameState.viewHints; - if (hints[ol.ViewHint.ANIMATING]) { - maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0; - maxNewLoads = 2; - } - if (hints[ol.ViewHint.INTERACTING]) { - maxTotalLoading = this.loadTilesWhileInteracting_ ? 8 : 0; - maxNewLoads = 2; - } - } - if (tileQueue.getTilesLoading() < maxTotalLoading) { - tileQueue.reprioritize(); // FIXME only call if view has changed - tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads); - } - } - - var postRenderFunctions = this.postRenderFunctions_; - var i, ii; - for (i = 0, ii = postRenderFunctions.length; i < ii; ++i) { - postRenderFunctions[i](this, frameState); - } - postRenderFunctions.length = 0; -}; - - -/** - * @private - */ -ol.Map.prototype.handleSizeChanged_ = function() { - this.render(); -}; - - -/** - * @private - */ -ol.Map.prototype.handleTargetChanged_ = function() { - // target may be undefined, null, a string or an Element. - // If it's a string we convert it to an Element before proceeding. - // If it's not now an Element we remove the viewport from the DOM. - // If it's an Element we append the viewport element to it. - - var targetElement; - if (this.getTarget()) { - targetElement = this.getTargetElement(); - } - - if (this.keyHandlerKeys_) { - for (var i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) { - ol.events.unlistenByKey(this.keyHandlerKeys_[i]); - } - this.keyHandlerKeys_ = null; - } - - if (!targetElement) { - ol.dom.removeNode(this.viewport_); - if (this.handleResize_ !== undefined) { - window.removeEventListener(ol.events.EventType.RESIZE, - this.handleResize_, false); - this.handleResize_ = undefined; - } - } else { - targetElement.appendChild(this.viewport_); - - var keyboardEventTarget = !this.keyboardEventTarget_ ? - targetElement : this.keyboardEventTarget_; - this.keyHandlerKeys_ = [ - ol.events.listen(keyboardEventTarget, ol.events.EventType.KEYDOWN, - this.handleBrowserEvent, this), - ol.events.listen(keyboardEventTarget, ol.events.EventType.KEYPRESS, - this.handleBrowserEvent, this) - ]; - - if (!this.handleResize_) { - this.handleResize_ = this.updateSize.bind(this); - window.addEventListener(ol.events.EventType.RESIZE, - this.handleResize_, false); - } - } - - this.updateSize(); - // updateSize calls setSize, so no need to call this.render - // ourselves here. -}; - - -/** - * @private - */ -ol.Map.prototype.handleTileChange_ = function() { - this.render(); -}; - - -/** - * @private - */ -ol.Map.prototype.handleViewPropertyChanged_ = function() { - this.render(); -}; - - -/** - * @private - */ -ol.Map.prototype.handleViewChanged_ = function() { - if (this.viewPropertyListenerKey_) { - ol.events.unlistenByKey(this.viewPropertyListenerKey_); - this.viewPropertyListenerKey_ = null; - } - if (this.viewChangeListenerKey_) { - ol.events.unlistenByKey(this.viewChangeListenerKey_); - this.viewChangeListenerKey_ = null; - } - var view = this.getView(); - if (view) { - this.viewport_.setAttribute('data-view', ol.getUid(view)); - this.viewPropertyListenerKey_ = ol.events.listen( - view, ol.ObjectEventType.PROPERTYCHANGE, - this.handleViewPropertyChanged_, this); - this.viewChangeListenerKey_ = ol.events.listen( - view, ol.events.EventType.CHANGE, - this.handleViewPropertyChanged_, this); - } - this.render(); -}; - - -/** - * @private - */ -ol.Map.prototype.handleLayerGroupChanged_ = function() { - if (this.layerGroupPropertyListenerKeys_) { - this.layerGroupPropertyListenerKeys_.forEach(ol.events.unlistenByKey); - this.layerGroupPropertyListenerKeys_ = null; - } - var layerGroup = this.getLayerGroup(); - if (layerGroup) { - this.layerGroupPropertyListenerKeys_ = [ - ol.events.listen( - layerGroup, ol.ObjectEventType.PROPERTYCHANGE, - this.render, this), - ol.events.listen( - layerGroup, ol.events.EventType.CHANGE, - this.render, this) - ]; - } - this.render(); -}; - - -/** - * @return {boolean} Is rendered. - */ -ol.Map.prototype.isRendered = function() { - return !!this.frameState_; -}; - - -/** - * Requests an immediate render in a synchronous manner. - * @api - */ -ol.Map.prototype.renderSync = function() { - if (this.animationDelayKey_) { - cancelAnimationFrame(this.animationDelayKey_); - } - this.animationDelay_(); -}; - - -/** - * Request a map rendering (at the next animation frame). - * @api - */ -ol.Map.prototype.render = function() { - if (this.animationDelayKey_ === undefined) { - this.animationDelayKey_ = requestAnimationFrame( - this.animationDelay_); - } -}; - - -/** - * Remove the given control from the map. - * @param {ol.control.Control} control Control. - * @return {ol.control.Control|undefined} The removed control (or undefined - * if the control was not found). - * @api - */ -ol.Map.prototype.removeControl = function(control) { - return this.getControls().remove(control); -}; - - -/** - * Remove the given interaction from the map. - * @param {ol.interaction.Interaction} interaction Interaction to remove. - * @return {ol.interaction.Interaction|undefined} The removed interaction (or - * undefined if the interaction was not found). - * @api - */ -ol.Map.prototype.removeInteraction = function(interaction) { - return this.getInteractions().remove(interaction); -}; - - -/** - * Removes the given layer from the map. - * @param {ol.layer.Base} layer Layer. - * @return {ol.layer.Base|undefined} The removed layer (or undefined if the - * layer was not found). - * @api - */ -ol.Map.prototype.removeLayer = function(layer) { - var layers = this.getLayerGroup().getLayers(); - return layers.remove(layer); -}; - - -/** - * Remove the given overlay from the map. - * @param {ol.Overlay} overlay Overlay. - * @return {ol.Overlay|undefined} The removed overlay (or undefined - * if the overlay was not found). - * @api - */ -ol.Map.prototype.removeOverlay = function(overlay) { - return this.getOverlays().remove(overlay); -}; - - -/** - * @param {number} time Time. - * @private - */ -ol.Map.prototype.renderFrame_ = function(time) { - var i, ii, viewState; - - var size = this.getSize(); - var view = this.getView(); - var extent = ol.extent.createEmpty(); - var previousFrameState = this.frameState_; - /** @type {?olx.FrameState} */ - var frameState = null; - if (size !== undefined && ol.size.hasArea(size) && view && view.isDef()) { - var viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined); - var layerStatesArray = this.getLayerGroup().getLayerStatesArray(); - var layerStates = {}; - for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerStates[ol.getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; - } - viewState = view.getState(); - frameState = /** @type {olx.FrameState} */ ({ - animate: false, - attributions: {}, - coordinateToPixelTransform: this.coordinateToPixelTransform_, - extent: extent, - focus: !this.focus_ ? viewState.center : this.focus_, - index: this.frameIndex_++, - layerStates: layerStates, - layerStatesArray: layerStatesArray, - logos: ol.obj.assign({}, this.logos_), - pixelRatio: this.pixelRatio_, - pixelToCoordinateTransform: this.pixelToCoordinateTransform_, - postRenderFunctions: [], - size: size, - skippedFeatureUids: this.skippedFeatureUids_, - tileQueue: this.tileQueue_, - time: time, - usedTiles: {}, - viewState: viewState, - viewHints: viewHints, - wantedTiles: {} - }); - } - - if (frameState) { - frameState.extent = ol.extent.getForViewAndSize(viewState.center, - viewState.resolution, viewState.rotation, frameState.size, extent); - } - - this.frameState_ = frameState; - this.renderer_.renderFrame(frameState); - - if (frameState) { - if (frameState.animate) { - this.render(); - } - Array.prototype.push.apply( - this.postRenderFunctions_, frameState.postRenderFunctions); - - if (previousFrameState) { - var moveStart = !this.previousExtent_ || - (!ol.extent.isEmpty(this.previousExtent_) && - !ol.extent.equals(frameState.extent, this.previousExtent_)); - if (moveStart) { - this.dispatchEvent( - new ol.MapEvent(ol.MapEventType.MOVESTART, this, previousFrameState)); - this.previousExtent_ = ol.extent.createOrUpdateEmpty(this.previousExtent_); - } - } - - var idle = this.previousExtent_ && - !frameState.viewHints[ol.ViewHint.ANIMATING] && - !frameState.viewHints[ol.ViewHint.INTERACTING] && - !ol.extent.equals(frameState.extent, this.previousExtent_); - - if (idle) { - this.dispatchEvent( - new ol.MapEvent(ol.MapEventType.MOVEEND, this, frameState)); - ol.extent.clone(frameState.extent, this.previousExtent_); - } - } - - this.dispatchEvent( - new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState)); - - setTimeout(this.handlePostRender.bind(this), 0); - -}; - - -/** - * Sets the layergroup of this map. - * @param {ol.layer.Group} layerGroup A layer group containing the layers in - * this map. - * @observable - * @api - */ -ol.Map.prototype.setLayerGroup = function(layerGroup) { - this.set(ol.MapProperty.LAYERGROUP, layerGroup); -}; - - -/** - * Set the size of this map. - * @param {ol.Size|undefined} size The size in pixels of the map in the DOM. - * @observable - * @api - */ -ol.Map.prototype.setSize = function(size) { - this.set(ol.MapProperty.SIZE, size); -}; - - -/** - * Set the target element to render this map into. - * @param {Element|string|undefined} target The Element or id of the Element - * that the map is rendered in. - * @observable - * @api - */ -ol.Map.prototype.setTarget = function(target) { - this.set(ol.MapProperty.TARGET, target); -}; - - -/** - * Set the view for this map. - * @param {ol.View} view The view that controls this map. - * @observable - * @api - */ -ol.Map.prototype.setView = function(view) { - this.set(ol.MapProperty.VIEW, view); -}; - - -/** - * @param {ol.Feature} feature Feature. - */ -ol.Map.prototype.skipFeature = function(feature) { - var featureUid = ol.getUid(feature).toString(); - this.skippedFeatureUids_[featureUid] = true; - this.render(); -}; - - -/** - * Force a recalculation of the map viewport size. This should be called when - * third-party code changes the size of the map viewport. - * @api - */ -ol.Map.prototype.updateSize = function() { - var targetElement = this.getTargetElement(); - - if (!targetElement) { - this.setSize(undefined); - } else { - var computedStyle = getComputedStyle(targetElement); - this.setSize([ - targetElement.offsetWidth - - parseFloat(computedStyle['borderLeftWidth']) - - parseFloat(computedStyle['paddingLeft']) - - parseFloat(computedStyle['paddingRight']) - - parseFloat(computedStyle['borderRightWidth']), - targetElement.offsetHeight - - parseFloat(computedStyle['borderTopWidth']) - - parseFloat(computedStyle['paddingTop']) - - parseFloat(computedStyle['paddingBottom']) - - parseFloat(computedStyle['borderBottomWidth']) - ]); - } -}; - - -/** - * @param {ol.Feature} feature Feature. - */ -ol.Map.prototype.unskipFeature = function(feature) { - var featureUid = ol.getUid(feature).toString(); - delete this.skippedFeatureUids_[featureUid]; - this.render(); -}; - - -/** - * @param {olx.MapOptions} options Map options. - * @return {ol.MapOptionsInternal} Internal map options. - */ -ol.Map.createOptionsInternal = function(options) { - - /** - * @type {Element|Document} - */ - var keyboardEventTarget = null; - if (options.keyboardEventTarget !== undefined) { - keyboardEventTarget = typeof options.keyboardEventTarget === 'string' ? - document.getElementById(options.keyboardEventTarget) : - options.keyboardEventTarget; - } - - /** - * @type {Object.<string, *>} - */ - var values = {}; - - var logos = {}; - if (options.logo === undefined || - (typeof options.logo === 'boolean' && options.logo)) { - logos[ol.OL_LOGO_URL] = ol.OL_URL; - } else { - var logo = options.logo; - if (typeof logo === 'string') { - logos[logo] = ''; - } else if (logo instanceof HTMLElement) { - logos[ol.getUid(logo).toString()] = logo; - } else if (logo) { - ol.asserts.assert(typeof logo.href == 'string', 44); // `logo.href` should be a string. - ol.asserts.assert(typeof logo.src == 'string', 45); // `logo.src` should be a string. - logos[logo.src] = logo.href; - } - } - - var layerGroup = (options.layers instanceof ol.layer.Group) ? - options.layers : new ol.layer.Group({layers: options.layers}); - values[ol.MapProperty.LAYERGROUP] = layerGroup; - - values[ol.MapProperty.TARGET] = options.target; - - values[ol.MapProperty.VIEW] = options.view !== undefined ? - options.view : new ol.View(); - - /** - * @type {function(new: ol.renderer.Map, Element, ol.Map)} - */ - var rendererConstructor = ol.renderer.Map; - - /** - * @type {Array.<ol.renderer.Type>} - */ - var rendererTypes; - if (options.renderer !== undefined) { - if (Array.isArray(options.renderer)) { - rendererTypes = options.renderer; - } else if (typeof options.renderer === 'string') { - rendererTypes = [options.renderer]; - } else { - ol.asserts.assert(false, 46); // Incorrect format for `renderer` option - } - if (rendererTypes.indexOf(/** @type {ol.renderer.Type} */ ('dom')) >= 0) { - rendererTypes = rendererTypes.concat(ol.DEFAULT_RENDERER_TYPES); - } - } else { - rendererTypes = ol.DEFAULT_RENDERER_TYPES; - } - - var i, ii; - for (i = 0, ii = rendererTypes.length; i < ii; ++i) { - /** @type {ol.renderer.Type} */ - var rendererType = rendererTypes[i]; - if (ol.ENABLE_CANVAS && rendererType == ol.renderer.Type.CANVAS) { - if (ol.has.CANVAS) { - rendererConstructor = ol.renderer.canvas.Map; - break; - } - } else if (ol.ENABLE_WEBGL && rendererType == ol.renderer.Type.WEBGL) { - if (ol.has.WEBGL) { - rendererConstructor = ol.renderer.webgl.Map; - break; - } - } - } - - var controls; - if (options.controls !== undefined) { - if (Array.isArray(options.controls)) { - controls = new ol.Collection(options.controls.slice()); - } else { - ol.asserts.assert(options.controls instanceof ol.Collection, - 47); // Expected `controls` to be an array or an `ol.Collection` - controls = options.controls; - } - } else { - controls = ol.control.defaults(); - } - - var interactions; - if (options.interactions !== undefined) { - if (Array.isArray(options.interactions)) { - interactions = new ol.Collection(options.interactions.slice()); - } else { - ol.asserts.assert(options.interactions instanceof ol.Collection, - 48); // Expected `interactions` to be an array or an `ol.Collection` - interactions = options.interactions; - } - } else { - interactions = ol.interaction.defaults(); - } - - var overlays; - if (options.overlays !== undefined) { - if (Array.isArray(options.overlays)) { - overlays = new ol.Collection(options.overlays.slice()); - } else { - ol.asserts.assert(options.overlays instanceof ol.Collection, - 49); // Expected `overlays` to be an array or an `ol.Collection` - overlays = options.overlays; - } - } else { - overlays = new ol.Collection(); - } - - return { - controls: controls, - interactions: interactions, - keyboardEventTarget: keyboardEventTarget, - logos: logos, - overlays: overlays, - rendererConstructor: rendererConstructor, - values: values - }; - -}; - -goog.provide('ol.OverlayPositioning'); - -/** - * Overlay position: `'bottom-left'`, `'bottom-center'`, `'bottom-right'`, - * `'center-left'`, `'center-center'`, `'center-right'`, `'top-left'`, - * `'top-center'`, `'top-right'` - * @enum {string} - */ -ol.OverlayPositioning = { - BOTTOM_LEFT: 'bottom-left', - BOTTOM_CENTER: 'bottom-center', - BOTTOM_RIGHT: 'bottom-right', - CENTER_LEFT: 'center-left', - CENTER_CENTER: 'center-center', - CENTER_RIGHT: 'center-right', - TOP_LEFT: 'top-left', - TOP_CENTER: 'top-center', - TOP_RIGHT: 'top-right' -}; - -goog.provide('ol.Overlay'); - -goog.require('ol'); -goog.require('ol.MapEventType'); -goog.require('ol.Object'); -goog.require('ol.OverlayPositioning'); -goog.require('ol.css'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.extent'); - - -/** - * @classdesc - * An element to be displayed over the map and attached to a single map - * location. Like {@link ol.control.Control}, Overlays are visible widgets. - * Unlike Controls, they are not in a fixed position on the screen, but are tied - * to a geographical coordinate, so panning the map will move an Overlay but not - * a Control. - * - * Example: - * - * var popup = new ol.Overlay({ - * element: document.getElementById('popup') - * }); - * popup.setPosition(coordinate); - * map.addOverlay(popup); - * - * @constructor - * @extends {ol.Object} - * @param {olx.OverlayOptions} options Overlay options. - * @api - */ -ol.Overlay = function(options) { - - ol.Object.call(this); - - /** - * @private - * @type {number|string|undefined} - */ - this.id_ = options.id; - - /** - * @private - * @type {boolean} - */ - this.insertFirst_ = options.insertFirst !== undefined ? - options.insertFirst : true; - - /** - * @private - * @type {boolean} - */ - this.stopEvent_ = options.stopEvent !== undefined ? options.stopEvent : true; - - /** - * @private - * @type {Element} - */ - this.element_ = document.createElement('DIV'); - this.element_.className = 'ol-overlay-container ' + ol.css.CLASS_SELECTABLE; - this.element_.style.position = 'absolute'; - - /** - * @protected - * @type {boolean} - */ - this.autoPan = options.autoPan !== undefined ? options.autoPan : false; - - /** - * @private - * @type {olx.OverlayPanOptions} - */ - this.autoPanAnimation_ = options.autoPanAnimation || - /** @type {olx.OverlayPanOptions} */ ({}); - - /** - * @private - * @type {number} - */ - this.autoPanMargin_ = options.autoPanMargin !== undefined ? - options.autoPanMargin : 20; - - /** - * @private - * @type {{bottom_: string, - * left_: string, - * right_: string, - * top_: string, - * visible: boolean}} - */ - this.rendered_ = { - bottom_: '', - left_: '', - right_: '', - top_: '', - visible: true - }; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.mapPostrenderListenerKey_ = null; - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.Overlay.Property_.ELEMENT), - this.handleElementChanged, this); - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.Overlay.Property_.MAP), - this.handleMapChanged, this); - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.Overlay.Property_.OFFSET), - this.handleOffsetChanged, this); - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.Overlay.Property_.POSITION), - this.handlePositionChanged, this); - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.Overlay.Property_.POSITIONING), - this.handlePositioningChanged, this); - - if (options.element !== undefined) { - this.setElement(options.element); - } - - this.setOffset(options.offset !== undefined ? options.offset : [0, 0]); - - this.setPositioning(options.positioning !== undefined ? - /** @type {ol.OverlayPositioning} */ (options.positioning) : - ol.OverlayPositioning.TOP_LEFT); - - if (options.position !== undefined) { - this.setPosition(options.position); - } - -}; -ol.inherits(ol.Overlay, ol.Object); - - -/** - * Get the DOM element of this overlay. - * @return {Element|undefined} The Element containing the overlay. - * @observable - * @api - */ -ol.Overlay.prototype.getElement = function() { - return /** @type {Element|undefined} */ ( - this.get(ol.Overlay.Property_.ELEMENT)); -}; - - -/** - * Get the overlay identifier which is set on constructor. - * @return {number|string|undefined} Id. - * @api - */ -ol.Overlay.prototype.getId = function() { - return this.id_; -}; - - -/** - * Get the map associated with this overlay. - * @return {ol.Map|undefined} The map that the overlay is part of. - * @observable - * @api - */ -ol.Overlay.prototype.getMap = function() { - return /** @type {ol.Map|undefined} */ ( - this.get(ol.Overlay.Property_.MAP)); -}; - - -/** - * Get the offset of this overlay. - * @return {Array.<number>} The offset. - * @observable - * @api - */ -ol.Overlay.prototype.getOffset = function() { - return /** @type {Array.<number>} */ ( - this.get(ol.Overlay.Property_.OFFSET)); -}; - - -/** - * Get the current position of this overlay. - * @return {ol.Coordinate|undefined} The spatial point that the overlay is - * anchored at. - * @observable - * @api - */ -ol.Overlay.prototype.getPosition = function() { - return /** @type {ol.Coordinate|undefined} */ ( - this.get(ol.Overlay.Property_.POSITION)); -}; - - -/** - * Get the current positioning of this overlay. - * @return {ol.OverlayPositioning} How the overlay is positioned - * relative to its point on the map. - * @observable - * @api - */ -ol.Overlay.prototype.getPositioning = function() { - return /** @type {ol.OverlayPositioning} */ ( - this.get(ol.Overlay.Property_.POSITIONING)); -}; - - -/** - * @protected - */ -ol.Overlay.prototype.handleElementChanged = function() { - ol.dom.removeChildren(this.element_); - var element = this.getElement(); - if (element) { - this.element_.appendChild(element); - } -}; - - -/** - * @protected - */ -ol.Overlay.prototype.handleMapChanged = function() { - if (this.mapPostrenderListenerKey_) { - ol.dom.removeNode(this.element_); - ol.events.unlistenByKey(this.mapPostrenderListenerKey_); - this.mapPostrenderListenerKey_ = null; - } - var map = this.getMap(); - if (map) { - this.mapPostrenderListenerKey_ = ol.events.listen(map, - ol.MapEventType.POSTRENDER, this.render, this); - this.updatePixelPosition(); - var container = this.stopEvent_ ? - map.getOverlayContainerStopEvent() : map.getOverlayContainer(); - if (this.insertFirst_) { - container.insertBefore(this.element_, container.childNodes[0] || null); - } else { - container.appendChild(this.element_); - } - } -}; - - -/** - * @protected - */ -ol.Overlay.prototype.render = function() { - this.updatePixelPosition(); -}; - - -/** - * @protected - */ -ol.Overlay.prototype.handleOffsetChanged = function() { - this.updatePixelPosition(); -}; - - -/** - * @protected - */ -ol.Overlay.prototype.handlePositionChanged = function() { - this.updatePixelPosition(); - if (this.get(ol.Overlay.Property_.POSITION) && this.autoPan) { - this.panIntoView_(); - } -}; - - -/** - * @protected - */ -ol.Overlay.prototype.handlePositioningChanged = function() { - this.updatePixelPosition(); -}; - - -/** - * Set the DOM element to be associated with this overlay. - * @param {Element|undefined} element The Element containing the overlay. - * @observable - * @api - */ -ol.Overlay.prototype.setElement = function(element) { - this.set(ol.Overlay.Property_.ELEMENT, element); -}; - - -/** - * Set the map to be associated with this overlay. - * @param {ol.Map|undefined} map The map that the overlay is part of. - * @observable - * @api - */ -ol.Overlay.prototype.setMap = function(map) { - this.set(ol.Overlay.Property_.MAP, map); -}; - - -/** - * Set the offset for this overlay. - * @param {Array.<number>} offset Offset. - * @observable - * @api - */ -ol.Overlay.prototype.setOffset = function(offset) { - this.set(ol.Overlay.Property_.OFFSET, offset); -}; - - -/** - * Set the position for this overlay. If the position is `undefined` the - * overlay is hidden. - * @param {ol.Coordinate|undefined} position The spatial point that the overlay - * is anchored at. - * @observable - * @api - */ -ol.Overlay.prototype.setPosition = function(position) { - this.set(ol.Overlay.Property_.POSITION, position); -}; - - -/** - * Pan the map so that the overlay is entirely visible in the current viewport - * (if necessary). - * @private - */ -ol.Overlay.prototype.panIntoView_ = function() { - var map = this.getMap(); - - if (!map || !map.getTargetElement()) { - return; - } - - var mapRect = this.getRect_(map.getTargetElement(), map.getSize()); - var element = /** @type {!Element} */ (this.getElement()); - var overlayRect = this.getRect_(element, - [ol.dom.outerWidth(element), ol.dom.outerHeight(element)]); - - var margin = this.autoPanMargin_; - if (!ol.extent.containsExtent(mapRect, overlayRect)) { - // the overlay is not completely inside the viewport, so pan the map - var offsetLeft = overlayRect[0] - mapRect[0]; - var offsetRight = mapRect[2] - overlayRect[2]; - var offsetTop = overlayRect[1] - mapRect[1]; - var offsetBottom = mapRect[3] - overlayRect[3]; - - var delta = [0, 0]; - if (offsetLeft < 0) { - // move map to the left - delta[0] = offsetLeft - margin; - } else if (offsetRight < 0) { - // move map to the right - delta[0] = Math.abs(offsetRight) + margin; - } - if (offsetTop < 0) { - // move map up - delta[1] = offsetTop - margin; - } else if (offsetBottom < 0) { - // move map down - delta[1] = Math.abs(offsetBottom) + margin; - } - - if (delta[0] !== 0 || delta[1] !== 0) { - var center = /** @type {ol.Coordinate} */ (map.getView().getCenter()); - var centerPx = map.getPixelFromCoordinate(center); - var newCenterPx = [ - centerPx[0] + delta[0], - centerPx[1] + delta[1] - ]; - - map.getView().animate({ - center: map.getCoordinateFromPixel(newCenterPx), - duration: this.autoPanAnimation_.duration, - easing: this.autoPanAnimation_.easing - }); - } - } -}; - - -/** - * Get the extent of an element relative to the document - * @param {Element|undefined} element The element. - * @param {ol.Size|undefined} size The size of the element. - * @return {ol.Extent} The extent. - * @private - */ -ol.Overlay.prototype.getRect_ = function(element, size) { - var box = element.getBoundingClientRect(); - var offsetX = box.left + window.pageXOffset; - var offsetY = box.top + window.pageYOffset; - return [ - offsetX, - offsetY, - offsetX + size[0], - offsetY + size[1] - ]; -}; - - -/** - * Set the positioning for this overlay. - * @param {ol.OverlayPositioning} positioning how the overlay is - * positioned relative to its point on the map. - * @observable - * @api - */ -ol.Overlay.prototype.setPositioning = function(positioning) { - this.set(ol.Overlay.Property_.POSITIONING, positioning); -}; - - -/** - * Modify the visibility of the element. - * @param {boolean} visible Element visibility. - * @protected - */ -ol.Overlay.prototype.setVisible = function(visible) { - if (this.rendered_.visible !== visible) { - this.element_.style.display = visible ? '' : 'none'; - this.rendered_.visible = visible; - } -}; - - -/** - * Update pixel position. - * @protected - */ -ol.Overlay.prototype.updatePixelPosition = function() { - var map = this.getMap(); - var position = this.getPosition(); - if (!map || !map.isRendered() || !position) { - this.setVisible(false); - return; - } - - var pixel = map.getPixelFromCoordinate(position); - var mapSize = map.getSize(); - this.updateRenderedPosition(pixel, mapSize); -}; - - -/** - * @param {ol.Pixel} pixel The pixel location. - * @param {ol.Size|undefined} mapSize The map size. - * @protected - */ -ol.Overlay.prototype.updateRenderedPosition = function(pixel, mapSize) { - var style = this.element_.style; - var offset = this.getOffset(); - - var positioning = this.getPositioning(); - - this.setVisible(true); - - var offsetX = offset[0]; - var offsetY = offset[1]; - if (positioning == ol.OverlayPositioning.BOTTOM_RIGHT || - positioning == ol.OverlayPositioning.CENTER_RIGHT || - positioning == ol.OverlayPositioning.TOP_RIGHT) { - if (this.rendered_.left_ !== '') { - this.rendered_.left_ = style.left = ''; - } - var right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px'; - if (this.rendered_.right_ != right) { - this.rendered_.right_ = style.right = right; - } - } else { - if (this.rendered_.right_ !== '') { - this.rendered_.right_ = style.right = ''; - } - if (positioning == ol.OverlayPositioning.BOTTOM_CENTER || - positioning == ol.OverlayPositioning.CENTER_CENTER || - positioning == ol.OverlayPositioning.TOP_CENTER) { - offsetX -= this.element_.offsetWidth / 2; - } - var left = Math.round(pixel[0] + offsetX) + 'px'; - if (this.rendered_.left_ != left) { - this.rendered_.left_ = style.left = left; - } - } - if (positioning == ol.OverlayPositioning.BOTTOM_LEFT || - positioning == ol.OverlayPositioning.BOTTOM_CENTER || - positioning == ol.OverlayPositioning.BOTTOM_RIGHT) { - if (this.rendered_.top_ !== '') { - this.rendered_.top_ = style.top = ''; - } - var bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px'; - if (this.rendered_.bottom_ != bottom) { - this.rendered_.bottom_ = style.bottom = bottom; - } - } else { - if (this.rendered_.bottom_ !== '') { - this.rendered_.bottom_ = style.bottom = ''; - } - if (positioning == ol.OverlayPositioning.CENTER_LEFT || - positioning == ol.OverlayPositioning.CENTER_CENTER || - positioning == ol.OverlayPositioning.CENTER_RIGHT) { - offsetY -= this.element_.offsetHeight / 2; - } - var top = Math.round(pixel[1] + offsetY) + 'px'; - if (this.rendered_.top_ != top) { - this.rendered_.top_ = style.top = top; - } - } -}; - - -/** - * @enum {string} - * @private - */ -ol.Overlay.Property_ = { - ELEMENT: 'element', - MAP: 'map', - OFFSET: 'offset', - POSITION: 'position', - POSITIONING: 'positioning' -}; - -goog.provide('ol.control.OverviewMap'); - -goog.require('ol'); -goog.require('ol.Collection'); -goog.require('ol.Map'); -goog.require('ol.MapEventType'); -goog.require('ol.MapProperty'); -goog.require('ol.Object'); -goog.require('ol.ObjectEventType'); -goog.require('ol.Overlay'); -goog.require('ol.OverlayPositioning'); -goog.require('ol.ViewProperty'); -goog.require('ol.control.Control'); -goog.require('ol.coordinate'); -goog.require('ol.css'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); - - -/** - * Create a new control with a map acting as an overview map for an other - * defined map. - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.OverviewMapOptions=} opt_options OverviewMap options. - * @api - */ -ol.control.OverviewMap = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - /** - * @type {boolean} - * @private - */ - this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; - - /** - * @private - * @type {boolean} - */ - this.collapsible_ = options.collapsible !== undefined ? - options.collapsible : true; - - if (!this.collapsible_) { - this.collapsed_ = false; - } - - var className = options.className !== undefined ? options.className : 'ol-overviewmap'; - - var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map'; - - var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB'; - - if (typeof collapseLabel === 'string') { - /** - * @private - * @type {Node} - */ - this.collapseLabel_ = document.createElement('span'); - this.collapseLabel_.textContent = collapseLabel; - } else { - this.collapseLabel_ = collapseLabel; - } - - var label = options.label !== undefined ? options.label : '\u00BB'; - - - if (typeof label === 'string') { - /** - * @private - * @type {Node} - */ - this.label_ = document.createElement('span'); - this.label_.textContent = label; - } else { - this.label_ = label; - } - - var activeLabel = (this.collapsible_ && !this.collapsed_) ? - this.collapseLabel_ : this.label_; - var button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(activeLabel); - - ol.events.listen(button, ol.events.EventType.CLICK, - this.handleClick_, this); - - /** - * @type {Element} - * @private - */ - this.ovmapDiv_ = document.createElement('DIV'); - this.ovmapDiv_.className = 'ol-overviewmap-map'; - - /** - * @type {ol.Map} - * @private - */ - this.ovmap_ = new ol.Map({ - controls: new ol.Collection(), - interactions: new ol.Collection(), - view: options.view - }); - var ovmap = this.ovmap_; - - if (options.layers) { - options.layers.forEach( - /** - * @param {ol.layer.Layer} layer Layer. - */ - function(layer) { - ovmap.addLayer(layer); - }, this); - } - - var box = document.createElement('DIV'); - box.className = 'ol-overviewmap-box'; - box.style.boxSizing = 'border-box'; - - /** - * @type {ol.Overlay} - * @private - */ - this.boxOverlay_ = new ol.Overlay({ - position: [0, 0], - positioning: ol.OverlayPositioning.BOTTOM_LEFT, - element: box - }); - this.ovmap_.addOverlay(this.boxOverlay_); - - var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + - ol.css.CLASS_CONTROL + - (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') + - (this.collapsible_ ? '' : ' ol-uncollapsible'); - var element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(this.ovmapDiv_); - element.appendChild(button); - - var render = options.render ? options.render : ol.control.OverviewMap.render; - - ol.control.Control.call(this, { - element: element, - render: render, - target: options.target - }); - - /* Interactive map */ - - var scope = this; - - var overlay = this.boxOverlay_; - var overlayBox = this.boxOverlay_.getElement(); - - /* Functions definition */ - - var computeDesiredMousePosition = function(mousePosition) { - return { - clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2), - clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2) - }; - }; - - var move = function(event) { - var coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event)); - - overlay.setPosition(coordinates); - }; - - var endMoving = function(event) { - var coordinates = ovmap.getEventCoordinate(event); - - scope.getMap().getView().setCenter(coordinates); - - window.removeEventListener('mousemove', move); - window.removeEventListener('mouseup', endMoving); - }; - - /* Binding */ - - overlayBox.addEventListener('mousedown', function() { - window.addEventListener('mousemove', move); - window.addEventListener('mouseup', endMoving); - }); -}; -ol.inherits(ol.control.OverviewMap, ol.control.Control); - - -/** - * @inheritDoc - * @api - */ -ol.control.OverviewMap.prototype.setMap = function(map) { - var oldMap = this.getMap(); - if (map === oldMap) { - return; - } - if (oldMap) { - var oldView = oldMap.getView(); - if (oldView) { - this.unbindView_(oldView); - } - this.ovmap_.setTarget(null); - } - ol.control.Control.prototype.setMap.call(this, map); - - if (map) { - this.ovmap_.setTarget(this.ovmapDiv_); - this.listenerKeys.push(ol.events.listen( - map, ol.ObjectEventType.PROPERTYCHANGE, - this.handleMapPropertyChange_, this)); - - // TODO: to really support map switching, this would need to be reworked - if (this.ovmap_.getLayers().getLength() === 0) { - this.ovmap_.setLayerGroup(map.getLayerGroup()); - } - - var view = map.getView(); - if (view) { - this.bindView_(view); - if (view.isDef()) { - this.ovmap_.updateSize(); - this.resetExtent_(); - } - } - } -}; - - -/** - * Handle map property changes. This only deals with changes to the map's view. - * @param {ol.Object.Event} event The propertychange event. - * @private - */ -ol.control.OverviewMap.prototype.handleMapPropertyChange_ = function(event) { - if (event.key === ol.MapProperty.VIEW) { - var oldView = /** @type {ol.View} */ (event.oldValue); - if (oldView) { - this.unbindView_(oldView); - } - var newView = this.getMap().getView(); - this.bindView_(newView); - } -}; - - -/** - * Register listeners for view property changes. - * @param {ol.View} view The view. - * @private - */ -ol.control.OverviewMap.prototype.bindView_ = function(view) { - ol.events.listen(view, - ol.Object.getChangeEventType(ol.ViewProperty.ROTATION), - this.handleRotationChanged_, this); -}; - - -/** - * Unregister listeners for view property changes. - * @param {ol.View} view The view. - * @private - */ -ol.control.OverviewMap.prototype.unbindView_ = function(view) { - ol.events.unlisten(view, - ol.Object.getChangeEventType(ol.ViewProperty.ROTATION), - this.handleRotationChanged_, this); -}; - - -/** - * Handle rotation changes to the main map. - * TODO: This should rotate the extent rectrangle instead of the - * overview map's view. - * @private - */ -ol.control.OverviewMap.prototype.handleRotationChanged_ = function() { - this.ovmap_.getView().setRotation(this.getMap().getView().getRotation()); -}; - - -/** - * Update the overview map element. - * @param {ol.MapEvent} mapEvent Map event. - * @this {ol.control.OverviewMap} - * @api - */ -ol.control.OverviewMap.render = function(mapEvent) { - this.validateExtent_(); - this.updateBox_(); -}; - - -/** - * Reset the overview map extent if the box size (width or - * height) is less than the size of the overview map size times minRatio - * or is greater than the size of the overview size times maxRatio. - * - * If the map extent was not reset, the box size can fits in the defined - * ratio sizes. This method then checks if is contained inside the overview - * map current extent. If not, recenter the overview map to the current - * main map center location. - * @private - */ -ol.control.OverviewMap.prototype.validateExtent_ = function() { - var map = this.getMap(); - var ovmap = this.ovmap_; - - if (!map.isRendered() || !ovmap.isRendered()) { - return; - } - - var mapSize = /** @type {ol.Size} */ (map.getSize()); - - var view = map.getView(); - var extent = view.calculateExtent(mapSize); - - var ovmapSize = /** @type {ol.Size} */ (ovmap.getSize()); - - var ovview = ovmap.getView(); - var ovextent = ovview.calculateExtent(ovmapSize); - - var topLeftPixel = - ovmap.getPixelFromCoordinate(ol.extent.getTopLeft(extent)); - var bottomRightPixel = - ovmap.getPixelFromCoordinate(ol.extent.getBottomRight(extent)); - - var boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]); - var boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]); - - var ovmapWidth = ovmapSize[0]; - var ovmapHeight = ovmapSize[1]; - - if (boxWidth < ovmapWidth * ol.OVERVIEWMAP_MIN_RATIO || - boxHeight < ovmapHeight * ol.OVERVIEWMAP_MIN_RATIO || - boxWidth > ovmapWidth * ol.OVERVIEWMAP_MAX_RATIO || - boxHeight > ovmapHeight * ol.OVERVIEWMAP_MAX_RATIO) { - this.resetExtent_(); - } else if (!ol.extent.containsExtent(ovextent, extent)) { - this.recenter_(); - } -}; - - -/** - * Reset the overview map extent to half calculated min and max ratio times - * the extent of the main map. - * @private - */ -ol.control.OverviewMap.prototype.resetExtent_ = function() { - if (ol.OVERVIEWMAP_MAX_RATIO === 0 || ol.OVERVIEWMAP_MIN_RATIO === 0) { - return; - } - - var map = this.getMap(); - var ovmap = this.ovmap_; - - var mapSize = /** @type {ol.Size} */ (map.getSize()); - - var view = map.getView(); - var extent = view.calculateExtent(mapSize); - - var ovview = ovmap.getView(); - - // get how many times the current map overview could hold different - // box sizes using the min and max ratio, pick the step in the middle used - // to calculate the extent from the main map to set it to the overview map, - var steps = Math.log( - ol.OVERVIEWMAP_MAX_RATIO / ol.OVERVIEWMAP_MIN_RATIO) / Math.LN2; - var ratio = 1 / (Math.pow(2, steps / 2) * ol.OVERVIEWMAP_MIN_RATIO); - ol.extent.scaleFromCenter(extent, ratio); - ovview.fit(extent); -}; - - -/** - * Set the center of the overview map to the map center without changing its - * resolution. - * @private - */ -ol.control.OverviewMap.prototype.recenter_ = function() { - var map = this.getMap(); - var ovmap = this.ovmap_; - - var view = map.getView(); - - var ovview = ovmap.getView(); - - ovview.setCenter(view.getCenter()); -}; - - -/** - * Update the box using the main map extent - * @private - */ -ol.control.OverviewMap.prototype.updateBox_ = function() { - var map = this.getMap(); - var ovmap = this.ovmap_; - - if (!map.isRendered() || !ovmap.isRendered()) { - return; - } - - var mapSize = /** @type {ol.Size} */ (map.getSize()); - - var view = map.getView(); - - var ovview = ovmap.getView(); - - var rotation = view.getRotation(); - - var overlay = this.boxOverlay_; - var box = this.boxOverlay_.getElement(); - var extent = view.calculateExtent(mapSize); - var ovresolution = ovview.getResolution(); - var bottomLeft = ol.extent.getBottomLeft(extent); - var topRight = ol.extent.getTopRight(extent); - - // set position using bottom left coordinates - var rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft); - overlay.setPosition(rotateBottomLeft); - - // set box size calculated from map extent size and overview map resolution - if (box) { - box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px'; - box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px'; - } -}; - - -/** - * @param {number} rotation Target rotation. - * @param {ol.Coordinate} coordinate Coordinate. - * @return {ol.Coordinate|undefined} Coordinate for rotation and center anchor. - * @private - */ -ol.control.OverviewMap.prototype.calculateCoordinateRotate_ = function( - rotation, coordinate) { - var coordinateRotate; - - var map = this.getMap(); - var view = map.getView(); - - var currentCenter = view.getCenter(); - - if (currentCenter) { - coordinateRotate = [ - coordinate[0] - currentCenter[0], - coordinate[1] - currentCenter[1] - ]; - ol.coordinate.rotate(coordinateRotate, rotation); - ol.coordinate.add(coordinateRotate, currentCenter); - } - return coordinateRotate; -}; - - -/** - * @param {Event} event The event to handle - * @private - */ -ol.control.OverviewMap.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleToggle_(); -}; - - -/** - * @private - */ -ol.control.OverviewMap.prototype.handleToggle_ = function() { - this.element.classList.toggle('ol-collapsed'); - if (this.collapsed_) { - ol.dom.replaceNode(this.collapseLabel_, this.label_); - } else { - ol.dom.replaceNode(this.label_, this.collapseLabel_); - } - this.collapsed_ = !this.collapsed_; - - // manage overview map if it had not been rendered before and control - // is expanded - var ovmap = this.ovmap_; - if (!this.collapsed_ && !ovmap.isRendered()) { - ovmap.updateSize(); - this.resetExtent_(); - ol.events.listenOnce(ovmap, ol.MapEventType.POSTRENDER, - function(event) { - this.updateBox_(); - }, - this); - } -}; - - -/** - * Return `true` if the overview map is collapsible, `false` otherwise. - * @return {boolean} True if the widget is collapsible. - * @api - */ -ol.control.OverviewMap.prototype.getCollapsible = function() { - return this.collapsible_; -}; - - -/** - * Set whether the overview map should be collapsible. - * @param {boolean} collapsible True if the widget is collapsible. - * @api - */ -ol.control.OverviewMap.prototype.setCollapsible = function(collapsible) { - if (this.collapsible_ === collapsible) { - return; - } - this.collapsible_ = collapsible; - this.element.classList.toggle('ol-uncollapsible'); - if (!collapsible && this.collapsed_) { - this.handleToggle_(); - } -}; - - -/** - * Collapse or expand the overview map according to the passed parameter. Will - * not do anything if the overview map isn't collapsible or if the current - * collapsed state is already the one requested. - * @param {boolean} collapsed True if the widget is collapsed. - * @api - */ -ol.control.OverviewMap.prototype.setCollapsed = function(collapsed) { - if (!this.collapsible_ || this.collapsed_ === collapsed) { - return; - } - this.handleToggle_(); -}; - - -/** - * Determine if the overview map is collapsed. - * @return {boolean} The overview map is collapsed. - * @api - */ -ol.control.OverviewMap.prototype.getCollapsed = function() { - return this.collapsed_; -}; - - -/** - * Return the overview map. - * @return {ol.Map} Overview map. - * @api - */ -ol.control.OverviewMap.prototype.getOverviewMap = function() { - return this.ovmap_; -}; - -goog.provide('ol.control.ScaleLineUnits'); - -/** - * Units for the scale line. Supported values are `'degrees'`, `'imperial'`, - * `'nautical'`, `'metric'`, `'us'`. - * @enum {string} - */ -ol.control.ScaleLineUnits = { - DEGREES: 'degrees', - IMPERIAL: 'imperial', - NAUTICAL: 'nautical', - METRIC: 'metric', - US: 'us' -}; - -goog.provide('ol.control.ScaleLine'); - -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.asserts'); -goog.require('ol.control.Control'); -goog.require('ol.control.ScaleLineUnits'); -goog.require('ol.css'); -goog.require('ol.events'); -goog.require('ol.proj'); -goog.require('ol.proj.Units'); - - -/** - * @classdesc - * A control displaying rough y-axis distances, calculated for the center of the - * viewport. For conformal projections (e.g. EPSG:3857, the default view - * projection in OpenLayers), the scale is valid for all directions. - * No scale line will be shown when the y-axis distance of a pixel at the - * viewport center cannot be calculated in the view projection. - * By default the scale line will show in the bottom left portion of the map, - * but this can be changed by using the css selector `.ol-scale-line`. - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.ScaleLineOptions=} opt_options Scale line options. - * @api - */ -ol.control.ScaleLine = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - var className = options.className !== undefined ? options.className : 'ol-scale-line'; - - /** - * @private - * @type {Element} - */ - this.innerElement_ = document.createElement('DIV'); - this.innerElement_.className = className + '-inner'; - - /** - * @private - * @type {Element} - */ - this.element_ = document.createElement('DIV'); - this.element_.className = className + ' ' + ol.css.CLASS_UNSELECTABLE; - this.element_.appendChild(this.innerElement_); - - /** - * @private - * @type {?olx.ViewState} - */ - this.viewState_ = null; - - /** - * @private - * @type {number} - */ - this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64; - - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = false; - - /** - * @private - * @type {number|undefined} - */ - this.renderedWidth_ = undefined; - - /** - * @private - * @type {string} - */ - this.renderedHTML_ = ''; - - var render = options.render ? options.render : ol.control.ScaleLine.render; - - ol.control.Control.call(this, { - element: this.element_, - render: render, - target: options.target - }); - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.control.ScaleLine.Property_.UNITS), - this.handleUnitsChanged_, this); - - this.setUnits(/** @type {ol.control.ScaleLineUnits} */ (options.units) || - ol.control.ScaleLineUnits.METRIC); - -}; -ol.inherits(ol.control.ScaleLine, ol.control.Control); - - -/** - * @const - * @type {Array.<number>} - */ -ol.control.ScaleLine.LEADING_DIGITS = [1, 2, 5]; - - -/** - * Return the units to use in the scale line. - * @return {ol.control.ScaleLineUnits|undefined} The units to use in the scale - * line. - * @observable - * @api - */ -ol.control.ScaleLine.prototype.getUnits = function() { - return /** @type {ol.control.ScaleLineUnits|undefined} */ ( - this.get(ol.control.ScaleLine.Property_.UNITS)); -}; - - -/** - * Update the scale line element. - * @param {ol.MapEvent} mapEvent Map event. - * @this {ol.control.ScaleLine} - * @api - */ -ol.control.ScaleLine.render = function(mapEvent) { - var frameState = mapEvent.frameState; - if (!frameState) { - this.viewState_ = null; - } else { - this.viewState_ = frameState.viewState; - } - this.updateElement_(); -}; - - -/** - * @private - */ -ol.control.ScaleLine.prototype.handleUnitsChanged_ = function() { - this.updateElement_(); -}; - - -/** - * Set the units to use in the scale line. - * @param {ol.control.ScaleLineUnits} units The units to use in the scale line. - * @observable - * @api - */ -ol.control.ScaleLine.prototype.setUnits = function(units) { - this.set(ol.control.ScaleLine.Property_.UNITS, units); -}; - - -/** - * @private - */ -ol.control.ScaleLine.prototype.updateElement_ = function() { - var viewState = this.viewState_; - - if (!viewState) { - if (this.renderedVisible_) { - this.element_.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - var center = viewState.center; - var projection = viewState.projection; - var metersPerUnit = projection.getMetersPerUnit(); - var pointResolution = - ol.proj.getPointResolution(projection, viewState.resolution, center) * - metersPerUnit; - - var nominalCount = this.minWidth_ * pointResolution; - var suffix = ''; - var units = this.getUnits(); - if (units == ol.control.ScaleLineUnits.DEGREES) { - var metersPerDegree = ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES]; - pointResolution /= metersPerDegree; - if (nominalCount < metersPerDegree / 60) { - suffix = '\u2033'; // seconds - pointResolution *= 3600; - } else if (nominalCount < metersPerDegree) { - suffix = '\u2032'; // minutes - pointResolution *= 60; - } else { - suffix = '\u00b0'; // degrees - } - } else if (units == ol.control.ScaleLineUnits.IMPERIAL) { - if (nominalCount < 0.9144) { - suffix = 'in'; - pointResolution /= 0.0254; - } else if (nominalCount < 1609.344) { - suffix = 'ft'; - pointResolution /= 0.3048; - } else { - suffix = 'mi'; - pointResolution /= 1609.344; - } - } else if (units == ol.control.ScaleLineUnits.NAUTICAL) { - pointResolution /= 1852; - suffix = 'nm'; - } else if (units == ol.control.ScaleLineUnits.METRIC) { - if (nominalCount < 0.001) { - suffix = 'μm'; - pointResolution *= 1000000; - } else if (nominalCount < 1) { - suffix = 'mm'; - pointResolution *= 1000; - } else if (nominalCount < 1000) { - suffix = 'm'; - } else { - suffix = 'km'; - pointResolution /= 1000; - } - } else if (units == ol.control.ScaleLineUnits.US) { - if (nominalCount < 0.9144) { - suffix = 'in'; - pointResolution *= 39.37; - } else if (nominalCount < 1609.344) { - suffix = 'ft'; - pointResolution /= 0.30480061; - } else { - suffix = 'mi'; - pointResolution /= 1609.3472; - } - } else { - ol.asserts.assert(false, 33); // Invalid units - } - - var i = 3 * Math.floor( - Math.log(this.minWidth_ * pointResolution) / Math.log(10)); - var count, width; - while (true) { - count = ol.control.ScaleLine.LEADING_DIGITS[((i % 3) + 3) % 3] * - Math.pow(10, Math.floor(i / 3)); - width = Math.round(count / pointResolution); - if (isNaN(width)) { - this.element_.style.display = 'none'; - this.renderedVisible_ = false; - return; - } else if (width >= this.minWidth_) { - break; - } - ++i; - } - - var html = count + ' ' + suffix; - if (this.renderedHTML_ != html) { - this.innerElement_.innerHTML = html; - this.renderedHTML_ = html; - } - - if (this.renderedWidth_ != width) { - this.innerElement_.style.width = width + 'px'; - this.renderedWidth_ = width; - } - - if (!this.renderedVisible_) { - this.element_.style.display = ''; - this.renderedVisible_ = true; - } - -}; - - -/** - * @enum {string} - * @private - */ -ol.control.ScaleLine.Property_ = { - UNITS: 'units' -}; - -// FIXME should possibly show tooltip when dragging? - -goog.provide('ol.control.ZoomSlider'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.control.Control'); -goog.require('ol.css'); -goog.require('ol.easing'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.EventType'); -goog.require('ol.math'); -goog.require('ol.pointer.EventType'); -goog.require('ol.pointer.PointerEventHandler'); - - -/** - * @classdesc - * A slider type of control for zooming. - * - * Example: - * - * map.addControl(new ol.control.ZoomSlider()); - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.ZoomSliderOptions=} opt_options Zoom slider options. - * @api - */ -ol.control.ZoomSlider = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - /** - * Will hold the current resolution of the view. - * - * @type {number|undefined} - * @private - */ - this.currentResolution_ = undefined; - - /** - * The direction of the slider. Will be determined from actual display of the - * container and defaults to ol.control.ZoomSlider.Direction_.VERTICAL. - * - * @type {ol.control.ZoomSlider.Direction_} - * @private - */ - this.direction_ = ol.control.ZoomSlider.Direction_.VERTICAL; - - /** - * @type {boolean} - * @private - */ - this.dragging_; - - /** - * @type {number} - * @private - */ - this.heightLimit_ = 0; - - /** - * @type {number} - * @private - */ - this.widthLimit_ = 0; - - /** - * @type {number|undefined} - * @private - */ - this.previousX_; - - /** - * @type {number|undefined} - * @private - */ - this.previousY_; - - /** - * The calculated thumb size (border box plus margins). Set when initSlider_ - * is called. - * @type {ol.Size} - * @private - */ - this.thumbSize_ = null; - - /** - * Whether the slider is initialized. - * @type {boolean} - * @private - */ - this.sliderInitialized_ = false; - - /** - * @type {number} - * @private - */ - this.duration_ = options.duration !== undefined ? options.duration : 200; - - var className = options.className !== undefined ? options.className : 'ol-zoomslider'; - var thumbElement = document.createElement('button'); - thumbElement.setAttribute('type', 'button'); - thumbElement.className = className + '-thumb ' + ol.css.CLASS_UNSELECTABLE; - var containerElement = document.createElement('div'); - containerElement.className = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + ol.css.CLASS_CONTROL; - containerElement.appendChild(thumbElement); - /** - * @type {ol.pointer.PointerEventHandler} - * @private - */ - this.dragger_ = new ol.pointer.PointerEventHandler(containerElement); - - ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERDOWN, - this.handleDraggerStart_, this); - ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERMOVE, - this.handleDraggerDrag_, this); - ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERUP, - this.handleDraggerEnd_, this); - - ol.events.listen(containerElement, ol.events.EventType.CLICK, - this.handleContainerClick_, this); - ol.events.listen(thumbElement, ol.events.EventType.CLICK, - ol.events.Event.stopPropagation); - - var render = options.render ? options.render : ol.control.ZoomSlider.render; - - ol.control.Control.call(this, { - element: containerElement, - render: render - }); -}; -ol.inherits(ol.control.ZoomSlider, ol.control.Control); - - -/** - * @inheritDoc - */ -ol.control.ZoomSlider.prototype.disposeInternal = function() { - this.dragger_.dispose(); - ol.control.Control.prototype.disposeInternal.call(this); -}; - - -/** - * The enum for available directions. - * - * @enum {number} - * @private - */ -ol.control.ZoomSlider.Direction_ = { - VERTICAL: 0, - HORIZONTAL: 1 -}; - - -/** - * @inheritDoc - */ -ol.control.ZoomSlider.prototype.setMap = function(map) { - ol.control.Control.prototype.setMap.call(this, map); - if (map) { - map.render(); - } -}; - - -/** - * Initializes the slider element. This will determine and set this controls - * direction_ and also constrain the dragging of the thumb to always be within - * the bounds of the container. - * - * @private - */ -ol.control.ZoomSlider.prototype.initSlider_ = function() { - var container = this.element; - var containerSize = { - width: container.offsetWidth, height: container.offsetHeight - }; - - var thumb = container.firstElementChild; - var computedStyle = getComputedStyle(thumb); - var thumbWidth = thumb.offsetWidth + - parseFloat(computedStyle['marginRight']) + - parseFloat(computedStyle['marginLeft']); - var thumbHeight = thumb.offsetHeight + - parseFloat(computedStyle['marginTop']) + - parseFloat(computedStyle['marginBottom']); - this.thumbSize_ = [thumbWidth, thumbHeight]; - - if (containerSize.width > containerSize.height) { - this.direction_ = ol.control.ZoomSlider.Direction_.HORIZONTAL; - this.widthLimit_ = containerSize.width - thumbWidth; - } else { - this.direction_ = ol.control.ZoomSlider.Direction_.VERTICAL; - this.heightLimit_ = containerSize.height - thumbHeight; - } - this.sliderInitialized_ = true; -}; - - -/** - * Update the zoomslider element. - * @param {ol.MapEvent} mapEvent Map event. - * @this {ol.control.ZoomSlider} - * @api - */ -ol.control.ZoomSlider.render = function(mapEvent) { - if (!mapEvent.frameState) { - return; - } - if (!this.sliderInitialized_) { - this.initSlider_(); - } - var res = mapEvent.frameState.viewState.resolution; - if (res !== this.currentResolution_) { - this.currentResolution_ = res; - this.setThumbPosition_(res); - } -}; - - -/** - * @param {Event} event The browser event to handle. - * @private - */ -ol.control.ZoomSlider.prototype.handleContainerClick_ = function(event) { - var view = this.getMap().getView(); - - var relativePosition = this.getRelativePosition_( - event.offsetX - this.thumbSize_[0] / 2, - event.offsetY - this.thumbSize_[1] / 2); - - var resolution = this.getResolutionForPosition_(relativePosition); - - view.animate({ - resolution: view.constrainResolution(resolution), - duration: this.duration_, - easing: ol.easing.easeOut - }); -}; - - -/** - * Handle dragger start events. - * @param {ol.pointer.PointerEvent} event The drag event. - * @private - */ -ol.control.ZoomSlider.prototype.handleDraggerStart_ = function(event) { - if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) { - this.getMap().getView().setHint(ol.ViewHint.INTERACTING, 1); - this.previousX_ = event.clientX; - this.previousY_ = event.clientY; - this.dragging_ = true; - } -}; - - -/** - * Handle dragger drag events. - * - * @param {ol.pointer.PointerEvent|Event} event The drag event. - * @private - */ -ol.control.ZoomSlider.prototype.handleDraggerDrag_ = function(event) { - if (this.dragging_) { - var element = this.element.firstElementChild; - var deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10); - var deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10); - var relativePosition = this.getRelativePosition_(deltaX, deltaY); - this.currentResolution_ = this.getResolutionForPosition_(relativePosition); - this.getMap().getView().setResolution(this.currentResolution_); - this.setThumbPosition_(this.currentResolution_); - this.previousX_ = event.clientX; - this.previousY_ = event.clientY; - } -}; - - -/** - * Handle dragger end events. - * @param {ol.pointer.PointerEvent|Event} event The drag event. - * @private - */ -ol.control.ZoomSlider.prototype.handleDraggerEnd_ = function(event) { - if (this.dragging_) { - var view = this.getMap().getView(); - view.setHint(ol.ViewHint.INTERACTING, -1); - - view.animate({ - resolution: view.constrainResolution(this.currentResolution_), - duration: this.duration_, - easing: ol.easing.easeOut - }); - - this.dragging_ = false; - this.previousX_ = undefined; - this.previousY_ = undefined; - } -}; - - -/** - * Positions the thumb inside its container according to the given resolution. - * - * @param {number} res The res. - * @private - */ -ol.control.ZoomSlider.prototype.setThumbPosition_ = function(res) { - var position = this.getPositionForResolution_(res); - var thumb = this.element.firstElementChild; - - if (this.direction_ == ol.control.ZoomSlider.Direction_.HORIZONTAL) { - thumb.style.left = this.widthLimit_ * position + 'px'; - } else { - thumb.style.top = this.heightLimit_ * position + 'px'; - } -}; - - -/** - * Calculates the relative position of the thumb given x and y offsets. The - * relative position scales from 0 to 1. The x and y offsets are assumed to be - * in pixel units within the dragger limits. - * - * @param {number} x Pixel position relative to the left of the slider. - * @param {number} y Pixel position relative to the top of the slider. - * @return {number} The relative position of the thumb. - * @private - */ -ol.control.ZoomSlider.prototype.getRelativePosition_ = function(x, y) { - var amount; - if (this.direction_ === ol.control.ZoomSlider.Direction_.HORIZONTAL) { - amount = x / this.widthLimit_; - } else { - amount = y / this.heightLimit_; - } - return ol.math.clamp(amount, 0, 1); -}; - - -/** - * Calculates the corresponding resolution of the thumb given its relative - * position (where 0 is the minimum and 1 is the maximum). - * - * @param {number} position The relative position of the thumb. - * @return {number} The corresponding resolution. - * @private - */ -ol.control.ZoomSlider.prototype.getResolutionForPosition_ = function(position) { - var fn = this.getMap().getView().getResolutionForValueFunction(); - return fn(1 - position); -}; - - -/** - * Determines the relative position of the slider for the given resolution. A - * relative position of 0 corresponds to the minimum view resolution. A - * relative position of 1 corresponds to the maximum view resolution. - * - * @param {number} res The resolution. - * @return {number} The relative position value (between 0 and 1). - * @private - */ -ol.control.ZoomSlider.prototype.getPositionForResolution_ = function(res) { - var fn = this.getMap().getView().getValueForResolutionFunction(); - return 1 - fn(res); -}; - -goog.provide('ol.control.ZoomToExtent'); - -goog.require('ol'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.control.Control'); -goog.require('ol.css'); - - -/** - * @classdesc - * A button control which, when pressed, changes the map view to a specific - * extent. To style this control use the css selector `.ol-zoom-extent`. - * - * @constructor - * @extends {ol.control.Control} - * @param {olx.control.ZoomToExtentOptions=} opt_options Options. - * @api - */ -ol.control.ZoomToExtent = function(opt_options) { - var options = opt_options ? opt_options : {}; - - /** - * @type {ol.Extent} - * @private - */ - this.extent_ = options.extent ? options.extent : null; - - var className = options.className !== undefined ? options.className : - 'ol-zoom-extent'; - - var label = options.label !== undefined ? options.label : 'E'; - var tipLabel = options.tipLabel !== undefined ? - options.tipLabel : 'Fit to extent'; - var button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild( - typeof label === 'string' ? document.createTextNode(label) : label - ); - - ol.events.listen(button, ol.events.EventType.CLICK, - this.handleClick_, this); - - var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + - ol.css.CLASS_CONTROL; - var element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); - - ol.control.Control.call(this, { - element: element, - target: options.target - }); -}; -ol.inherits(ol.control.ZoomToExtent, ol.control.Control); - - -/** - * @param {Event} event The event to handle - * @private - */ -ol.control.ZoomToExtent.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleZoomToExtent_(); -}; - - -/** - * @private - */ -ol.control.ZoomToExtent.prototype.handleZoomToExtent_ = function() { - var map = this.getMap(); - var view = map.getView(); - var extent = !this.extent_ ? view.getProjection().getExtent() : this.extent_; - view.fit(extent); -}; - -goog.provide('ol.DeviceOrientation'); - -goog.require('ol.events'); -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.has'); -goog.require('ol.math'); - - -/** - * @classdesc - * The ol.DeviceOrientation class provides access to information from - * DeviceOrientation events. See the [HTML 5 DeviceOrientation Specification]( - * http://www.w3.org/TR/orientation-event/) for more details. - * - * Many new computers, and especially mobile phones - * and tablets, provide hardware support for device orientation. Web - * developers targeting mobile devices will be especially interested in this - * class. - * - * Device orientation data are relative to a common starting point. For mobile - * devices, the starting point is to lay your phone face up on a table with the - * top of the phone pointing north. This represents the zero state. All - * angles are then relative to this state. For computers, it is the same except - * the screen is open at 90 degrees. - * - * Device orientation is reported as three angles - `alpha`, `beta`, and - * `gamma` - relative to the starting position along the three planar axes X, Y - * and Z. The X axis runs from the left edge to the right edge through the - * middle of the device. Similarly, the Y axis runs from the bottom to the top - * of the device through the middle. The Z axis runs from the back to the front - * through the middle. In the starting position, the X axis points to the - * right, the Y axis points away from you and the Z axis points straight up - * from the device lying flat. - * - * The three angles representing the device orientation are relative to the - * three axes. `alpha` indicates how much the device has been rotated around the - * Z axis, which is commonly interpreted as the compass heading (see note - * below). `beta` indicates how much the device has been rotated around the X - * axis, or how much it is tilted from front to back. `gamma` indicates how - * much the device has been rotated around the Y axis, or how much it is tilted - * from left to right. - * - * For most browsers, the `alpha` value returns the compass heading so if the - * device points north, it will be 0. With Safari on iOS, the 0 value of - * `alpha` is calculated from when device orientation was first requested. - * ol.DeviceOrientation provides the `heading` property which normalizes this - * behavior across all browsers for you. - * - * It is important to note that the HTML 5 DeviceOrientation specification - * indicates that `alpha`, `beta` and `gamma` are in degrees while the - * equivalent properties in ol.DeviceOrientation are in radians for consistency - * with all other uses of angles throughout OpenLayers. - * - * To get notified of device orientation changes, register a listener for the - * generic `change` event on your `ol.DeviceOrientation` instance. - * - * @see {@link http://www.w3.org/TR/orientation-event/} - * - * @constructor - * @extends {ol.Object} - * @param {olx.DeviceOrientationOptions=} opt_options Options. - * @api - */ -ol.DeviceOrientation = function(opt_options) { - - ol.Object.call(this); - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.listenerKey_ = null; - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.DeviceOrientation.Property_.TRACKING), - this.handleTrackingChanged_, this); - - this.setTracking(options.tracking !== undefined ? options.tracking : false); - -}; -ol.inherits(ol.DeviceOrientation, ol.Object); - - -/** - * @inheritDoc - */ -ol.DeviceOrientation.prototype.disposeInternal = function() { - this.setTracking(false); - ol.Object.prototype.disposeInternal.call(this); -}; - - -/** - * @private - * @param {Event} originalEvent Event. - */ -ol.DeviceOrientation.prototype.orientationChange_ = function(originalEvent) { - var event = /** @type {DeviceOrientationEvent} */ (originalEvent); - if (event.alpha !== null) { - var alpha = ol.math.toRadians(event.alpha); - this.set(ol.DeviceOrientation.Property_.ALPHA, alpha); - // event.absolute is undefined in iOS. - if (typeof event.absolute === 'boolean' && event.absolute) { - this.set(ol.DeviceOrientation.Property_.HEADING, alpha); - } else if (typeof event.webkitCompassHeading === 'number' && - event.webkitCompassAccuracy != -1) { - var heading = ol.math.toRadians(event.webkitCompassHeading); - this.set(ol.DeviceOrientation.Property_.HEADING, heading); - } - } - if (event.beta !== null) { - this.set(ol.DeviceOrientation.Property_.BETA, - ol.math.toRadians(event.beta)); - } - if (event.gamma !== null) { - this.set(ol.DeviceOrientation.Property_.GAMMA, - ol.math.toRadians(event.gamma)); - } - this.changed(); -}; - - -/** - * Rotation around the device z-axis (in radians). - * @return {number|undefined} The euler angle in radians of the device from the - * standard Z axis. - * @observable - * @api - */ -ol.DeviceOrientation.prototype.getAlpha = function() { - return /** @type {number|undefined} */ ( - this.get(ol.DeviceOrientation.Property_.ALPHA)); -}; - - -/** - * Rotation around the device x-axis (in radians). - * @return {number|undefined} The euler angle in radians of the device from the - * planar X axis. - * @observable - * @api - */ -ol.DeviceOrientation.prototype.getBeta = function() { - return /** @type {number|undefined} */ ( - this.get(ol.DeviceOrientation.Property_.BETA)); -}; - - -/** - * Rotation around the device y-axis (in radians). - * @return {number|undefined} The euler angle in radians of the device from the - * planar Y axis. - * @observable - * @api - */ -ol.DeviceOrientation.prototype.getGamma = function() { - return /** @type {number|undefined} */ ( - this.get(ol.DeviceOrientation.Property_.GAMMA)); -}; - - -/** - * The heading of the device relative to north (in radians). - * @return {number|undefined} The heading of the device relative to north, in - * radians, normalizing for different browser behavior. - * @observable - * @api - */ -ol.DeviceOrientation.prototype.getHeading = function() { - return /** @type {number|undefined} */ ( - this.get(ol.DeviceOrientation.Property_.HEADING)); -}; - - -/** - * Determine if orientation is being tracked. - * @return {boolean} Changes in device orientation are being tracked. - * @observable - * @api - */ -ol.DeviceOrientation.prototype.getTracking = function() { - return /** @type {boolean} */ ( - this.get(ol.DeviceOrientation.Property_.TRACKING)); -}; - - -/** - * @private - */ -ol.DeviceOrientation.prototype.handleTrackingChanged_ = function() { - if (ol.has.DEVICE_ORIENTATION) { - var tracking = this.getTracking(); - if (tracking && !this.listenerKey_) { - this.listenerKey_ = ol.events.listen(window, 'deviceorientation', - this.orientationChange_, this); - } else if (!tracking && this.listenerKey_ !== null) { - ol.events.unlistenByKey(this.listenerKey_); - this.listenerKey_ = null; - } - } -}; - - -/** - * Enable or disable tracking of device orientation events. - * @param {boolean} tracking The status of tracking changes to alpha, beta and - * gamma. If true, changes are tracked and reported immediately. - * @observable - * @api - */ -ol.DeviceOrientation.prototype.setTracking = function(tracking) { - this.set(ol.DeviceOrientation.Property_.TRACKING, tracking); -}; - - -/** - * @enum {string} - * @private - */ -ol.DeviceOrientation.Property_ = { - ALPHA: 'alpha', - BETA: 'beta', - GAMMA: 'gamma', - HEADING: 'heading', - TRACKING: 'tracking' -}; - -goog.provide('ol.ImageState'); - -/** - * @enum {number} - */ -ol.ImageState = { - IDLE: 0, - LOADING: 1, - LOADED: 2, - ERROR: 3 -}; - -goog.provide('ol.style.Image'); - - -/** - * @classdesc - * A base class used for creating subclasses and not instantiated in - * apps. Base class for {@link ol.style.Icon}, {@link ol.style.Circle} and - * {@link ol.style.RegularShape}. - * - * @constructor - * @abstract - * @param {ol.StyleImageOptions} options Options. - * @api - */ -ol.style.Image = function(options) { - - /** - * @private - * @type {number} - */ - this.opacity_ = options.opacity; - - /** - * @private - * @type {boolean} - */ - this.rotateWithView_ = options.rotateWithView; - - /** - * @private - * @type {number} - */ - this.rotation_ = options.rotation; - - /** - * @private - * @type {number} - */ - this.scale_ = options.scale; - - /** - * @private - * @type {boolean} - */ - this.snapToPixel_ = options.snapToPixel; - -}; - - -/** - * Get the symbolizer opacity. - * @return {number} Opacity. - * @api - */ -ol.style.Image.prototype.getOpacity = function() { - return this.opacity_; -}; - - -/** - * Determine whether the symbolizer rotates with the map. - * @return {boolean} Rotate with map. - * @api - */ -ol.style.Image.prototype.getRotateWithView = function() { - return this.rotateWithView_; -}; - - -/** - * Get the symoblizer rotation. - * @return {number} Rotation. - * @api - */ -ol.style.Image.prototype.getRotation = function() { - return this.rotation_; -}; - - -/** - * Get the symbolizer scale. - * @return {number} Scale. - * @api - */ -ol.style.Image.prototype.getScale = function() { - return this.scale_; -}; - - -/** - * Determine whether the symbolizer should be snapped to a pixel. - * @return {boolean} The symbolizer should snap to a pixel. - * @api - */ -ol.style.Image.prototype.getSnapToPixel = function() { - return this.snapToPixel_; -}; - - -/** - * Get the anchor point in pixels. The anchor determines the center point for the - * symbolizer. - * @abstract - * @return {Array.<number>} Anchor. - */ -ol.style.Image.prototype.getAnchor = function() {}; - - -/** - * Get the image element for the symbolizer. - * @abstract - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element. - */ -ol.style.Image.prototype.getImage = function(pixelRatio) {}; - - -/** - * @abstract - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element. - */ -ol.style.Image.prototype.getHitDetectionImage = function(pixelRatio) {}; - - -/** - * @abstract - * @return {ol.ImageState} Image state. - */ -ol.style.Image.prototype.getImageState = function() {}; - - -/** - * @abstract - * @return {ol.Size} Image size. - */ -ol.style.Image.prototype.getImageSize = function() {}; - - -/** - * @abstract - * @return {ol.Size} Size of the hit-detection image. - */ -ol.style.Image.prototype.getHitDetectionImageSize = function() {}; - - -/** - * Get the origin of the symbolizer. - * @abstract - * @return {Array.<number>} Origin. - */ -ol.style.Image.prototype.getOrigin = function() {}; - - -/** - * Get the size of the symbolizer (in pixels). - * @abstract - * @return {ol.Size} Size. - */ -ol.style.Image.prototype.getSize = function() {}; - - -/** - * Set the opacity. - * - * @param {number} opacity Opacity. - * @api - */ -ol.style.Image.prototype.setOpacity = function(opacity) { - this.opacity_ = opacity; -}; - - -/** - * Set whether to rotate the style with the view. - * - * @param {boolean} rotateWithView Rotate with map. - */ -ol.style.Image.prototype.setRotateWithView = function(rotateWithView) { - this.rotateWithView_ = rotateWithView; -}; - - -/** - * Set the rotation. - * - * @param {number} rotation Rotation. - * @api - */ -ol.style.Image.prototype.setRotation = function(rotation) { - this.rotation_ = rotation; -}; - - -/** - * Set the scale. - * - * @param {number} scale Scale. - * @api - */ -ol.style.Image.prototype.setScale = function(scale) { - this.scale_ = scale; -}; - - -/** - * Set whether to snap the image to the closest pixel. - * - * @param {boolean} snapToPixel Snap to pixel? - */ -ol.style.Image.prototype.setSnapToPixel = function(snapToPixel) { - this.snapToPixel_ = snapToPixel; -}; - - -/** - * @abstract - * @param {function(this: T, ol.events.Event)} listener Listener function. - * @param {T} thisArg Value to use as `this` when executing `listener`. - * @return {ol.EventsKey|undefined} Listener key. - * @template T - */ -ol.style.Image.prototype.listenImageChange = function(listener, thisArg) {}; - - -/** - * Load not yet loaded URI. - * @abstract - */ -ol.style.Image.prototype.load = function() {}; - - -/** - * @abstract - * @param {function(this: T, ol.events.Event)} listener Listener function. - * @param {T} thisArg Value to use as `this` when executing `listener`. - * @template T - */ -ol.style.Image.prototype.unlistenImageChange = function(listener, thisArg) {}; - -goog.provide('ol.style.RegularShape'); - -goog.require('ol'); -goog.require('ol.colorlike'); -goog.require('ol.dom'); -goog.require('ol.has'); -goog.require('ol.ImageState'); -goog.require('ol.render.canvas'); -goog.require('ol.style.Image'); - - -/** - * @classdesc - * Set regular shape style for vector features. The resulting shape will be - * a regular polygon when `radius` is provided, or a star when `radius1` and - * `radius2` are provided. - * - * @constructor - * @param {olx.style.RegularShapeOptions} options Options. - * @extends {ol.style.Image} - * @api - */ -ol.style.RegularShape = function(options) { - /** - * @private - * @type {Array.<string>} - */ - this.checksums_ = null; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.hitDetectionCanvas_ = null; - - /** - * @private - * @type {ol.style.Fill} - */ - this.fill_ = options.fill !== undefined ? options.fill : null; - - /** - * @private - * @type {Array.<number>} - */ - this.origin_ = [0, 0]; - - /** - * @private - * @type {number} - */ - this.points_ = options.points; - - /** - * @protected - * @type {number} - */ - this.radius_ = /** @type {number} */ (options.radius !== undefined ? - options.radius : options.radius1); - - /** - * @private - * @type {number|undefined} - */ - this.radius2_ = options.radius2; - - /** - * @private - * @type {number} - */ - this.angle_ = options.angle !== undefined ? options.angle : 0; - - /** - * @private - * @type {ol.style.Stroke} - */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; - - /** - * @private - * @type {Array.<number>} - */ - this.anchor_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.size_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.imageSize_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.hitDetectionImageSize_ = null; - - /** - * @protected - * @type {ol.style.AtlasManager|undefined} - */ - this.atlasManager_ = options.atlasManager; - - this.render_(this.atlasManager_); - - /** - * @type {boolean} - */ - var snapToPixel = options.snapToPixel !== undefined ? - options.snapToPixel : true; - - /** - * @type {boolean} - */ - var rotateWithView = options.rotateWithView !== undefined ? - options.rotateWithView : false; - - ol.style.Image.call(this, { - opacity: 1, - rotateWithView: rotateWithView, - rotation: options.rotation !== undefined ? options.rotation : 0, - scale: 1, - snapToPixel: snapToPixel - }); -}; -ol.inherits(ol.style.RegularShape, ol.style.Image); - - -/** - * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. - * @return {ol.style.RegularShape} The cloned style. - * @api - */ -ol.style.RegularShape.prototype.clone = function() { - var style = new ol.style.RegularShape({ - fill: this.getFill() ? this.getFill().clone() : undefined, - points: this.getPoints(), - radius: this.getRadius(), - radius2: this.getRadius2(), - angle: this.getAngle(), - snapToPixel: this.getSnapToPixel(), - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - atlasManager: this.atlasManager_ - }); - style.setOpacity(this.getOpacity()); - style.setScale(this.getScale()); - return style; -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.RegularShape.prototype.getAnchor = function() { - return this.anchor_; -}; - - -/** - * Get the angle used in generating the shape. - * @return {number} Shape's rotation in radians. - * @api - */ -ol.style.RegularShape.prototype.getAngle = function() { - return this.angle_; -}; - - -/** - * Get the fill style for the shape. - * @return {ol.style.Fill} Fill style. - * @api - */ -ol.style.RegularShape.prototype.getFill = function() { - return this.fill_; -}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.getHitDetectionImage = function(pixelRatio) { - return this.hitDetectionCanvas_; -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.RegularShape.prototype.getImage = function(pixelRatio) { - return this.canvas_; -}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.getImageSize = function() { - return this.imageSize_; -}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.getHitDetectionImageSize = function() { - return this.hitDetectionImageSize_; -}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.getImageState = function() { - return ol.ImageState.LOADED; -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.RegularShape.prototype.getOrigin = function() { - return this.origin_; -}; - - -/** - * Get the number of points for generating the shape. - * @return {number} Number of points for stars and regular polygons. - * @api - */ -ol.style.RegularShape.prototype.getPoints = function() { - return this.points_; -}; - - -/** - * Get the (primary) radius for the shape. - * @return {number} Radius. - * @api - */ -ol.style.RegularShape.prototype.getRadius = function() { - return this.radius_; -}; - - -/** - * Get the secondary radius for the shape. - * @return {number|undefined} Radius2. - * @api - */ -ol.style.RegularShape.prototype.getRadius2 = function() { - return this.radius2_; -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.RegularShape.prototype.getSize = function() { - return this.size_; -}; - - -/** - * Get the stroke style for the shape. - * @return {ol.style.Stroke} Stroke style. - * @api - */ -ol.style.RegularShape.prototype.getStroke = function() { - return this.stroke_; -}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.listenImageChange = function(listener, thisArg) {}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.load = function() {}; - - -/** - * @inheritDoc - */ -ol.style.RegularShape.prototype.unlistenImageChange = function(listener, thisArg) {}; - - -/** - * @protected - * @param {ol.style.AtlasManager|undefined} atlasManager An atlas manager. - */ -ol.style.RegularShape.prototype.render_ = function(atlasManager) { - var imageSize; - var lineCap = ''; - var lineJoin = ''; - var miterLimit = 0; - var lineDash = null; - var strokeStyle; - var strokeWidth = 0; - - if (this.stroke_) { - strokeStyle = this.stroke_.getColor(); - if (strokeStyle === null) { - strokeStyle = ol.render.canvas.defaultStrokeStyle; - } - strokeStyle = ol.colorlike.asColorLike(strokeStyle); - strokeWidth = this.stroke_.getWidth(); - if (strokeWidth === undefined) { - strokeWidth = ol.render.canvas.defaultLineWidth; - } - lineDash = this.stroke_.getLineDash(); - if (!ol.has.CANVAS_LINE_DASH) { - lineDash = null; - } - lineJoin = this.stroke_.getLineJoin(); - if (lineJoin === undefined) { - lineJoin = ol.render.canvas.defaultLineJoin; - } - lineCap = this.stroke_.getLineCap(); - if (lineCap === undefined) { - lineCap = ol.render.canvas.defaultLineCap; - } - miterLimit = this.stroke_.getMiterLimit(); - if (miterLimit === undefined) { - miterLimit = ol.render.canvas.defaultMiterLimit; - } - } - - var size = 2 * (this.radius_ + strokeWidth) + 1; - - /** @type {ol.RegularShapeRenderOptions} */ - var renderOptions = { - strokeStyle: strokeStyle, - strokeWidth: strokeWidth, - size: size, - lineCap: lineCap, - lineDash: lineDash, - lineJoin: lineJoin, - miterLimit: miterLimit - }; - - if (atlasManager === undefined) { - // no atlas manager is used, create a new canvas - var context = ol.dom.createCanvasContext2D(size, size); - this.canvas_ = context.canvas; - - // canvas.width and height are rounded to the closest integer - size = this.canvas_.width; - imageSize = size; - - this.draw_(renderOptions, context, 0, 0); - - this.createHitDetectionCanvas_(renderOptions); - } else { - // an atlas manager is used, add the symbol to an atlas - size = Math.round(size); - - var hasCustomHitDetectionImage = !this.fill_; - var renderHitDetectionCallback; - if (hasCustomHitDetectionImage) { - // render the hit-detection image into a separate atlas image - renderHitDetectionCallback = - this.drawHitDetectionCanvas_.bind(this, renderOptions); - } - - var id = this.getChecksum(); - var info = atlasManager.add( - id, size, size, this.draw_.bind(this, renderOptions), - renderHitDetectionCallback); - - this.canvas_ = info.image; - this.origin_ = [info.offsetX, info.offsetY]; - imageSize = info.image.width; - - if (hasCustomHitDetectionImage) { - this.hitDetectionCanvas_ = info.hitImage; - this.hitDetectionImageSize_ = - [info.hitImage.width, info.hitImage.height]; - } else { - this.hitDetectionCanvas_ = this.canvas_; - this.hitDetectionImageSize_ = [imageSize, imageSize]; - } - } - - this.anchor_ = [size / 2, size / 2]; - this.size_ = [size, size]; - this.imageSize_ = [imageSize, imageSize]; -}; - - -/** - * @private - * @param {ol.RegularShapeRenderOptions} renderOptions Render options. - * @param {CanvasRenderingContext2D} context The rendering context. - * @param {number} x The origin for the symbol (x). - * @param {number} y The origin for the symbol (y). - */ -ol.style.RegularShape.prototype.draw_ = function(renderOptions, context, x, y) { - var i, angle0, radiusC; - // reset transform - context.setTransform(1, 0, 0, 1, 0, 0); - - // then move to (x, y) - context.translate(x, y); - - context.beginPath(); - - var points = this.points_; - if (points === Infinity) { - context.arc( - renderOptions.size / 2, renderOptions.size / 2, - this.radius_, 0, 2 * Math.PI, true); - } else { - var radius2 = (this.radius2_ !== undefined) ? this.radius2_ - : this.radius_; - if (radius2 !== this.radius_) { - points = 2 * points; - } - for (i = 0; i <= points; i++) { - angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; - radiusC = i % 2 === 0 ? this.radius_ : radius2; - context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), - renderOptions.size / 2 + radiusC * Math.sin(angle0)); - } - } - - - if (this.fill_) { - var color = this.fill_.getColor(); - if (color === null) { - color = ol.render.canvas.defaultFillStyle; - } - context.fillStyle = ol.colorlike.asColorLike(color); - context.fill(); - } - if (this.stroke_) { - context.strokeStyle = renderOptions.strokeStyle; - context.lineWidth = renderOptions.strokeWidth; - if (renderOptions.lineDash) { - context.setLineDash(renderOptions.lineDash); - } - context.lineCap = renderOptions.lineCap; - context.lineJoin = renderOptions.lineJoin; - context.miterLimit = renderOptions.miterLimit; - context.stroke(); - } - context.closePath(); -}; - - -/** - * @private - * @param {ol.RegularShapeRenderOptions} renderOptions Render options. - */ -ol.style.RegularShape.prototype.createHitDetectionCanvas_ = function(renderOptions) { - this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; - if (this.fill_) { - this.hitDetectionCanvas_ = this.canvas_; - return; - } - - // if no fill style is set, create an extra hit-detection image with a - // default fill style - var context = ol.dom.createCanvasContext2D(renderOptions.size, renderOptions.size); - this.hitDetectionCanvas_ = context.canvas; - - this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); -}; - - -/** - * @private - * @param {ol.RegularShapeRenderOptions} renderOptions Render options. - * @param {CanvasRenderingContext2D} context The context. - * @param {number} x The origin for the symbol (x). - * @param {number} y The origin for the symbol (y). - */ -ol.style.RegularShape.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) { - // reset transform - context.setTransform(1, 0, 0, 1, 0, 0); - - // then move to (x, y) - context.translate(x, y); - - context.beginPath(); - - var points = this.points_; - if (points === Infinity) { - context.arc( - renderOptions.size / 2, renderOptions.size / 2, - this.radius_, 0, 2 * Math.PI, true); - } else { - var radius2 = (this.radius2_ !== undefined) ? this.radius2_ - : this.radius_; - if (radius2 !== this.radius_) { - points = 2 * points; - } - var i, radiusC, angle0; - for (i = 0; i <= points; i++) { - angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; - radiusC = i % 2 === 0 ? this.radius_ : radius2; - context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), - renderOptions.size / 2 + radiusC * Math.sin(angle0)); - } - } - - context.fillStyle = ol.render.canvas.defaultFillStyle; - context.fill(); - if (this.stroke_) { - context.strokeStyle = renderOptions.strokeStyle; - context.lineWidth = renderOptions.strokeWidth; - if (renderOptions.lineDash) { - context.setLineDash(renderOptions.lineDash); - } - context.stroke(); - } - context.closePath(); -}; - - -/** - * @return {string} The checksum. - */ -ol.style.RegularShape.prototype.getChecksum = function() { - var strokeChecksum = this.stroke_ ? - this.stroke_.getChecksum() : '-'; - var fillChecksum = this.fill_ ? - this.fill_.getChecksum() : '-'; - - var recalculate = !this.checksums_ || - (strokeChecksum != this.checksums_[1] || - fillChecksum != this.checksums_[2] || - this.radius_ != this.checksums_[3] || - this.radius2_ != this.checksums_[4] || - this.angle_ != this.checksums_[5] || - this.points_ != this.checksums_[6]); - - if (recalculate) { - var checksum = 'r' + strokeChecksum + fillChecksum + - (this.radius_ !== undefined ? this.radius_.toString() : '-') + - (this.radius2_ !== undefined ? this.radius2_.toString() : '-') + - (this.angle_ !== undefined ? this.angle_.toString() : '-') + - (this.points_ !== undefined ? this.points_.toString() : '-'); - this.checksums_ = [checksum, strokeChecksum, fillChecksum, - this.radius_, this.radius2_, this.angle_, this.points_]; - } - - return this.checksums_[0]; -}; - -goog.provide('ol.style.Circle'); - -goog.require('ol'); -goog.require('ol.style.RegularShape'); - - -/** - * @classdesc - * Set circle style for vector features. - * - * @constructor - * @param {olx.style.CircleOptions=} opt_options Options. - * @extends {ol.style.RegularShape} - * @api - */ -ol.style.Circle = function(opt_options) { - - var options = opt_options || {}; - - ol.style.RegularShape.call(this, { - points: Infinity, - fill: options.fill, - radius: options.radius, - snapToPixel: options.snapToPixel, - stroke: options.stroke, - atlasManager: options.atlasManager - }); - -}; -ol.inherits(ol.style.Circle, ol.style.RegularShape); - - -/** - * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. - * @return {ol.style.Circle} The cloned style. - * @override - * @api - */ -ol.style.Circle.prototype.clone = function() { - var style = new ol.style.Circle({ - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - radius: this.getRadius(), - snapToPixel: this.getSnapToPixel(), - atlasManager: this.atlasManager_ - }); - style.setOpacity(this.getOpacity()); - style.setScale(this.getScale()); - return style; -}; - - -/** - * Set the circle radius. - * - * @param {number} radius Circle radius. - * @api - */ -ol.style.Circle.prototype.setRadius = function(radius) { - this.radius_ = radius; - this.render_(this.atlasManager_); -}; - -goog.provide('ol.style.Fill'); - -goog.require('ol'); -goog.require('ol.color'); - - -/** - * @classdesc - * Set fill style for vector features. - * - * @constructor - * @param {olx.style.FillOptions=} opt_options Options. - * @api - */ -ol.style.Fill = function(opt_options) { - - var options = opt_options || {}; - - /** - * @private - * @type {ol.Color|ol.ColorLike} - */ - this.color_ = options.color !== undefined ? options.color : null; - - /** - * @private - * @type {string|undefined} - */ - this.checksum_ = undefined; -}; - - -/** - * Clones the style. The color is not cloned if it is an {@link ol.ColorLike}. - * @return {ol.style.Fill} The cloned style. - * @api - */ -ol.style.Fill.prototype.clone = function() { - var color = this.getColor(); - return new ol.style.Fill({ - color: (color && color.slice) ? color.slice() : color || undefined - }); -}; - - -/** - * Get the fill color. - * @return {ol.Color|ol.ColorLike} Color. - * @api - */ -ol.style.Fill.prototype.getColor = function() { - return this.color_; -}; - - -/** - * Set the color. - * - * @param {ol.Color|ol.ColorLike} color Color. - * @api - */ -ol.style.Fill.prototype.setColor = function(color) { - this.color_ = color; - this.checksum_ = undefined; -}; - - -/** - * @return {string} The checksum. - */ -ol.style.Fill.prototype.getChecksum = function() { - if (this.checksum_ === undefined) { - if ( - this.color_ instanceof CanvasPattern || - this.color_ instanceof CanvasGradient - ) { - this.checksum_ = ol.getUid(this.color_).toString(); - } else { - this.checksum_ = 'f' + (this.color_ ? - ol.color.asString(this.color_) : '-'); - } - } - - return this.checksum_; -}; - -goog.provide('ol.style.Style'); - -goog.require('ol.asserts'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.style.Circle'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Stroke'); - - -/** - * @classdesc - * Container for vector feature rendering styles. Any changes made to the style - * or its children through `set*()` methods will not take effect until the - * feature or layer that uses the style is re-rendered. - * - * @constructor - * @struct - * @param {olx.style.StyleOptions=} opt_options Style options. - * @api - */ -ol.style.Style = function(opt_options) { - - var options = opt_options || {}; - - /** - * @private - * @type {string|ol.geom.Geometry|ol.StyleGeometryFunction} - */ - this.geometry_ = null; - - /** - * @private - * @type {!ol.StyleGeometryFunction} - */ - this.geometryFunction_ = ol.style.Style.defaultGeometryFunction; - - if (options.geometry !== undefined) { - this.setGeometry(options.geometry); - } - - /** - * @private - * @type {ol.style.Fill} - */ - this.fill_ = options.fill !== undefined ? options.fill : null; - - /** - * @private - * @type {ol.style.Image} - */ - this.image_ = options.image !== undefined ? options.image : null; - - /** - * @private - * @type {ol.style.Stroke} - */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; - - /** - * @private - * @type {ol.style.Text} - */ - this.text_ = options.text !== undefined ? options.text : null; - - /** - * @private - * @type {number|undefined} - */ - this.zIndex_ = options.zIndex; - -}; - - -/** - * Clones the style. - * @return {ol.style.Style} The cloned style. - * @api - */ -ol.style.Style.prototype.clone = function() { - var geometry = this.getGeometry(); - if (geometry && geometry.clone) { - geometry = geometry.clone(); - } - return new ol.style.Style({ - geometry: geometry, - fill: this.getFill() ? this.getFill().clone() : undefined, - image: this.getImage() ? this.getImage().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - text: this.getText() ? this.getText().clone() : undefined, - zIndex: this.getZIndex() - }); -}; - - -/** - * Get the geometry to be rendered. - * @return {string|ol.geom.Geometry|ol.StyleGeometryFunction} - * Feature property or geometry or function that returns the geometry that will - * be rendered with this style. - * @api - */ -ol.style.Style.prototype.getGeometry = function() { - return this.geometry_; -}; - - -/** - * Get the function used to generate a geometry for rendering. - * @return {!ol.StyleGeometryFunction} Function that is called with a feature - * and returns the geometry to render instead of the feature's geometry. - * @api - */ -ol.style.Style.prototype.getGeometryFunction = function() { - return this.geometryFunction_; -}; - - -/** - * Get the fill style. - * @return {ol.style.Fill} Fill style. - * @api - */ -ol.style.Style.prototype.getFill = function() { - return this.fill_; -}; - - -/** - * Set the fill style. - * @param {ol.style.Fill} fill Fill style. - * @api - */ -ol.style.Style.prototype.setFill = function(fill) { - this.fill_ = fill; -}; - - -/** - * Get the image style. - * @return {ol.style.Image} Image style. - * @api - */ -ol.style.Style.prototype.getImage = function() { - return this.image_; -}; - - -/** - * Set the image style. - * @param {ol.style.Image} image Image style. - * @api - */ -ol.style.Style.prototype.setImage = function(image) { - this.image_ = image; -}; - - -/** - * Get the stroke style. - * @return {ol.style.Stroke} Stroke style. - * @api - */ -ol.style.Style.prototype.getStroke = function() { - return this.stroke_; -}; - - -/** - * Set the stroke style. - * @param {ol.style.Stroke} stroke Stroke style. - * @api - */ -ol.style.Style.prototype.setStroke = function(stroke) { - this.stroke_ = stroke; -}; - - -/** - * Get the text style. - * @return {ol.style.Text} Text style. - * @api - */ -ol.style.Style.prototype.getText = function() { - return this.text_; -}; - - -/** - * Set the text style. - * @param {ol.style.Text} text Text style. - * @api - */ -ol.style.Style.prototype.setText = function(text) { - this.text_ = text; -}; - - -/** - * Get the z-index for the style. - * @return {number|undefined} ZIndex. - * @api - */ -ol.style.Style.prototype.getZIndex = function() { - return this.zIndex_; -}; - - -/** - * Set a geometry that is rendered instead of the feature's geometry. - * - * @param {string|ol.geom.Geometry|ol.StyleGeometryFunction} geometry - * Feature property or geometry or function returning a geometry to render - * for this style. - * @api - */ -ol.style.Style.prototype.setGeometry = function(geometry) { - if (typeof geometry === 'function') { - this.geometryFunction_ = geometry; - } else if (typeof geometry === 'string') { - this.geometryFunction_ = function(feature) { - return /** @type {ol.geom.Geometry} */ (feature.get(geometry)); - }; - } else if (!geometry) { - this.geometryFunction_ = ol.style.Style.defaultGeometryFunction; - } else if (geometry !== undefined) { - this.geometryFunction_ = function() { - return /** @type {ol.geom.Geometry} */ (geometry); - }; - } - this.geometry_ = geometry; -}; - - -/** - * Set the z-index. - * - * @param {number|undefined} zIndex ZIndex. - * @api - */ -ol.style.Style.prototype.setZIndex = function(zIndex) { - this.zIndex_ = zIndex; -}; - - -/** - * Convert the provided object into a style function. Functions passed through - * unchanged. Arrays of ol.style.Style or single style objects wrapped in a - * new style function. - * @param {ol.StyleFunction|Array.<ol.style.Style>|ol.style.Style} obj - * A style function, a single style, or an array of styles. - * @return {ol.StyleFunction} A style function. - */ -ol.style.Style.createFunction = function(obj) { - var styleFunction; - - if (typeof obj === 'function') { - styleFunction = obj; - } else { - /** - * @type {Array.<ol.style.Style>} - */ - var styles; - if (Array.isArray(obj)) { - styles = obj; - } else { - ol.asserts.assert(obj instanceof ol.style.Style, - 41); // Expected an `ol.style.Style` or an array of `ol.style.Style` - styles = [obj]; - } - styleFunction = function() { - return styles; - }; - } - return styleFunction; -}; - - -/** - * @type {Array.<ol.style.Style>} - * @private - */ -ol.style.Style.default_ = null; - - -/** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {number} resolution Resolution. - * @return {Array.<ol.style.Style>} Style. - */ -ol.style.Style.defaultFunction = function(feature, resolution) { - // We don't use an immediately-invoked function - // and a closure so we don't get an error at script evaluation time in - // browsers that do not support Canvas. (ol.style.Circle does - // canvas.getContext('2d') at construction time, which will cause an.error - // in such browsers.) - if (!ol.style.Style.default_) { - var fill = new ol.style.Fill({ - color: 'rgba(255,255,255,0.4)' - }); - var stroke = new ol.style.Stroke({ - color: '#3399CC', - width: 1.25 - }); - ol.style.Style.default_ = [ - new ol.style.Style({ - image: new ol.style.Circle({ - fill: fill, - stroke: stroke, - radius: 5 - }), - fill: fill, - stroke: stroke - }) - ]; - } - return ol.style.Style.default_; -}; - - -/** - * Default styles for editing features. - * @return {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} Styles - */ -ol.style.Style.createDefaultEditing = function() { - /** @type {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} */ - var styles = {}; - var white = [255, 255, 255, 1]; - var blue = [0, 153, 255, 1]; - var width = 3; - styles[ol.geom.GeometryType.POLYGON] = [ - new ol.style.Style({ - fill: new ol.style.Fill({ - color: [255, 255, 255, 0.5] - }) - }) - ]; - styles[ol.geom.GeometryType.MULTI_POLYGON] = - styles[ol.geom.GeometryType.POLYGON]; - - styles[ol.geom.GeometryType.LINE_STRING] = [ - new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: white, - width: width + 2 - }) - }), - new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: blue, - width: width - }) - }) - ]; - styles[ol.geom.GeometryType.MULTI_LINE_STRING] = - styles[ol.geom.GeometryType.LINE_STRING]; - - styles[ol.geom.GeometryType.CIRCLE] = - styles[ol.geom.GeometryType.POLYGON].concat( - styles[ol.geom.GeometryType.LINE_STRING] - ); - - - styles[ol.geom.GeometryType.POINT] = [ - new ol.style.Style({ - image: new ol.style.Circle({ - radius: width * 2, - fill: new ol.style.Fill({ - color: blue - }), - stroke: new ol.style.Stroke({ - color: white, - width: width / 2 - }) - }), - zIndex: Infinity - }) - ]; - styles[ol.geom.GeometryType.MULTI_POINT] = - styles[ol.geom.GeometryType.POINT]; - - styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = - styles[ol.geom.GeometryType.POLYGON].concat( - styles[ol.geom.GeometryType.LINE_STRING], - styles[ol.geom.GeometryType.POINT] - ); - - return styles; -}; - - -/** - * Function that is called with a feature and returns its default geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature to get the geometry - * for. - * @return {ol.geom.Geometry|ol.render.Feature|undefined} Geometry to render. - */ -ol.style.Style.defaultGeometryFunction = function(feature) { - return feature.getGeometry(); -}; - -goog.provide('ol.Feature'); - -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.geom.Geometry'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * A vector object for geographic features with a geometry and other - * attribute properties, similar to the features in vector file formats like - * GeoJSON. - * - * Features can be styled individually with `setStyle`; otherwise they use the - * style of their vector layer. - * - * Note that attribute properties are set as {@link ol.Object} properties on - * the feature object, so they are observable, and have get/set accessors. - * - * Typically, a feature has a single geometry property. You can set the - * geometry using the `setGeometry` method and get it with `getGeometry`. - * It is possible to store more than one geometry on a feature using attribute - * properties. By default, the geometry used for rendering is identified by - * the property name `geometry`. If you want to use another geometry property - * for rendering, use the `setGeometryName` method to change the attribute - * property associated with the geometry for the feature. For example: - * - * ```js - * var feature = new ol.Feature({ - * geometry: new ol.geom.Polygon(polyCoords), - * labelPoint: new ol.geom.Point(labelCoords), - * name: 'My Polygon' - * }); - * - * // get the polygon geometry - * var poly = feature.getGeometry(); - * - * // Render the feature as a point using the coordinates from labelPoint - * feature.setGeometryName('labelPoint'); - * - * // get the point geometry - * var point = feature.getGeometry(); - * ``` - * - * @constructor - * @extends {ol.Object} - * @param {ol.geom.Geometry|Object.<string, *>=} opt_geometryOrProperties - * You may pass a Geometry object directly, or an object literal - * containing properties. If you pass an object literal, you may - * include a Geometry associated with a `geometry` key. - * @api - */ -ol.Feature = function(opt_geometryOrProperties) { - - ol.Object.call(this); - - /** - * @private - * @type {number|string|undefined} - */ - this.id_ = undefined; - - /** - * @type {string} - * @private - */ - this.geometryName_ = 'geometry'; - - /** - * User provided style. - * @private - * @type {ol.style.Style|Array.<ol.style.Style>| - * ol.FeatureStyleFunction} - */ - this.style_ = null; - - /** - * @private - * @type {ol.FeatureStyleFunction|undefined} - */ - this.styleFunction_ = undefined; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.geometryChangeKey_ = null; - - ol.events.listen( - this, ol.Object.getChangeEventType(this.geometryName_), - this.handleGeometryChanged_, this); - - if (opt_geometryOrProperties !== undefined) { - if (opt_geometryOrProperties instanceof ol.geom.Geometry || - !opt_geometryOrProperties) { - var geometry = opt_geometryOrProperties; - this.setGeometry(geometry); - } else { - /** @type {Object.<string, *>} */ - var properties = opt_geometryOrProperties; - this.setProperties(properties); - } - } -}; -ol.inherits(ol.Feature, ol.Object); - - -/** - * Clone this feature. If the original feature has a geometry it - * is also cloned. The feature id is not set in the clone. - * @return {ol.Feature} The clone. - * @api - */ -ol.Feature.prototype.clone = function() { - var clone = new ol.Feature(this.getProperties()); - clone.setGeometryName(this.getGeometryName()); - var geometry = this.getGeometry(); - if (geometry) { - clone.setGeometry(geometry.clone()); - } - var style = this.getStyle(); - if (style) { - clone.setStyle(style); - } - return clone; -}; - - -/** - * Get the feature's default geometry. A feature may have any number of named - * geometries. The "default" geometry (the one that is rendered by default) is - * set when calling {@link ol.Feature#setGeometry}. - * @return {ol.geom.Geometry|undefined} The default geometry for the feature. - * @api - * @observable - */ -ol.Feature.prototype.getGeometry = function() { - return /** @type {ol.geom.Geometry|undefined} */ ( - this.get(this.geometryName_)); -}; - - -/** - * Get the feature identifier. This is a stable identifier for the feature and - * is either set when reading data from a remote source or set explicitly by - * calling {@link ol.Feature#setId}. - * @return {number|string|undefined} Id. - * @api - */ -ol.Feature.prototype.getId = function() { - return this.id_; -}; - - -/** - * Get the name of the feature's default geometry. By default, the default - * geometry is named `geometry`. - * @return {string} Get the property name associated with the default geometry - * for this feature. - * @api - */ -ol.Feature.prototype.getGeometryName = function() { - return this.geometryName_; -}; - - -/** - * Get the feature's style. Will return what was provided to the - * {@link ol.Feature#setStyle} method. - * @return {ol.style.Style|Array.<ol.style.Style>| - * ol.FeatureStyleFunction|ol.StyleFunction} The feature style. - * @api - */ -ol.Feature.prototype.getStyle = function() { - return this.style_; -}; - - -/** - * Get the feature's style function. - * @return {ol.FeatureStyleFunction|undefined} Return a function - * representing the current style of this feature. - * @api - */ -ol.Feature.prototype.getStyleFunction = function() { - return this.styleFunction_; -}; - - -/** - * @private - */ -ol.Feature.prototype.handleGeometryChange_ = function() { - this.changed(); -}; - - -/** - * @private - */ -ol.Feature.prototype.handleGeometryChanged_ = function() { - if (this.geometryChangeKey_) { - ol.events.unlistenByKey(this.geometryChangeKey_); - this.geometryChangeKey_ = null; - } - var geometry = this.getGeometry(); - if (geometry) { - this.geometryChangeKey_ = ol.events.listen(geometry, - ol.events.EventType.CHANGE, this.handleGeometryChange_, this); - } - this.changed(); -}; - - -/** - * Set the default geometry for the feature. This will update the property - * with the name returned by {@link ol.Feature#getGeometryName}. - * @param {ol.geom.Geometry|undefined} geometry The new geometry. - * @api - * @observable - */ -ol.Feature.prototype.setGeometry = function(geometry) { - this.set(this.geometryName_, geometry); -}; - - -/** - * Set the style for the feature. This can be a single style object, an array - * of styles, or a function that takes a resolution and returns an array of - * styles. If it is `null` the feature has no style (a `null` style). - * @param {ol.style.Style|Array.<ol.style.Style>| - * ol.FeatureStyleFunction|ol.StyleFunction} style Style for this feature. - * @api - * @fires ol.events.Event#event:change - */ -ol.Feature.prototype.setStyle = function(style) { - this.style_ = style; - this.styleFunction_ = !style ? - undefined : ol.Feature.createStyleFunction(style); - this.changed(); -}; - - -/** - * Set the feature id. The feature id is considered stable and may be used when - * requesting features or comparing identifiers returned from a remote source. - * The feature id can be used with the {@link ol.source.Vector#getFeatureById} - * method. - * @param {number|string|undefined} id The feature id. - * @api - * @fires ol.events.Event#event:change - */ -ol.Feature.prototype.setId = function(id) { - this.id_ = id; - this.changed(); -}; - - -/** - * Set the property name to be used when getting the feature's default geometry. - * When calling {@link ol.Feature#getGeometry}, the value of the property with - * this name will be returned. - * @param {string} name The property name of the default geometry. - * @api - */ -ol.Feature.prototype.setGeometryName = function(name) { - ol.events.unlisten( - this, ol.Object.getChangeEventType(this.geometryName_), - this.handleGeometryChanged_, this); - this.geometryName_ = name; - ol.events.listen( - this, ol.Object.getChangeEventType(this.geometryName_), - this.handleGeometryChanged_, this); - this.handleGeometryChanged_(); -}; - - -/** - * Convert the provided object into a feature style function. Functions passed - * through unchanged. Arrays of ol.style.Style or single style objects wrapped - * in a new feature style function. - * @param {ol.FeatureStyleFunction|!Array.<ol.style.Style>|!ol.style.Style} obj - * A feature style function, a single style, or an array of styles. - * @return {ol.FeatureStyleFunction} A style function. - */ -ol.Feature.createStyleFunction = function(obj) { - var styleFunction; - - if (typeof obj === 'function') { - if (obj.length == 2) { - styleFunction = function(resolution) { - return /** @type {ol.StyleFunction} */ (obj)(this, resolution); - }; - } else { - styleFunction = obj; - } - } else { - /** - * @type {Array.<ol.style.Style>} - */ - var styles; - if (Array.isArray(obj)) { - styles = obj; - } else { - ol.asserts.assert(obj instanceof ol.style.Style, - 41); // Expected an `ol.style.Style` or an array of `ol.style.Style` - styles = [obj]; - } - styleFunction = function() { - return styles; - }; - } - return styleFunction; -}; - -goog.provide('ol.format.FormatType'); - - -/** - * @enum {string} - */ -ol.format.FormatType = { - ARRAY_BUFFER: 'arraybuffer', - JSON: 'json', - TEXT: 'text', - XML: 'xml' -}; - -goog.provide('ol.xml'); - -goog.require('ol.array'); - - -/** - * This document should be used when creating nodes for XML serializations. This - * document is also used by {@link ol.xml.createElementNS} and - * {@link ol.xml.setAttributeNS} - * @const - * @type {Document} - */ -ol.xml.DOCUMENT = document.implementation.createDocument('', '', null); - - -/** - * @param {string} namespaceURI Namespace URI. - * @param {string} qualifiedName Qualified name. - * @return {Node} Node. - */ -ol.xml.createElementNS = function(namespaceURI, qualifiedName) { - return ol.xml.DOCUMENT.createElementNS(namespaceURI, qualifiedName); -}; - - -/** - * Recursively grab all text content of child nodes into a single string. - * @param {Node} node Node. - * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line - * breaks. - * @return {string} All text content. - * @api - */ -ol.xml.getAllTextContent = function(node, normalizeWhitespace) { - return ol.xml.getAllTextContent_(node, normalizeWhitespace, []).join(''); -}; - - -/** - * Recursively grab all text content of child nodes into a single string. - * @param {Node} node Node. - * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line - * breaks. - * @param {Array.<string>} accumulator Accumulator. - * @private - * @return {Array.<string>} Accumulator. - */ -ol.xml.getAllTextContent_ = function(node, normalizeWhitespace, accumulator) { - if (node.nodeType == Node.CDATA_SECTION_NODE || - node.nodeType == Node.TEXT_NODE) { - if (normalizeWhitespace) { - accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, '')); - } else { - accumulator.push(node.nodeValue); - } - } else { - var n; - for (n = node.firstChild; n; n = n.nextSibling) { - ol.xml.getAllTextContent_(n, normalizeWhitespace, accumulator); - } - } - return accumulator; -}; - - -/** - * @param {?} value Value. - * @return {boolean} Is document. - */ -ol.xml.isDocument = function(value) { - return value instanceof Document; -}; - - -/** - * @param {?} value Value. - * @return {boolean} Is node. - */ -ol.xml.isNode = function(value) { - return value instanceof Node; -}; - - -/** - * @param {Node} node Node. - * @param {?string} namespaceURI Namespace URI. - * @param {string} name Attribute name. - * @return {string} Value - */ -ol.xml.getAttributeNS = function(node, namespaceURI, name) { - return node.getAttributeNS(namespaceURI, name) || ''; -}; - - -/** - * @param {Node} node Node. - * @param {?string} namespaceURI Namespace URI. - * @param {string} name Attribute name. - * @param {string|number} value Value. - */ -ol.xml.setAttributeNS = function(node, namespaceURI, name, value) { - node.setAttributeNS(namespaceURI, name, value); -}; - - -/** - * Parse an XML string to an XML Document. - * @param {string} xml XML. - * @return {Document} Document. - * @api - */ -ol.xml.parse = function(xml) { - return new DOMParser().parseFromString(xml, 'application/xml'); -}; - - -/** - * Make an array extender function for extending the array at the top of the - * object stack. - * @param {function(this: T, Node, Array.<*>): (Array.<*>|undefined)} - * valueReader Value reader. - * @param {T=} opt_this The object to use as `this` in `valueReader`. - * @return {ol.XmlParser} Parser. - * @template T - */ -ol.xml.makeArrayExtender = function(valueReader, opt_this) { - return ( - /** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - */ - function(node, objectStack) { - var value = valueReader.call(opt_this, node, objectStack); - if (value !== undefined) { - var array = /** @type {Array.<*>} */ - (objectStack[objectStack.length - 1]); - ol.array.extend(array, value); - } - }); -}; - - -/** - * Make an array pusher function for pushing to the array at the top of the - * object stack. - * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. - * @param {T=} opt_this The object to use as `this` in `valueReader`. - * @return {ol.XmlParser} Parser. - * @template T - */ -ol.xml.makeArrayPusher = function(valueReader, opt_this) { - return ( - /** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - */ - function(node, objectStack) { - var value = valueReader.call(opt_this !== undefined ? opt_this : this, - node, objectStack); - if (value !== undefined) { - var array = objectStack[objectStack.length - 1]; - array.push(value); - } - }); -}; - - -/** - * Make an object stack replacer function for replacing the object at the - * top of the stack. - * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. - * @param {T=} opt_this The object to use as `this` in `valueReader`. - * @return {ol.XmlParser} Parser. - * @template T - */ -ol.xml.makeReplacer = function(valueReader, opt_this) { - return ( - /** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - */ - function(node, objectStack) { - var value = valueReader.call(opt_this !== undefined ? opt_this : this, - node, objectStack); - if (value !== undefined) { - objectStack[objectStack.length - 1] = value; - } - }); -}; - - -/** - * Make an object property pusher function for adding a property to the - * object at the top of the stack. - * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. - * @param {string=} opt_property Property. - * @param {T=} opt_this The object to use as `this` in `valueReader`. - * @return {ol.XmlParser} Parser. - * @template T - */ -ol.xml.makeObjectPropertyPusher = function(valueReader, opt_property, opt_this) { - return ( - /** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - */ - function(node, objectStack) { - var value = valueReader.call(opt_this !== undefined ? opt_this : this, - node, objectStack); - if (value !== undefined) { - var object = /** @type {Object} */ - (objectStack[objectStack.length - 1]); - var property = opt_property !== undefined ? - opt_property : node.localName; - var array; - if (property in object) { - array = object[property]; - } else { - array = object[property] = []; - } - array.push(value); - } - }); -}; - - -/** - * Make an object property setter function. - * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. - * @param {string=} opt_property Property. - * @param {T=} opt_this The object to use as `this` in `valueReader`. - * @return {ol.XmlParser} Parser. - * @template T - */ -ol.xml.makeObjectPropertySetter = function(valueReader, opt_property, opt_this) { - return ( - /** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - */ - function(node, objectStack) { - var value = valueReader.call(opt_this !== undefined ? opt_this : this, - node, objectStack); - if (value !== undefined) { - var object = /** @type {Object} */ - (objectStack[objectStack.length - 1]); - var property = opt_property !== undefined ? - opt_property : node.localName; - object[property] = value; - } - }); -}; - - -/** - * Create a serializer that appends nodes written by its `nodeWriter` to its - * designated parent. The parent is the `node` of the - * {@link ol.XmlNodeStackItem} at the top of the `objectStack`. - * @param {function(this: T, Node, V, Array.<*>)} - * nodeWriter Node writer. - * @param {T=} opt_this The object to use as `this` in `nodeWriter`. - * @return {ol.XmlSerializer} Serializer. - * @template T, V - */ -ol.xml.makeChildAppender = function(nodeWriter, opt_this) { - return function(node, value, objectStack) { - nodeWriter.call(opt_this !== undefined ? opt_this : this, - node, value, objectStack); - var parent = objectStack[objectStack.length - 1]; - var parentNode = parent.node; - parentNode.appendChild(node); - }; -}; - - -/** - * Create a serializer that calls the provided `nodeWriter` from - * {@link ol.xml.serialize}. This can be used by the parent writer to have the - * 'nodeWriter' called with an array of values when the `nodeWriter` was - * designed to serialize a single item. An example would be a LineString - * geometry writer, which could be reused for writing MultiLineString - * geometries. - * @param {function(this: T, Node, V, Array.<*>)} - * nodeWriter Node writer. - * @param {T=} opt_this The object to use as `this` in `nodeWriter`. - * @return {ol.XmlSerializer} Serializer. - * @template T, V - */ -ol.xml.makeArraySerializer = function(nodeWriter, opt_this) { - var serializersNS, nodeFactory; - return function(node, value, objectStack) { - if (serializersNS === undefined) { - serializersNS = {}; - var serializers = {}; - serializers[node.localName] = nodeWriter; - serializersNS[node.namespaceURI] = serializers; - nodeFactory = ol.xml.makeSimpleNodeFactory(node.localName); - } - ol.xml.serialize(serializersNS, nodeFactory, value, objectStack); - }; -}; - - -/** - * Create a node factory which can use the `opt_keys` passed to - * {@link ol.xml.serialize} or {@link ol.xml.pushSerializeAndPop} as node names, - * or a fixed node name. The namespace of the created nodes can either be fixed, - * or the parent namespace will be used. - * @param {string=} opt_nodeName Fixed node name which will be used for all - * created nodes. If not provided, the 3rd argument to the resulting node - * factory needs to be provided and will be the nodeName. - * @param {string=} opt_namespaceURI Fixed namespace URI which will be used for - * all created nodes. If not provided, the namespace of the parent node will - * be used. - * @return {function(*, Array.<*>, string=): (Node|undefined)} Node factory. - */ -ol.xml.makeSimpleNodeFactory = function(opt_nodeName, opt_namespaceURI) { - var fixedNodeName = opt_nodeName; - return ( - /** - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node} Node. - */ - function(value, objectStack, opt_nodeName) { - var context = objectStack[objectStack.length - 1]; - var node = context.node; - var nodeName = fixedNodeName; - if (nodeName === undefined) { - nodeName = opt_nodeName; - } - var namespaceURI = opt_namespaceURI; - if (opt_namespaceURI === undefined) { - namespaceURI = node.namespaceURI; - } - return ol.xml.createElementNS(namespaceURI, /** @type {string} */ (nodeName)); - } - ); -}; - - -/** - * A node factory that creates a node using the parent's `namespaceURI` and the - * `nodeName` passed by {@link ol.xml.serialize} or - * {@link ol.xml.pushSerializeAndPop} to the node factory. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - */ -ol.xml.OBJECT_PROPERTY_NODE_FACTORY = ol.xml.makeSimpleNodeFactory(); - - -/** - * Create an array of `values` to be used with {@link ol.xml.serialize} or - * {@link ol.xml.pushSerializeAndPop}, where `orderedKeys` has to be provided as - * `opt_key` argument. - * @param {Object.<string, V>} object Key-value pairs for the sequence. Keys can - * be a subset of the `orderedKeys`. - * @param {Array.<string>} orderedKeys Keys in the order of the sequence. - * @return {Array.<V>} Values in the order of the sequence. The resulting array - * has the same length as the `orderedKeys` array. Values that are not - * present in `object` will be `undefined` in the resulting array. - * @template V - */ -ol.xml.makeSequence = function(object, orderedKeys) { - var length = orderedKeys.length; - var sequence = new Array(length); - for (var i = 0; i < length; ++i) { - sequence[i] = object[orderedKeys[i]]; - } - return sequence; -}; - - -/** - * Create a namespaced structure, using the same values for each namespace. - * This can be used as a starting point for versioned parsers, when only a few - * values are version specific. - * @param {Array.<string>} namespaceURIs Namespace URIs. - * @param {T} structure Structure. - * @param {Object.<string, T>=} opt_structureNS Namespaced structure to add to. - * @return {Object.<string, T>} Namespaced structure. - * @template T - */ -ol.xml.makeStructureNS = function(namespaceURIs, structure, opt_structureNS) { - /** - * @type {Object.<string, *>} - */ - var structureNS = opt_structureNS !== undefined ? opt_structureNS : {}; - var i, ii; - for (i = 0, ii = namespaceURIs.length; i < ii; ++i) { - structureNS[namespaceURIs[i]] = structure; - } - return structureNS; -}; - - -/** - * Parse a node using the parsers and object stack. - * @param {Object.<string, Object.<string, ol.XmlParser>>} parsersNS - * Parsers by namespace. - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @param {*=} opt_this The object to use as `this`. - */ -ol.xml.parseNode = function(parsersNS, node, objectStack, opt_this) { - var n; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - var parsers = parsersNS[n.namespaceURI]; - if (parsers !== undefined) { - var parser = parsers[n.localName]; - if (parser !== undefined) { - parser.call(opt_this, n, objectStack); - } - } - } -}; - - -/** - * Push an object on top of the stack, parse and return the popped object. - * @param {T} object Object. - * @param {Object.<string, Object.<string, ol.XmlParser>>} parsersNS - * Parsers by namespace. - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @param {*=} opt_this The object to use as `this`. - * @return {T} Object. - * @template T - */ -ol.xml.pushParseAndPop = function( - object, parsersNS, node, objectStack, opt_this) { - objectStack.push(object); - ol.xml.parseNode(parsersNS, node, objectStack, opt_this); - return objectStack.pop(); -}; - - -/** - * Walk through an array of `values` and call a serializer for each value. - * @param {Object.<string, Object.<string, ol.XmlSerializer>>} serializersNS - * Namespaced serializers. - * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory - * Node factory. The `nodeFactory` creates the node whose namespace and name - * will be used to choose a node writer from `serializersNS`. This - * separation allows us to decide what kind of node to create, depending on - * the value we want to serialize. An example for this would be different - * geometry writers based on the geometry type. - * @param {Array.<*>} values Values to serialize. An example would be an array - * of {@link ol.Feature} instances. - * @param {Array.<*>} objectStack Node stack. - * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the - * `nodeFactory`. This is used for serializing object literals where the - * node name relates to the property key. The array length of `opt_keys` has - * to match the length of `values`. For serializing a sequence, `opt_keys` - * determines the order of the sequence. - * @param {T=} opt_this The object to use as `this` for the node factory and - * serializers. - * @template T - */ -ol.xml.serialize = function( - serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) { - var length = (opt_keys !== undefined ? opt_keys : values).length; - var value, node; - for (var i = 0; i < length; ++i) { - value = values[i]; - if (value !== undefined) { - node = nodeFactory.call(opt_this, value, objectStack, - opt_keys !== undefined ? opt_keys[i] : undefined); - if (node !== undefined) { - serializersNS[node.namespaceURI][node.localName] - .call(opt_this, node, value, objectStack); - } - } - } -}; - - -/** - * @param {O} object Object. - * @param {Object.<string, Object.<string, ol.XmlSerializer>>} serializersNS - * Namespaced serializers. - * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory - * Node factory. The `nodeFactory` creates the node whose namespace and name - * will be used to choose a node writer from `serializersNS`. This - * separation allows us to decide what kind of node to create, depending on - * the value we want to serialize. An example for this would be different - * geometry writers based on the geometry type. - * @param {Array.<*>} values Values to serialize. An example would be an array - * of {@link ol.Feature} instances. - * @param {Array.<*>} objectStack Node stack. - * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the - * `nodeFactory`. This is used for serializing object literals where the - * node name relates to the property key. The array length of `opt_keys` has - * to match the length of `values`. For serializing a sequence, `opt_keys` - * determines the order of the sequence. - * @param {T=} opt_this The object to use as `this` for the node factory and - * serializers. - * @return {O|undefined} Object. - * @template O, T - */ -ol.xml.pushSerializeAndPop = function(object, - serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) { - objectStack.push(object); - ol.xml.serialize( - serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this); - return objectStack.pop(); -}; - -goog.provide('ol.featureloader'); - -goog.require('ol'); -goog.require('ol.format.FormatType'); -goog.require('ol.xml'); - - -/** - * @param {string|ol.FeatureUrlFunction} url Feature URL service. - * @param {ol.format.Feature} format Feature format. - * @param {function(this:ol.VectorTile, Array.<ol.Feature>, ol.proj.Projection)|function(this:ol.source.Vector, Array.<ol.Feature>)} success - * Function called with the loaded features and optionally with the data - * projection. Called with the vector tile or source as `this`. - * @param {function(this:ol.VectorTile)|function(this:ol.source.Vector)} failure - * Function called when loading failed. Called with the vector tile or - * source as `this`. - * @return {ol.FeatureLoader} The feature loader. - */ -ol.featureloader.loadFeaturesXhr = function(url, format, success, failure) { - return ( - /** - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {ol.proj.Projection} projection Projection. - * @this {ol.source.Vector|ol.VectorTile} - */ - function(extent, resolution, projection) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', - typeof url === 'function' ? url(extent, resolution, projection) : url, - true); - if (format.getType() == ol.format.FormatType.ARRAY_BUFFER) { - xhr.responseType = 'arraybuffer'; - } - /** - * @param {Event} event Event. - * @private - */ - xhr.onload = function(event) { - // status will be 0 for file:// urls - if (!xhr.status || xhr.status >= 200 && xhr.status < 300) { - var type = format.getType(); - /** @type {Document|Node|Object|string|undefined} */ - var source; - if (type == ol.format.FormatType.JSON || - type == ol.format.FormatType.TEXT) { - source = xhr.responseText; - } else if (type == ol.format.FormatType.XML) { - source = xhr.responseXML; - if (!source) { - source = ol.xml.parse(xhr.responseText); - } - } else if (type == ol.format.FormatType.ARRAY_BUFFER) { - source = /** @type {ArrayBuffer} */ (xhr.response); - } - if (source) { - success.call(this, format.readFeatures(source, - {featureProjection: projection}), - format.readProjection(source)); - } else { - failure.call(this); - } - } else { - failure.call(this); - } - }.bind(this); - /** - * @private - */ - xhr.onerror = function() { - failure.call(this); - }.bind(this); - xhr.send(); - }); -}; - - -/** - * Create an XHR feature loader for a `url` and `format`. The feature loader - * loads features (with XHR), parses the features, and adds them to the - * vector source. - * @param {string|ol.FeatureUrlFunction} url Feature URL service. - * @param {ol.format.Feature} format Feature format. - * @return {ol.FeatureLoader} The feature loader. - * @api - */ -ol.featureloader.xhr = function(url, format) { - return ol.featureloader.loadFeaturesXhr(url, format, - /** - * @param {Array.<ol.Feature>} features The loaded features. - * @param {ol.proj.Projection} dataProjection Data projection. - * @this {ol.source.Vector} - */ - function(features, dataProjection) { - this.addFeatures(features); - }, /* FIXME handle error */ ol.nullFunction); -}; - -goog.provide('ol.format.Feature'); - -goog.require('ol.geom.Geometry'); -goog.require('ol.obj'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for feature formats. - * {ol.format.Feature} subclasses provide the ability to decode and encode - * {@link ol.Feature} objects from a variety of commonly used geospatial - * file formats. See the documentation for each format for more details. - * - * @constructor - * @abstract - * @api - */ -ol.format.Feature = function() { - - /** - * @protected - * @type {ol.proj.Projection} - */ - this.defaultDataProjection = null; - - /** - * @protected - * @type {ol.proj.Projection} - */ - this.defaultFeatureProjection = null; - -}; - - -/** - * Adds the data projection to the read options. - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Options. - * @return {olx.format.ReadOptions|undefined} Options. - * @protected - */ -ol.format.Feature.prototype.getReadOptions = function(source, opt_options) { - var options; - if (opt_options) { - options = { - dataProjection: opt_options.dataProjection ? - opt_options.dataProjection : this.readProjection(source), - featureProjection: opt_options.featureProjection - }; - } - return this.adaptOptions(options); -}; - - -/** - * Sets the `defaultDataProjection` on the options, if no `dataProjection` - * is set. - * @param {olx.format.WriteOptions|olx.format.ReadOptions|undefined} options - * Options. - * @protected - * @return {olx.format.WriteOptions|olx.format.ReadOptions|undefined} - * Updated options. - */ -ol.format.Feature.prototype.adaptOptions = function(options) { - return ol.obj.assign({ - dataProjection: this.defaultDataProjection, - featureProjection: this.defaultFeatureProjection - }, options); -}; - - -/** - * @abstract - * @return {ol.format.FormatType} Format. - */ -ol.format.Feature.prototype.getType = function() {}; - - -/** - * Read a single feature from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - */ -ol.format.Feature.prototype.readFeature = function(source, opt_options) {}; - - -/** - * Read all features from a source. - * - * @abstract - * @param {Document|Node|ArrayBuffer|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - */ -ol.format.Feature.prototype.readFeatures = function(source, opt_options) {}; - - -/** - * Read a single geometry from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.Feature.prototype.readGeometry = function(source, opt_options) {}; - - -/** - * Read the projection from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - */ -ol.format.Feature.prototype.readProjection = function(source) {}; - - -/** - * Encode a feature in this format. - * - * @abstract - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} Result. - */ -ol.format.Feature.prototype.writeFeature = function(feature, opt_options) {}; - - -/** - * Encode an array of features in this format. - * - * @abstract - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} Result. - */ -ol.format.Feature.prototype.writeFeatures = function(features, opt_options) {}; - - -/** - * Write a single geometry in this format. - * - * @abstract - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} Result. - */ -ol.format.Feature.prototype.writeGeometry = function(geometry, opt_options) {}; - - -/** - * @param {ol.geom.Geometry|ol.Extent} geometry Geometry. - * @param {boolean} write Set to true for writing, false for reading. - * @param {(olx.format.WriteOptions|olx.format.ReadOptions)=} opt_options - * Options. - * @return {ol.geom.Geometry|ol.Extent} Transformed geometry. - * @protected - */ -ol.format.Feature.transformWithOptions = function( - geometry, write, opt_options) { - var featureProjection = opt_options ? - ol.proj.get(opt_options.featureProjection) : null; - var dataProjection = opt_options ? - ol.proj.get(opt_options.dataProjection) : null; - /** - * @type {ol.geom.Geometry|ol.Extent} - */ - var transformed; - if (featureProjection && dataProjection && - !ol.proj.equivalent(featureProjection, dataProjection)) { - if (geometry instanceof ol.geom.Geometry) { - transformed = (write ? geometry.clone() : geometry).transform( - write ? featureProjection : dataProjection, - write ? dataProjection : featureProjection); - } else { - // FIXME this is necessary because ol.format.GML treats extents - // as geometries - transformed = ol.proj.transformExtent( - geometry, - dataProjection, - featureProjection); - } - } else { - transformed = geometry; - } - if (write && opt_options && opt_options.decimals !== undefined) { - var power = Math.pow(10, opt_options.decimals); - // if decimals option on write, round each coordinate appropriately - /** - * @param {Array.<number>} coordinates Coordinates. - * @return {Array.<number>} Transformed coordinates. - */ - var transform = function(coordinates) { - for (var i = 0, ii = coordinates.length; i < ii; ++i) { - coordinates[i] = Math.round(coordinates[i] * power) / power; - } - return coordinates; - }; - if (transformed === geometry) { - transformed = transformed.clone(); - } - transformed.applyTransform(transform); - } - return transformed; -}; - -goog.provide('ol.format.JSONFeature'); - -goog.require('ol'); -goog.require('ol.format.Feature'); -goog.require('ol.format.FormatType'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for JSON feature formats. - * - * @constructor - * @abstract - * @extends {ol.format.Feature} - */ -ol.format.JSONFeature = function() { - ol.format.Feature.call(this); -}; -ol.inherits(ol.format.JSONFeature, ol.format.Feature); - - -/** - * @param {Document|Node|Object|string} source Source. - * @private - * @return {Object} Object. - */ -ol.format.JSONFeature.prototype.getObject_ = function(source) { - if (typeof source === 'string') { - var object = JSON.parse(source); - return object ? /** @type {Object} */ (object) : null; - } else if (source !== null) { - return source; - } else { - return null; - } -}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.getType = function() { - return ol.format.FormatType.JSON; -}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.readFeature = function(source, opt_options) { - return this.readFeatureFromObject( - this.getObject_(source), this.getReadOptions(source, opt_options)); -}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.readFeatures = function(source, opt_options) { - return this.readFeaturesFromObject( - this.getObject_(source), this.getReadOptions(source, opt_options)); -}; - - -/** - * @abstract - * @param {Object} object Object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @protected - * @return {ol.Feature} Feature. - */ -ol.format.JSONFeature.prototype.readFeatureFromObject = function(object, opt_options) {}; - - -/** - * @abstract - * @param {Object} object Object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @protected - * @return {Array.<ol.Feature>} Features. - */ -ol.format.JSONFeature.prototype.readFeaturesFromObject = function(object, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.readGeometry = function(source, opt_options) { - return this.readGeometryFromObject( - this.getObject_(source), this.getReadOptions(source, opt_options)); -}; - - -/** - * @abstract - * @param {Object} object Object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @protected - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.JSONFeature.prototype.readGeometryFromObject = function(object, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.readProjection = function(source) { - return this.readProjectionFromObject(this.getObject_(source)); -}; - - -/** - * @abstract - * @param {Object} object Object. - * @protected - * @return {ol.proj.Projection} Projection. - */ -ol.format.JSONFeature.prototype.readProjectionFromObject = function(object) {}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.writeFeature = function(feature, opt_options) { - return JSON.stringify(this.writeFeatureObject(feature, opt_options)); -}; - - -/** - * @abstract - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ -ol.format.JSONFeature.prototype.writeFeatureObject = function(feature, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.writeFeatures = function(features, opt_options) { - return JSON.stringify(this.writeFeaturesObject(features, opt_options)); -}; - - -/** - * @abstract - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ -ol.format.JSONFeature.prototype.writeFeaturesObject = function(features, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.JSONFeature.prototype.writeGeometry = function(geometry, opt_options) { - return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); -}; - - -/** - * @abstract - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ -ol.format.JSONFeature.prototype.writeGeometryObject = function(geometry, opt_options) {}; - -goog.provide('ol.geom.flat.interpolate'); - -goog.require('ol.array'); -goog.require('ol.math'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} fraction Fraction. - * @param {Array.<number>=} opt_dest Destination. - * @return {Array.<number>} Destination. - */ -ol.geom.flat.interpolate.lineString = function(flatCoordinates, offset, end, stride, fraction, opt_dest) { - var pointX = NaN; - var pointY = NaN; - var n = (end - offset) / stride; - if (n === 1) { - pointX = flatCoordinates[offset]; - pointY = flatCoordinates[offset + 1]; - } else if (n == 2) { - pointX = (1 - fraction) * flatCoordinates[offset] + - fraction * flatCoordinates[offset + stride]; - pointY = (1 - fraction) * flatCoordinates[offset + 1] + - fraction * flatCoordinates[offset + stride + 1]; - } else if (n !== 0) { - var x1 = flatCoordinates[offset]; - var y1 = flatCoordinates[offset + 1]; - var length = 0; - var cumulativeLengths = [0]; - var i; - for (i = offset + stride; i < end; i += stride) { - var x2 = flatCoordinates[i]; - var y2 = flatCoordinates[i + 1]; - length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - cumulativeLengths.push(length); - x1 = x2; - y1 = y2; - } - var target = fraction * length; - var index = ol.array.binarySearch(cumulativeLengths, target); - if (index < 0) { - var t = (target - cumulativeLengths[-index - 2]) / - (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]); - var o = offset + (-index - 2) * stride; - pointX = ol.math.lerp( - flatCoordinates[o], flatCoordinates[o + stride], t); - pointY = ol.math.lerp( - flatCoordinates[o + 1], flatCoordinates[o + stride + 1], t); - } else { - pointX = flatCoordinates[offset + index * stride]; - pointY = flatCoordinates[offset + index * stride + 1]; - } - } - if (opt_dest) { - opt_dest[0] = pointX; - opt_dest[1] = pointY; - return opt_dest; - } else { - return [pointX, pointY]; - } -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} m M. - * @param {boolean} extrapolate Extrapolate. - * @return {ol.Coordinate} Coordinate. - */ -ol.geom.flat.interpolate.lineStringCoordinateAtM = function(flatCoordinates, offset, end, stride, m, extrapolate) { - if (end == offset) { - return null; - } - var coordinate; - if (m < flatCoordinates[offset + stride - 1]) { - if (extrapolate) { - coordinate = flatCoordinates.slice(offset, offset + stride); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } else if (flatCoordinates[end - 1] < m) { - if (extrapolate) { - coordinate = flatCoordinates.slice(end - stride, end); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } - // FIXME use O(1) search - if (m == flatCoordinates[offset + stride - 1]) { - return flatCoordinates.slice(offset, offset + stride); - } - var lo = offset / stride; - var hi = end / stride; - while (lo < hi) { - var mid = (lo + hi) >> 1; - if (m < flatCoordinates[(mid + 1) * stride - 1]) { - hi = mid; - } else { - lo = mid + 1; - } - } - var m0 = flatCoordinates[lo * stride - 1]; - if (m == m0) { - return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride); - } - var m1 = flatCoordinates[(lo + 1) * stride - 1]; - var t = (m - m0) / (m1 - m0); - coordinate = []; - var i; - for (i = 0; i < stride - 1; ++i) { - coordinate.push(ol.math.lerp(flatCoordinates[(lo - 1) * stride + i], - flatCoordinates[lo * stride + i], t)); - } - coordinate.push(m); - return coordinate; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @param {number} m M. - * @param {boolean} extrapolate Extrapolate. - * @param {boolean} interpolate Interpolate. - * @return {ol.Coordinate} Coordinate. - */ -ol.geom.flat.interpolate.lineStringsCoordinateAtM = function( - flatCoordinates, offset, ends, stride, m, extrapolate, interpolate) { - if (interpolate) { - return ol.geom.flat.interpolate.lineStringCoordinateAtM( - flatCoordinates, offset, ends[ends.length - 1], stride, m, extrapolate); - } - var coordinate; - if (m < flatCoordinates[stride - 1]) { - if (extrapolate) { - coordinate = flatCoordinates.slice(0, stride); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } - if (flatCoordinates[flatCoordinates.length - 1] < m) { - if (extrapolate) { - coordinate = flatCoordinates.slice(flatCoordinates.length - stride); - coordinate[stride - 1] = m; - return coordinate; - } else { - return null; - } - } - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - if (offset == end) { - continue; - } - if (m < flatCoordinates[offset + stride - 1]) { - return null; - } else if (m <= flatCoordinates[end - 1]) { - return ol.geom.flat.interpolate.lineStringCoordinateAtM( - flatCoordinates, offset, end, stride, m, false); - } - offset = end; - } - return null; -}; - -goog.provide('ol.geom.flat.length'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {number} Length. - */ -ol.geom.flat.length.lineString = function(flatCoordinates, offset, end, stride) { - var x1 = flatCoordinates[offset]; - var y1 = flatCoordinates[offset + 1]; - var length = 0; - var i; - for (i = offset + stride; i < end; i += stride) { - var x2 = flatCoordinates[i]; - var y2 = flatCoordinates[i + 1]; - length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - x1 = x2; - y1 = y2; - } - return length; -}; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {number} Perimeter. - */ -ol.geom.flat.length.linearRing = function(flatCoordinates, offset, end, stride) { - var perimeter = - ol.geom.flat.length.lineString(flatCoordinates, offset, end, stride); - var dx = flatCoordinates[end - stride] - flatCoordinates[offset]; - var dy = flatCoordinates[end - stride + 1] - flatCoordinates[offset + 1]; - perimeter += Math.sqrt(dx * dx + dy * dy); - return perimeter; -}; - -goog.provide('ol.geom.LineString'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.closest'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.geom.flat.interpolate'); -goog.require('ol.geom.flat.intersectsextent'); -goog.require('ol.geom.flat.length'); -goog.require('ol.geom.flat.segments'); -goog.require('ol.geom.flat.simplify'); - - -/** - * @classdesc - * Linestring geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.LineString = function(coordinates, opt_layout) { - - ol.geom.SimpleGeometry.call(this); - - /** - * @private - * @type {ol.Coordinate} - */ - this.flatMidpoint_ = null; - - /** - * @private - * @type {number} - */ - this.flatMidpointRevision_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; - - this.setCoordinates(coordinates, opt_layout); - -}; -ol.inherits(ol.geom.LineString, ol.geom.SimpleGeometry); - - -/** - * Append the passed coordinate to the coordinates of the linestring. - * @param {ol.Coordinate} coordinate Coordinate. - * @api - */ -ol.geom.LineString.prototype.appendCoordinate = function(coordinate) { - if (!this.flatCoordinates) { - this.flatCoordinates = coordinate.slice(); - } else { - ol.array.extend(this.flatCoordinates, coordinate); - } - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.LineString} Clone. - * @override - * @api - */ -ol.geom.LineString.prototype.clone = function() { - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); - return lineString; -}; - - -/** - * @inheritDoc - */ -ol.geom.LineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return ol.geom.flat.closest.getClosestPoint( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * Iterate over each segment, calling the provided callback. - * If the callback returns a truthy value the function returns that - * value immediately. Otherwise the function returns `false`. - * - * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function - * called for each segment. - * @param {S=} opt_this The object to be used as the value of 'this' - * within callback. - * @return {T|boolean} Value. - * @template T,S - * @api - */ -ol.geom.LineString.prototype.forEachSegment = function(callback, opt_this) { - return ol.geom.flat.segments.forEach(this.flatCoordinates, 0, - this.flatCoordinates.length, this.stride, callback, opt_this); -}; - - -/** - * Returns the coordinate at `m` using linear interpolation, or `null` if no - * such coordinate exists. - * - * `opt_extrapolate` controls extrapolation beyond the range of Ms in the - * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first - * M will return the first coordinate and Ms greater than the last M will - * return the last coordinate. - * - * @param {number} m M. - * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. - * @return {ol.Coordinate} Coordinate. - * @api - */ -ol.geom.LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) { - if (this.layout != ol.geom.GeometryLayout.XYM && - this.layout != ol.geom.GeometryLayout.XYZM) { - return null; - } - var extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; - return ol.geom.flat.interpolate.lineStringCoordinateAtM(this.flatCoordinates, 0, - this.flatCoordinates.length, this.stride, m, extrapolate); -}; - - -/** - * Return the coordinates of the linestring. - * @return {Array.<ol.Coordinate>} Coordinates. - * @override - * @api - */ -ol.geom.LineString.prototype.getCoordinates = function() { - return ol.geom.flat.inflate.coordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * Return the coordinate at the provided fraction along the linestring. - * The `fraction` is a number between 0 and 1, where 0 is the start of the - * linestring and 1 is the end. - * @param {number} fraction Fraction. - * @param {ol.Coordinate=} opt_dest Optional coordinate whose values will - * be modified. If not provided, a new coordinate will be returned. - * @return {ol.Coordinate} Coordinate of the interpolated point. - * @api - */ -ol.geom.LineString.prototype.getCoordinateAt = function(fraction, opt_dest) { - return ol.geom.flat.interpolate.lineString( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - fraction, opt_dest); -}; - - -/** - * Return the length of the linestring on projected plane. - * @return {number} Length (on projected plane). - * @api - */ -ol.geom.LineString.prototype.getLength = function() { - return ol.geom.flat.length.lineString( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * @return {Array.<number>} Flat midpoint. - */ -ol.geom.LineString.prototype.getFlatMidpoint = function() { - if (this.flatMidpointRevision_ != this.getRevision()) { - this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_); - this.flatMidpointRevision_ = this.getRevision(); - } - return this.flatMidpoint_; -}; - - -/** - * @inheritDoc - */ -ol.geom.LineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - var simplifiedFlatCoordinates = []; - simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - squaredTolerance, simplifiedFlatCoordinates, 0); - var simplifiedLineString = new ol.geom.LineString(null); - simplifiedLineString.setFlatCoordinates( - ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates); - return simplifiedLineString; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.LineString.prototype.getType = function() { - return ol.geom.GeometryType.LINE_STRING; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.LineString.prototype.intersectsExtent = function(extent) { - return ol.geom.flat.intersectsextent.lineString( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - extent); -}; - - -/** - * Set the coordinates of the linestring. - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -ol.geom.LineString.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); - } else { - this.setLayout(opt_layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = ol.geom.flat.deflate.coordinates( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - */ -ol.geom.LineString.prototype.setFlatCoordinates = function(layout, flatCoordinates) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.changed(); -}; - -goog.provide('ol.geom.MultiLineString'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.closest'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.geom.flat.interpolate'); -goog.require('ol.geom.flat.intersectsextent'); -goog.require('ol.geom.flat.simplify'); - - -/** - * @classdesc - * Multi-linestring geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.MultiLineString = function(coordinates, opt_layout) { - - ol.geom.SimpleGeometry.call(this); - - /** - * @type {Array.<number>} - * @private - */ - this.ends_ = []; - - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; - - this.setCoordinates(coordinates, opt_layout); - -}; -ol.inherits(ol.geom.MultiLineString, ol.geom.SimpleGeometry); - - -/** - * Append the passed linestring to the multilinestring. - * @param {ol.geom.LineString} lineString LineString. - * @api - */ -ol.geom.MultiLineString.prototype.appendLineString = function(lineString) { - if (!this.flatCoordinates) { - this.flatCoordinates = lineString.getFlatCoordinates().slice(); - } else { - ol.array.extend( - this.flatCoordinates, lineString.getFlatCoordinates().slice()); - } - this.ends_.push(this.flatCoordinates.length); - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.MultiLineString} Clone. - * @override - * @api - */ -ol.geom.MultiLineString.prototype.clone = function() { - var multiLineString = new ol.geom.MultiLineString(null); - multiLineString.setFlatCoordinates( - this.layout, this.flatCoordinates.slice(), this.ends_.slice()); - return multiLineString; -}; - - -/** - * @inheritDoc - */ -ol.geom.MultiLineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta( - this.flatCoordinates, 0, this.ends_, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return ol.geom.flat.closest.getsClosestPoint( - this.flatCoordinates, 0, this.ends_, this.stride, - this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * Returns the coordinate at `m` using linear interpolation, or `null` if no - * such coordinate exists. - * - * `opt_extrapolate` controls extrapolation beyond the range of Ms in the - * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first - * M will return the first coordinate and Ms greater than the last M will - * return the last coordinate. - * - * `opt_interpolate` controls interpolation between consecutive LineStrings - * within the MultiLineString. If `opt_interpolate` is `true` the coordinates - * will be linearly interpolated between the last coordinate of one LineString - * and the first coordinate of the next LineString. If `opt_interpolate` is - * `false` then the function will return `null` for Ms falling between - * LineStrings. - * - * @param {number} m M. - * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. - * @param {boolean=} opt_interpolate Interpolate. Default is `false`. - * @return {ol.Coordinate} Coordinate. - * @api - */ -ol.geom.MultiLineString.prototype.getCoordinateAtM = function(m, opt_extrapolate, opt_interpolate) { - if ((this.layout != ol.geom.GeometryLayout.XYM && - this.layout != ol.geom.GeometryLayout.XYZM) || - this.flatCoordinates.length === 0) { - return null; - } - var extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; - var interpolate = opt_interpolate !== undefined ? opt_interpolate : false; - return ol.geom.flat.interpolate.lineStringsCoordinateAtM(this.flatCoordinates, 0, - this.ends_, this.stride, m, extrapolate, interpolate); -}; - - -/** - * Return the coordinates of the multilinestring. - * @return {Array.<Array.<ol.Coordinate>>} Coordinates. - * @override - * @api - */ -ol.geom.MultiLineString.prototype.getCoordinates = function() { - return ol.geom.flat.inflate.coordinatess( - this.flatCoordinates, 0, this.ends_, this.stride); -}; - - -/** - * @return {Array.<number>} Ends. - */ -ol.geom.MultiLineString.prototype.getEnds = function() { - return this.ends_; -}; - - -/** - * Return the linestring at the specified index. - * @param {number} index Index. - * @return {ol.geom.LineString} LineString. - * @api - */ -ol.geom.MultiLineString.prototype.getLineString = function(index) { - if (index < 0 || this.ends_.length <= index) { - return null; - } - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice( - index === 0 ? 0 : this.ends_[index - 1], this.ends_[index])); - return lineString; -}; - - -/** - * Return the linestrings of this multilinestring. - * @return {Array.<ol.geom.LineString>} LineStrings. - * @api - */ -ol.geom.MultiLineString.prototype.getLineStrings = function() { - var flatCoordinates = this.flatCoordinates; - var ends = this.ends_; - var layout = this.layout; - /** @type {Array.<ol.geom.LineString>} */ - var lineStrings = []; - var offset = 0; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(layout, flatCoordinates.slice(offset, end)); - lineStrings.push(lineString); - offset = end; - } - return lineStrings; -}; - - -/** - * @return {Array.<number>} Flat midpoints. - */ -ol.geom.MultiLineString.prototype.getFlatMidpoints = function() { - var midpoints = []; - var flatCoordinates = this.flatCoordinates; - var offset = 0; - var ends = this.ends_; - var stride = this.stride; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - var end = ends[i]; - var midpoint = ol.geom.flat.interpolate.lineString( - flatCoordinates, offset, end, stride, 0.5); - ol.array.extend(midpoints, midpoint); - offset = end; - } - return midpoints; -}; - - -/** - * @inheritDoc - */ -ol.geom.MultiLineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - var simplifiedFlatCoordinates = []; - var simplifiedEnds = []; - simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeuckers( - this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance, - simplifiedFlatCoordinates, 0, simplifiedEnds); - var simplifiedMultiLineString = new ol.geom.MultiLineString(null); - simplifiedMultiLineString.setFlatCoordinates( - ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds); - return simplifiedMultiLineString; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.MultiLineString.prototype.getType = function() { - return ol.geom.GeometryType.MULTI_LINE_STRING; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.MultiLineString.prototype.intersectsExtent = function(extent) { - return ol.geom.flat.intersectsextent.lineStrings( - this.flatCoordinates, 0, this.ends_, this.stride, extent); -}; - - -/** - * Set the coordinates of the multilinestring. - * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -ol.geom.MultiLineString.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_); - } else { - this.setLayout(opt_layout, coordinates, 2); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - var ends = ol.geom.flat.deflate.coordinatess( - this.flatCoordinates, 0, coordinates, this.stride, this.ends_); - this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {Array.<number>} ends Ends. - */ -ol.geom.MultiLineString.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.ends_ = ends; - this.changed(); -}; - - -/** - * @param {Array.<ol.geom.LineString>} lineStrings LineStrings. - */ -ol.geom.MultiLineString.prototype.setLineStrings = function(lineStrings) { - var layout = this.getLayout(); - var flatCoordinates = []; - var ends = []; - var i, ii; - for (i = 0, ii = lineStrings.length; i < ii; ++i) { - var lineString = lineStrings[i]; - if (i === 0) { - layout = lineString.getLayout(); - } - ol.array.extend(flatCoordinates, lineString.getFlatCoordinates()); - ends.push(flatCoordinates.length); - } - this.setFlatCoordinates(layout, flatCoordinates, ends); -}; - -goog.provide('ol.geom.MultiPoint'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.math'); - - -/** - * @classdesc - * Multi-point geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.MultiPoint = function(coordinates, opt_layout) { - ol.geom.SimpleGeometry.call(this); - this.setCoordinates(coordinates, opt_layout); -}; -ol.inherits(ol.geom.MultiPoint, ol.geom.SimpleGeometry); - - -/** - * Append the passed point to this multipoint. - * @param {ol.geom.Point} point Point. - * @api - */ -ol.geom.MultiPoint.prototype.appendPoint = function(point) { - if (!this.flatCoordinates) { - this.flatCoordinates = point.getFlatCoordinates().slice(); - } else { - ol.array.extend(this.flatCoordinates, point.getFlatCoordinates()); - } - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.MultiPoint} Clone. - * @override - * @api - */ -ol.geom.MultiPoint.prototype.clone = function() { - var multiPoint = new ol.geom.MultiPoint(null); - multiPoint.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); - return multiPoint; -}; - - -/** - * @inheritDoc - */ -ol.geom.MultiPoint.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - var flatCoordinates = this.flatCoordinates; - var stride = this.stride; - var i, ii, j; - for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { - var squaredDistance = ol.math.squaredDistance( - x, y, flatCoordinates[i], flatCoordinates[i + 1]); - if (squaredDistance < minSquaredDistance) { - minSquaredDistance = squaredDistance; - for (j = 0; j < stride; ++j) { - closestPoint[j] = flatCoordinates[i + j]; - } - closestPoint.length = stride; - } - } - return minSquaredDistance; -}; - - -/** - * Return the coordinates of the multipoint. - * @return {Array.<ol.Coordinate>} Coordinates. - * @override - * @api - */ -ol.geom.MultiPoint.prototype.getCoordinates = function() { - return ol.geom.flat.inflate.coordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * Return the point at the specified index. - * @param {number} index Index. - * @return {ol.geom.Point} Point. - * @api - */ -ol.geom.MultiPoint.prototype.getPoint = function(index) { - var n = !this.flatCoordinates ? - 0 : this.flatCoordinates.length / this.stride; - if (index < 0 || n <= index) { - return null; - } - var point = new ol.geom.Point(null); - point.setFlatCoordinates(this.layout, this.flatCoordinates.slice( - index * this.stride, (index + 1) * this.stride)); - return point; -}; - - -/** - * Return the points of this multipoint. - * @return {Array.<ol.geom.Point>} Points. - * @api - */ -ol.geom.MultiPoint.prototype.getPoints = function() { - var flatCoordinates = this.flatCoordinates; - var layout = this.layout; - var stride = this.stride; - /** @type {Array.<ol.geom.Point>} */ - var points = []; - var i, ii; - for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { - var point = new ol.geom.Point(null); - point.setFlatCoordinates(layout, flatCoordinates.slice(i, i + stride)); - points.push(point); - } - return points; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.MultiPoint.prototype.getType = function() { - return ol.geom.GeometryType.MULTI_POINT; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.MultiPoint.prototype.intersectsExtent = function(extent) { - var flatCoordinates = this.flatCoordinates; - var stride = this.stride; - var i, ii, x, y; - for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { - x = flatCoordinates[i]; - y = flatCoordinates[i + 1]; - if (ol.extent.containsXY(extent, x, y)) { - return true; - } - } - return false; -}; - - -/** - * Set the coordinates of the multipoint. - * @param {Array.<ol.Coordinate>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -ol.geom.MultiPoint.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); - } else { - this.setLayout(opt_layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = ol.geom.flat.deflate.coordinates( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - */ -ol.geom.MultiPoint.prototype.setFlatCoordinates = function(layout, flatCoordinates) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.changed(); -}; - -goog.provide('ol.geom.flat.center'); - -goog.require('ol.extent'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<Array.<number>>} endss Endss. - * @param {number} stride Stride. - * @return {Array.<number>} Flat centers. - */ -ol.geom.flat.center.linearRingss = function(flatCoordinates, offset, endss, stride) { - var flatCenters = []; - var i, ii; - var extent = ol.extent.createEmpty(); - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i]; - extent = ol.extent.createOrUpdateFromFlatCoordinates( - flatCoordinates, offset, ends[0], stride); - flatCenters.push((extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2); - offset = ends[ends.length - 1]; - } - return flatCenters; -}; - -goog.provide('ol.geom.MultiPolygon'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.Polygon'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.area'); -goog.require('ol.geom.flat.center'); -goog.require('ol.geom.flat.closest'); -goog.require('ol.geom.flat.contains'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.geom.flat.interiorpoint'); -goog.require('ol.geom.flat.intersectsextent'); -goog.require('ol.geom.flat.orient'); -goog.require('ol.geom.flat.simplify'); - - -/** - * @classdesc - * Multi-polygon geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.MultiPolygon = function(coordinates, opt_layout) { - - ol.geom.SimpleGeometry.call(this); - - /** - * @type {Array.<Array.<number>>} - * @private - */ - this.endss_ = []; - - /** - * @private - * @type {number} - */ - this.flatInteriorPointsRevision_ = -1; - - /** - * @private - * @type {Array.<number>} - */ - this.flatInteriorPoints_ = null; - - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; - - /** - * @private - * @type {number} - */ - this.orientedRevision_ = -1; - - /** - * @private - * @type {Array.<number>} - */ - this.orientedFlatCoordinates_ = null; - - this.setCoordinates(coordinates, opt_layout); - -}; -ol.inherits(ol.geom.MultiPolygon, ol.geom.SimpleGeometry); - - -/** - * Append the passed polygon to this multipolygon. - * @param {ol.geom.Polygon} polygon Polygon. - * @api - */ -ol.geom.MultiPolygon.prototype.appendPolygon = function(polygon) { - /** @type {Array.<number>} */ - var ends; - if (!this.flatCoordinates) { - this.flatCoordinates = polygon.getFlatCoordinates().slice(); - ends = polygon.getEnds().slice(); - this.endss_.push(); - } else { - var offset = this.flatCoordinates.length; - ol.array.extend(this.flatCoordinates, polygon.getFlatCoordinates()); - ends = polygon.getEnds().slice(); - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - ends[i] += offset; - } - } - this.endss_.push(ends); - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.MultiPolygon} Clone. - * @override - * @api - */ -ol.geom.MultiPolygon.prototype.clone = function() { - var multiPolygon = new ol.geom.MultiPolygon(null); - - var len = this.endss_.length; - var newEndss = new Array(len); - for (var i = 0; i < len; ++i) { - newEndss[i] = this.endss_[i].slice(); - } - - multiPolygon.setFlatCoordinates( - this.layout, this.flatCoordinates.slice(), newEndss); - return multiPolygon; -}; - - -/** - * @inheritDoc - */ -ol.geom.MultiPolygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getssMaxSquaredDelta( - this.flatCoordinates, 0, this.endss_, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return ol.geom.flat.closest.getssClosestPoint( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, - this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * @inheritDoc - */ -ol.geom.MultiPolygon.prototype.containsXY = function(x, y) { - return ol.geom.flat.contains.linearRingssContainsXY( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y); -}; - - -/** - * Return the area of the multipolygon on projected plane. - * @return {number} Area (on projected plane). - * @api - */ -ol.geom.MultiPolygon.prototype.getArea = function() { - return ol.geom.flat.area.linearRingss( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride); -}; - - -/** - * Get the coordinate array for this geometry. This array has the structure - * of a GeoJSON coordinate array for multi-polygons. - * - * @param {boolean=} opt_right Orient coordinates according to the right-hand - * rule (counter-clockwise for exterior and clockwise for interior rings). - * If `false`, coordinates will be oriented according to the left-hand rule - * (clockwise for exterior and counter-clockwise for interior rings). - * By default, coordinate orientation will depend on how the geometry was - * constructed. - * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinates. - * @override - * @api - */ -ol.geom.MultiPolygon.prototype.getCoordinates = function(opt_right) { - var flatCoordinates; - if (opt_right !== undefined) { - flatCoordinates = this.getOrientedFlatCoordinates().slice(); - ol.geom.flat.orient.orientLinearRingss( - flatCoordinates, 0, this.endss_, this.stride, opt_right); - } else { - flatCoordinates = this.flatCoordinates; - } - - return ol.geom.flat.inflate.coordinatesss( - flatCoordinates, 0, this.endss_, this.stride); -}; - - -/** - * @return {Array.<Array.<number>>} Endss. - */ -ol.geom.MultiPolygon.prototype.getEndss = function() { - return this.endss_; -}; - - -/** - * @return {Array.<number>} Flat interior points. - */ -ol.geom.MultiPolygon.prototype.getFlatInteriorPoints = function() { - if (this.flatInteriorPointsRevision_ != this.getRevision()) { - var flatCenters = ol.geom.flat.center.linearRingss( - this.flatCoordinates, 0, this.endss_, this.stride); - this.flatInteriorPoints_ = ol.geom.flat.interiorpoint.linearRingss( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, - flatCenters); - this.flatInteriorPointsRevision_ = this.getRevision(); - } - return this.flatInteriorPoints_; -}; - - -/** - * Return the interior points as {@link ol.geom.MultiPoint multipoint}. - * @return {ol.geom.MultiPoint} Interior points. - * @api - */ -ol.geom.MultiPolygon.prototype.getInteriorPoints = function() { - var interiorPoints = new ol.geom.MultiPoint(null); - interiorPoints.setFlatCoordinates(ol.geom.GeometryLayout.XY, - this.getFlatInteriorPoints().slice()); - return interiorPoints; -}; - - -/** - * @return {Array.<number>} Oriented flat coordinates. - */ -ol.geom.MultiPolygon.prototype.getOrientedFlatCoordinates = function() { - if (this.orientedRevision_ != this.getRevision()) { - var flatCoordinates = this.flatCoordinates; - if (ol.geom.flat.orient.linearRingssAreOriented( - flatCoordinates, 0, this.endss_, this.stride)) { - this.orientedFlatCoordinates_ = flatCoordinates; - } else { - this.orientedFlatCoordinates_ = flatCoordinates.slice(); - this.orientedFlatCoordinates_.length = - ol.geom.flat.orient.orientLinearRingss( - this.orientedFlatCoordinates_, 0, this.endss_, this.stride); - } - this.orientedRevision_ = this.getRevision(); - } - return this.orientedFlatCoordinates_; -}; - - -/** - * @inheritDoc - */ -ol.geom.MultiPolygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - var simplifiedFlatCoordinates = []; - var simplifiedEndss = []; - simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizess( - this.flatCoordinates, 0, this.endss_, this.stride, - Math.sqrt(squaredTolerance), - simplifiedFlatCoordinates, 0, simplifiedEndss); - var simplifiedMultiPolygon = new ol.geom.MultiPolygon(null); - simplifiedMultiPolygon.setFlatCoordinates( - ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEndss); - return simplifiedMultiPolygon; -}; - - -/** - * Return the polygon at the specified index. - * @param {number} index Index. - * @return {ol.geom.Polygon} Polygon. - * @api - */ -ol.geom.MultiPolygon.prototype.getPolygon = function(index) { - if (index < 0 || this.endss_.length <= index) { - return null; - } - var offset; - if (index === 0) { - offset = 0; - } else { - var prevEnds = this.endss_[index - 1]; - offset = prevEnds[prevEnds.length - 1]; - } - var ends = this.endss_[index].slice(); - var end = ends[ends.length - 1]; - if (offset !== 0) { - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - ends[i] -= offset; - } - } - var polygon = new ol.geom.Polygon(null); - polygon.setFlatCoordinates( - this.layout, this.flatCoordinates.slice(offset, end), ends); - return polygon; -}; - - -/** - * Return the polygons of this multipolygon. - * @return {Array.<ol.geom.Polygon>} Polygons. - * @api - */ -ol.geom.MultiPolygon.prototype.getPolygons = function() { - var layout = this.layout; - var flatCoordinates = this.flatCoordinates; - var endss = this.endss_; - var polygons = []; - var offset = 0; - var i, ii, j, jj; - for (i = 0, ii = endss.length; i < ii; ++i) { - var ends = endss[i].slice(); - var end = ends[ends.length - 1]; - if (offset !== 0) { - for (j = 0, jj = ends.length; j < jj; ++j) { - ends[j] -= offset; - } - } - var polygon = new ol.geom.Polygon(null); - polygon.setFlatCoordinates( - layout, flatCoordinates.slice(offset, end), ends); - polygons.push(polygon); - offset = end; - } - return polygons; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.MultiPolygon.prototype.getType = function() { - return ol.geom.GeometryType.MULTI_POLYGON; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.MultiPolygon.prototype.intersectsExtent = function(extent) { - return ol.geom.flat.intersectsextent.linearRingss( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent); -}; - - -/** - * Set the coordinates of the multipolygon. - * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -ol.geom.MultiPolygon.prototype.setCoordinates = function(coordinates, opt_layout) { - if (!coordinates) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.endss_); - } else { - this.setLayout(opt_layout, coordinates, 3); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - var endss = ol.geom.flat.deflate.coordinatesss( - this.flatCoordinates, 0, coordinates, this.stride, this.endss_); - if (endss.length === 0) { - this.flatCoordinates.length = 0; - } else { - var lastEnds = endss[endss.length - 1]; - this.flatCoordinates.length = lastEnds.length === 0 ? - 0 : lastEnds[lastEnds.length - 1]; - } - this.changed(); - } -}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {Array.<Array.<number>>} endss Endss. - */ -ol.geom.MultiPolygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, endss) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.endss_ = endss; - this.changed(); -}; - - -/** - * @param {Array.<ol.geom.Polygon>} polygons Polygons. - */ -ol.geom.MultiPolygon.prototype.setPolygons = function(polygons) { - var layout = this.getLayout(); - var flatCoordinates = []; - var endss = []; - var i, ii, ends; - for (i = 0, ii = polygons.length; i < ii; ++i) { - var polygon = polygons[i]; - if (i === 0) { - layout = polygon.getLayout(); - } - var offset = flatCoordinates.length; - ends = polygon.getEnds(); - var j, jj; - for (j = 0, jj = ends.length; j < jj; ++j) { - ends[j] += offset; - } - ol.array.extend(flatCoordinates, polygon.getFlatCoordinates()); - endss.push(ends); - } - this.setFlatCoordinates(layout, flatCoordinates, endss); -}; - -goog.provide('ol.format.EsriJSON'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.asserts'); -goog.require('ol.extent'); -goog.require('ol.format.Feature'); -goog.require('ol.format.JSONFeature'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.LinearRing'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.geom.flat.deflate'); -goog.require('ol.geom.flat.orient'); -goog.require('ol.obj'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Feature format for reading and writing data in the EsriJSON format. - * - * @constructor - * @extends {ol.format.JSONFeature} - * @param {olx.format.EsriJSONOptions=} opt_options Options. - * @api - */ -ol.format.EsriJSON = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.JSONFeature.call(this); - - /** - * Name of the geometry attribute for features. - * @type {string|undefined} - * @private - */ - this.geometryName_ = options.geometryName; - -}; -ol.inherits(ol.format.EsriJSON, ol.format.JSONFeature); - - -/** - * @param {EsriJSONGeometry} object Object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @private - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.EsriJSON.readGeometry_ = function(object, opt_options) { - if (!object) { - return null; - } - /** @type {ol.geom.GeometryType} */ - var type; - if (typeof object.x === 'number' && typeof object.y === 'number') { - type = ol.geom.GeometryType.POINT; - } else if (object.points) { - type = ol.geom.GeometryType.MULTI_POINT; - } else if (object.paths) { - if (object.paths.length === 1) { - type = ol.geom.GeometryType.LINE_STRING; - } else { - type = ol.geom.GeometryType.MULTI_LINE_STRING; - } - } else if (object.rings) { - var layout = ol.format.EsriJSON.getGeometryLayout_(object); - var rings = ol.format.EsriJSON.convertRings_(object.rings, layout); - object = /** @type {EsriJSONGeometry} */(ol.obj.assign({}, object)); - if (rings.length === 1) { - type = ol.geom.GeometryType.POLYGON; - object.rings = rings[0]; - } else { - type = ol.geom.GeometryType.MULTI_POLYGON; - object.rings = rings; - } - } - var geometryReader = ol.format.EsriJSON.GEOMETRY_READERS_[type]; - return /** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions( - geometryReader(object), false, opt_options)); -}; - - -/** - * Determines inner and outer rings. - * Checks if any polygons in this array contain any other polygons in this - * array. It is used for checking for holes. - * Logic inspired by: https://github.com/Esri/terraformer-arcgis-parser - * @param {Array.<!Array.<!Array.<number>>>} rings Rings. - * @param {ol.geom.GeometryLayout} layout Geometry layout. - * @private - * @return {Array.<!Array.<!Array.<number>>>} Transformed rings. - */ -ol.format.EsriJSON.convertRings_ = function(rings, layout) { - var flatRing = []; - var outerRings = []; - var holes = []; - var i, ii; - for (i = 0, ii = rings.length; i < ii; ++i) { - flatRing.length = 0; - ol.geom.flat.deflate.coordinates(flatRing, 0, rings[i], layout.length); - // is this ring an outer ring? is it clockwise? - var clockwise = ol.geom.flat.orient.linearRingIsClockwise(flatRing, 0, - flatRing.length, layout.length); - if (clockwise) { - outerRings.push([rings[i]]); - } else { - holes.push(rings[i]); - } - } - while (holes.length) { - var hole = holes.shift(); - var matched = false; - // loop over all outer rings and see if they contain our hole. - for (i = outerRings.length - 1; i >= 0; i--) { - var outerRing = outerRings[i][0]; - if (ol.extent.containsExtent(new ol.geom.LinearRing( - outerRing).getExtent(), - new ol.geom.LinearRing(hole).getExtent())) { - // the hole is contained push it into our polygon - outerRings[i].push(hole); - matched = true; - break; - } - } - if (!matched) { - // no outer rings contain this hole turn it into and outer - // ring (reverse it) - outerRings.push([hole.reverse()]); - } - } - return outerRings; -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.Geometry} Point. - */ -ol.format.EsriJSON.readPointGeometry_ = function(object) { - var point; - if (object.m !== undefined && object.z !== undefined) { - point = new ol.geom.Point([object.x, object.y, object.z, object.m], - ol.geom.GeometryLayout.XYZM); - } else if (object.z !== undefined) { - point = new ol.geom.Point([object.x, object.y, object.z], - ol.geom.GeometryLayout.XYZ); - } else if (object.m !== undefined) { - point = new ol.geom.Point([object.x, object.y, object.m], - ol.geom.GeometryLayout.XYM); - } else { - point = new ol.geom.Point([object.x, object.y]); - } - return point; -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.Geometry} LineString. - */ -ol.format.EsriJSON.readLineStringGeometry_ = function(object) { - var layout = ol.format.EsriJSON.getGeometryLayout_(object); - return new ol.geom.LineString(object.paths[0], layout); -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.Geometry} MultiLineString. - */ -ol.format.EsriJSON.readMultiLineStringGeometry_ = function(object) { - var layout = ol.format.EsriJSON.getGeometryLayout_(object); - return new ol.geom.MultiLineString(object.paths, layout); -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.GeometryLayout} The geometry layout to use. - */ -ol.format.EsriJSON.getGeometryLayout_ = function(object) { - var layout = ol.geom.GeometryLayout.XY; - if (object.hasZ === true && object.hasM === true) { - layout = ol.geom.GeometryLayout.XYZM; - } else if (object.hasZ === true) { - layout = ol.geom.GeometryLayout.XYZ; - } else if (object.hasM === true) { - layout = ol.geom.GeometryLayout.XYM; - } - return layout; -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.Geometry} MultiPoint. - */ -ol.format.EsriJSON.readMultiPointGeometry_ = function(object) { - var layout = ol.format.EsriJSON.getGeometryLayout_(object); - return new ol.geom.MultiPoint(object.points, layout); -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.Geometry} MultiPolygon. - */ -ol.format.EsriJSON.readMultiPolygonGeometry_ = function(object) { - var layout = ol.format.EsriJSON.getGeometryLayout_(object); - return new ol.geom.MultiPolygon( - /** @type {Array.<Array.<Array.<Array.<number>>>>} */(object.rings), - layout); -}; - - -/** - * @param {EsriJSONGeometry} object Object. - * @private - * @return {ol.geom.Geometry} Polygon. - */ -ol.format.EsriJSON.readPolygonGeometry_ = function(object) { - var layout = ol.format.EsriJSON.getGeometryLayout_(object); - return new ol.geom.Polygon(object.rings, layout); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONGeometry} EsriJSON geometry. - */ -ol.format.EsriJSON.writePointGeometry_ = function(geometry, opt_options) { - var coordinates = /** @type {ol.geom.Point} */ (geometry).getCoordinates(); - var esriJSON; - var layout = /** @type {ol.geom.Point} */ (geometry).getLayout(); - if (layout === ol.geom.GeometryLayout.XYZ) { - esriJSON = /** @type {EsriJSONPoint} */ ({ - x: coordinates[0], - y: coordinates[1], - z: coordinates[2] - }); - } else if (layout === ol.geom.GeometryLayout.XYM) { - esriJSON = /** @type {EsriJSONPoint} */ ({ - x: coordinates[0], - y: coordinates[1], - m: coordinates[2] - }); - } else if (layout === ol.geom.GeometryLayout.XYZM) { - esriJSON = /** @type {EsriJSONPoint} */ ({ - x: coordinates[0], - y: coordinates[1], - z: coordinates[2], - m: coordinates[3] - }); - } else if (layout === ol.geom.GeometryLayout.XY) { - esriJSON = /** @type {EsriJSONPoint} */ ({ - x: coordinates[0], - y: coordinates[1] - }); - } else { - ol.asserts.assert(false, 34); // Invalid geometry layout - } - return /** @type {EsriJSONGeometry} */ (esriJSON); -}; - - -/** - * @param {ol.geom.SimpleGeometry} geometry Geometry. - * @private - * @return {Object} Object with boolean hasZ and hasM keys. - */ -ol.format.EsriJSON.getHasZM_ = function(geometry) { - var layout = geometry.getLayout(); - return { - hasZ: (layout === ol.geom.GeometryLayout.XYZ || - layout === ol.geom.GeometryLayout.XYZM), - hasM: (layout === ol.geom.GeometryLayout.XYM || - layout === ol.geom.GeometryLayout.XYZM) - }; -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONPolyline} EsriJSON geometry. - */ -ol.format.EsriJSON.writeLineStringGeometry_ = function(geometry, opt_options) { - var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.LineString} */ (geometry)); - return /** @type {EsriJSONPolyline} */ ({ - hasZ: hasZM.hasZ, - hasM: hasZM.hasM, - paths: [ - /** @type {ol.geom.LineString} */ (geometry).getCoordinates() - ] - }); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONPolygon} EsriJSON geometry. - */ -ol.format.EsriJSON.writePolygonGeometry_ = function(geometry, opt_options) { - // Esri geometries use the left-hand rule - var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.Polygon} */ (geometry)); - return /** @type {EsriJSONPolygon} */ ({ - hasZ: hasZM.hasZ, - hasM: hasZM.hasM, - rings: /** @type {ol.geom.Polygon} */ (geometry).getCoordinates(false) - }); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONPolyline} EsriJSON geometry. - */ -ol.format.EsriJSON.writeMultiLineStringGeometry_ = function(geometry, opt_options) { - var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.MultiLineString} */ (geometry)); - return /** @type {EsriJSONPolyline} */ ({ - hasZ: hasZM.hasZ, - hasM: hasZM.hasM, - paths: /** @type {ol.geom.MultiLineString} */ (geometry).getCoordinates() - }); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONMultipoint} EsriJSON geometry. - */ -ol.format.EsriJSON.writeMultiPointGeometry_ = function(geometry, opt_options) { - var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.MultiPoint} */ (geometry)); - return /** @type {EsriJSONMultipoint} */ ({ - hasZ: hasZM.hasZ, - hasM: hasZM.hasM, - points: /** @type {ol.geom.MultiPoint} */ (geometry).getCoordinates() - }); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONPolygon} EsriJSON geometry. - */ -ol.format.EsriJSON.writeMultiPolygonGeometry_ = function(geometry, - opt_options) { - var hasZM = ol.format.EsriJSON.getHasZM_(/** @type {ol.geom.MultiPolygon} */ (geometry)); - var coordinates = /** @type {ol.geom.MultiPolygon} */ (geometry).getCoordinates(false); - var output = []; - for (var i = 0; i < coordinates.length; i++) { - for (var x = coordinates[i].length - 1; x >= 0; x--) { - output.push(coordinates[i][x]); - } - } - return /** @type {EsriJSONPolygon} */ ({ - hasZ: hasZM.hasZ, - hasM: hasZM.hasM, - rings: output - }); -}; - - -/** - * @const - * @private - * @type {Object.<ol.geom.GeometryType, function(EsriJSONGeometry): ol.geom.Geometry>} - */ -ol.format.EsriJSON.GEOMETRY_READERS_ = {}; -ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.POINT] = - ol.format.EsriJSON.readPointGeometry_; -ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.LINE_STRING] = - ol.format.EsriJSON.readLineStringGeometry_; -ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.POLYGON] = - ol.format.EsriJSON.readPolygonGeometry_; -ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_POINT] = - ol.format.EsriJSON.readMultiPointGeometry_; -ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_LINE_STRING] = - ol.format.EsriJSON.readMultiLineStringGeometry_; -ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_POLYGON] = - ol.format.EsriJSON.readMultiPolygonGeometry_; - - -/** - * @const - * @private - * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (EsriJSONGeometry)>} - */ -ol.format.EsriJSON.GEOMETRY_WRITERS_ = {}; -ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.POINT] = - ol.format.EsriJSON.writePointGeometry_; -ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.LINE_STRING] = - ol.format.EsriJSON.writeLineStringGeometry_; -ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.POLYGON] = - ol.format.EsriJSON.writePolygonGeometry_; -ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_POINT] = - ol.format.EsriJSON.writeMultiPointGeometry_; -ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_LINE_STRING] = - ol.format.EsriJSON.writeMultiLineStringGeometry_; -ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_POLYGON] = - ol.format.EsriJSON.writeMultiPolygonGeometry_; - - -/** - * Read a feature from a EsriJSON Feature source. Only works for Feature, - * use `readFeatures` to read FeatureCollection source. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.EsriJSON.prototype.readFeature; - - -/** - * Read all features from a EsriJSON source. Works with both Feature and - * FeatureCollection sources. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.EsriJSON.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.EsriJSON.prototype.readFeatureFromObject = function( - object, opt_options) { - var esriJSONFeature = /** @type {EsriJSONFeature} */ (object); - var geometry = ol.format.EsriJSON.readGeometry_(esriJSONFeature.geometry, - opt_options); - var feature = new ol.Feature(); - if (this.geometryName_) { - feature.setGeometryName(this.geometryName_); - } - feature.setGeometry(geometry); - if (opt_options && opt_options.idField && - esriJSONFeature.attributes[opt_options.idField]) { - feature.setId(/** @type {number} */( - esriJSONFeature.attributes[opt_options.idField])); - } - if (esriJSONFeature.attributes) { - feature.setProperties(esriJSONFeature.attributes); - } - return feature; -}; - - -/** - * @inheritDoc - */ -ol.format.EsriJSON.prototype.readFeaturesFromObject = function( - object, opt_options) { - var esriJSONObject = /** @type {EsriJSONObject} */ (object); - var options = opt_options ? opt_options : {}; - if (esriJSONObject.features) { - var esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ - (object); - /** @type {Array.<ol.Feature>} */ - var features = []; - var esriJSONFeatures = esriJSONFeatureCollection.features; - var i, ii; - options.idField = object.objectIdFieldName; - for (i = 0, ii = esriJSONFeatures.length; i < ii; ++i) { - features.push(this.readFeatureFromObject(esriJSONFeatures[i], - options)); - } - return features; - } else { - return [this.readFeatureFromObject(object, options)]; - } -}; - - -/** - * Read a geometry from a EsriJSON source. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.geom.Geometry} Geometry. - * @api - */ -ol.format.EsriJSON.prototype.readGeometry; - - -/** - * @inheritDoc - */ -ol.format.EsriJSON.prototype.readGeometryFromObject = function( - object, opt_options) { - return ol.format.EsriJSON.readGeometry_( - /** @type {EsriJSONGeometry} */ (object), opt_options); -}; - - -/** - * Read the projection from a EsriJSON source. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.EsriJSON.prototype.readProjection; - - -/** - * @inheritDoc - */ -ol.format.EsriJSON.prototype.readProjectionFromObject = function(object) { - var esriJSONObject = /** @type {EsriJSONObject} */ (object); - if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) { - var crs = esriJSONObject.spatialReference.wkid; - return ol.proj.get('EPSG:' + crs); - } else { - return null; - } -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {EsriJSONGeometry} EsriJSON geometry. - */ -ol.format.EsriJSON.writeGeometry_ = function(geometry, opt_options) { - var geometryWriter = ol.format.EsriJSON.GEOMETRY_WRITERS_[geometry.getType()]; - return geometryWriter(/** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, true, opt_options)), - opt_options); -}; - - -/** - * Encode a geometry as a EsriJSON string. - * - * @function - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} EsriJSON. - * @api - */ -ol.format.EsriJSON.prototype.writeGeometry; - - -/** - * Encode a geometry as a EsriJSON object. - * - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {EsriJSONGeometry} Object. - * @override - * @api - */ -ol.format.EsriJSON.prototype.writeGeometryObject = function(geometry, - opt_options) { - return ol.format.EsriJSON.writeGeometry_(geometry, - this.adaptOptions(opt_options)); -}; - - -/** - * Encode a feature as a EsriJSON Feature string. - * - * @function - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} EsriJSON. - * @api - */ -ol.format.EsriJSON.prototype.writeFeature; - - -/** - * Encode a feature as a esriJSON Feature object. - * - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {Object} Object. - * @override - * @api - */ -ol.format.EsriJSON.prototype.writeFeatureObject = function( - feature, opt_options) { - opt_options = this.adaptOptions(opt_options); - var object = {}; - var geometry = feature.getGeometry(); - if (geometry) { - object['geometry'] = - ol.format.EsriJSON.writeGeometry_(geometry, opt_options); - } - var properties = feature.getProperties(); - delete properties[feature.getGeometryName()]; - if (!ol.obj.isEmpty(properties)) { - object['attributes'] = properties; - } else { - object['attributes'] = {}; - } - if (opt_options && opt_options.featureProjection) { - object['spatialReference'] = /** @type {EsriJSONCRS} */({ - wkid: ol.proj.get( - opt_options.featureProjection).getCode().split(':').pop() - }); - } - return object; -}; - - -/** - * Encode an array of features as EsriJSON. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} EsriJSON. - * @api - */ -ol.format.EsriJSON.prototype.writeFeatures; - - -/** - * Encode an array of features as a EsriJSON object. - * - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {Object} EsriJSON Object. - * @override - * @api - */ -ol.format.EsriJSON.prototype.writeFeaturesObject = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - var objects = []; - var i, ii; - for (i = 0, ii = features.length; i < ii; ++i) { - objects.push(this.writeFeatureObject(features[i], opt_options)); - } - return /** @type {EsriJSONFeatureCollection} */ ({ - 'features': objects - }); -}; - -goog.provide('ol.format.filter.Filter'); - - -/** - * @classdesc - * Abstract class; normally only used for creating subclasses and not instantiated in apps. - * Base class for WFS GetFeature filters. - * - * @constructor - * @param {!string} tagName The XML tag name for this filter. - * @struct - * @api - */ -ol.format.filter.Filter = function(tagName) { - - /** - * @private - * @type {!string} - */ - this.tagName_ = tagName; -}; - -/** - * The XML tag name for a filter. - * @returns {!string} Name. - */ -ol.format.filter.Filter.prototype.getTagName = function() { - return this.tagName_; -}; - -goog.provide('ol.format.filter.LogicalNary'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.format.filter.Filter'); - - -/** - * @classdesc - * Abstract class; normally only used for creating subclasses and not instantiated in apps. - * Base class for WFS GetFeature n-ary logical filters. - * - * @constructor - * @param {!string} tagName The XML tag name for this filter. - * @param {...ol.format.filter.Filter} conditions Conditions. - * @extends {ol.format.filter.Filter} - */ -ol.format.filter.LogicalNary = function(tagName, conditions) { - - ol.format.filter.Filter.call(this, tagName); - - /** - * @public - * @type {Array.<ol.format.filter.Filter>} - */ - this.conditions = Array.prototype.slice.call(arguments, 1); - ol.asserts.assert(this.conditions.length >= 2, 57); // At least 2 conditions are required. -}; -ol.inherits(ol.format.filter.LogicalNary, ol.format.filter.Filter); - -goog.provide('ol.format.filter.And'); - -goog.require('ol'); -goog.require('ol.format.filter.LogicalNary'); - -/** - * @classdesc - * Represents a logical `<And>` operator between two or more filter conditions. - * - * @constructor - * @param {...ol.format.filter.Filter} conditions Conditions. - * @extends {ol.format.filter.LogicalNary} - * @api - */ -ol.format.filter.And = function(conditions) { - var params = ['And'].concat(Array.prototype.slice.call(arguments)); - ol.format.filter.LogicalNary.apply(this, params); -}; -ol.inherits(ol.format.filter.And, ol.format.filter.LogicalNary); - -goog.provide('ol.format.filter.Bbox'); - -goog.require('ol'); -goog.require('ol.format.filter.Filter'); - - -/** - * @classdesc - * Represents a `<BBOX>` operator to test whether a geometry-valued property - * intersects a fixed bounding box - * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!ol.Extent} extent Extent. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {ol.format.filter.Filter} - * @api - */ -ol.format.filter.Bbox = function(geometryName, extent, opt_srsName) { - - ol.format.filter.Filter.call(this, 'BBOX'); - - /** - * @public - * @type {!string} - */ - this.geometryName = geometryName; - - /** - * @public - * @type {ol.Extent} - */ - this.extent = extent; - - /** - * @public - * @type {string|undefined} - */ - this.srsName = opt_srsName; -}; -ol.inherits(ol.format.filter.Bbox, ol.format.filter.Filter); - -goog.provide('ol.format.filter.Comparison'); - -goog.require('ol'); -goog.require('ol.format.filter.Filter'); - - -/** - * @classdesc - * Abstract class; normally only used for creating subclasses and not instantiated in apps. - * Base class for WFS GetFeature property comparison filters. - * - * @constructor - * @param {!string} tagName The XML tag name for this filter. - * @param {!string} propertyName Name of the context property to compare. - * @extends {ol.format.filter.Filter} - * @api - */ -ol.format.filter.Comparison = function(tagName, propertyName) { - - ol.format.filter.Filter.call(this, tagName); - - /** - * @public - * @type {!string} - */ - this.propertyName = propertyName; -}; -ol.inherits(ol.format.filter.Comparison, ol.format.filter.Filter); - -goog.provide('ol.format.filter.During'); - -goog.require('ol'); -goog.require('ol.format.filter.Comparison'); - - -/** - * @classdesc - * Represents a `<During>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!string} begin The begin date in ISO-8601 format. - * @param {!string} end The end date in ISO-8601 format. - * @extends {ol.format.filter.Comparison} - * @api - */ -ol.format.filter.During = function(propertyName, begin, end) { - ol.format.filter.Comparison.call(this, 'During', propertyName); - - /** - * @public - * @type {!string} - */ - this.begin = begin; - - /** - * @public - * @type {!string} - */ - this.end = end; -}; -ol.inherits(ol.format.filter.During, ol.format.filter.Comparison); - -goog.provide('ol.format.filter.ComparisonBinary'); - -goog.require('ol'); -goog.require('ol.format.filter.Comparison'); - - -/** - * @classdesc - * Abstract class; normally only used for creating subclasses and not instantiated in apps. - * Base class for WFS GetFeature property binary comparison filters. - * - * @constructor - * @param {!string} tagName The XML tag name for this filter. - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {ol.format.filter.Comparison} - * @api - */ -ol.format.filter.ComparisonBinary = function( - tagName, propertyName, expression, opt_matchCase) { - - ol.format.filter.Comparison.call(this, tagName, propertyName); - - /** - * @public - * @type {!(string|number)} - */ - this.expression = expression; - - /** - * @public - * @type {boolean|undefined} - */ - this.matchCase = opt_matchCase; -}; -ol.inherits(ol.format.filter.ComparisonBinary, ol.format.filter.Comparison); - -goog.provide('ol.format.filter.EqualTo'); - -goog.require('ol'); -goog.require('ol.format.filter.ComparisonBinary'); - - -/** - * @classdesc - * Represents a `<PropertyIsEqualTo>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {ol.format.filter.ComparisonBinary} - * @api - */ -ol.format.filter.EqualTo = function(propertyName, expression, opt_matchCase) { - ol.format.filter.ComparisonBinary.call(this, 'PropertyIsEqualTo', propertyName, expression, opt_matchCase); -}; -ol.inherits(ol.format.filter.EqualTo, ol.format.filter.ComparisonBinary); - -goog.provide('ol.format.filter.GreaterThan'); - -goog.require('ol'); -goog.require('ol.format.filter.ComparisonBinary'); - - -/** - * @classdesc - * Represents a `<PropertyIsGreaterThan>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {ol.format.filter.ComparisonBinary} - * @api - */ -ol.format.filter.GreaterThan = function(propertyName, expression) { - ol.format.filter.ComparisonBinary.call(this, 'PropertyIsGreaterThan', propertyName, expression); -}; -ol.inherits(ol.format.filter.GreaterThan, ol.format.filter.ComparisonBinary); - -goog.provide('ol.format.filter.GreaterThanOrEqualTo'); - -goog.require('ol'); -goog.require('ol.format.filter.ComparisonBinary'); - - -/** - * @classdesc - * Represents a `<PropertyIsGreaterThanOrEqualTo>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {ol.format.filter.ComparisonBinary} - * @api - */ -ol.format.filter.GreaterThanOrEqualTo = function(propertyName, expression) { - ol.format.filter.ComparisonBinary.call(this, 'PropertyIsGreaterThanOrEqualTo', propertyName, expression); -}; -ol.inherits(ol.format.filter.GreaterThanOrEqualTo, ol.format.filter.ComparisonBinary); - -goog.provide('ol.format.filter.Spatial'); - -goog.require('ol'); -goog.require('ol.format.filter.Filter'); - - -/** - * @classdesc - * Represents a spatial operator to test whether a geometry-valued property - * relates to a given geometry. - * - * @constructor - * @param {!string} tagName The XML tag name for this filter. - * @param {!string} geometryName Geometry name to use. - * @param {!ol.geom.Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {ol.format.filter.Filter} - * @api - */ -ol.format.filter.Spatial = function(tagName, geometryName, geometry, opt_srsName) { - - ol.format.filter.Filter.call(this, tagName); - - /** - * @public - * @type {!string} - */ - this.geometryName = geometryName || 'the_geom'; - - /** - * @public - * @type {ol.geom.Geometry} - */ - this.geometry = geometry; - - /** - * @public - * @type {string|undefined} - */ - this.srsName = opt_srsName; -}; -ol.inherits(ol.format.filter.Spatial, ol.format.filter.Filter); - -goog.provide('ol.format.filter.Intersects'); - -goog.require('ol'); -goog.require('ol.format.filter.Spatial'); - - -/** - * @classdesc - * Represents a `<Intersects>` operator to test whether a geometry-valued property - * intersects a given geometry. - * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!ol.geom.Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {ol.format.filter.Spatial} - * @api - */ -ol.format.filter.Intersects = function(geometryName, geometry, opt_srsName) { - - ol.format.filter.Spatial.call(this, 'Intersects', geometryName, geometry, opt_srsName); - -}; -ol.inherits(ol.format.filter.Intersects, ol.format.filter.Spatial); - -goog.provide('ol.format.filter.IsBetween'); - -goog.require('ol'); -goog.require('ol.format.filter.Comparison'); - - -/** - * @classdesc - * Represents a `<PropertyIsBetween>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} lowerBoundary The lower bound of the range. - * @param {!number} upperBoundary The upper bound of the range. - * @extends {ol.format.filter.Comparison} - * @api - */ -ol.format.filter.IsBetween = function(propertyName, lowerBoundary, upperBoundary) { - ol.format.filter.Comparison.call(this, 'PropertyIsBetween', propertyName); - - /** - * @public - * @type {!number} - */ - this.lowerBoundary = lowerBoundary; - - /** - * @public - * @type {!number} - */ - this.upperBoundary = upperBoundary; -}; -ol.inherits(ol.format.filter.IsBetween, ol.format.filter.Comparison); - -goog.provide('ol.format.filter.IsLike'); - -goog.require('ol'); -goog.require('ol.format.filter.Comparison'); - - -/** - * @classdesc - * Represents a `<PropertyIsLike>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!string} pattern Text pattern. - * @param {string=} opt_wildCard Pattern character which matches any sequence of - * zero or more string characters. Default is '*'. - * @param {string=} opt_singleChar pattern character which matches any single - * string character. Default is '.'. - * @param {string=} opt_escapeChar Escape character which can be used to escape - * the pattern characters. Default is '!'. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {ol.format.filter.Comparison} - * @api - */ -ol.format.filter.IsLike = function(propertyName, pattern, - opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { - ol.format.filter.Comparison.call(this, 'PropertyIsLike', propertyName); - - /** - * @public - * @type {!string} - */ - this.pattern = pattern; - - /** - * @public - * @type {!string} - */ - this.wildCard = (opt_wildCard !== undefined) ? opt_wildCard : '*'; - - /** - * @public - * @type {!string} - */ - this.singleChar = (opt_singleChar !== undefined) ? opt_singleChar : '.'; - - /** - * @public - * @type {!string} - */ - this.escapeChar = (opt_escapeChar !== undefined) ? opt_escapeChar : '!'; - - /** - * @public - * @type {boolean|undefined} - */ - this.matchCase = opt_matchCase; -}; -ol.inherits(ol.format.filter.IsLike, ol.format.filter.Comparison); - -goog.provide('ol.format.filter.IsNull'); - -goog.require('ol'); -goog.require('ol.format.filter.Comparison'); - - -/** - * @classdesc - * Represents a `<PropertyIsNull>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @extends {ol.format.filter.Comparison} - * @api - */ -ol.format.filter.IsNull = function(propertyName) { - ol.format.filter.Comparison.call(this, 'PropertyIsNull', propertyName); -}; -ol.inherits(ol.format.filter.IsNull, ol.format.filter.Comparison); - -goog.provide('ol.format.filter.LessThan'); - -goog.require('ol'); -goog.require('ol.format.filter.ComparisonBinary'); - - -/** - * @classdesc - * Represents a `<PropertyIsLessThan>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {ol.format.filter.ComparisonBinary} - * @api - */ -ol.format.filter.LessThan = function(propertyName, expression) { - ol.format.filter.ComparisonBinary.call(this, 'PropertyIsLessThan', propertyName, expression); -}; -ol.inherits(ol.format.filter.LessThan, ol.format.filter.ComparisonBinary); - -goog.provide('ol.format.filter.LessThanOrEqualTo'); - -goog.require('ol'); -goog.require('ol.format.filter.ComparisonBinary'); - - -/** - * @classdesc - * Represents a `<PropertyIsLessThanOrEqualTo>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {ol.format.filter.ComparisonBinary} - * @api - */ -ol.format.filter.LessThanOrEqualTo = function(propertyName, expression) { - ol.format.filter.ComparisonBinary.call(this, 'PropertyIsLessThanOrEqualTo', propertyName, expression); -}; -ol.inherits(ol.format.filter.LessThanOrEqualTo, ol.format.filter.ComparisonBinary); - -goog.provide('ol.format.filter.Not'); - -goog.require('ol'); -goog.require('ol.format.filter.Filter'); - - -/** - * @classdesc - * Represents a logical `<Not>` operator for a filter condition. - * - * @constructor - * @param {!ol.format.filter.Filter} condition Filter condition. - * @extends {ol.format.filter.Filter} - * @api - */ -ol.format.filter.Not = function(condition) { - - ol.format.filter.Filter.call(this, 'Not'); - - /** - * @public - * @type {!ol.format.filter.Filter} - */ - this.condition = condition; -}; -ol.inherits(ol.format.filter.Not, ol.format.filter.Filter); - -goog.provide('ol.format.filter.NotEqualTo'); - -goog.require('ol'); -goog.require('ol.format.filter.ComparisonBinary'); - - -/** - * @classdesc - * Represents a `<PropertyIsNotEqualTo>` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {ol.format.filter.ComparisonBinary} - * @api - */ -ol.format.filter.NotEqualTo = function(propertyName, expression, opt_matchCase) { - ol.format.filter.ComparisonBinary.call(this, 'PropertyIsNotEqualTo', propertyName, expression, opt_matchCase); -}; -ol.inherits(ol.format.filter.NotEqualTo, ol.format.filter.ComparisonBinary); - -goog.provide('ol.format.filter.Or'); - -goog.require('ol'); -goog.require('ol.format.filter.LogicalNary'); - - -/** - * @classdesc - * Represents a logical `<Or>` operator between two ore more filter conditions. - * - * @constructor - * @param {...ol.format.filter.Filter} conditions Conditions. - * @extends {ol.format.filter.LogicalNary} - * @api - */ -ol.format.filter.Or = function(conditions) { - var params = ['Or'].concat(Array.prototype.slice.call(arguments)); - ol.format.filter.LogicalNary.apply(this, params); -}; -ol.inherits(ol.format.filter.Or, ol.format.filter.LogicalNary); - -goog.provide('ol.format.filter.Within'); - -goog.require('ol'); -goog.require('ol.format.filter.Spatial'); - - -/** - * @classdesc - * Represents a `<Within>` operator to test whether a geometry-valued property - * is within a given geometry. - * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!ol.geom.Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {ol.format.filter.Spatial} - * @api - */ -ol.format.filter.Within = function(geometryName, geometry, opt_srsName) { - - ol.format.filter.Spatial.call(this, 'Within', geometryName, geometry, opt_srsName); - -}; -ol.inherits(ol.format.filter.Within, ol.format.filter.Spatial); - -goog.provide('ol.format.filter'); - -goog.require('ol.format.filter.And'); -goog.require('ol.format.filter.Bbox'); -goog.require('ol.format.filter.During'); -goog.require('ol.format.filter.EqualTo'); -goog.require('ol.format.filter.GreaterThan'); -goog.require('ol.format.filter.GreaterThanOrEqualTo'); -goog.require('ol.format.filter.Intersects'); -goog.require('ol.format.filter.IsBetween'); -goog.require('ol.format.filter.IsLike'); -goog.require('ol.format.filter.IsNull'); -goog.require('ol.format.filter.LessThan'); -goog.require('ol.format.filter.LessThanOrEqualTo'); -goog.require('ol.format.filter.Not'); -goog.require('ol.format.filter.NotEqualTo'); -goog.require('ol.format.filter.Or'); -goog.require('ol.format.filter.Within'); - - -/** - * Create a logical `<And>` operator between two or more filter conditions. - * - * @param {...ol.format.filter.Filter} conditions Filter conditions. - * @returns {!ol.format.filter.And} `<And>` operator. - * @api - */ -ol.format.filter.and = function(conditions) { - var params = [null].concat(Array.prototype.slice.call(arguments)); - return new (Function.prototype.bind.apply(ol.format.filter.And, params)); -}; - - -/** - * Create a logical `<Or>` operator between two or more filter conditions. - * - * @param {...ol.format.filter.Filter} conditions Filter conditions. - * @returns {!ol.format.filter.Or} `<Or>` operator. - * @api - */ -ol.format.filter.or = function(conditions) { - var params = [null].concat(Array.prototype.slice.call(arguments)); - return new (Function.prototype.bind.apply(ol.format.filter.Or, params)); -}; - - -/** - * Represents a logical `<Not>` operator for a filter condition. - * - * @param {!ol.format.filter.Filter} condition Filter condition. - * @returns {!ol.format.filter.Not} `<Not>` operator. - * @api - */ -ol.format.filter.not = function(condition) { - return new ol.format.filter.Not(condition); -}; - - -/** - * Create a `<BBOX>` operator to test whether a geometry-valued property - * intersects a fixed bounding box - * - * @param {!string} geometryName Geometry name to use. - * @param {!ol.Extent} extent Extent. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @returns {!ol.format.filter.Bbox} `<BBOX>` operator. - * @api - */ -ol.format.filter.bbox = function(geometryName, extent, opt_srsName) { - return new ol.format.filter.Bbox(geometryName, extent, opt_srsName); -}; - -/** - * Create a `<Intersects>` operator to test whether a geometry-valued property - * intersects a given geometry. - * - * @param {!string} geometryName Geometry name to use. - * @param {!ol.geom.Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @returns {!ol.format.filter.Intersects} `<Intersects>` operator. - * @api - */ -ol.format.filter.intersects = function(geometryName, geometry, opt_srsName) { - return new ol.format.filter.Intersects(geometryName, geometry, opt_srsName); -}; - -/** - * Create a `<Within>` operator to test whether a geometry-valued property - * is within a given geometry. - * - * @param {!string} geometryName Geometry name to use. - * @param {!ol.geom.Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @returns {!ol.format.filter.Within} `<Within>` operator. - * @api - */ -ol.format.filter.within = function(geometryName, geometry, opt_srsName) { - return new ol.format.filter.Within(geometryName, geometry, opt_srsName); -}; - - -/** - * Creates a `<PropertyIsEqualTo>` comparison operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @returns {!ol.format.filter.EqualTo} `<PropertyIsEqualTo>` operator. - * @api - */ -ol.format.filter.equalTo = function(propertyName, expression, opt_matchCase) { - return new ol.format.filter.EqualTo(propertyName, expression, opt_matchCase); -}; - - -/** - * Creates a `<PropertyIsNotEqualTo>` comparison operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @returns {!ol.format.filter.NotEqualTo} `<PropertyIsNotEqualTo>` operator. - * @api - */ -ol.format.filter.notEqualTo = function(propertyName, expression, opt_matchCase) { - return new ol.format.filter.NotEqualTo(propertyName, expression, opt_matchCase); -}; - - -/** - * Creates a `<PropertyIsLessThan>` comparison operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @returns {!ol.format.filter.LessThan} `<PropertyIsLessThan>` operator. - * @api - */ -ol.format.filter.lessThan = function(propertyName, expression) { - return new ol.format.filter.LessThan(propertyName, expression); -}; - - -/** - * Creates a `<PropertyIsLessThanOrEqualTo>` comparison operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @returns {!ol.format.filter.LessThanOrEqualTo} `<PropertyIsLessThanOrEqualTo>` operator. - * @api - */ -ol.format.filter.lessThanOrEqualTo = function(propertyName, expression) { - return new ol.format.filter.LessThanOrEqualTo(propertyName, expression); -}; - - -/** - * Creates a `<PropertyIsGreaterThan>` comparison operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @returns {!ol.format.filter.GreaterThan} `<PropertyIsGreaterThan>` operator. - * @api - */ -ol.format.filter.greaterThan = function(propertyName, expression) { - return new ol.format.filter.GreaterThan(propertyName, expression); -}; - - -/** - * Creates a `<PropertyIsGreaterThanOrEqualTo>` comparison operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @returns {!ol.format.filter.GreaterThanOrEqualTo} `<PropertyIsGreaterThanOrEqualTo>` operator. - * @api - */ -ol.format.filter.greaterThanOrEqualTo = function(propertyName, expression) { - return new ol.format.filter.GreaterThanOrEqualTo(propertyName, expression); -}; - - -/** - * Creates a `<PropertyIsNull>` comparison operator to test whether a property value - * is null. - * - * @param {!string} propertyName Name of the context property to compare. - * @returns {!ol.format.filter.IsNull} `<PropertyIsNull>` operator. - * @api - */ -ol.format.filter.isNull = function(propertyName) { - return new ol.format.filter.IsNull(propertyName); -}; - - -/** - * Creates a `<PropertyIsBetween>` comparison operator to test whether an expression - * value lies within a range given by a lower and upper bound (inclusive). - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} lowerBoundary The lower bound of the range. - * @param {!number} upperBoundary The upper bound of the range. - * @returns {!ol.format.filter.IsBetween} `<PropertyIsBetween>` operator. - * @api - */ -ol.format.filter.between = function(propertyName, lowerBoundary, upperBoundary) { - return new ol.format.filter.IsBetween(propertyName, lowerBoundary, upperBoundary); -}; - - -/** - * Represents a `<PropertyIsLike>` comparison operator that matches a string property - * value against a text pattern. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!string} pattern Text pattern. - * @param {string=} opt_wildCard Pattern character which matches any sequence of - * zero or more string characters. Default is '*'. - * @param {string=} opt_singleChar pattern character which matches any single - * string character. Default is '.'. - * @param {string=} opt_escapeChar Escape character which can be used to escape - * the pattern characters. Default is '!'. - * @param {boolean=} opt_matchCase Case-sensitive? - * @returns {!ol.format.filter.IsLike} `<PropertyIsLike>` operator. - * @api - */ -ol.format.filter.like = function(propertyName, pattern, - opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { - return new ol.format.filter.IsLike(propertyName, pattern, - opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase); -}; - - -/** - * Create a `<During>` temporal operator. - * - * @param {!string} propertyName Name of the context property to compare. - * @param {!string} begin The begin date in ISO-8601 format. - * @param {!string} end The end date in ISO-8601 format. - * @returns {!ol.format.filter.During} `<During>` operator. - * @api - */ -ol.format.filter.during = function(propertyName, begin, end) { - return new ol.format.filter.During(propertyName, begin, end); -}; - -goog.provide('ol.geom.GeometryCollection'); - -goog.require('ol'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.geom.Geometry'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.obj'); - - -/** - * @classdesc - * An array of {@link ol.geom.Geometry} objects. - * - * @constructor - * @extends {ol.geom.Geometry} - * @param {Array.<ol.geom.Geometry>=} opt_geometries Geometries. - * @api - */ -ol.geom.GeometryCollection = function(opt_geometries) { - - ol.geom.Geometry.call(this); - - /** - * @private - * @type {Array.<ol.geom.Geometry>} - */ - this.geometries_ = opt_geometries ? opt_geometries : null; - - this.listenGeometriesChange_(); -}; -ol.inherits(ol.geom.GeometryCollection, ol.geom.Geometry); - - -/** - * @param {Array.<ol.geom.Geometry>} geometries Geometries. - * @private - * @return {Array.<ol.geom.Geometry>} Cloned geometries. - */ -ol.geom.GeometryCollection.cloneGeometries_ = function(geometries) { - var clonedGeometries = []; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - clonedGeometries.push(geometries[i].clone()); - } - return clonedGeometries; -}; - - -/** - * @private - */ -ol.geom.GeometryCollection.prototype.unlistenGeometriesChange_ = function() { - var i, ii; - if (!this.geometries_) { - return; - } - for (i = 0, ii = this.geometries_.length; i < ii; ++i) { - ol.events.unlisten( - this.geometries_[i], ol.events.EventType.CHANGE, - this.changed, this); - } -}; - - -/** - * @private - */ -ol.geom.GeometryCollection.prototype.listenGeometriesChange_ = function() { - var i, ii; - if (!this.geometries_) { - return; - } - for (i = 0, ii = this.geometries_.length; i < ii; ++i) { - ol.events.listen( - this.geometries_[i], ol.events.EventType.CHANGE, - this.changed, this); - } -}; - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.GeometryCollection} Clone. - * @override - * @api - */ -ol.geom.GeometryCollection.prototype.clone = function() { - var geometryCollection = new ol.geom.GeometryCollection(null); - geometryCollection.setGeometries(this.geometries_); - return geometryCollection; -}; - - -/** - * @inheritDoc - */ -ol.geom.GeometryCollection.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < - ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - var geometries = this.geometries_; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - minSquaredDistance = geometries[i].closestPointXY( - x, y, closestPoint, minSquaredDistance); - } - return minSquaredDistance; -}; - - -/** - * @inheritDoc - */ -ol.geom.GeometryCollection.prototype.containsXY = function(x, y) { - var geometries = this.geometries_; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - if (geometries[i].containsXY(x, y)) { - return true; - } - } - return false; -}; - - -/** - * @inheritDoc - */ -ol.geom.GeometryCollection.prototype.computeExtent = function(extent) { - ol.extent.createOrUpdateEmpty(extent); - var geometries = this.geometries_; - for (var i = 0, ii = geometries.length; i < ii; ++i) { - ol.extent.extend(extent, geometries[i].getExtent()); - } - return extent; -}; - - -/** - * Return the geometries that make up this geometry collection. - * @return {Array.<ol.geom.Geometry>} Geometries. - * @api - */ -ol.geom.GeometryCollection.prototype.getGeometries = function() { - return ol.geom.GeometryCollection.cloneGeometries_(this.geometries_); -}; - - -/** - * @return {Array.<ol.geom.Geometry>} Geometries. - */ -ol.geom.GeometryCollection.prototype.getGeometriesArray = function() { - return this.geometries_; -}; - - -/** - * @inheritDoc - */ -ol.geom.GeometryCollection.prototype.getSimplifiedGeometry = function(squaredTolerance) { - if (this.simplifiedGeometryRevision != this.getRevision()) { - ol.obj.clear(this.simplifiedGeometryCache); - this.simplifiedGeometryMaxMinSquaredTolerance = 0; - this.simplifiedGeometryRevision = this.getRevision(); - } - if (squaredTolerance < 0 || - (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && - squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) { - return this; - } - var key = squaredTolerance.toString(); - if (this.simplifiedGeometryCache.hasOwnProperty(key)) { - return this.simplifiedGeometryCache[key]; - } else { - var simplifiedGeometries = []; - var geometries = this.geometries_; - var simplified = false; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - var geometry = geometries[i]; - var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); - simplifiedGeometries.push(simplifiedGeometry); - if (simplifiedGeometry !== geometry) { - simplified = true; - } - } - if (simplified) { - var simplifiedGeometryCollection = new ol.geom.GeometryCollection(null); - simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries); - this.simplifiedGeometryCache[key] = simplifiedGeometryCollection; - return simplifiedGeometryCollection; - } else { - this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; - return this; - } - } -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.GeometryCollection.prototype.getType = function() { - return ol.geom.GeometryType.GEOMETRY_COLLECTION; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.GeometryCollection.prototype.intersectsExtent = function(extent) { - var geometries = this.geometries_; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - if (geometries[i].intersectsExtent(extent)) { - return true; - } - } - return false; -}; - - -/** - * @return {boolean} Is empty. - */ -ol.geom.GeometryCollection.prototype.isEmpty = function() { - return this.geometries_.length === 0; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.GeometryCollection.prototype.rotate = function(angle, anchor) { - var geometries = this.geometries_; - for (var i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].rotate(angle, anchor); - } - this.changed(); -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.GeometryCollection.prototype.scale = function(sx, opt_sy, opt_anchor) { - var anchor = opt_anchor; - if (!anchor) { - anchor = ol.extent.getCenter(this.getExtent()); - } - var geometries = this.geometries_; - for (var i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].scale(sx, opt_sy, anchor); - } - this.changed(); -}; - - -/** - * Set the geometries that make up this geometry collection. - * @param {Array.<ol.geom.Geometry>} geometries Geometries. - * @api - */ -ol.geom.GeometryCollection.prototype.setGeometries = function(geometries) { - this.setGeometriesArray( - ol.geom.GeometryCollection.cloneGeometries_(geometries)); -}; - - -/** - * @param {Array.<ol.geom.Geometry>} geometries Geometries. - */ -ol.geom.GeometryCollection.prototype.setGeometriesArray = function(geometries) { - this.unlistenGeometriesChange_(); - this.geometries_ = geometries; - this.listenGeometriesChange_(); - this.changed(); -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.GeometryCollection.prototype.applyTransform = function(transformFn) { - var geometries = this.geometries_; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].applyTransform(transformFn); - } - this.changed(); -}; - - -/** - * Translate the geometry. - * @param {number} deltaX Delta X. - * @param {number} deltaY Delta Y. - * @override - * @api - */ -ol.geom.GeometryCollection.prototype.translate = function(deltaX, deltaY) { - var geometries = this.geometries_; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].translate(deltaX, deltaY); - } - this.changed(); -}; - - -/** - * @inheritDoc - */ -ol.geom.GeometryCollection.prototype.disposeInternal = function() { - this.unlistenGeometriesChange_(); - ol.geom.Geometry.prototype.disposeInternal.call(this); -}; - -// TODO: serialize dataProjection as crs member when writing -// see https://github.com/openlayers/openlayers/issues/2078 - -goog.provide('ol.format.GeoJSON'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.JSONFeature'); -goog.require('ol.geom.GeometryCollection'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.obj'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Feature format for reading and writing data in the GeoJSON format. - * - * @constructor - * @extends {ol.format.JSONFeature} - * @param {olx.format.GeoJSONOptions=} opt_options Options. - * @api - */ -ol.format.GeoJSON = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.JSONFeature.call(this); - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get( - options.defaultDataProjection ? - options.defaultDataProjection : 'EPSG:4326'); - - - if (options.featureProjection) { - this.defaultFeatureProjection = ol.proj.get(options.featureProjection); - } - - /** - * Name of the geometry attribute for features. - * @type {string|undefined} - * @private - */ - this.geometryName_ = options.geometryName; - -}; -ol.inherits(ol.format.GeoJSON, ol.format.JSONFeature); - - -/** - * @param {GeoJSONGeometry|GeoJSONGeometryCollection} object Object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @private - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.GeoJSON.readGeometry_ = function(object, opt_options) { - if (!object) { - return null; - } - var geometryReader = ol.format.GeoJSON.GEOMETRY_READERS_[object.type]; - return /** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions( - geometryReader(object), false, opt_options)); -}; - - -/** - * @param {GeoJSONGeometryCollection} object Object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @private - * @return {ol.geom.GeometryCollection} Geometry collection. - */ -ol.format.GeoJSON.readGeometryCollectionGeometry_ = function( - object, opt_options) { - var geometries = object.geometries.map( - /** - * @param {GeoJSONGeometry} geometry Geometry. - * @return {ol.geom.Geometry} geometry Geometry. - */ - function(geometry) { - return ol.format.GeoJSON.readGeometry_(geometry, opt_options); - }); - return new ol.geom.GeometryCollection(geometries); -}; - - -/** - * @param {GeoJSONGeometry} object Object. - * @private - * @return {ol.geom.Point} Point. - */ -ol.format.GeoJSON.readPointGeometry_ = function(object) { - return new ol.geom.Point(object.coordinates); -}; - - -/** - * @param {GeoJSONGeometry} object Object. - * @private - * @return {ol.geom.LineString} LineString. - */ -ol.format.GeoJSON.readLineStringGeometry_ = function(object) { - return new ol.geom.LineString(object.coordinates); -}; - - -/** - * @param {GeoJSONGeometry} object Object. - * @private - * @return {ol.geom.MultiLineString} MultiLineString. - */ -ol.format.GeoJSON.readMultiLineStringGeometry_ = function(object) { - return new ol.geom.MultiLineString(object.coordinates); -}; - - -/** - * @param {GeoJSONGeometry} object Object. - * @private - * @return {ol.geom.MultiPoint} MultiPoint. - */ -ol.format.GeoJSON.readMultiPointGeometry_ = function(object) { - return new ol.geom.MultiPoint(object.coordinates); -}; - - -/** - * @param {GeoJSONGeometry} object Object. - * @private - * @return {ol.geom.MultiPolygon} MultiPolygon. - */ -ol.format.GeoJSON.readMultiPolygonGeometry_ = function(object) { - return new ol.geom.MultiPolygon(object.coordinates); -}; - - -/** - * @param {GeoJSONGeometry} object Object. - * @private - * @return {ol.geom.Polygon} Polygon. - */ -ol.format.GeoJSON.readPolygonGeometry_ = function(object) { - return new ol.geom.Polygon(object.coordinates); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry|GeoJSONGeometryCollection} GeoJSON geometry. - */ -ol.format.GeoJSON.writeGeometry_ = function(geometry, opt_options) { - var geometryWriter = ol.format.GeoJSON.GEOMETRY_WRITERS_[geometry.getType()]; - return geometryWriter(/** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, true, opt_options)), - opt_options); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @private - * @return {GeoJSONGeometryCollection} Empty GeoJSON geometry collection. - */ -ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_ = function(geometry) { - return /** @type {GeoJSONGeometryCollection} */ ({ - type: 'GeometryCollection', - geometries: [] - }); -}; - - -/** - * @param {ol.geom.GeometryCollection} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometryCollection} GeoJSON geometry collection. - */ -ol.format.GeoJSON.writeGeometryCollectionGeometry_ = function( - geometry, opt_options) { - var geometries = geometry.getGeometriesArray().map(function(geometry) { - var options = ol.obj.assign({}, opt_options); - delete options.featureProjection; - return ol.format.GeoJSON.writeGeometry_(geometry, options); - }); - return /** @type {GeoJSONGeometryCollection} */ ({ - type: 'GeometryCollection', - geometries: geometries - }); -}; - - -/** - * @param {ol.geom.LineString} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry} GeoJSON geometry. - */ -ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry, opt_options) { - return /** @type {GeoJSONGeometry} */ ({ - type: 'LineString', - coordinates: geometry.getCoordinates() - }); -}; - - -/** - * @param {ol.geom.MultiLineString} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry} GeoJSON geometry. - */ -ol.format.GeoJSON.writeMultiLineStringGeometry_ = function(geometry, opt_options) { - return /** @type {GeoJSONGeometry} */ ({ - type: 'MultiLineString', - coordinates: geometry.getCoordinates() - }); -}; - - -/** - * @param {ol.geom.MultiPoint} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry} GeoJSON geometry. - */ -ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry, opt_options) { - return /** @type {GeoJSONGeometry} */ ({ - type: 'MultiPoint', - coordinates: geometry.getCoordinates() - }); -}; - - -/** - * @param {ol.geom.MultiPolygon} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry} GeoJSON geometry. - */ -ol.format.GeoJSON.writeMultiPolygonGeometry_ = function(geometry, opt_options) { - var right; - if (opt_options) { - right = opt_options.rightHanded; - } - return /** @type {GeoJSONGeometry} */ ({ - type: 'MultiPolygon', - coordinates: geometry.getCoordinates(right) - }); -}; - - -/** - * @param {ol.geom.Point} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry} GeoJSON geometry. - */ -ol.format.GeoJSON.writePointGeometry_ = function(geometry, opt_options) { - return /** @type {GeoJSONGeometry} */ ({ - type: 'Point', - coordinates: geometry.getCoordinates() - }); -}; - - -/** - * @param {ol.geom.Polygon} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @private - * @return {GeoJSONGeometry} GeoJSON geometry. - */ -ol.format.GeoJSON.writePolygonGeometry_ = function(geometry, opt_options) { - var right; - if (opt_options) { - right = opt_options.rightHanded; - } - return /** @type {GeoJSONGeometry} */ ({ - type: 'Polygon', - coordinates: geometry.getCoordinates(right) - }); -}; - - -/** - * @const - * @private - * @type {Object.<string, function(GeoJSONObject): ol.geom.Geometry>} - */ -ol.format.GeoJSON.GEOMETRY_READERS_ = { - 'Point': ol.format.GeoJSON.readPointGeometry_, - 'LineString': ol.format.GeoJSON.readLineStringGeometry_, - 'Polygon': ol.format.GeoJSON.readPolygonGeometry_, - 'MultiPoint': ol.format.GeoJSON.readMultiPointGeometry_, - 'MultiLineString': ol.format.GeoJSON.readMultiLineStringGeometry_, - 'MultiPolygon': ol.format.GeoJSON.readMultiPolygonGeometry_, - 'GeometryCollection': ol.format.GeoJSON.readGeometryCollectionGeometry_ -}; - - -/** - * @const - * @private - * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (GeoJSONGeometry|GeoJSONGeometryCollection)>} - */ -ol.format.GeoJSON.GEOMETRY_WRITERS_ = { - 'Point': ol.format.GeoJSON.writePointGeometry_, - 'LineString': ol.format.GeoJSON.writeLineStringGeometry_, - 'Polygon': ol.format.GeoJSON.writePolygonGeometry_, - 'MultiPoint': ol.format.GeoJSON.writeMultiPointGeometry_, - 'MultiLineString': ol.format.GeoJSON.writeMultiLineStringGeometry_, - 'MultiPolygon': ol.format.GeoJSON.writeMultiPolygonGeometry_, - 'GeometryCollection': ol.format.GeoJSON.writeGeometryCollectionGeometry_, - 'Circle': ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_ -}; - - -/** - * Read a feature from a GeoJSON Feature source. Only works for Feature or - * geometry types. Use {@link ol.format.GeoJSON#readFeatures} to read - * FeatureCollection source. If feature at source has an id, it will be used - * as Feature id by calling {@link ol.Feature#setId} internally. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.GeoJSON.prototype.readFeature; - - -/** - * Read all features from a GeoJSON source. Works for all GeoJSON types. - * If the source includes only geometries, features will be created with those - * geometries. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.GeoJSON.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.GeoJSON.prototype.readFeatureFromObject = function( - object, opt_options) { - /** - * @type {GeoJSONFeature} - */ - var geoJSONFeature = null; - if (object.type === 'Feature') { - geoJSONFeature = /** @type {GeoJSONFeature} */ (object); - } else { - geoJSONFeature = /** @type {GeoJSONFeature} */ ({ - type: 'Feature', - geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */ (object) - }); - } - - var geometry = ol.format.GeoJSON.readGeometry_(geoJSONFeature.geometry, opt_options); - var feature = new ol.Feature(); - if (this.geometryName_) { - feature.setGeometryName(this.geometryName_); - } - feature.setGeometry(geometry); - if (geoJSONFeature.id !== undefined) { - feature.setId(geoJSONFeature.id); - } - if (geoJSONFeature.properties) { - feature.setProperties(geoJSONFeature.properties); - } - return feature; -}; - - -/** - * @inheritDoc - */ -ol.format.GeoJSON.prototype.readFeaturesFromObject = function( - object, opt_options) { - var geoJSONObject = /** @type {GeoJSONObject} */ (object); - /** @type {Array.<ol.Feature>} */ - var features = null; - if (geoJSONObject.type === 'FeatureCollection') { - var geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */ - (object); - features = []; - var geoJSONFeatures = geoJSONFeatureCollection.features; - var i, ii; - for (i = 0, ii = geoJSONFeatures.length; i < ii; ++i) { - features.push(this.readFeatureFromObject(geoJSONFeatures[i], - opt_options)); - } - } else { - features = [this.readFeatureFromObject(object, opt_options)]; - } - return features; -}; - - -/** - * Read a geometry from a GeoJSON source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.geom.Geometry} Geometry. - * @api - */ -ol.format.GeoJSON.prototype.readGeometry; - - -/** - * @inheritDoc - */ -ol.format.GeoJSON.prototype.readGeometryFromObject = function( - object, opt_options) { - return ol.format.GeoJSON.readGeometry_( - /** @type {GeoJSONGeometry} */ (object), opt_options); -}; - - -/** - * Read the projection from a GeoJSON source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.GeoJSON.prototype.readProjection; - - -/** - * @inheritDoc - */ -ol.format.GeoJSON.prototype.readProjectionFromObject = function(object) { - var geoJSONObject = /** @type {GeoJSONObject} */ (object); - var crs = geoJSONObject.crs; - var projection; - if (crs) { - if (crs.type == 'name') { - projection = ol.proj.get(crs.properties.name); - } else if (crs.type == 'EPSG') { - // 'EPSG' is not part of the GeoJSON specification, but is generated by - // GeoServer. - // TODO: remove this when http://jira.codehaus.org/browse/GEOS-5996 - // is fixed and widely deployed. - projection = ol.proj.get('EPSG:' + crs.properties.code); - } else { - ol.asserts.assert(false, 36); // Unknown SRS type - } - } else { - projection = this.defaultDataProjection; - } - return /** @type {ol.proj.Projection} */ (projection); -}; - - -/** - * Encode a feature as a GeoJSON Feature string. - * - * @function - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} GeoJSON. - * @override - * @api - */ -ol.format.GeoJSON.prototype.writeFeature; - - -/** - * Encode a feature as a GeoJSON Feature object. - * - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {GeoJSONFeature} Object. - * @override - * @api - */ -ol.format.GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) { - opt_options = this.adaptOptions(opt_options); - - var object = /** @type {GeoJSONFeature} */ ({ - 'type': 'Feature' - }); - var id = feature.getId(); - if (id !== undefined) { - object.id = id; - } - var geometry = feature.getGeometry(); - if (geometry) { - object.geometry = - ol.format.GeoJSON.writeGeometry_(geometry, opt_options); - } else { - object.geometry = null; - } - var properties = feature.getProperties(); - delete properties[feature.getGeometryName()]; - if (!ol.obj.isEmpty(properties)) { - object.properties = properties; - } else { - object.properties = null; - } - return object; -}; - - -/** - * Encode an array of features as GeoJSON. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} GeoJSON. - * @api - */ -ol.format.GeoJSON.prototype.writeFeatures; - - -/** - * Encode an array of features as a GeoJSON object. - * - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {GeoJSONFeatureCollection} GeoJSON Object. - * @override - * @api - */ -ol.format.GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - var objects = []; - var i, ii; - for (i = 0, ii = features.length; i < ii; ++i) { - objects.push(this.writeFeatureObject(features[i], opt_options)); - } - return /** @type {GeoJSONFeatureCollection} */ ({ - type: 'FeatureCollection', - features: objects - }); -}; - - -/** - * Encode a geometry as a GeoJSON string. - * - * @function - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} GeoJSON. - * @api - */ -ol.format.GeoJSON.prototype.writeGeometry; - - -/** - * Encode a geometry as a GeoJSON object. - * - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object. - * @override - * @api - */ -ol.format.GeoJSON.prototype.writeGeometryObject = function(geometry, - opt_options) { - return ol.format.GeoJSON.writeGeometry_(geometry, - this.adaptOptions(opt_options)); -}; - -goog.provide('ol.format.XMLFeature'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.format.Feature'); -goog.require('ol.format.FormatType'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for XML feature formats. - * - * @constructor - * @abstract - * @extends {ol.format.Feature} - */ -ol.format.XMLFeature = function() { - - /** - * @type {XMLSerializer} - * @private - */ - this.xmlSerializer_ = new XMLSerializer(); - - ol.format.Feature.call(this); -}; -ol.inherits(ol.format.XMLFeature, ol.format.Feature); - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.getType = function() { - return ol.format.FormatType.XML; -}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.readFeature = function(source, opt_options) { - if (ol.xml.isDocument(source)) { - return this.readFeatureFromDocument( - /** @type {Document} */ (source), opt_options); - } else if (ol.xml.isNode(source)) { - return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readFeatureFromDocument(doc, opt_options); - } else { - return null; - } -}; - - -/** - * @param {Document} doc Document. - * @param {olx.format.ReadOptions=} opt_options Options. - * @return {ol.Feature} Feature. - */ -ol.format.XMLFeature.prototype.readFeatureFromDocument = function( - doc, opt_options) { - var features = this.readFeaturesFromDocument(doc, opt_options); - if (features.length > 0) { - return features[0]; - } else { - return null; - } -}; - - -/** - * @param {Node} node Node. - * @param {olx.format.ReadOptions=} opt_options Options. - * @return {ol.Feature} Feature. - */ -ol.format.XMLFeature.prototype.readFeatureFromNode = function(node, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.readFeatures = function(source, opt_options) { - if (ol.xml.isDocument(source)) { - return this.readFeaturesFromDocument( - /** @type {Document} */ (source), opt_options); - } else if (ol.xml.isNode(source)) { - return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readFeaturesFromDocument(doc, opt_options); - } else { - return []; - } -}; - - -/** - * @param {Document} doc Document. - * @param {olx.format.ReadOptions=} opt_options Options. - * @protected - * @return {Array.<ol.Feature>} Features. - */ -ol.format.XMLFeature.prototype.readFeaturesFromDocument = function( - doc, opt_options) { - /** @type {Array.<ol.Feature>} */ - var features = []; - var n; - for (n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - ol.array.extend(features, this.readFeaturesFromNode(n, opt_options)); - } - } - return features; -}; - - -/** - * @abstract - * @param {Node} node Node. - * @param {olx.format.ReadOptions=} opt_options Options. - * @protected - * @return {Array.<ol.Feature>} Features. - */ -ol.format.XMLFeature.prototype.readFeaturesFromNode = function(node, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.readGeometry = function(source, opt_options) { - if (ol.xml.isDocument(source)) { - return this.readGeometryFromDocument( - /** @type {Document} */ (source), opt_options); - } else if (ol.xml.isNode(source)) { - return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readGeometryFromDocument(doc, opt_options); - } else { - return null; - } -}; - - -/** - * @param {Document} doc Document. - * @param {olx.format.ReadOptions=} opt_options Options. - * @protected - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.XMLFeature.prototype.readGeometryFromDocument = function(doc, opt_options) { - return null; // not implemented -}; - - -/** - * @param {Node} node Node. - * @param {olx.format.ReadOptions=} opt_options Options. - * @protected - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.XMLFeature.prototype.readGeometryFromNode = function(node, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.readProjection = function(source) { - if (ol.xml.isDocument(source)) { - return this.readProjectionFromDocument(/** @type {Document} */ (source)); - } else if (ol.xml.isNode(source)) { - return this.readProjectionFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readProjectionFromDocument(doc); - } else { - return null; - } -}; - - -/** - * @param {Document} doc Document. - * @protected - * @return {ol.proj.Projection} Projection. - */ -ol.format.XMLFeature.prototype.readProjectionFromDocument = function(doc) { - return this.defaultDataProjection; -}; - - -/** - * @param {Node} node Node. - * @protected - * @return {ol.proj.Projection} Projection. - */ -ol.format.XMLFeature.prototype.readProjectionFromNode = function(node) { - return this.defaultDataProjection; -}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.writeFeature = function(feature, opt_options) { - var node = this.writeFeatureNode(feature, opt_options); - return this.xmlSerializer_.serializeToString(node); -}; - - -/** - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Options. - * @protected - * @return {Node} Node. - */ -ol.format.XMLFeature.prototype.writeFeatureNode = function(feature, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.writeFeatures = function(features, opt_options) { - var node = this.writeFeaturesNode(features, opt_options); - return this.xmlSerializer_.serializeToString(node); -}; - - -/** - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - */ -ol.format.XMLFeature.prototype.writeFeaturesNode = function(features, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -ol.format.XMLFeature.prototype.writeGeometry = function(geometry, opt_options) { - var node = this.writeGeometryNode(geometry, opt_options); - return this.xmlSerializer_.serializeToString(node); -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - */ -ol.format.XMLFeature.prototype.writeGeometryNode = function(geometry, opt_options) { - return null; // not implemented -}; - -// FIXME Envelopes should not be treated as geometries! readEnvelope_ is part -// of GEOMETRY_PARSERS_ and methods using GEOMETRY_PARSERS_ do not expect -// envelopes/extents, only geometries! -goog.provide('ol.format.GMLBase'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.XMLFeature'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.LinearRing'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Feature base format for reading and writing data in the GML format. - * This class cannot be instantiated, it contains only base content that - * is shared with versioned format classes ol.format.GML2 and - * ol.format.GML3. - * - * @constructor - * @abstract - * @param {olx.format.GMLOptions=} opt_options - * Optional configuration object. - * @extends {ol.format.XMLFeature} - */ -ol.format.GMLBase = function(opt_options) { - var options = /** @type {olx.format.GMLOptions} */ - (opt_options ? opt_options : {}); - - /** - * @protected - * @type {Array.<string>|string|undefined} - */ - this.featureType = options.featureType; - - /** - * @protected - * @type {Object.<string, string>|string|undefined} - */ - this.featureNS = options.featureNS; - - /** - * @protected - * @type {string} - */ - this.srsName = options.srsName; - - /** - * @protected - * @type {string} - */ - this.schemaLocation = ''; - - /** - * @type {Object.<string, Object.<string, Object>>} - */ - this.FEATURE_COLLECTION_PARSERS = {}; - this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS] = { - 'featureMember': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readFeaturesInternal), - 'featureMembers': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readFeaturesInternal) - }; - - ol.format.XMLFeature.call(this); -}; -ol.inherits(ol.format.GMLBase, ol.format.XMLFeature); - - -/** - * @const - * @type {string} - */ -ol.format.GMLBase.GMLNS = 'http://www.opengis.net/gml'; - - -/** - * A regular expression that matches if a string only contains whitespace - * characters. It will e.g. match `''`, `' '`, `'\n'` etc. The non-breaking - * space (0xa0) is explicitly included as IE doesn't include it in its - * definition of `\s`. - * - * Information from `goog.string.isEmptyOrWhitespace`: https://github.com/google/closure-library/blob/e877b1e/closure/goog/string/string.js#L156-L160 - * - * @const - * @type {RegExp} - * @private - */ -ol.format.GMLBase.ONLY_WHITESPACE_RE_ = /^[\s\xa0]*$/; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Array.<ol.Feature> | undefined} Features. - */ -ol.format.GMLBase.prototype.readFeaturesInternal = function(node, objectStack) { - var localName = node.localName; - var features = null; - if (localName == 'FeatureCollection') { - if (node.namespaceURI === 'http://www.opengis.net/wfs') { - features = ol.xml.pushParseAndPop([], - this.FEATURE_COLLECTION_PARSERS, node, - objectStack, this); - } else { - features = ol.xml.pushParseAndPop(null, - this.FEATURE_COLLECTION_PARSERS, node, - objectStack, this); - } - } else if (localName == 'featureMembers' || localName == 'featureMember') { - var context = objectStack[0]; - var featureType = context['featureType']; - var featureNS = context['featureNS']; - var i, ii, prefix = 'p', defaultPrefix = 'p0'; - if (!featureType && node.childNodes) { - featureType = [], featureNS = {}; - for (i = 0, ii = node.childNodes.length; i < ii; ++i) { - var child = node.childNodes[i]; - if (child.nodeType === 1) { - var ft = child.nodeName.split(':').pop(); - if (featureType.indexOf(ft) === -1) { - var key = ''; - var count = 0; - var uri = child.namespaceURI; - for (var candidate in featureNS) { - if (featureNS[candidate] === uri) { - key = candidate; - break; - } - ++count; - } - if (!key) { - key = prefix + count; - featureNS[key] = uri; - } - featureType.push(key + ':' + ft); - } - } - } - if (localName != 'featureMember') { - // recheck featureType for each featureMember - context['featureType'] = featureType; - context['featureNS'] = featureNS; - } - } - if (typeof featureNS === 'string') { - var ns = featureNS; - featureNS = {}; - featureNS[defaultPrefix] = ns; - } - var parsersNS = {}; - var featureTypes = Array.isArray(featureType) ? featureType : [featureType]; - for (var p in featureNS) { - var parsers = {}; - for (i = 0, ii = featureTypes.length; i < ii; ++i) { - var featurePrefix = featureTypes[i].indexOf(':') === -1 ? - defaultPrefix : featureTypes[i].split(':')[0]; - if (featurePrefix === p) { - parsers[featureTypes[i].split(':').pop()] = - (localName == 'featureMembers') ? - ol.xml.makeArrayPusher(this.readFeatureElement, this) : - ol.xml.makeReplacer(this.readFeatureElement, this); - } - } - parsersNS[featureNS[p]] = parsers; - } - if (localName == 'featureMember') { - features = ol.xml.pushParseAndPop(undefined, parsersNS, node, objectStack); - } else { - features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack); - } - } - if (features === null) { - features = []; - } - return features; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.Geometry|undefined} Geometry. - */ -ol.format.GMLBase.prototype.readGeometryElement = function(node, objectStack) { - var context = /** @type {Object} */ (objectStack[0]); - context['srsName'] = node.firstElementChild.getAttribute('srsName'); - /** @type {ol.geom.Geometry} */ - var geometry = ol.xml.pushParseAndPop(null, - this.GEOMETRY_PARSERS_, node, objectStack, this); - if (geometry) { - return /** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, false, context)); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.Feature} Feature. - */ -ol.format.GMLBase.prototype.readFeatureElement = function(node, objectStack) { - var n; - var fid = node.getAttribute('fid') || - ol.xml.getAttributeNS(node, ol.format.GMLBase.GMLNS, 'id'); - var values = {}, geometryName; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - var localName = n.localName; - // Assume attribute elements have one child node and that the child - // is a text or CDATA node (to be treated as text). - // Otherwise assume it is a geometry node. - if (n.childNodes.length === 0 || - (n.childNodes.length === 1 && - (n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) { - var value = ol.xml.getAllTextContent(n, false); - if (ol.format.GMLBase.ONLY_WHITESPACE_RE_.test(value)) { - value = undefined; - } - values[localName] = value; - } else { - // boundedBy is an extent and must not be considered as a geometry - if (localName !== 'boundedBy') { - geometryName = localName; - } - values[localName] = this.readGeometryElement(n, objectStack); - } - } - var feature = new ol.Feature(values); - if (geometryName) { - feature.setGeometryName(geometryName); - } - if (fid) { - feature.setId(fid); - } - return feature; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.Point|undefined} Point. - */ -ol.format.GMLBase.prototype.readPoint = function(node, objectStack) { - var flatCoordinates = - this.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - var point = new ol.geom.Point(null); - point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); - return point; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.MultiPoint|undefined} MultiPoint. - */ -ol.format.GMLBase.prototype.readMultiPoint = function(node, objectStack) { - /** @type {Array.<Array.<number>>} */ - var coordinates = ol.xml.pushParseAndPop([], - this.MULTIPOINT_PARSERS_, node, objectStack, this); - if (coordinates) { - return new ol.geom.MultiPoint(coordinates); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.MultiLineString|undefined} MultiLineString. - */ -ol.format.GMLBase.prototype.readMultiLineString = function(node, objectStack) { - /** @type {Array.<ol.geom.LineString>} */ - var lineStrings = ol.xml.pushParseAndPop([], - this.MULTILINESTRING_PARSERS_, node, objectStack, this); - if (lineStrings) { - var multiLineString = new ol.geom.MultiLineString(null); - multiLineString.setLineStrings(lineStrings); - return multiLineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.MultiPolygon|undefined} MultiPolygon. - */ -ol.format.GMLBase.prototype.readMultiPolygon = function(node, objectStack) { - /** @type {Array.<ol.geom.Polygon>} */ - var polygons = ol.xml.pushParseAndPop([], - this.MULTIPOLYGON_PARSERS_, node, objectStack, this); - if (polygons) { - var multiPolygon = new ol.geom.MultiPolygon(null); - multiPolygon.setPolygons(polygons); - return multiPolygon; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GMLBase.prototype.pointMemberParser_ = function(node, objectStack) { - ol.xml.parseNode(this.POINTMEMBER_PARSERS_, - node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GMLBase.prototype.lineStringMemberParser_ = function(node, objectStack) { - ol.xml.parseNode(this.LINESTRINGMEMBER_PARSERS_, - node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GMLBase.prototype.polygonMemberParser_ = function(node, objectStack) { - ol.xml.parseNode(this.POLYGONMEMBER_PARSERS_, node, - objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.LineString|undefined} LineString. - */ -ol.format.GMLBase.prototype.readLineString = function(node, objectStack) { - var flatCoordinates = - this.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); - return lineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>|undefined} LinearRing flat coordinates. - */ -ol.format.GMLBase.prototype.readFlatLinearRing_ = function(node, objectStack) { - var ring = ol.xml.pushParseAndPop(null, - this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, - objectStack, this); - if (ring) { - return ring; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.LinearRing|undefined} LinearRing. - */ -ol.format.GMLBase.prototype.readLinearRing = function(node, objectStack) { - var flatCoordinates = - this.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - var ring = new ol.geom.LinearRing(null); - ring.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); - return ring; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.geom.Polygon|undefined} Polygon. - */ -ol.format.GMLBase.prototype.readPolygon = function(node, objectStack) { - /** @type {Array.<Array.<number>>} */ - var flatLinearRings = ol.xml.pushParseAndPop([null], - this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); - if (flatLinearRings && flatLinearRings[0]) { - var polygon = new ol.geom.Polygon(null); - var flatCoordinates = flatLinearRings[0]; - var ends = [flatCoordinates.length]; - var i, ii; - for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { - ol.array.extend(flatCoordinates, flatLinearRings[i]); - ends.push(flatCoordinates.length); - } - polygon.setFlatCoordinates( - ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); - return polygon; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>} Flat coordinates. - */ -ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_ = function(node, objectStack) { - return ol.xml.pushParseAndPop(null, - this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, - objectStack, this); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GMLBase.prototype.MULTIPOINT_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.pointMemberParser_), - 'pointMembers': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.pointMemberParser_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GMLBase.prototype.MULTILINESTRING_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.lineStringMemberParser_), - 'lineStringMembers': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.lineStringMemberParser_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GMLBase.prototype.MULTIPOLYGON_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'polygonMember': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.polygonMemberParser_), - 'polygonMembers': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.polygonMemberParser_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GMLBase.prototype.POINTMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GMLBase.prototype.LINESTRINGMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineString': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.readLineString) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GMLBase.prototype.POLYGONMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Polygon': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.readPolygon) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @protected - */ -ol.format.GMLBase.prototype.RING_PARSERS = { - 'http://www.opengis.net/gml': { - 'LinearRing': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readFlatLinearRing_) - } -}; - - -/** - * @inheritDoc - */ -ol.format.GMLBase.prototype.readGeometryFromNode = function(node, opt_options) { - var geometry = this.readGeometryElement(node, - [this.getReadOptions(node, opt_options ? opt_options : {})]); - return geometry ? geometry : null; -}; - - -/** - * Read all features from a GML FeatureCollection. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.GMLBase.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.GMLBase.prototype.readFeaturesFromNode = function(node, opt_options) { - var options = { - featureType: this.featureType, - featureNS: this.featureNS - }; - if (opt_options) { - ol.obj.assign(options, this.getReadOptions(node, opt_options)); - } - var features = this.readFeaturesInternal(node, [options]); - return features || []; -}; - - -/** - * @inheritDoc - */ -ol.format.GMLBase.prototype.readProjectionFromNode = function(node) { - return ol.proj.get(this.srsName ? this.srsName : - node.firstElementChild.getAttribute('srsName')); -}; - -goog.provide('ol.format.XSD'); - -goog.require('ol.xml'); -goog.require('ol.string'); - - -/** - * @const - * @type {string} - */ -ol.format.XSD.NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema'; - - -/** - * @param {Node} node Node. - * @return {boolean|undefined} Boolean. - */ -ol.format.XSD.readBoolean = function(node) { - var s = ol.xml.getAllTextContent(node, false); - return ol.format.XSD.readBooleanString(s); -}; - - -/** - * @param {string} string String. - * @return {boolean|undefined} Boolean. - */ -ol.format.XSD.readBooleanString = function(string) { - var m = /^\s*(true|1)|(false|0)\s*$/.exec(string); - if (m) { - return m[1] !== undefined || false; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @return {number|undefined} DateTime in seconds. - */ -ol.format.XSD.readDateTime = function(node) { - var s = ol.xml.getAllTextContent(node, false); - var dateTime = Date.parse(s); - return isNaN(dateTime) ? undefined : dateTime / 1000; -}; - - -/** - * @param {Node} node Node. - * @return {number|undefined} Decimal. - */ -ol.format.XSD.readDecimal = function(node) { - var s = ol.xml.getAllTextContent(node, false); - return ol.format.XSD.readDecimalString(s); -}; - - -/** - * @param {string} string String. - * @return {number|undefined} Decimal. - */ -ol.format.XSD.readDecimalString = function(string) { - // FIXME check spec - var m = /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(string); - if (m) { - return parseFloat(m[1]); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @return {number|undefined} Non negative integer. - */ -ol.format.XSD.readNonNegativeInteger = function(node) { - var s = ol.xml.getAllTextContent(node, false); - return ol.format.XSD.readNonNegativeIntegerString(s); -}; - - -/** - * @param {string} string String. - * @return {number|undefined} Non negative integer. - */ -ol.format.XSD.readNonNegativeIntegerString = function(string) { - var m = /^\s*(\d+)\s*$/.exec(string); - if (m) { - return parseInt(m[1], 10); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @return {string|undefined} String. - */ -ol.format.XSD.readString = function(node) { - return ol.xml.getAllTextContent(node, false).trim(); -}; - - -/** - * @param {Node} node Node to append a TextNode with the boolean to. - * @param {boolean} bool Boolean. - */ -ol.format.XSD.writeBooleanTextNode = function(node, bool) { - ol.format.XSD.writeStringTextNode(node, (bool) ? '1' : '0'); -}; - - -/** - * @param {Node} node Node to append a CDATA Section with the string to. - * @param {string} string String. - */ -ol.format.XSD.writeCDATASection = function(node, string) { - node.appendChild(ol.xml.DOCUMENT.createCDATASection(string)); -}; - - -/** - * @param {Node} node Node to append a TextNode with the dateTime to. - * @param {number} dateTime DateTime in seconds. - */ -ol.format.XSD.writeDateTimeTextNode = function(node, dateTime) { - var date = new Date(dateTime * 1000); - var string = date.getUTCFullYear() + '-' + - ol.string.padNumber(date.getUTCMonth() + 1, 2) + '-' + - ol.string.padNumber(date.getUTCDate(), 2) + 'T' + - ol.string.padNumber(date.getUTCHours(), 2) + ':' + - ol.string.padNumber(date.getUTCMinutes(), 2) + ':' + - ol.string.padNumber(date.getUTCSeconds(), 2) + 'Z'; - node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); -}; - - -/** - * @param {Node} node Node to append a TextNode with the decimal to. - * @param {number} decimal Decimal. - */ -ol.format.XSD.writeDecimalTextNode = function(node, decimal) { - var string = decimal.toPrecision(); - node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); -}; - - -/** - * @param {Node} node Node to append a TextNode with the decimal to. - * @param {number} nonNegativeInteger Non negative integer. - */ -ol.format.XSD.writeNonNegativeIntegerTextNode = function(node, nonNegativeInteger) { - var string = nonNegativeInteger.toString(); - node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); -}; - - -/** - * @param {Node} node Node to append a TextNode with the string to. - * @param {string} string String. - */ -ol.format.XSD.writeStringTextNode = function(node, string) { - node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); -}; - -goog.provide('ol.format.GML3'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.format.Feature'); -goog.require('ol.format.GMLBase'); -goog.require('ol.format.XSD'); -goog.require('ol.geom.Geometry'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Polygon'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Feature format for reading and writing data in the GML format - * version 3.1.1. - * Currently only supports GML 3.1.1 Simple Features profile. - * - * @constructor - * @param {olx.format.GMLOptions=} opt_options - * Optional configuration object. - * @extends {ol.format.GMLBase} - * @api - */ -ol.format.GML3 = function(opt_options) { - var options = /** @type {olx.format.GMLOptions} */ - (opt_options ? opt_options : {}); - - ol.format.GMLBase.call(this, options); - - /** - * @private - * @type {boolean} - */ - this.surface_ = options.surface !== undefined ? options.surface : false; - - /** - * @private - * @type {boolean} - */ - this.curve_ = options.curve !== undefined ? options.curve : false; - - /** - * @private - * @type {boolean} - */ - this.multiCurve_ = options.multiCurve !== undefined ? - options.multiCurve : true; - - /** - * @private - * @type {boolean} - */ - this.multiSurface_ = options.multiSurface !== undefined ? - options.multiSurface : true; - - /** - * @inheritDoc - */ - this.schemaLocation = options.schemaLocation ? - options.schemaLocation : ol.format.GML3.schemaLocation_; - -}; -ol.inherits(ol.format.GML3, ol.format.GMLBase); - - -/** - * @const - * @type {string} - * @private - */ -ol.format.GML3.schemaLocation_ = ol.format.GMLBase.GMLNS + - ' http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/' + - '1.0.0/gmlsf.xsd'; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.MultiLineString|undefined} MultiLineString. - */ -ol.format.GML3.prototype.readMultiCurve_ = function(node, objectStack) { - /** @type {Array.<ol.geom.LineString>} */ - var lineStrings = ol.xml.pushParseAndPop([], - this.MULTICURVE_PARSERS_, node, objectStack, this); - if (lineStrings) { - var multiLineString = new ol.geom.MultiLineString(null); - multiLineString.setLineStrings(lineStrings); - return multiLineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.MultiPolygon|undefined} MultiPolygon. - */ -ol.format.GML3.prototype.readMultiSurface_ = function(node, objectStack) { - /** @type {Array.<ol.geom.Polygon>} */ - var polygons = ol.xml.pushParseAndPop([], - this.MULTISURFACE_PARSERS_, node, objectStack, this); - if (polygons) { - var multiPolygon = new ol.geom.MultiPolygon(null); - multiPolygon.setPolygons(polygons); - return multiPolygon; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GML3.prototype.curveMemberParser_ = function(node, objectStack) { - ol.xml.parseNode(this.CURVEMEMBER_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GML3.prototype.surfaceMemberParser_ = function(node, objectStack) { - ol.xml.parseNode(this.SURFACEMEMBER_PARSERS_, - node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<(Array.<number>)>|undefined} flat coordinates. - */ -ol.format.GML3.prototype.readPatch_ = function(node, objectStack) { - return ol.xml.pushParseAndPop([null], - this.PATCHES_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>|undefined} flat coordinates. - */ -ol.format.GML3.prototype.readSegment_ = function(node, objectStack) { - return ol.xml.pushParseAndPop([null], - this.SEGMENTS_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<(Array.<number>)>|undefined} flat coordinates. - */ -ol.format.GML3.prototype.readPolygonPatch_ = function(node, objectStack) { - return ol.xml.pushParseAndPop([null], - this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>|undefined} flat coordinates. - */ -ol.format.GML3.prototype.readLineStringSegment_ = function(node, objectStack) { - return ol.xml.pushParseAndPop([null], - this.GEOMETRY_FLAT_COORDINATES_PARSERS_, - node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GML3.prototype.interiorParser_ = function(node, objectStack) { - /** @type {Array.<number>|undefined} */ - var flatLinearRing = ol.xml.pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - var flatLinearRings = /** @type {Array.<Array.<number>>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings.push(flatLinearRing); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GML3.prototype.exteriorParser_ = function(node, objectStack) { - /** @type {Array.<number>|undefined} */ - var flatLinearRing = ol.xml.pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - var flatLinearRings = /** @type {Array.<Array.<number>>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings[0] = flatLinearRing; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.Polygon|undefined} Polygon. - */ -ol.format.GML3.prototype.readSurface_ = function(node, objectStack) { - /** @type {Array.<Array.<number>>} */ - var flatLinearRings = ol.xml.pushParseAndPop([null], - this.SURFACE_PARSERS_, node, objectStack, this); - if (flatLinearRings && flatLinearRings[0]) { - var polygon = new ol.geom.Polygon(null); - var flatCoordinates = flatLinearRings[0]; - var ends = [flatCoordinates.length]; - var i, ii; - for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { - ol.array.extend(flatCoordinates, flatLinearRings[i]); - ends.push(flatCoordinates.length); - } - polygon.setFlatCoordinates( - ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); - return polygon; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.LineString|undefined} LineString. - */ -ol.format.GML3.prototype.readCurve_ = function(node, objectStack) { - /** @type {Array.<number>} */ - var flatCoordinates = ol.xml.pushParseAndPop([null], - this.CURVE_PARSERS_, node, objectStack, this); - if (flatCoordinates) { - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); - return lineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.Extent|undefined} Envelope. - */ -ol.format.GML3.prototype.readEnvelope_ = function(node, objectStack) { - /** @type {Array.<number>} */ - var flatCoordinates = ol.xml.pushParseAndPop([null], - this.ENVELOPE_PARSERS_, node, objectStack, this); - return ol.extent.createOrUpdate(flatCoordinates[1][0], - flatCoordinates[1][1], flatCoordinates[2][0], - flatCoordinates[2][1]); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>|undefined} Flat coordinates. - */ -ol.format.GML3.prototype.readFlatPos_ = function(node, objectStack) { - var s = ol.xml.getAllTextContent(node, false); - var re = /^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/; - /** @type {Array.<number>} */ - var flatCoordinates = []; - var m; - while ((m = re.exec(s))) { - flatCoordinates.push(parseFloat(m[1])); - s = s.substr(m[0].length); - } - if (s !== '') { - return undefined; - } - var context = objectStack[0]; - var containerSrs = context['srsName']; - var axisOrientation = 'enu'; - if (containerSrs) { - var proj = ol.proj.get(containerSrs); - axisOrientation = proj.getAxisOrientation(); - } - if (axisOrientation === 'neu') { - var i, ii; - for (i = 0, ii = flatCoordinates.length; i < ii; i += 3) { - var y = flatCoordinates[i]; - var x = flatCoordinates[i + 1]; - flatCoordinates[i] = x; - flatCoordinates[i + 1] = y; - } - } - var len = flatCoordinates.length; - if (len == 2) { - flatCoordinates.push(0); - } - if (len === 0) { - return undefined; - } - return flatCoordinates; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>|undefined} Flat coordinates. - */ -ol.format.GML3.prototype.readFlatPosList_ = function(node, objectStack) { - var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); - var context = objectStack[0]; - var containerSrs = context['srsName']; - var containerDimension = node.parentNode.getAttribute('srsDimension'); - var axisOrientation = 'enu'; - if (containerSrs) { - var proj = ol.proj.get(containerSrs); - axisOrientation = proj.getAxisOrientation(); - } - var coords = s.split(/\s+/); - // The "dimension" attribute is from the GML 3.0.1 spec. - var dim = 2; - if (node.getAttribute('srsDimension')) { - dim = ol.format.XSD.readNonNegativeIntegerString( - node.getAttribute('srsDimension')); - } else if (node.getAttribute('dimension')) { - dim = ol.format.XSD.readNonNegativeIntegerString( - node.getAttribute('dimension')); - } else if (containerDimension) { - dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension); - } - var x, y, z; - var flatCoordinates = []; - for (var i = 0, ii = coords.length; i < ii; i += dim) { - x = parseFloat(coords[i]); - y = parseFloat(coords[i + 1]); - z = (dim === 3) ? parseFloat(coords[i + 2]) : 0; - if (axisOrientation.substr(0, 2) === 'en') { - flatCoordinates.push(x, y, z); - } else { - flatCoordinates.push(y, x, z); - } - } - return flatCoordinates; -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'pos': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPos_), - 'posList': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPosList_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'interior': ol.format.GML3.prototype.interiorParser_, - 'exterior': ol.format.GML3.prototype.exteriorParser_ - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.GEOMETRY_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint), - 'MultiPoint': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readMultiPoint), - 'LineString': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readLineString), - 'MultiLineString': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readMultiLineString), - 'LinearRing': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readLinearRing), - 'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon), - 'MultiPolygon': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readMultiPolygon), - 'Surface': ol.xml.makeReplacer(ol.format.GML3.prototype.readSurface_), - 'MultiSurface': ol.xml.makeReplacer( - ol.format.GML3.prototype.readMultiSurface_), - 'Curve': ol.xml.makeReplacer(ol.format.GML3.prototype.readCurve_), - 'MultiCurve': ol.xml.makeReplacer( - ol.format.GML3.prototype.readMultiCurve_), - 'Envelope': ol.xml.makeReplacer(ol.format.GML3.prototype.readEnvelope_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.MULTICURVE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'curveMember': ol.xml.makeArrayPusher( - ol.format.GML3.prototype.curveMemberParser_), - 'curveMembers': ol.xml.makeArrayPusher( - ol.format.GML3.prototype.curveMemberParser_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.MULTISURFACE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': ol.xml.makeArrayPusher( - ol.format.GML3.prototype.surfaceMemberParser_), - 'surfaceMembers': ol.xml.makeArrayPusher( - ol.format.GML3.prototype.surfaceMemberParser_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.CURVEMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineString': ol.xml.makeArrayPusher( - ol.format.GMLBase.prototype.readLineString), - 'Curve': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readCurve_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.SURFACEMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Polygon': ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readPolygon), - 'Surface': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readSurface_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.SURFACE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'patches': ol.xml.makeReplacer(ol.format.GML3.prototype.readPatch_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.CURVE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'segments': ol.xml.makeReplacer(ol.format.GML3.prototype.readSegment_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.ENVELOPE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': ol.xml.makeArrayPusher( - ol.format.GML3.prototype.readFlatPosList_), - 'upperCorner': ol.xml.makeArrayPusher( - ol.format.GML3.prototype.readFlatPosList_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.PATCHES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'PolygonPatch': ol.xml.makeReplacer( - ol.format.GML3.prototype.readPolygonPatch_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML3.prototype.SEGMENTS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineStringSegment': ol.xml.makeReplacer( - ol.format.GML3.prototype.readLineStringSegment_) - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Point} value Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - var axisOrientation = 'enu'; - if (srsName) { - axisOrientation = ol.proj.get(srsName).getAxisOrientation(); - } - var point = value.getCoordinates(); - var coords; - // only 2d for simple features profile - if (axisOrientation.substr(0, 2) === 'en') { - coords = (point[0] + ' ' + point[1]); - } else { - coords = (point[1] + ' ' + point[0]); - } - if (hasZ) { - // For newly created points, Z can be undefined. - var z = point[2] || 0; - coords += ' ' + z; - } - ol.format.XSD.writeStringTextNode(node, coords); -}; - - -/** - * @param {Array.<number>} point Point geometry. - * @param {string=} opt_srsName Optional srsName - * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. - * @return {string} The coords string. - * @private - */ -ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName, opt_hasZ) { - var axisOrientation = 'enu'; - if (opt_srsName) { - axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); - } - var coords = ((axisOrientation.substr(0, 2) === 'en') ? - point[0] + ' ' + point[1] : - point[1] + ' ' + point[0]); - if (opt_hasZ) { - // For newly created points, Z can be undefined. - var z = point[2] || 0; - coords += ' ' + z; - } - - return coords; -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString|ol.geom.LinearRing} value Geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writePosList_ = function(node, value, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - // only 2d for simple features profile - var points = value.getCoordinates(); - var len = points.length; - var parts = new Array(len); - var point; - for (var i = 0; i < len; ++i) { - point = points[i]; - parts[i] = this.getCoords_(point, srsName, hasZ); - } - ol.format.XSD.writeStringTextNode(node, parts.join(' ')); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Point} geometry Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writePoint_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var pos = ol.xml.createElementNS(node.namespaceURI, 'pos'); - node.appendChild(pos); - this.writePos_(pos, geometry, objectStack); -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML3.ENVELOPE_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'upperCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.Extent} extent Extent. - * @param {Array.<*>} objectStack Node stack. - */ -ol.format.GML3.prototype.writeEnvelope = function(node, extent, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var keys = ['lowerCorner', 'upperCorner']; - var values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - ({node: node}), ol.format.GML3.ENVELOPE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - values, - objectStack, keys, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LinearRing} geometry LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeLinearRing_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var posList = ol.xml.createElementNS(node.namespaceURI, 'posList'); - node.appendChild(posList); - this.writePosList_(posList, geometry, objectStack); -}; - - -/** - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node} Node. - * @private - */ -ol.format.GML3.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var context = objectStack[objectStack.length - 1]; - var parentNode = context.node; - var exteriorWritten = context['exteriorWritten']; - if (exteriorWritten === undefined) { - context['exteriorWritten'] = true; - } - return ol.xml.createElementNS(parentNode.namespaceURI, - exteriorWritten !== undefined ? 'interior' : 'exterior'); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} geometry Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - if (node.nodeName !== 'PolygonPatch' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { - var rings = geometry.getLinearRings(); - ol.xml.pushSerializeAndPop( - {node: node, hasZ: hasZ, srsName: srsName}, - ol.format.GML3.RING_SERIALIZERS_, - this.RING_NODE_FACTORY_, - rings, objectStack, undefined, this); - } else if (node.nodeName === 'Surface') { - var patches = ol.xml.createElementNS(node.namespaceURI, 'patches'); - node.appendChild(patches); - this.writeSurfacePatches_( - patches, geometry, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} geometry LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeCurveOrLineString_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (node.nodeName !== 'LineStringSegment' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'LineString' || - node.nodeName === 'LineStringSegment') { - var posList = ol.xml.createElementNS(node.namespaceURI, 'posList'); - node.appendChild(posList); - this.writePosList_(posList, geometry, objectStack); - } else if (node.nodeName === 'Curve') { - var segments = ol.xml.createElementNS(node.namespaceURI, 'segments'); - node.appendChild(segments); - this.writeCurveSegments_(segments, - geometry, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - var surface = context['surface']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var polygons = geometry.getPolygons(); - ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, - ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.MultiPoint} geometry MultiPoint geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeMultiPoint_ = function(node, geometry, - objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - var hasZ = context['hasZ']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var points = geometry.getPoints(); - ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, - ol.format.GML3.POINTMEMBER_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('pointMember'), points, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.MultiLineString} geometry MultiLineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - var curve = context['curve']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var lines = geometry.getLineStrings(); - ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, - ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LinearRing} ring LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeRing_ = function(node, ring, objectStack) { - var linearRing = ol.xml.createElementNS(node.namespaceURI, 'LinearRing'); - node.appendChild(linearRing); - this.writeLinearRing_(linearRing, ring, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeSurfaceOrPolygonMember_ = function(node, polygon, objectStack) { - var child = this.GEOMETRY_NODE_FACTORY_( - polygon, objectStack); - if (child) { - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Point} point Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writePointMember_ = function(node, point, objectStack) { - var child = ol.xml.createElementNS(node.namespaceURI, 'Point'); - node.appendChild(child); - this.writePoint_(child, point, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeLineStringOrCurveMember_ = function(node, line, objectStack) { - var child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); - if (child) { - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeSurfacePatches_ = function(node, polygon, objectStack) { - var child = ol.xml.createElementNS(node.namespaceURI, 'PolygonPatch'); - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeCurveSegments_ = function(node, line, objectStack) { - var child = ol.xml.createElementNS(node.namespaceURI, - 'LineStringSegment'); - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Geometry|ol.Extent} geometry Geometry. - * @param {Array.<*>} objectStack Node stack. - */ -ol.format.GML3.prototype.writeGeometryElement = function(node, geometry, objectStack) { - var context = /** @type {olx.format.WriteOptions} */ (objectStack[objectStack.length - 1]); - var item = ol.obj.assign({}, context); - item.node = node; - var value; - if (Array.isArray(geometry)) { - if (context.dataProjection) { - value = ol.proj.transformExtent( - geometry, context.featureProjection, context.dataProjection); - } else { - value = geometry; - } - } else { - value = - ol.format.Feature.transformWithOptions(/** @type {ol.geom.Geometry} */ (geometry), true, context); - } - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - (item), ol.format.GML3.GEOMETRY_SERIALIZERS_, - this.GEOMETRY_NODE_FACTORY_, [value], - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - */ -ol.format.GML3.prototype.writeFeatureElement = function(node, feature, objectStack) { - var fid = feature.getId(); - if (fid) { - node.setAttribute('fid', fid); - } - var context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var featureNS = context['featureNS']; - var geometryName = feature.getGeometryName(); - if (!context.serializers) { - context.serializers = {}; - context.serializers[featureNS] = {}; - } - var properties = feature.getProperties(); - var keys = [], values = []; - for (var key in properties) { - var value = properties[key]; - if (value !== null) { - keys.push(key); - values.push(value); - if (key == geometryName || value instanceof ol.geom.Geometry) { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = ol.xml.makeChildAppender( - this.writeGeometryElement, this); - } - } else { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = ol.xml.makeChildAppender( - ol.format.XSD.writeStringTextNode); - } - } - } - } - var item = ol.obj.assign({}, context); - item.node = node; - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - (item), context.serializers, - ol.xml.makeSimpleNodeFactory(undefined, featureNS), - values, - objectStack, keys); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<ol.Feature>} features Features. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML3.prototype.writeFeatureMembers_ = function(node, features, objectStack) { - var context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var featureType = context['featureType']; - var featureNS = context['featureNS']; - var serializers = {}; - serializers[featureNS] = {}; - serializers[featureNS][featureType] = ol.xml.makeChildAppender( - this.writeFeatureElement, this); - var item = ol.obj.assign({}, context); - item.node = node; - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - (item), - serializers, - ol.xml.makeSimpleNodeFactory(featureType, featureNS), features, - objectStack); -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeSurfaceOrPolygonMember_), - 'polygonMember': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeSurfaceOrPolygonMember_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML3.POINTMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writePointMember_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeLineStringOrCurveMember_), - 'curveMember': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeLineStringOrCurveMember_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML3.RING_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'exterior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_), - 'interior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML3.GEOMETRY_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'Curve': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeCurveOrLineString_), - 'MultiCurve': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeMultiCurveOrLineString_), - 'Point': ol.xml.makeChildAppender(ol.format.GML3.prototype.writePoint_), - 'MultiPoint': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeMultiPoint_), - 'LineString': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeCurveOrLineString_), - 'MultiLineString': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeMultiCurveOrLineString_), - 'LinearRing': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeLinearRing_), - 'Polygon': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeSurfaceOrPolygon_), - 'MultiPolygon': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_), - 'Surface': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeSurfaceOrPolygon_), - 'MultiSurface': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_), - 'Envelope': ol.xml.makeChildAppender( - ol.format.GML3.prototype.writeEnvelope) - } -}; - - -/** - * @const - * @type {Object.<string, string>} - * @private - */ -ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_ = { - 'MultiLineString': 'lineStringMember', - 'MultiCurve': 'curveMember', - 'MultiPolygon': 'polygonMember', - 'MultiSurface': 'surfaceMember' -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.GML3.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var parentNode = objectStack[objectStack.length - 1].node; - return ol.xml.createElementNS('http://www.opengis.net/gml', - ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_[parentNode.nodeName]); -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.GML3.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var context = objectStack[objectStack.length - 1]; - var multiSurface = context['multiSurface']; - var surface = context['surface']; - var curve = context['curve']; - var multiCurve = context['multiCurve']; - var nodeName; - if (!Array.isArray(value)) { - nodeName = /** @type {ol.geom.Geometry} */ (value).getType(); - if (nodeName === 'MultiPolygon' && multiSurface === true) { - nodeName = 'MultiSurface'; - } else if (nodeName === 'Polygon' && surface === true) { - nodeName = 'Surface'; - } else if (nodeName === 'LineString' && curve === true) { - nodeName = 'Curve'; - } else if (nodeName === 'MultiLineString' && multiCurve === true) { - nodeName = 'MultiCurve'; - } - } else { - nodeName = 'Envelope'; - } - return ol.xml.createElementNS('http://www.opengis.net/gml', - nodeName); -}; - - -/** - * Encode a geometry in GML 3.1.1 Simple Features. - * - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -ol.format.GML3.prototype.writeGeometryNode = function(geometry, opt_options) { - opt_options = this.adaptOptions(opt_options); - var geom = ol.xml.createElementNS('http://www.opengis.net/gml', 'geom'); - var context = {node: geom, hasZ: this.hasZ, srsName: this.srsName, - curve: this.curve_, surface: this.surface_, - multiSurface: this.multiSurface_, multiCurve: this.multiCurve_}; - if (opt_options) { - ol.obj.assign(context, opt_options); - } - this.writeGeometryElement(geom, geometry, [context]); - return geom; -}; - - -/** - * Encode an array of features in GML 3.1.1 Simple Features. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {string} Result. - * @api - */ -ol.format.GML3.prototype.writeFeatures; - - -/** - * Encode an array of features in the GML 3.1.1 format as an XML node. - * - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -ol.format.GML3.prototype.writeFeaturesNode = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - var node = ol.xml.createElementNS('http://www.opengis.net/gml', - 'featureMembers'); - ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation', this.schemaLocation); - var context = { - srsName: this.srsName, - hasZ: this.hasZ, - curve: this.curve_, - surface: this.surface_, - multiSurface: this.multiSurface_, - multiCurve: this.multiCurve_, - featureNS: this.featureNS, - featureType: this.featureType - }; - if (opt_options) { - ol.obj.assign(context, opt_options); - } - this.writeFeatureMembers_(node, features, [context]); - return node; -}; - -goog.provide('ol.format.GML'); - -goog.require('ol.format.GML3'); - - -/** - * @classdesc - * Feature format for reading and writing data in the GML format - * version 3.1.1. - * Currently only supports GML 3.1.1 Simple Features profile. - * - * @constructor - * @param {olx.format.GMLOptions=} opt_options - * Optional configuration object. - * @extends {ol.format.GMLBase} - * @api - */ -ol.format.GML = ol.format.GML3; - - -/** - * Encode an array of features in GML 3.1.1 Simple Features. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {string} Result. - * @api - */ -ol.format.GML.prototype.writeFeatures; - - -/** - * Encode an array of features in the GML 3.1.1 format as an XML node. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - * @api - */ -ol.format.GML.prototype.writeFeaturesNode; - -goog.provide('ol.format.GML2'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.format.Feature'); -goog.require('ol.format.GMLBase'); -goog.require('ol.format.XSD'); -goog.require('ol.geom.Geometry'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Feature format for reading and writing data in the GML format, - * version 2.1.2. - * - * @constructor - * @param {olx.format.GMLOptions=} opt_options Optional configuration object. - * @extends {ol.format.GMLBase} - * @api - */ -ol.format.GML2 = function(opt_options) { - var options = /** @type {olx.format.GMLOptions} */ - (opt_options ? opt_options : {}); - - ol.format.GMLBase.call(this, options); - - this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][ - 'featureMember'] = - ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal); - - /** - * @inheritDoc - */ - this.schemaLocation = options.schemaLocation ? - options.schemaLocation : ol.format.GML2.schemaLocation_; - -}; -ol.inherits(ol.format.GML2, ol.format.GMLBase); - - -/** - * @const - * @type {string} - * @private - */ -ol.format.GML2.schemaLocation_ = ol.format.GMLBase.GMLNS + - ' http://schemas.opengis.net/gml/2.1.2/feature.xsd'; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>|undefined} Flat coordinates. - */ -ol.format.GML2.prototype.readFlatCoordinates_ = function(node, objectStack) { - var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); - var context = /** @type {ol.XmlNodeStackItem} */ (objectStack[0]); - var containerSrs = context['srsName']; - var axisOrientation = 'enu'; - if (containerSrs) { - var proj = ol.proj.get(containerSrs); - if (proj) { - axisOrientation = proj.getAxisOrientation(); - } - } - var coordsGroups = s.trim().split(/\s+/); - var x, y, z; - var flatCoordinates = []; - for (var i = 0, ii = coordsGroups.length; i < ii; i++) { - var coords = coordsGroups[i].split(/,+/); - x = parseFloat(coords[0]); - y = parseFloat(coords[1]); - z = (coords.length === 3) ? parseFloat(coords[2]) : 0; - if (axisOrientation.substr(0, 2) === 'en') { - flatCoordinates.push(x, y, z); - } else { - flatCoordinates.push(y, x, z); - } - } - return flatCoordinates; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.Extent|undefined} Envelope. - */ -ol.format.GML2.prototype.readBox_ = function(node, objectStack) { - /** @type {Array.<number>} */ - var flatCoordinates = ol.xml.pushParseAndPop([null], - this.BOX_PARSERS_, node, objectStack, this); - return ol.extent.createOrUpdate(flatCoordinates[1][0], - flatCoordinates[1][1], flatCoordinates[1][3], - flatCoordinates[1][4]); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GML2.prototype.innerBoundaryIsParser_ = function(node, objectStack) { - /** @type {Array.<number>|undefined} */ - var flatLinearRing = ol.xml.pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - var flatLinearRings = /** @type {Array.<Array.<number>>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings.push(flatLinearRing); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GML2.prototype.outerBoundaryIsParser_ = function(node, objectStack) { - /** @type {Array.<number>|undefined} */ - var flatLinearRing = ol.xml.pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - var flatLinearRings = /** @type {Array.<Array.<number>>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings[0] = flatLinearRing; - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'coordinates': ol.xml.makeReplacer( - ol.format.GML2.prototype.readFlatCoordinates_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'innerBoundaryIs': ol.format.GML2.prototype.innerBoundaryIsParser_, - 'outerBoundaryIs': ol.format.GML2.prototype.outerBoundaryIsParser_ - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML2.prototype.BOX_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'coordinates': ol.xml.makeArrayPusher( - ol.format.GML2.prototype.readFlatCoordinates_) - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GML2.prototype.GEOMETRY_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint), - 'MultiPoint': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readMultiPoint), - 'LineString': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readLineString), - 'MultiLineString': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readMultiLineString), - 'LinearRing': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readLinearRing), - 'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon), - 'MultiPolygon': ol.xml.makeReplacer( - ol.format.GMLBase.prototype.readMultiPolygon), - 'Box': ol.xml.makeReplacer(ol.format.GML2.prototype.readBox_) - } -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.GML2.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var context = objectStack[objectStack.length - 1]; - var multiSurface = context['multiSurface']; - var surface = context['surface']; - var multiCurve = context['multiCurve']; - var nodeName; - if (!Array.isArray(value)) { - nodeName = /** @type {ol.geom.Geometry} */ (value).getType(); - if (nodeName === 'MultiPolygon' && multiSurface === true) { - nodeName = 'MultiSurface'; - } else if (nodeName === 'Polygon' && surface === true) { - nodeName = 'Surface'; - } else if (nodeName === 'MultiLineString' && multiCurve === true) { - nodeName = 'MultiCurve'; - } - } else { - nodeName = 'Envelope'; - } - return ol.xml.createElementNS('http://www.opengis.net/gml', - nodeName); -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - */ -ol.format.GML2.prototype.writeFeatureElement = function(node, feature, objectStack) { - var fid = feature.getId(); - if (fid) { - node.setAttribute('fid', fid); - } - var context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var featureNS = context['featureNS']; - var geometryName = feature.getGeometryName(); - if (!context.serializers) { - context.serializers = {}; - context.serializers[featureNS] = {}; - } - var properties = feature.getProperties(); - var keys = [], values = []; - for (var key in properties) { - var value = properties[key]; - if (value !== null) { - keys.push(key); - values.push(value); - if (key == geometryName || value instanceof ol.geom.Geometry) { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = ol.xml.makeChildAppender( - this.writeGeometryElement, this); - } - } else { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = ol.xml.makeChildAppender( - ol.format.XSD.writeStringTextNode); - } - } - } - } - var item = ol.obj.assign({}, context); - item.node = node; - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - (item), context.serializers, - ol.xml.makeSimpleNodeFactory(undefined, featureNS), - values, - objectStack, keys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Geometry|ol.Extent} geometry Geometry. - * @param {Array.<*>} objectStack Node stack. - */ -ol.format.GML2.prototype.writeGeometryElement = function(node, geometry, objectStack) { - var context = /** @type {olx.format.WriteOptions} */ (objectStack[objectStack.length - 1]); - var item = ol.obj.assign({}, context); - item.node = node; - var value; - if (Array.isArray(geometry)) { - if (context.dataProjection) { - value = ol.proj.transformExtent( - geometry, context.featureProjection, context.dataProjection); - } else { - value = geometry; - } - } else { - value = - ol.format.Feature.transformWithOptions(/** @type {ol.geom.Geometry} */ (geometry), true, context); - } - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - (item), ol.format.GML2.GEOMETRY_SERIALIZERS_, - this.GEOMETRY_NODE_FACTORY_, [value], - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} geometry LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeCurveOrLineString_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (node.nodeName !== 'LineStringSegment' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'LineString' || - node.nodeName === 'LineStringSegment') { - var coordinates = this.createCoordinatesNode_(node.namespaceURI); - node.appendChild(coordinates); - this.writeCoordinates_(coordinates, geometry, objectStack); - } else if (node.nodeName === 'Curve') { - var segments = ol.xml.createElementNS(node.namespaceURI, 'segments'); - node.appendChild(segments); - this.writeCurveSegments_(segments, - geometry, objectStack); - } -}; - - -/** - * @param {string} namespaceURI XML namespace. - * @returns {Node} coordinates node. - * @private - */ -ol.format.GML2.prototype.createCoordinatesNode_ = function(namespaceURI) { - var coordinates = ol.xml.createElementNS(namespaceURI, 'coordinates'); - coordinates.setAttribute('decimal', '.'); - coordinates.setAttribute('cs', ','); - coordinates.setAttribute('ts', ' '); - - return coordinates; -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString|ol.geom.LinearRing} value Geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeCoordinates_ = function(node, value, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - // only 2d for simple features profile - var points = value.getCoordinates(); - var len = points.length; - var parts = new Array(len); - var point; - for (var i = 0; i < len; ++i) { - point = points[i]; - parts[i] = this.getCoords_(point, srsName, hasZ); - } - ol.format.XSD.writeStringTextNode(node, parts.join(' ')); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeCurveSegments_ = function(node, line, objectStack) { - var child = ol.xml.createElementNS(node.namespaceURI, - 'LineStringSegment'); - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} geometry Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - if (node.nodeName !== 'PolygonPatch' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { - var rings = geometry.getLinearRings(); - ol.xml.pushSerializeAndPop( - {node: node, hasZ: hasZ, srsName: srsName}, - ol.format.GML2.RING_SERIALIZERS_, - this.RING_NODE_FACTORY_, - rings, objectStack, undefined, this); - } else if (node.nodeName === 'Surface') { - var patches = ol.xml.createElementNS(node.namespaceURI, 'patches'); - node.appendChild(patches); - this.writeSurfacePatches_( - patches, geometry, objectStack); - } -}; - - -/** - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node} Node. - * @private - */ -ol.format.GML2.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var context = objectStack[objectStack.length - 1]; - var parentNode = context.node; - var exteriorWritten = context['exteriorWritten']; - if (exteriorWritten === undefined) { - context['exteriorWritten'] = true; - } - return ol.xml.createElementNS(parentNode.namespaceURI, - exteriorWritten !== undefined ? 'innerBoundaryIs' : 'outerBoundaryIs'); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeSurfacePatches_ = function(node, polygon, objectStack) { - var child = ol.xml.createElementNS(node.namespaceURI, 'PolygonPatch'); - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LinearRing} ring LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeRing_ = function(node, ring, objectStack) { - var linearRing = ol.xml.createElementNS(node.namespaceURI, 'LinearRing'); - node.appendChild(linearRing); - this.writeLinearRing_(linearRing, ring, objectStack); -}; - - -/** - * @param {Array.<number>} point Point geometry. - * @param {string=} opt_srsName Optional srsName - * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. - * @return {string} The coords string. - * @private - */ -ol.format.GML2.prototype.getCoords_ = function(point, opt_srsName, opt_hasZ) { - var axisOrientation = 'enu'; - if (opt_srsName) { - axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); - } - var coords = ((axisOrientation.substr(0, 2) === 'en') ? - point[0] + ',' + point[1] : - point[1] + ',' + point[0]); - if (opt_hasZ) { - // For newly created points, Z can be undefined. - var z = point[2] || 0; - coords += ',' + z; - } - - return coords; -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.MultiLineString} geometry MultiLineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - var curve = context['curve']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var lines = geometry.getLineStrings(); - ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, - ol.format.GML2.LINESTRINGORCURVEMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Point} geometry Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writePoint_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var coordinates = this.createCoordinatesNode_(node.namespaceURI); - node.appendChild(coordinates); - var point = geometry.getCoordinates(); - var coord = this.getCoords_(point, srsName, hasZ); - ol.format.XSD.writeStringTextNode(coordinates, coord); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.MultiPoint} geometry MultiPoint geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeMultiPoint_ = function(node, geometry, - objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var points = geometry.getPoints(); - ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, - ol.format.GML2.POINTMEMBER_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('pointMember'), points, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Point} point Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writePointMember_ = function(node, point, objectStack) { - var child = ol.xml.createElementNS(node.namespaceURI, 'Point'); - node.appendChild(child); - this.writePoint_(child, point, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeLineStringOrCurveMember_ = function(node, line, objectStack) { - var child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); - if (child) { - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LinearRing} geometry LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeLinearRing_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var coordinates = this.createCoordinatesNode_(node.namespaceURI); - node.appendChild(coordinates); - this.writeCoordinates_(coordinates, geometry, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { - var context = objectStack[objectStack.length - 1]; - var hasZ = context['hasZ']; - var srsName = context['srsName']; - var surface = context['surface']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var polygons = geometry.getPolygons(); - ol.xml.pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, - ol.format.GML2.SURFACEORPOLYGONMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeSurfaceOrPolygonMember_ = function(node, polygon, objectStack) { - var child = this.GEOMETRY_NODE_FACTORY_( - polygon, objectStack); - if (child) { - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.Extent} extent Extent. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GML2.prototype.writeEnvelope = function(node, extent, objectStack) { - var context = objectStack[objectStack.length - 1]; - var srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - var keys = ['lowerCorner', 'upperCorner']; - var values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - ({node: node}), ol.format.GML2.ENVELOPE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - values, - objectStack, keys, this); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML2.GEOMETRY_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'Curve': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeCurveOrLineString_), - 'MultiCurve': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeMultiCurveOrLineString_), - 'Point': ol.xml.makeChildAppender(ol.format.GML2.prototype.writePoint_), - 'MultiPoint': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeMultiPoint_), - 'LineString': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeCurveOrLineString_), - 'MultiLineString': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeMultiCurveOrLineString_), - 'LinearRing': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeLinearRing_), - 'Polygon': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeSurfaceOrPolygon_), - 'MultiPolygon': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_), - 'Surface': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeSurfaceOrPolygon_), - 'MultiSurface': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_), - 'Envelope': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeEnvelope) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML2.RING_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'outerBoundaryIs': ol.xml.makeChildAppender(ol.format.GML2.prototype.writeRing_), - 'innerBoundaryIs': ol.xml.makeChildAppender(ol.format.GML2.prototype.writeRing_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML2.POINTMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writePointMember_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML2.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeLineStringOrCurveMember_), - 'curveMember': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeLineStringOrCurveMember_) - } -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.GML2.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var parentNode = objectStack[objectStack.length - 1].node; - return ol.xml.createElementNS('http://www.opengis.net/gml', - ol.format.GML2.MULTIGEOMETRY_TO_MEMBER_NODENAME_[parentNode.nodeName]); -}; - -/** - * @const - * @type {Object.<string, string>} - * @private - */ -ol.format.GML2.MULTIGEOMETRY_TO_MEMBER_NODENAME_ = { - 'MultiLineString': 'lineStringMember', - 'MultiCurve': 'curveMember', - 'MultiPolygon': 'polygonMember', - 'MultiSurface': 'surfaceMember' -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML2.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeSurfaceOrPolygonMember_), - 'polygonMember': ol.xml.makeChildAppender( - ol.format.GML2.prototype.writeSurfaceOrPolygonMember_) - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GML2.ENVELOPE_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'upperCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) - } -}; - -goog.provide('ol.format.GPX'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.array'); -goog.require('ol.format.Feature'); -goog.require('ol.format.XMLFeature'); -goog.require('ol.format.XSD'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.Point'); -goog.require('ol.proj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Feature format for reading and writing data in the GPX format. - * - * @constructor - * @extends {ol.format.XMLFeature} - * @param {olx.format.GPXOptions=} opt_options Options. - * @api - */ -ol.format.GPX = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.XMLFeature.call(this); - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get('EPSG:4326'); - - /** - * @type {function(ol.Feature, Node)|undefined} - * @private - */ - this.readExtensions_ = options.readExtensions; -}; -ol.inherits(ol.format.GPX, ol.format.XMLFeature); - - -/** - * @const - * @private - * @type {Array.<string>} - */ -ol.format.GPX.NAMESPACE_URIS_ = [ - null, - 'http://www.topografix.com/GPX/1/0', - 'http://www.topografix.com/GPX/1/1' -]; - - -/** - * @const - * @type {string} - * @private - */ -ol.format.GPX.SCHEMA_LOCATION_ = 'http://www.topografix.com/GPX/1/1 ' + - 'http://www.topografix.com/GPX/1/1/gpx.xsd'; - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {ol.LayoutOptions} layoutOptions Layout options. - * @param {Node} node Node. - * @param {Object} values Values. - * @private - * @return {Array.<number>} Flat coordinates. - */ -ol.format.GPX.appendCoordinate_ = function(flatCoordinates, layoutOptions, node, values) { - flatCoordinates.push( - parseFloat(node.getAttribute('lon')), - parseFloat(node.getAttribute('lat'))); - if ('ele' in values) { - flatCoordinates.push(/** @type {number} */ (values['ele'])); - delete values['ele']; - layoutOptions.hasZ = true; - } else { - flatCoordinates.push(0); - } - if ('time' in values) { - flatCoordinates.push(/** @type {number} */ (values['time'])); - delete values['time']; - layoutOptions.hasM = true; - } else { - flatCoordinates.push(0); - } - return flatCoordinates; -}; - - -/** - * Choose GeometryLayout based on flags in layoutOptions and adjust flatCoordinates - * and ends arrays by shrinking them accordingly (removing unused zero entries). - * - * @param {ol.LayoutOptions} layoutOptions Layout options. - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {Array.<number>=} ends Ends. - * @return {ol.geom.GeometryLayout} Layout. - */ -ol.format.GPX.applyLayoutOptions_ = function(layoutOptions, flatCoordinates, ends) { - var layout = ol.geom.GeometryLayout.XY; - var stride = 2; - if (layoutOptions.hasZ && layoutOptions.hasM) { - layout = ol.geom.GeometryLayout.XYZM; - stride = 4; - } else if (layoutOptions.hasZ) { - layout = ol.geom.GeometryLayout.XYZ; - stride = 3; - } else if (layoutOptions.hasM) { - layout = ol.geom.GeometryLayout.XYM; - stride = 3; - } - if (stride !== 4) { - var i, ii; - for (i = 0, ii = flatCoordinates.length / 4; i < ii; i++) { - flatCoordinates[i * stride] = flatCoordinates[i * 4]; - flatCoordinates[i * stride + 1] = flatCoordinates[i * 4 + 1]; - if (layoutOptions.hasZ) { - flatCoordinates[i * stride + 2] = flatCoordinates[i * 4 + 2]; - } - if (layoutOptions.hasM) { - flatCoordinates[i * stride + 2] = flatCoordinates[i * 4 + 3]; - } - } - flatCoordinates.length = flatCoordinates.length / 4 * stride; - if (ends) { - for (i = 0, ii = ends.length; i < ii; i++) { - ends[i] = ends[i] / 4 * stride; - } - } - } - return layout; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.parseLink_ = function(node, objectStack) { - var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var href = node.getAttribute('href'); - if (href !== null) { - values['link'] = href; - } - ol.xml.parseNode(ol.format.GPX.LINK_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.parseExtensions_ = function(node, objectStack) { - var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); - values['extensionsNode_'] = node; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.parseRtePt_ = function(node, objectStack) { - var values = ol.xml.pushParseAndPop( - {}, ol.format.GPX.RTEPT_PARSERS_, node, objectStack); - if (values) { - var rteValues = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var flatCoordinates = /** @type {Array.<number>} */ - (rteValues['flatCoordinates']); - var layoutOptions = /** @type {ol.LayoutOptions} */ - (rteValues['layoutOptions']); - ol.format.GPX.appendCoordinate_(flatCoordinates, layoutOptions, node, values); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.parseTrkPt_ = function(node, objectStack) { - var values = ol.xml.pushParseAndPop( - {}, ol.format.GPX.TRKPT_PARSERS_, node, objectStack); - if (values) { - var trkValues = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var flatCoordinates = /** @type {Array.<number>} */ - (trkValues['flatCoordinates']); - var layoutOptions = /** @type {ol.LayoutOptions} */ - (trkValues['layoutOptions']); - ol.format.GPX.appendCoordinate_(flatCoordinates, layoutOptions, node, values); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.parseTrkSeg_ = function(node, objectStack) { - var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); - ol.xml.parseNode(ol.format.GPX.TRKSEG_PARSERS_, node, objectStack); - var flatCoordinates = /** @type {Array.<number>} */ - (values['flatCoordinates']); - var ends = /** @type {Array.<number>} */ (values['ends']); - ends.push(flatCoordinates.length); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.Feature|undefined} Track. - */ -ol.format.GPX.readRte_ = function(node, objectStack) { - var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); - var values = ol.xml.pushParseAndPop({ - 'flatCoordinates': [], - 'layoutOptions': {} - }, ol.format.GPX.RTE_PARSERS_, node, objectStack); - if (!values) { - return undefined; - } - var flatCoordinates = /** @type {Array.<number>} */ - (values['flatCoordinates']); - delete values['flatCoordinates']; - var layoutOptions = /** @type {ol.LayoutOptions} */ (values['layoutOptions']); - delete values['layoutOptions']; - var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, flatCoordinates); - var geometry = new ol.geom.LineString(null); - geometry.setFlatCoordinates(layout, flatCoordinates); - ol.format.Feature.transformWithOptions(geometry, false, options); - var feature = new ol.Feature(geometry); - feature.setProperties(values); - return feature; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.Feature|undefined} Track. - */ -ol.format.GPX.readTrk_ = function(node, objectStack) { - var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); - var values = ol.xml.pushParseAndPop({ - 'flatCoordinates': [], - 'ends': [], - 'layoutOptions': {} - }, ol.format.GPX.TRK_PARSERS_, node, objectStack); - if (!values) { - return undefined; - } - var flatCoordinates = /** @type {Array.<number>} */ - (values['flatCoordinates']); - delete values['flatCoordinates']; - var ends = /** @type {Array.<number>} */ (values['ends']); - delete values['ends']; - var layoutOptions = /** @type {ol.LayoutOptions} */ (values['layoutOptions']); - delete values['layoutOptions']; - var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, flatCoordinates, ends); - var geometry = new ol.geom.MultiLineString(null); - geometry.setFlatCoordinates(layout, flatCoordinates, ends); - ol.format.Feature.transformWithOptions(geometry, false, options); - var feature = new ol.Feature(geometry); - feature.setProperties(values); - return feature; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.Feature|undefined} Waypoint. - */ -ol.format.GPX.readWpt_ = function(node, objectStack) { - var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); - var values = ol.xml.pushParseAndPop( - {}, ol.format.GPX.WPT_PARSERS_, node, objectStack); - if (!values) { - return undefined; - } - var layoutOptions = /** @type {ol.LayoutOptions} */ ({}); - var coordinates = ol.format.GPX.appendCoordinate_([], layoutOptions, node, values); - var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, coordinates); - var geometry = new ol.geom.Point(coordinates, layout); - ol.format.Feature.transformWithOptions(geometry, false, options); - var feature = new ol.Feature(geometry); - feature.setProperties(values); - return feature; -}; - - -/** - * @const - * @type {Object.<string, function(Node, Array.<*>): (ol.Feature|undefined)>} - * @private - */ -ol.format.GPX.FEATURE_READER_ = { - 'rte': ol.format.GPX.readRte_, - 'trk': ol.format.GPX.readTrk_, - 'wpt': ol.format.GPX.readWpt_ -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.GPX_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'rte': ol.xml.makeArrayPusher(ol.format.GPX.readRte_), - 'trk': ol.xml.makeArrayPusher(ol.format.GPX.readTrk_), - 'wpt': ol.xml.makeArrayPusher(ol.format.GPX.readWpt_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.LINK_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'text': - ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkText'), - 'type': - ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkType') - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.RTE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'link': ol.format.GPX.parseLink_, - 'number': - ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger), - 'extensions': ol.format.GPX.parseExtensions_, - 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'rtept': ol.format.GPX.parseRtePt_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.RTEPT_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.TRK_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'link': ol.format.GPX.parseLink_, - 'number': - ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger), - 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'extensions': ol.format.GPX.parseExtensions_, - 'trkseg': ol.format.GPX.parseTrkSeg_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.TRKSEG_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'trkpt': ol.format.GPX.parseTrkPt_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.TRKPT_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.GPX.WPT_PARSERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime), - 'magvar': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'geoidheight': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'link': ol.format.GPX.parseLink_, - 'sym': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'fix': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'sat': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'hdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'vdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'pdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'ageofdgpsdata': - ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'dgpsid': - ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger), - 'extensions': ol.format.GPX.parseExtensions_ - }); - - -/** - * @param {Array.<ol.Feature>} features List of features. - * @private - */ -ol.format.GPX.prototype.handleReadExtensions_ = function(features) { - if (!features) { - features = []; - } - for (var i = 0, ii = features.length; i < ii; ++i) { - var feature = features[i]; - if (this.readExtensions_) { - var extensionsNode = feature.get('extensionsNode_') || null; - this.readExtensions_(feature, extensionsNode); - } - feature.set('extensionsNode_', undefined); - } -}; - - -/** - * Read the first feature from a GPX source. - * Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`) - * into MultiLineString. Any properties on route and track waypoints are ignored. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.GPX.prototype.readFeature; - - -/** - * @inheritDoc - */ -ol.format.GPX.prototype.readFeatureFromNode = function(node, opt_options) { - if (!ol.array.includes(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) { - return null; - } - var featureReader = ol.format.GPX.FEATURE_READER_[node.localName]; - if (!featureReader) { - return null; - } - var feature = featureReader(node, [this.getReadOptions(node, opt_options)]); - if (!feature) { - return null; - } - this.handleReadExtensions_([feature]); - return feature; -}; - - -/** - * Read all features from a GPX source. - * Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`) - * into MultiLineString. Any properties on route and track waypoints are ignored. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.GPX.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.GPX.prototype.readFeaturesFromNode = function(node, opt_options) { - if (!ol.array.includes(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) { - return []; - } - if (node.localName == 'gpx') { - /** @type {Array.<ol.Feature>} */ - var features = ol.xml.pushParseAndPop([], ol.format.GPX.GPX_PARSERS_, - node, [this.getReadOptions(node, opt_options)]); - if (features) { - this.handleReadExtensions_(features); - return features; - } else { - return []; - } - } - return []; -}; - - -/** - * Read the projection from a GPX source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.GPX.prototype.readProjection; - - -/** - * @param {Node} node Node. - * @param {string} value Value for the link's `href` attribute. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.GPX.writeLink_ = function(node, value, objectStack) { - node.setAttribute('href', value); - var context = objectStack[objectStack.length - 1]; - var properties = context['properties']; - var link = [ - properties['linkText'], - properties['linkType'] - ]; - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ({node: node}), - ol.format.GPX.LINK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - link, objectStack, ol.format.GPX.LINK_SEQUENCE_); -}; - - -/** - * @param {Node} node Node. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.writeWptType_ = function(node, coordinate, objectStack) { - var context = objectStack[objectStack.length - 1]; - var parentNode = context.node; - var namespaceURI = parentNode.namespaceURI; - var properties = context['properties']; - //FIXME Projection handling - ol.xml.setAttributeNS(node, null, 'lat', coordinate[1]); - ol.xml.setAttributeNS(node, null, 'lon', coordinate[0]); - var geometryLayout = context['geometryLayout']; - switch (geometryLayout) { - case ol.geom.GeometryLayout.XYZM: - if (coordinate[3] !== 0) { - properties['time'] = coordinate[3]; - } - // fall through - case ol.geom.GeometryLayout.XYZ: - if (coordinate[2] !== 0) { - properties['ele'] = coordinate[2]; - } - break; - case ol.geom.GeometryLayout.XYM: - if (coordinate[2] !== 0) { - properties['time'] = coordinate[2]; - } - break; - default: - // pass - } - var orderedKeys = (node.nodeName == 'rtept') ? - ol.format.GPX.RTEPT_TYPE_SEQUENCE_[namespaceURI] : - ol.format.GPX.WPT_TYPE_SEQUENCE_[namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - ({node: node, 'properties': properties}), - ol.format.GPX.WPT_TYPE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.writeRte_ = function(node, feature, objectStack) { - var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); - var properties = feature.getProperties(); - var context = {node: node, 'properties': properties}; - var geometry = feature.getGeometry(); - if (geometry) { - geometry = /** @type {ol.geom.LineString} */ - (ol.format.Feature.transformWithOptions(geometry, true, options)); - context['geometryLayout'] = geometry.getLayout(); - properties['rtept'] = geometry.getCoordinates(); - } - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.GPX.RTE_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, - ol.format.GPX.RTE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.writeTrk_ = function(node, feature, objectStack) { - var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); - var properties = feature.getProperties(); - /** @type {ol.XmlNodeStackItem} */ - var context = {node: node, 'properties': properties}; - var geometry = feature.getGeometry(); - if (geometry) { - geometry = /** @type {ol.geom.MultiLineString} */ - (ol.format.Feature.transformWithOptions(geometry, true, options)); - properties['trkseg'] = geometry.getLineStrings(); - } - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.GPX.TRK_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, - ol.format.GPX.TRK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LineString} lineString LineString. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.writeTrkSeg_ = function(node, lineString, objectStack) { - /** @type {ol.XmlNodeStackItem} */ - var context = {node: node, 'geometryLayout': lineString.getLayout(), - 'properties': {}}; - ol.xml.pushSerializeAndPop(context, - ol.format.GPX.TRKSEG_SERIALIZERS_, ol.format.GPX.TRKSEG_NODE_FACTORY_, - lineString.getCoordinates(), objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.GPX.writeWpt_ = function(node, feature, objectStack) { - var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); - var context = objectStack[objectStack.length - 1]; - context['properties'] = feature.getProperties(); - var geometry = feature.getGeometry(); - if (geometry) { - geometry = /** @type {ol.geom.Point} */ - (ol.format.Feature.transformWithOptions(geometry, true, options)); - context['geometryLayout'] = geometry.getLayout(); - ol.format.GPX.writeWptType_(node, geometry.getCoordinates(), objectStack); - } -}; - - -/** - * @const - * @type {Array.<string>} - * @private - */ -ol.format.GPX.LINK_SEQUENCE_ = ['text', 'type']; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GPX.LINK_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'text': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.GPX.RTE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, [ - 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'rtept' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GPX.RTE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_), - 'number': ol.xml.makeChildAppender( - ol.format.XSD.writeNonNegativeIntegerTextNode), - 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'rtept': ol.xml.makeArraySerializer(ol.xml.makeChildAppender( - ol.format.GPX.writeWptType_)) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.GPX.RTEPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, [ - 'ele', 'time' - ]); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.GPX.TRK_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, [ - 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'trkseg' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GPX.TRK_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_), - 'number': ol.xml.makeChildAppender( - ol.format.XSD.writeNonNegativeIntegerTextNode), - 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'trkseg': ol.xml.makeArraySerializer(ol.xml.makeChildAppender( - ol.format.GPX.writeTrkSeg_)) - }); - - -/** - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.GPX.TRKSEG_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('trkpt'); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GPX.TRKSEG_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'trkpt': ol.xml.makeChildAppender(ol.format.GPX.writeWptType_) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.GPX.WPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, [ - 'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src', - 'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop', - 'ageofdgpsdata', 'dgpsid' - ]); - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GPX.WPT_TYPE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'ele': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'time': ol.xml.makeChildAppender(ol.format.XSD.writeDateTimeTextNode), - 'magvar': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'geoidheight': ol.xml.makeChildAppender( - ol.format.XSD.writeDecimalTextNode), - 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_), - 'sym': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'fix': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'sat': ol.xml.makeChildAppender( - ol.format.XSD.writeNonNegativeIntegerTextNode), - 'hdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'vdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'pdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'ageofdgpsdata': ol.xml.makeChildAppender( - ol.format.XSD.writeDecimalTextNode), - 'dgpsid': ol.xml.makeChildAppender( - ol.format.XSD.writeNonNegativeIntegerTextNode) - }); - - -/** - * @const - * @type {Object.<string, string>} - * @private - */ -ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_ = { - 'Point': 'wpt', - 'LineString': 'rte', - 'MultiLineString': 'trk' -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.GPX.GPX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - var geometry = /** @type {ol.Feature} */ (value).getGeometry(); - if (geometry) { - var nodeName = ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_[geometry.getType()]; - if (nodeName) { - var parentNode = objectStack[objectStack.length - 1].node; - return ol.xml.createElementNS(parentNode.namespaceURI, nodeName); - } - } -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.GPX.GPX_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.GPX.NAMESPACE_URIS_, { - 'rte': ol.xml.makeChildAppender(ol.format.GPX.writeRte_), - 'trk': ol.xml.makeChildAppender(ol.format.GPX.writeTrk_), - 'wpt': ol.xml.makeChildAppender(ol.format.GPX.writeWpt_) - }); - - -/** - * Encode an array of features in the GPX format. - * LineString geometries are output as routes (`<rte>`), and MultiLineString - * as tracks (`<trk>`). - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} Result. - * @api - */ -ol.format.GPX.prototype.writeFeatures; - - -/** - * Encode an array of features in the GPX format as an XML node. - * LineString geometries are output as routes (`<rte>`), and MultiLineString - * as tracks (`<trk>`). - * - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -ol.format.GPX.prototype.writeFeaturesNode = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - //FIXME Serialize metadata - var gpx = ol.xml.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx'); - var xmlnsUri = 'http://www.w3.org/2000/xmlns/'; - var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance'; - ol.xml.setAttributeNS(gpx, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri); - ol.xml.setAttributeNS(gpx, xmlSchemaInstanceUri, 'xsi:schemaLocation', - ol.format.GPX.SCHEMA_LOCATION_); - gpx.setAttribute('version', '1.1'); - gpx.setAttribute('creator', 'OpenLayers'); - - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ - ({node: gpx}), ol.format.GPX.GPX_SERIALIZERS_, - ol.format.GPX.GPX_NODE_FACTORY_, features, [opt_options]); - return gpx; -}; - -goog.provide('ol.format.IGCZ'); - -/** - * IGC altitude/z. One of 'barometric', 'gps', 'none'. - * @enum {string} - */ -ol.format.IGCZ = { - BAROMETRIC: 'barometric', - GPS: 'gps', - NONE: 'none' -}; - -goog.provide('ol.format.TextFeature'); - -goog.require('ol'); -goog.require('ol.format.Feature'); -goog.require('ol.format.FormatType'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for text feature formats. - * - * @constructor - * @abstract - * @extends {ol.format.Feature} - */ -ol.format.TextFeature = function() { - ol.format.Feature.call(this); -}; -ol.inherits(ol.format.TextFeature, ol.format.Feature); - - -/** - * @param {Document|Node|Object|string} source Source. - * @private - * @return {string} Text. - */ -ol.format.TextFeature.prototype.getText_ = function(source) { - if (typeof source === 'string') { - return source; - } else { - return ''; - } -}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.getType = function() { - return ol.format.FormatType.TEXT; -}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.readFeature = function(source, opt_options) { - return this.readFeatureFromText( - this.getText_(source), this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {string} text Text. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @protected - * @return {ol.Feature} Feature. - */ -ol.format.TextFeature.prototype.readFeatureFromText = function(text, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.readFeatures = function(source, opt_options) { - return this.readFeaturesFromText( - this.getText_(source), this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {string} text Text. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @protected - * @return {Array.<ol.Feature>} Features. - */ -ol.format.TextFeature.prototype.readFeaturesFromText = function(text, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.readGeometry = function(source, opt_options) { - return this.readGeometryFromText( - this.getText_(source), this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {string} text Text. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @protected - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.TextFeature.prototype.readGeometryFromText = function(text, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.readProjection = function(source) { - return this.readProjectionFromText(this.getText_(source)); -}; - - -/** - * @param {string} text Text. - * @protected - * @return {ol.proj.Projection} Projection. - */ -ol.format.TextFeature.prototype.readProjectionFromText = function(text) { - return this.defaultDataProjection; -}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.writeFeature = function(feature, opt_options) { - return this.writeFeatureText(feature, this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {ol.Feature} feature Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ -ol.format.TextFeature.prototype.writeFeatureText = function(feature, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.writeFeatures = function( - features, opt_options) { - return this.writeFeaturesText(features, this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ -ol.format.TextFeature.prototype.writeFeaturesText = function(features, opt_options) {}; - - -/** - * @inheritDoc - */ -ol.format.TextFeature.prototype.writeGeometry = function( - geometry, opt_options) { - return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ -ol.format.TextFeature.prototype.writeGeometryText = function(geometry, opt_options) {}; - -goog.provide('ol.format.IGC'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.IGCZ'); -goog.require('ol.format.TextFeature'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Feature format for `*.igc` flight recording files. - * - * @constructor - * @extends {ol.format.TextFeature} - * @param {olx.format.IGCOptions=} opt_options Options. - * @api - */ -ol.format.IGC = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.TextFeature.call(this); - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get('EPSG:4326'); - - /** - * @private - * @type {ol.format.IGCZ} - */ - this.altitudeMode_ = options.altitudeMode ? - options.altitudeMode : ol.format.IGCZ.NONE; - -}; -ol.inherits(ol.format.IGC, ol.format.TextFeature); - - -/** - * @const - * @type {RegExp} - * @private - */ -ol.format.IGC.B_RECORD_RE_ = - /^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/; - - -/** - * @const - * @type {RegExp} - * @private - */ -ol.format.IGC.H_RECORD_RE_ = /^H.([A-Z]{3}).*?:(.*)/; - - -/** - * @const - * @type {RegExp} - * @private - */ -ol.format.IGC.HFDTE_RECORD_RE_ = /^HFDTE(\d{2})(\d{2})(\d{2})/; - - -/** - * A regular expression matching the newline characters `\r\n`, `\r` and `\n`. - * - * @const - * @type {RegExp} - * @private - */ -ol.format.IGC.NEWLINE_RE_ = /\r\n|\r|\n/; - - -/** - * Read the feature from the IGC source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.IGC.prototype.readFeature; - - -/** - * @inheritDoc - */ -ol.format.IGC.prototype.readFeatureFromText = function(text, opt_options) { - var altitudeMode = this.altitudeMode_; - var lines = text.split(ol.format.IGC.NEWLINE_RE_); - /** @type {Object.<string, string>} */ - var properties = {}; - var flatCoordinates = []; - var year = 2000; - var month = 0; - var day = 1; - var lastDateTime = -1; - var i, ii; - for (i = 0, ii = lines.length; i < ii; ++i) { - var line = lines[i]; - var m; - if (line.charAt(0) == 'B') { - m = ol.format.IGC.B_RECORD_RE_.exec(line); - if (m) { - var hour = parseInt(m[1], 10); - var minute = parseInt(m[2], 10); - var second = parseInt(m[3], 10); - var y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000; - if (m[6] == 'S') { - y = -y; - } - var x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000; - if (m[9] == 'W') { - x = -x; - } - flatCoordinates.push(x, y); - if (altitudeMode != ol.format.IGCZ.NONE) { - var z; - if (altitudeMode == ol.format.IGCZ.GPS) { - z = parseInt(m[11], 10); - } else if (altitudeMode == ol.format.IGCZ.BAROMETRIC) { - z = parseInt(m[12], 10); - } else { - z = 0; - } - flatCoordinates.push(z); - } - var dateTime = Date.UTC(year, month, day, hour, minute, second); - // Detect UTC midnight wrap around. - if (dateTime < lastDateTime) { - dateTime = Date.UTC(year, month, day + 1, hour, minute, second); - } - flatCoordinates.push(dateTime / 1000); - lastDateTime = dateTime; - } - } else if (line.charAt(0) == 'H') { - m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line); - if (m) { - day = parseInt(m[1], 10); - month = parseInt(m[2], 10) - 1; - year = 2000 + parseInt(m[3], 10); - } else { - m = ol.format.IGC.H_RECORD_RE_.exec(line); - if (m) { - properties[m[1]] = m[2].trim(); - } - } - } - } - if (flatCoordinates.length === 0) { - return null; - } - var lineString = new ol.geom.LineString(null); - var layout = altitudeMode == ol.format.IGCZ.NONE ? - ol.geom.GeometryLayout.XYM : ol.geom.GeometryLayout.XYZM; - lineString.setFlatCoordinates(layout, flatCoordinates); - var feature = new ol.Feature(ol.format.Feature.transformWithOptions( - lineString, false, opt_options)); - feature.setProperties(properties); - return feature; -}; - - -/** - * Read the feature from the source. As IGC sources contain a single - * feature, this will return the feature in an array. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.IGC.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.IGC.prototype.readFeaturesFromText = function(text, opt_options) { - var feature = this.readFeatureFromText(text, opt_options); - if (feature) { - return [feature]; - } else { - return []; - } -}; - - -/** - * Read the projection from the IGC source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.IGC.prototype.readProjection; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.IGC.prototype.writeFeatureText = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.IGC.prototype.writeFeaturesText = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.IGC.prototype.writeGeometryText = function(geometry, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.IGC.prototype.readGeometryFromText = function(text, opt_options) {}; - -goog.provide('ol.style.IconAnchorUnits'); - -/** - * Icon anchor units. One of 'fraction', 'pixels'. - * @enum {string} - */ -ol.style.IconAnchorUnits = { - FRACTION: 'fraction', - PIXELS: 'pixels' -}; - -goog.provide('ol.style.IconImage'); - -goog.require('ol'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.EventTarget'); -goog.require('ol.events.EventType'); -goog.require('ol.ImageState'); -goog.require('ol.style'); - - -/** - * @constructor - * @param {Image|HTMLCanvasElement} image Image. - * @param {string|undefined} src Src. - * @param {ol.Size} size Size. - * @param {?string} crossOrigin Cross origin. - * @param {ol.ImageState} imageState Image state. - * @param {ol.Color} color Color. - * @extends {ol.events.EventTarget} - */ -ol.style.IconImage = function(image, src, size, crossOrigin, imageState, - color) { - - ol.events.EventTarget.call(this); - - /** - * @private - * @type {Image|HTMLCanvasElement} - */ - this.hitDetectionImage_ = null; - - /** - * @private - * @type {Image|HTMLCanvasElement} - */ - this.image_ = !image ? new Image() : image; - - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = color ? - /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) : - null; - - /** - * @private - * @type {ol.Color} - */ - this.color_ = color; - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.imageListenerKeys_ = null; - - /** - * @private - * @type {ol.ImageState} - */ - this.imageState_ = imageState; - - /** - * @private - * @type {ol.Size} - */ - this.size_ = size; - - /** - * @private - * @type {string|undefined} - */ - this.src_ = src; - - /** - * @private - * @type {boolean} - */ - this.tainting_ = false; - if (this.imageState_ == ol.ImageState.LOADED) { - this.determineTainting_(); - } - -}; -ol.inherits(ol.style.IconImage, ol.events.EventTarget); - - -/** - * @param {Image|HTMLCanvasElement} image Image. - * @param {string} src Src. - * @param {ol.Size} size Size. - * @param {?string} crossOrigin Cross origin. - * @param {ol.ImageState} imageState Image state. - * @param {ol.Color} color Color. - * @return {ol.style.IconImage} Icon image. - */ -ol.style.IconImage.get = function(image, src, size, crossOrigin, imageState, - color) { - var iconImageCache = ol.style.iconImageCache; - var iconImage = iconImageCache.get(src, crossOrigin, color); - if (!iconImage) { - iconImage = new ol.style.IconImage( - image, src, size, crossOrigin, imageState, color); - iconImageCache.set(src, crossOrigin, color, iconImage); - } - return iconImage; -}; - - -/** - * @private - */ -ol.style.IconImage.prototype.determineTainting_ = function() { - var context = ol.dom.createCanvasContext2D(1, 1); - try { - context.drawImage(this.image_, 0, 0); - context.getImageData(0, 0, 1, 1); - } catch (e) { - this.tainting_ = true; - } -}; - - -/** - * @private - */ -ol.style.IconImage.prototype.dispatchChangeEvent_ = function() { - this.dispatchEvent(ol.events.EventType.CHANGE); -}; - - -/** - * @private - */ -ol.style.IconImage.prototype.handleImageError_ = function() { - this.imageState_ = ol.ImageState.ERROR; - this.unlistenImage_(); - this.dispatchChangeEvent_(); -}; - - -/** - * @private - */ -ol.style.IconImage.prototype.handleImageLoad_ = function() { - this.imageState_ = ol.ImageState.LOADED; - if (this.size_) { - this.image_.width = this.size_[0]; - this.image_.height = this.size_[1]; - } - this.size_ = [this.image_.width, this.image_.height]; - this.unlistenImage_(); - this.determineTainting_(); - this.replaceColor_(); - this.dispatchChangeEvent_(); -}; - - -/** - * @param {number} pixelRatio Pixel ratio. - * @return {Image|HTMLCanvasElement} Image or Canvas element. - */ -ol.style.IconImage.prototype.getImage = function(pixelRatio) { - return this.canvas_ ? this.canvas_ : this.image_; -}; - - -/** - * @return {ol.ImageState} Image state. - */ -ol.style.IconImage.prototype.getImageState = function() { - return this.imageState_; -}; - - -/** - * @param {number} pixelRatio Pixel ratio. - * @return {Image|HTMLCanvasElement} Image element. - */ -ol.style.IconImage.prototype.getHitDetectionImage = function(pixelRatio) { - if (!this.hitDetectionImage_) { - if (this.tainting_) { - var width = this.size_[0]; - var height = this.size_[1]; - var context = ol.dom.createCanvasContext2D(width, height); - context.fillRect(0, 0, width, height); - this.hitDetectionImage_ = context.canvas; - } else { - this.hitDetectionImage_ = this.image_; - } - } - return this.hitDetectionImage_; -}; - - -/** - * @return {ol.Size} Image size. - */ -ol.style.IconImage.prototype.getSize = function() { - return this.size_; -}; - - -/** - * @return {string|undefined} Image src. - */ -ol.style.IconImage.prototype.getSrc = function() { - return this.src_; -}; - - -/** - * Load not yet loaded URI. - */ -ol.style.IconImage.prototype.load = function() { - if (this.imageState_ == ol.ImageState.IDLE) { - this.imageState_ = ol.ImageState.LOADING; - this.imageListenerKeys_ = [ - ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, - this.handleImageError_, this), - ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, - this.handleImageLoad_, this) - ]; - try { - this.image_.src = this.src_; - } catch (e) { - this.handleImageError_(); - } - } -}; - - -/** - * @private - */ -ol.style.IconImage.prototype.replaceColor_ = function() { - if (this.tainting_ || this.color_ === null) { - return; - } - - this.canvas_.width = this.image_.width; - this.canvas_.height = this.image_.height; - - var ctx = this.canvas_.getContext('2d'); - ctx.drawImage(this.image_, 0, 0); - - var imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height); - var data = imgData.data; - var r = this.color_[0] / 255.0; - var g = this.color_[1] / 255.0; - var b = this.color_[2] / 255.0; - - for (var i = 0, ii = data.length; i < ii; i += 4) { - data[i] *= r; - data[i + 1] *= g; - data[i + 2] *= b; - } - ctx.putImageData(imgData, 0, 0); -}; - - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -ol.style.IconImage.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(ol.events.unlistenByKey); - this.imageListenerKeys_ = null; -}; - -goog.provide('ol.style.IconOrigin'); - -/** - * Icon origin. One of 'bottom-left', 'bottom-right', 'top-left', 'top-right'. - * @enum {string} - */ -ol.style.IconOrigin = { - BOTTOM_LEFT: 'bottom-left', - BOTTOM_RIGHT: 'bottom-right', - TOP_LEFT: 'top-left', - TOP_RIGHT: 'top-right' -}; - -goog.provide('ol.style.Icon'); - -goog.require('ol'); -goog.require('ol.ImageState'); -goog.require('ol.asserts'); -goog.require('ol.color'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.style.IconAnchorUnits'); -goog.require('ol.style.IconImage'); -goog.require('ol.style.IconOrigin'); -goog.require('ol.style.Image'); - - -/** - * @classdesc - * Set icon style for vector features. - * - * @constructor - * @param {olx.style.IconOptions=} opt_options Options. - * @extends {ol.style.Image} - * @api - */ -ol.style.Icon = function(opt_options) { - - var options = opt_options || {}; - - /** - * @private - * @type {Array.<number>} - */ - this.anchor_ = options.anchor !== undefined ? options.anchor : [0.5, 0.5]; - - /** - * @private - * @type {Array.<number>} - */ - this.normalizedAnchor_ = null; - - /** - * @private - * @type {ol.style.IconOrigin} - */ - this.anchorOrigin_ = options.anchorOrigin !== undefined ? - options.anchorOrigin : ol.style.IconOrigin.TOP_LEFT; - - /** - * @private - * @type {ol.style.IconAnchorUnits} - */ - this.anchorXUnits_ = options.anchorXUnits !== undefined ? - options.anchorXUnits : ol.style.IconAnchorUnits.FRACTION; - - /** - * @private - * @type {ol.style.IconAnchorUnits} - */ - this.anchorYUnits_ = options.anchorYUnits !== undefined ? - options.anchorYUnits : ol.style.IconAnchorUnits.FRACTION; - - /** - * @private - * @type {?string} - */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; - - /** - * @type {Image|HTMLCanvasElement} - */ - var image = options.img !== undefined ? options.img : null; - - /** - * @type {ol.Size} - */ - var imgSize = options.imgSize !== undefined ? options.imgSize : null; - - /** - * @type {string|undefined} - */ - var src = options.src; - - ol.asserts.assert(!(src !== undefined && image), - 4); // `image` and `src` cannot be provided at the same time - ol.asserts.assert(!image || (image && imgSize), - 5); // `imgSize` must be set when `image` is provided - - if ((src === undefined || src.length === 0) && image) { - src = image.src || ol.getUid(image).toString(); - } - ol.asserts.assert(src !== undefined && src.length > 0, - 6); // A defined and non-empty `src` or `image` must be provided - - /** - * @type {ol.ImageState} - */ - var imageState = options.src !== undefined ? - ol.ImageState.IDLE : ol.ImageState.LOADED; - - /** - * @private - * @type {ol.Color} - */ - this.color_ = options.color !== undefined ? ol.color.asArray(options.color) : - null; - - /** - * @private - * @type {ol.style.IconImage} - */ - this.iconImage_ = ol.style.IconImage.get( - image, /** @type {string} */ (src), imgSize, this.crossOrigin_, imageState, this.color_); - - /** - * @private - * @type {Array.<number>} - */ - this.offset_ = options.offset !== undefined ? options.offset : [0, 0]; - - /** - * @private - * @type {ol.style.IconOrigin} - */ - this.offsetOrigin_ = options.offsetOrigin !== undefined ? - options.offsetOrigin : ol.style.IconOrigin.TOP_LEFT; - - /** - * @private - * @type {Array.<number>} - */ - this.origin_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.size_ = options.size !== undefined ? options.size : null; - - /** - * @type {number} - */ - var opacity = options.opacity !== undefined ? options.opacity : 1; - - /** - * @type {boolean} - */ - var rotateWithView = options.rotateWithView !== undefined ? - options.rotateWithView : false; - - /** - * @type {number} - */ - var rotation = options.rotation !== undefined ? options.rotation : 0; - - /** - * @type {number} - */ - var scale = options.scale !== undefined ? options.scale : 1; - - /** - * @type {boolean} - */ - var snapToPixel = options.snapToPixel !== undefined ? - options.snapToPixel : true; - - ol.style.Image.call(this, { - opacity: opacity, - rotation: rotation, - scale: scale, - snapToPixel: snapToPixel, - rotateWithView: rotateWithView - }); - -}; -ol.inherits(ol.style.Icon, ol.style.Image); - - -/** - * Clones the style. - * @return {ol.style.Icon} The cloned style. - * @api - */ -ol.style.Icon.prototype.clone = function() { - var oldImage = this.getImage(1); - var newImage; - if (this.iconImage_.getImageState() === ol.ImageState.LOADED) { - if (oldImage.tagName.toUpperCase() === 'IMG') { - newImage = /** @type {Image} */ (oldImage.cloneNode(true)); - } else { - newImage = /** @type {HTMLCanvasElement} */ (document.createElement('canvas')); - var context = newImage.getContext('2d'); - newImage.width = oldImage.width; - newImage.height = oldImage.height; - context.drawImage(oldImage, 0, 0); - } - } - return new ol.style.Icon({ - anchor: this.anchor_.slice(), - anchorOrigin: this.anchorOrigin_, - anchorXUnits: this.anchorXUnits_, - anchorYUnits: this.anchorYUnits_, - crossOrigin: this.crossOrigin_, - color: (this.color_ && this.color_.slice) ? this.color_.slice() : this.color_ || undefined, - img: newImage ? newImage : undefined, - imgSize: newImage ? this.iconImage_.getSize().slice() : undefined, - src: newImage ? undefined : this.getSrc(), - offset: this.offset_.slice(), - offsetOrigin: this.offsetOrigin_, - size: this.size_ !== null ? this.size_.slice() : undefined, - opacity: this.getOpacity(), - scale: this.getScale(), - snapToPixel: this.getSnapToPixel(), - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView() - }); -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.Icon.prototype.getAnchor = function() { - if (this.normalizedAnchor_) { - return this.normalizedAnchor_; - } - var anchor = this.anchor_; - var size = this.getSize(); - if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION || - this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { - if (!size) { - return null; - } - anchor = this.anchor_.slice(); - if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION) { - anchor[0] *= size[0]; - } - if (this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { - anchor[1] *= size[1]; - } - } - - if (this.anchorOrigin_ != ol.style.IconOrigin.TOP_LEFT) { - if (!size) { - return null; - } - if (anchor === this.anchor_) { - anchor = this.anchor_.slice(); - } - if (this.anchorOrigin_ == ol.style.IconOrigin.TOP_RIGHT || - this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { - anchor[0] = -anchor[0] + size[0]; - } - if (this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT || - this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { - anchor[1] = -anchor[1] + size[1]; - } - } - this.normalizedAnchor_ = anchor; - return this.normalizedAnchor_; -}; - - -/** - * Get the icon color. - * @return {ol.Color} Color. - * @api - */ -ol.style.Icon.prototype.getColor = function() { - return this.color_; -}; - - -/** - * Get the image icon. - * @param {number} pixelRatio Pixel ratio. - * @return {Image|HTMLCanvasElement} Image or Canvas element. - * @override - * @api - */ -ol.style.Icon.prototype.getImage = function(pixelRatio) { - return this.iconImage_.getImage(pixelRatio); -}; - - -/** - * @override - */ -ol.style.Icon.prototype.getImageSize = function() { - return this.iconImage_.getSize(); -}; - - -/** - * @override - */ -ol.style.Icon.prototype.getHitDetectionImageSize = function() { - return this.getImageSize(); -}; - - -/** - * @override - */ -ol.style.Icon.prototype.getImageState = function() { - return this.iconImage_.getImageState(); -}; - - -/** - * @override - */ -ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) { - return this.iconImage_.getHitDetectionImage(pixelRatio); -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.Icon.prototype.getOrigin = function() { - if (this.origin_) { - return this.origin_; - } - var offset = this.offset_; - - if (this.offsetOrigin_ != ol.style.IconOrigin.TOP_LEFT) { - var size = this.getSize(); - var iconImageSize = this.iconImage_.getSize(); - if (!size || !iconImageSize) { - return null; - } - offset = offset.slice(); - if (this.offsetOrigin_ == ol.style.IconOrigin.TOP_RIGHT || - this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { - offset[0] = iconImageSize[0] - size[0] - offset[0]; - } - if (this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT || - this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { - offset[1] = iconImageSize[1] - size[1] - offset[1]; - } - } - this.origin_ = offset; - return this.origin_; -}; - - -/** - * Get the image URL. - * @return {string|undefined} Image src. - * @api - */ -ol.style.Icon.prototype.getSrc = function() { - return this.iconImage_.getSrc(); -}; - - -/** - * @inheritDoc - * @api - */ -ol.style.Icon.prototype.getSize = function() { - return !this.size_ ? this.iconImage_.getSize() : this.size_; -}; - - -/** - * @override - */ -ol.style.Icon.prototype.listenImageChange = function(listener, thisArg) { - return ol.events.listen(this.iconImage_, ol.events.EventType.CHANGE, - listener, thisArg); -}; - - -/** - * Load not yet loaded URI. - * When rendering a feature with an icon style, the vector renderer will - * automatically call this method. However, you might want to call this - * method yourself for preloading or other purposes. - * @override - * @api - */ -ol.style.Icon.prototype.load = function() { - this.iconImage_.load(); -}; - - -/** - * @override - */ -ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) { - ol.events.unlisten(this.iconImage_, ol.events.EventType.CHANGE, - listener, thisArg); -}; - -goog.provide('ol.style.Text'); - - -goog.require('ol.style.Fill'); - - -/** - * @classdesc - * Set text style for vector features. - * - * @constructor - * @param {olx.style.TextOptions=} opt_options Options. - * @api - */ -ol.style.Text = function(opt_options) { - - var options = opt_options || {}; - - /** - * @private - * @type {string|undefined} - */ - this.font_ = options.font; - - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = options.rotation; - - /** - * @private - * @type {boolean|undefined} - */ - this.rotateWithView_ = options.rotateWithView; - - /** - * @private - * @type {number|undefined} - */ - this.scale_ = options.scale; - - /** - * @private - * @type {string|undefined} - */ - this.text_ = options.text; - - /** - * @private - * @type {string|undefined} - */ - this.textAlign_ = options.textAlign; - - /** - * @private - * @type {string|undefined} - */ - this.textBaseline_ = options.textBaseline; - - /** - * @private - * @type {ol.style.Fill} - */ - this.fill_ = options.fill !== undefined ? options.fill : - new ol.style.Fill({color: ol.style.Text.DEFAULT_FILL_COLOR_}); - - /** - * @private - * @type {ol.style.Stroke} - */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; - - /** - * @private - * @type {number} - */ - this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0; - - /** - * @private - * @type {number} - */ - this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0; -}; - - -/** - * The default fill color to use if no fill was set at construction time; a - * blackish `#333`. - * - * @const {string} - * @private - */ -ol.style.Text.DEFAULT_FILL_COLOR_ = '#333'; - - -/** - * Clones the style. - * @return {ol.style.Text} The cloned style. - * @api - */ -ol.style.Text.prototype.clone = function() { - return new ol.style.Text({ - font: this.getFont(), - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - scale: this.getScale(), - text: this.getText(), - textAlign: this.getTextAlign(), - textBaseline: this.getTextBaseline(), - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - offsetX: this.getOffsetX(), - offsetY: this.getOffsetY() - }); -}; - - -/** - * Get the font name. - * @return {string|undefined} Font. - * @api - */ -ol.style.Text.prototype.getFont = function() { - return this.font_; -}; - - -/** - * Get the x-offset for the text. - * @return {number} Horizontal text offset. - * @api - */ -ol.style.Text.prototype.getOffsetX = function() { - return this.offsetX_; -}; - - -/** - * Get the y-offset for the text. - * @return {number} Vertical text offset. - * @api - */ -ol.style.Text.prototype.getOffsetY = function() { - return this.offsetY_; -}; - - -/** - * Get the fill style for the text. - * @return {ol.style.Fill} Fill style. - * @api - */ -ol.style.Text.prototype.getFill = function() { - return this.fill_; -}; - - -/** - * Determine whether the text rotates with the map. - * @return {boolean|undefined} Rotate with map. - * @api - */ -ol.style.Text.prototype.getRotateWithView = function() { - return this.rotateWithView_; -}; - - -/** - * Get the text rotation. - * @return {number|undefined} Rotation. - * @api - */ -ol.style.Text.prototype.getRotation = function() { - return this.rotation_; -}; - - -/** - * Get the text scale. - * @return {number|undefined} Scale. - * @api - */ -ol.style.Text.prototype.getScale = function() { - return this.scale_; -}; - - -/** - * Get the stroke style for the text. - * @return {ol.style.Stroke} Stroke style. - * @api - */ -ol.style.Text.prototype.getStroke = function() { - return this.stroke_; -}; - - -/** - * Get the text to be rendered. - * @return {string|undefined} Text. - * @api - */ -ol.style.Text.prototype.getText = function() { - return this.text_; -}; - - -/** - * Get the text alignment. - * @return {string|undefined} Text align. - * @api - */ -ol.style.Text.prototype.getTextAlign = function() { - return this.textAlign_; -}; - - -/** - * Get the text baseline. - * @return {string|undefined} Text baseline. - * @api - */ -ol.style.Text.prototype.getTextBaseline = function() { - return this.textBaseline_; -}; - - -/** - * Set the font. - * - * @param {string|undefined} font Font. - * @api - */ -ol.style.Text.prototype.setFont = function(font) { - this.font_ = font; -}; - - -/** - * Set the x offset. - * - * @param {number} offsetX Horizontal text offset. - * @api - */ -ol.style.Text.prototype.setOffsetX = function(offsetX) { - this.offsetX_ = offsetX; -}; - - -/** - * Set the y offset. - * - * @param {number} offsetY Vertical text offset. - * @api - */ -ol.style.Text.prototype.setOffsetY = function(offsetY) { - this.offsetY_ = offsetY; -}; - - -/** - * Set the fill. - * - * @param {ol.style.Fill} fill Fill style. - * @api - */ -ol.style.Text.prototype.setFill = function(fill) { - this.fill_ = fill; -}; - - -/** - * Set the rotation. - * - * @param {number|undefined} rotation Rotation. - * @api - */ -ol.style.Text.prototype.setRotation = function(rotation) { - this.rotation_ = rotation; -}; - - -/** - * Set the scale. - * - * @param {number|undefined} scale Scale. - * @api - */ -ol.style.Text.prototype.setScale = function(scale) { - this.scale_ = scale; -}; - - -/** - * Set the stroke. - * - * @param {ol.style.Stroke} stroke Stroke style. - * @api - */ -ol.style.Text.prototype.setStroke = function(stroke) { - this.stroke_ = stroke; -}; - - -/** - * Set the text. - * - * @param {string|undefined} text Text. - * @api - */ -ol.style.Text.prototype.setText = function(text) { - this.text_ = text; -}; - - -/** - * Set the text alignment. - * - * @param {string|undefined} textAlign Text align. - * @api - */ -ol.style.Text.prototype.setTextAlign = function(textAlign) { - this.textAlign_ = textAlign; -}; - - -/** - * Set the text baseline. - * - * @param {string|undefined} textBaseline Text baseline. - * @api - */ -ol.style.Text.prototype.setTextBaseline = function(textBaseline) { - this.textBaseline_ = textBaseline; -}; - -// FIXME http://earth.google.com/kml/1.0 namespace? -// FIXME why does node.getAttribute return an unknown type? -// FIXME serialize arbitrary feature properties -// FIXME don't parse style if extractStyles is false - -goog.provide('ol.format.KML'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.array'); -goog.require('ol.asserts'); -goog.require('ol.color'); -goog.require('ol.format.Feature'); -goog.require('ol.format.XMLFeature'); -goog.require('ol.format.XSD'); -goog.require('ol.geom.GeometryCollection'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.math'); -goog.require('ol.proj'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Icon'); -goog.require('ol.style.IconAnchorUnits'); -goog.require('ol.style.IconOrigin'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); -goog.require('ol.style.Text'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Feature format for reading and writing data in the KML format. - * - * Note that the KML format uses the URL() constructor. Older browsers such as IE - * which do not support this will need a URL polyfill to be loaded before use. - * - * @constructor - * @extends {ol.format.XMLFeature} - * @param {olx.format.KMLOptions=} opt_options Options. - * @api - */ -ol.format.KML = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.XMLFeature.call(this); - - if (!ol.format.KML.DEFAULT_STYLE_ARRAY_) { - ol.format.KML.createStyleDefaults_(); - } - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get('EPSG:4326'); - - /** - * @private - * @type {Array.<ol.style.Style>} - */ - this.defaultStyle_ = options.defaultStyle ? - options.defaultStyle : ol.format.KML.DEFAULT_STYLE_ARRAY_; - - /** - * @private - * @type {boolean} - */ - this.extractStyles_ = options.extractStyles !== undefined ? - options.extractStyles : true; - - /** - * @private - * @type {boolean} - */ - this.writeStyles_ = options.writeStyles !== undefined ? - options.writeStyles : true; - - /** - * @private - * @type {Object.<string, (Array.<ol.style.Style>|string)>} - */ - this.sharedStyles_ = {}; - - /** - * @private - * @type {boolean} - */ - this.showPointNames_ = options.showPointNames !== undefined ? - options.showPointNames : true; - -}; -ol.inherits(ol.format.KML, ol.format.XMLFeature); - - -/** - * @const - * @type {Array.<string>} - * @private - */ -ol.format.KML.GX_NAMESPACE_URIS_ = [ - 'http://www.google.com/kml/ext/2.2' -]; - - -/** - * @const - * @type {Array.<string>} - * @private - */ -ol.format.KML.NAMESPACE_URIS_ = [ - null, - 'http://earth.google.com/kml/2.0', - 'http://earth.google.com/kml/2.1', - 'http://earth.google.com/kml/2.2', - 'http://www.opengis.net/kml/2.2' -]; - - -/** - * @const - * @type {string} - * @private - */ -ol.format.KML.SCHEMA_LOCATION_ = 'http://www.opengis.net/kml/2.2 ' + - 'https://developers.google.com/kml/schema/kml22gx.xsd'; - - -/** - * @return {Array.<ol.style.Style>} Default style. - * @private - */ -ol.format.KML.createStyleDefaults_ = function() { - /** - * @const - * @type {ol.Color} - * @private - */ - ol.format.KML.DEFAULT_COLOR_ = [255, 255, 255, 1]; - - /** - * @const - * @type {ol.style.Fill} - * @private - */ - ol.format.KML.DEFAULT_FILL_STYLE_ = new ol.style.Fill({ - color: ol.format.KML.DEFAULT_COLOR_ - }); - - /** - * @const - * @type {ol.Size} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_ = [20, 2]; // FIXME maybe [8, 32] ? - - /** - * @const - * @type {ol.style.IconAnchorUnits} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_ = - ol.style.IconAnchorUnits.PIXELS; - - /** - * @const - * @type {ol.style.IconAnchorUnits} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_ = - ol.style.IconAnchorUnits.PIXELS; - - /** - * @const - * @type {ol.Size} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_ = [64, 64]; - - /** - * @const - * @type {string} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_ = - 'https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png'; - - /** - * @const - * @type {number} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_ = 0.5; - - /** - * @const - * @type {ol.style.Image} - * @private - */ - ol.format.KML.DEFAULT_IMAGE_STYLE_ = new ol.style.Icon({ - anchor: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_, - anchorOrigin: ol.style.IconOrigin.BOTTOM_LEFT, - anchorXUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_, - anchorYUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_, - crossOrigin: 'anonymous', - rotation: 0, - scale: ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_, - size: ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_, - src: ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_ - }); - - /** - * @const - * @type {string} - * @private - */ - ol.format.KML.DEFAULT_NO_IMAGE_STYLE_ = 'NO_IMAGE'; - - /** - * @const - * @type {ol.style.Stroke} - * @private - */ - ol.format.KML.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({ - color: ol.format.KML.DEFAULT_COLOR_, - width: 1 - }); - - /** - * @const - * @type {ol.style.Stroke} - * @private - */ - ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_ = new ol.style.Stroke({ - color: [51, 51, 51, 1], - width: 2 - }); - - /** - * @const - * @type {ol.style.Text} - * @private - */ - ol.format.KML.DEFAULT_TEXT_STYLE_ = new ol.style.Text({ - font: 'bold 16px Helvetica', - fill: ol.format.KML.DEFAULT_FILL_STYLE_, - stroke: ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_, - scale: 0.8 - }); - - /** - * @const - * @type {ol.style.Style} - * @private - */ - ol.format.KML.DEFAULT_STYLE_ = new ol.style.Style({ - fill: ol.format.KML.DEFAULT_FILL_STYLE_, - image: ol.format.KML.DEFAULT_IMAGE_STYLE_, - text: ol.format.KML.DEFAULT_TEXT_STYLE_, - stroke: ol.format.KML.DEFAULT_STROKE_STYLE_, - zIndex: 0 - }); - - /** - * @const - * @type {Array.<ol.style.Style>} - * @private - */ - ol.format.KML.DEFAULT_STYLE_ARRAY_ = [ol.format.KML.DEFAULT_STYLE_]; - - return ol.format.KML.DEFAULT_STYLE_ARRAY_; -}; - - -/** - * @const - * @type {Object.<string, ol.style.IconAnchorUnits>} - * @private - */ -ol.format.KML.ICON_ANCHOR_UNITS_MAP_ = { - 'fraction': ol.style.IconAnchorUnits.FRACTION, - 'pixels': ol.style.IconAnchorUnits.PIXELS, - 'insetPixels': ol.style.IconAnchorUnits.PIXELS -}; - - -/** - * @param {ol.style.Style|undefined} foundStyle Style. - * @param {string} name Name. - * @return {ol.style.Style} style Style. - * @private - */ -ol.format.KML.createNameStyleFunction_ = function(foundStyle, name) { - var textStyle = null; - var textOffset = [0, 0]; - var textAlign = 'start'; - if (foundStyle.getImage()) { - var imageSize = foundStyle.getImage().getImageSize(); - if (imageSize === null) { - imageSize = ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_; - } - if (imageSize.length == 2) { - var imageScale = foundStyle.getImage().getScale(); - // Offset the label to be centered to the right of the icon, if there is - // one. - textOffset[0] = imageScale * imageSize[0] / 2; - textOffset[1] = -imageScale * imageSize[1] / 2; - textAlign = 'left'; - } - } - if (foundStyle.getText() !== null) { - // clone the text style, customizing it with name, alignments and offset. - // Note that kml does not support many text options that OpenLayers does (rotation, textBaseline). - var foundText = foundStyle.getText(); - textStyle = foundText.clone(); - textStyle.setFont(foundText.getFont() || ol.format.KML.DEFAULT_TEXT_STYLE_.getFont()); - textStyle.setScale(foundText.getScale() || ol.format.KML.DEFAULT_TEXT_STYLE_.getScale()); - textStyle.setFill(foundText.getFill() || ol.format.KML.DEFAULT_TEXT_STYLE_.getFill()); - textStyle.setStroke(foundText.getStroke() || ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_); - } else { - textStyle = ol.format.KML.DEFAULT_TEXT_STYLE_.clone(); - } - textStyle.setText(name); - textStyle.setOffsetX(textOffset[0]); - textStyle.setOffsetY(textOffset[1]); - textStyle.setTextAlign(textAlign); - - var nameStyle = new ol.style.Style({ - text: textStyle - }); - return nameStyle; -}; - - -/** - * @param {Array.<ol.style.Style>|undefined} style Style. - * @param {string} styleUrl Style URL. - * @param {Array.<ol.style.Style>} defaultStyle Default style. - * @param {Object.<string, (Array.<ol.style.Style>|string)>} sharedStyles Shared - * styles. - * @param {boolean|undefined} showPointNames true to show names for point - * placemarks. - * @return {ol.FeatureStyleFunction} Feature style function. - * @private - */ -ol.format.KML.createFeatureStyleFunction_ = function(style, styleUrl, - defaultStyle, sharedStyles, showPointNames) { - - return ( - /** - * @param {number} resolution Resolution. - * @return {Array.<ol.style.Style>} Style. - * @this {ol.Feature} - */ - function(resolution) { - var drawName = showPointNames; - /** @type {ol.style.Style|undefined} */ - var nameStyle; - var name = ''; - if (drawName) { - if (this.getGeometry()) { - drawName = (this.getGeometry().getType() === - ol.geom.GeometryType.POINT); - } - } - - if (drawName) { - name = /** @type {string} */ (this.get('name')); - drawName = drawName && name; - } - - if (style) { - if (drawName) { - nameStyle = ol.format.KML.createNameStyleFunction_(style[0], - name); - return style.concat(nameStyle); - } - return style; - } - if (styleUrl) { - var foundStyle = ol.format.KML.findStyle_(styleUrl, defaultStyle, - sharedStyles); - if (drawName) { - nameStyle = ol.format.KML.createNameStyleFunction_(foundStyle[0], - name); - return foundStyle.concat(nameStyle); - } - return foundStyle; - } - if (drawName) { - nameStyle = ol.format.KML.createNameStyleFunction_(defaultStyle[0], - name); - return defaultStyle.concat(nameStyle); - } - return defaultStyle; - }); -}; - - -/** - * @param {Array.<ol.style.Style>|string|undefined} styleValue Style value. - * @param {Array.<ol.style.Style>} defaultStyle Default style. - * @param {Object.<string, (Array.<ol.style.Style>|string)>} sharedStyles - * Shared styles. - * @return {Array.<ol.style.Style>} Style. - * @private - */ -ol.format.KML.findStyle_ = function(styleValue, defaultStyle, sharedStyles) { - if (Array.isArray(styleValue)) { - return styleValue; - } else if (typeof styleValue === 'string') { - // KML files in the wild occasionally forget the leading `#` on styleUrls - // defined in the same document. Add a leading `#` if it enables to find - // a style. - if (!(styleValue in sharedStyles) && ('#' + styleValue in sharedStyles)) { - styleValue = '#' + styleValue; - } - return ol.format.KML.findStyle_( - sharedStyles[styleValue], defaultStyle, sharedStyles); - } else { - return defaultStyle; - } -}; - - -/** - * @param {Node} node Node. - * @private - * @return {ol.Color|undefined} Color. - */ -ol.format.KML.readColor_ = function(node) { - var s = ol.xml.getAllTextContent(node, false); - // The KML specification states that colors should not include a leading `#` - // but we tolerate them. - var m = /^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(s); - if (m) { - var hexColor = m[1]; - return [ - parseInt(hexColor.substr(6, 2), 16), - parseInt(hexColor.substr(4, 2), 16), - parseInt(hexColor.substr(2, 2), 16), - parseInt(hexColor.substr(0, 2), 16) / 255 - ]; - - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @private - * @return {Array.<number>|undefined} Flat coordinates. - */ -ol.format.KML.readFlatCoordinates_ = function(node) { - var s = ol.xml.getAllTextContent(node, false); - var flatCoordinates = []; - // The KML specification states that coordinate tuples should not include - // spaces, but we tolerate them. - var re = - /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i; - var m; - while ((m = re.exec(s))) { - var x = parseFloat(m[1]); - var y = parseFloat(m[2]); - var z = m[3] ? parseFloat(m[3]) : 0; - flatCoordinates.push(x, y, z); - s = s.substr(m[0].length); - } - if (s !== '') { - return undefined; - } - return flatCoordinates; -}; - - -/** - * @param {Node} node Node. - * @private - * @return {string} URI. - */ -ol.format.KML.readURI_ = function(node) { - var s = ol.xml.getAllTextContent(node, false).trim(); - if (node.baseURI && node.baseURI !== 'about:blank') { - var url = new URL(s, node.baseURI); - return url.href; - } else { - return s; - } -}; - - -/** - * @param {Node} node Node. - * @private - * @return {ol.KMLVec2_} Vec2. - */ -ol.format.KML.readVec2_ = function(node) { - var xunits = node.getAttribute('xunits'); - var yunits = node.getAttribute('yunits'); - var origin; - if (xunits !== 'insetPixels') { - if (yunits !== 'insetPixels') { - origin = ol.style.IconOrigin.BOTTOM_LEFT; - } else { - origin = ol.style.IconOrigin.TOP_LEFT; - } - } else { - if (yunits !== 'insetPixels') { - origin = ol.style.IconOrigin.BOTTOM_RIGHT; - } else { - origin = ol.style.IconOrigin.TOP_RIGHT; - } - } - return { - x: parseFloat(node.getAttribute('x')), - xunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[xunits], - y: parseFloat(node.getAttribute('y')), - yunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[yunits], - origin: origin - }; -}; - - -/** - * @param {Node} node Node. - * @private - * @return {number|undefined} Scale. - */ -ol.format.KML.readScale_ = function(node) { - return ol.format.XSD.readDecimal(node); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<ol.style.Style>|string|undefined} StyleMap. - */ -ol.format.KML.readStyleMapValue_ = function(node, objectStack) { - return ol.xml.pushParseAndPop(undefined, - ol.format.KML.STYLE_MAP_PARSERS_, node, objectStack); -}; - /** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.IconStyleParser_ = function(node, objectStack) { - // FIXME refreshMode - // FIXME refreshInterval - // FIXME viewRefreshTime - // FIXME viewBoundScale - // FIXME viewFormat - // FIXME httpQuery - var object = ol.xml.pushParseAndPop( - {}, ol.format.KML.ICON_STYLE_PARSERS_, node, objectStack); - if (!object) { - return; - } - var styleObject = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var IconObject = 'Icon' in object ? object['Icon'] : {}; - var drawIcon = (!('Icon' in object) || Object.keys(IconObject).length > 0); - var src; - var href = /** @type {string|undefined} */ - (IconObject['href']); - if (href) { - src = href; - } else if (drawIcon) { - src = ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_; - } - var anchor, anchorXUnits, anchorYUnits; - var anchorOrigin = ol.style.IconOrigin.BOTTOM_LEFT; - var hotSpot = /** @type {ol.KMLVec2_|undefined} */ - (object['hotSpot']); - if (hotSpot) { - anchor = [hotSpot.x, hotSpot.y]; - anchorXUnits = hotSpot.xunits; - anchorYUnits = hotSpot.yunits; - anchorOrigin = hotSpot.origin; - } else if (src === ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) { - anchor = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_; - anchorXUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_; - anchorYUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_; - } else if (/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(src)) { - anchor = [0.5, 0]; - anchorXUnits = ol.style.IconAnchorUnits.FRACTION; - anchorYUnits = ol.style.IconAnchorUnits.FRACTION; - } - - var offset; - var x = /** @type {number|undefined} */ - (IconObject['x']); - var y = /** @type {number|undefined} */ - (IconObject['y']); - if (x !== undefined && y !== undefined) { - offset = [x, y]; - } - - var size; - var w = /** @type {number|undefined} */ - (IconObject['w']); - var h = /** @type {number|undefined} */ - (IconObject['h']); - if (w !== undefined && h !== undefined) { - size = [w, h]; - } - - var rotation; - var heading = /** @type {number} */ - (object['heading']); - if (heading !== undefined) { - rotation = ol.math.toRadians(heading); - } - - var scale = /** @type {number|undefined} */ - (object['scale']); - - if (drawIcon) { - if (src == ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) { - size = ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_; - if (scale === undefined) { - scale = ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_; - } - } - - var imageStyle = new ol.style.Icon({ - anchor: anchor, - anchorOrigin: anchorOrigin, - anchorXUnits: anchorXUnits, - anchorYUnits: anchorYUnits, - crossOrigin: 'anonymous', // FIXME should this be configurable? - offset: offset, - offsetOrigin: ol.style.IconOrigin.BOTTOM_LEFT, - rotation: rotation, - scale: scale, - size: size, - src: src - }); - styleObject['imageStyle'] = imageStyle; - } else { - // handle the case when we explicitly want to draw no icon. - styleObject['imageStyle'] = ol.format.KML.DEFAULT_NO_IMAGE_STYLE_; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.LabelStyleParser_ = function(node, objectStack) { - // FIXME colorMode - var object = ol.xml.pushParseAndPop( - {}, ol.format.KML.LABEL_STYLE_PARSERS_, node, objectStack); - if (!object) { - return; - } - var styleObject = objectStack[objectStack.length - 1]; - var textStyle = new ol.style.Text({ - fill: new ol.style.Fill({ - color: /** @type {ol.Color} */ - ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_) - }), - scale: /** @type {number|undefined} */ - (object['scale']) - }); - styleObject['textStyle'] = textStyle; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.LineStyleParser_ = function(node, objectStack) { - // FIXME colorMode - // FIXME gx:outerColor - // FIXME gx:outerWidth - // FIXME gx:physicalWidth - // FIXME gx:labelVisibility - var object = ol.xml.pushParseAndPop( - {}, ol.format.KML.LINE_STYLE_PARSERS_, node, objectStack); - if (!object) { - return; - } - var styleObject = objectStack[objectStack.length - 1]; - var strokeStyle = new ol.style.Stroke({ - color: /** @type {ol.Color} */ - ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_), - width: /** @type {number} */ ('width' in object ? object['width'] : 1) - }); - styleObject['strokeStyle'] = strokeStyle; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.PolyStyleParser_ = function(node, objectStack) { - // FIXME colorMode - var object = ol.xml.pushParseAndPop( - {}, ol.format.KML.POLY_STYLE_PARSERS_, node, objectStack); - if (!object) { - return; - } - var styleObject = objectStack[objectStack.length - 1]; - var fillStyle = new ol.style.Fill({ - color: /** @type {ol.Color} */ - ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_) - }); - styleObject['fillStyle'] = fillStyle; - var fill = /** @type {boolean|undefined} */ (object['fill']); - if (fill !== undefined) { - styleObject['fill'] = fill; - } - var outline = - /** @type {boolean|undefined} */ (object['outline']); - if (outline !== undefined) { - styleObject['outline'] = outline; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>} LinearRing flat coordinates. - */ -ol.format.KML.readFlatLinearRing_ = function(node, objectStack) { - return ol.xml.pushParseAndPop(null, - ol.format.KML.FLAT_LINEAR_RING_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.gxCoordParser_ = function(node, objectStack) { - var gxTrackObject = /** @type {ol.KMLGxTrackObject_} */ - (objectStack[objectStack.length - 1]); - var flatCoordinates = gxTrackObject.flatCoordinates; - var s = ol.xml.getAllTextContent(node, false); - var re = - /^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i; - var m = re.exec(s); - if (m) { - var x = parseFloat(m[1]); - var y = parseFloat(m[2]); - var z = parseFloat(m[3]); - flatCoordinates.push(x, y, z, 0); - } else { - flatCoordinates.push(0, 0, 0, 0); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.MultiLineString|undefined} MultiLineString. - */ -ol.format.KML.readGxMultiTrack_ = function(node, objectStack) { - var lineStrings = ol.xml.pushParseAndPop([], - ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_, node, objectStack); - if (!lineStrings) { - return undefined; - } - var multiLineString = new ol.geom.MultiLineString(null); - multiLineString.setLineStrings(lineStrings); - return multiLineString; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.LineString|undefined} LineString. - */ -ol.format.KML.readGxTrack_ = function(node, objectStack) { - var gxTrackObject = ol.xml.pushParseAndPop( - /** @type {ol.KMLGxTrackObject_} */ ({ - flatCoordinates: [], - whens: [] - }), ol.format.KML.GX_TRACK_PARSERS_, node, objectStack); - if (!gxTrackObject) { - return undefined; - } - var flatCoordinates = gxTrackObject.flatCoordinates; - var whens = gxTrackObject.whens; - var i, ii; - for (i = 0, ii = Math.min(flatCoordinates.length, whens.length); i < ii; - ++i) { - flatCoordinates[4 * i + 3] = whens[i]; - } - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates); - return lineString; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object} Icon object. - */ -ol.format.KML.readIcon_ = function(node, objectStack) { - var iconObject = ol.xml.pushParseAndPop( - {}, ol.format.KML.ICON_PARSERS_, node, objectStack); - if (iconObject) { - return iconObject; - } else { - return null; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<number>} Flat coordinates. - */ -ol.format.KML.readFlatCoordinatesFromNode_ = function(node, objectStack) { - return ol.xml.pushParseAndPop(null, - ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.LineString|undefined} LineString. - */ -ol.format.KML.readLineString_ = function(node, objectStack) { - var properties = ol.xml.pushParseAndPop({}, - ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, - objectStack); - var flatCoordinates = - ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - var lineString = new ol.geom.LineString(null); - lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); - lineString.setProperties(properties); - return lineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.Polygon|undefined} Polygon. - */ -ol.format.KML.readLinearRing_ = function(node, objectStack) { - var properties = ol.xml.pushParseAndPop({}, - ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, - objectStack); - var flatCoordinates = - ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - var polygon = new ol.geom.Polygon(null); - polygon.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates, - [flatCoordinates.length]); - polygon.setProperties(properties); - return polygon; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.KML.readMultiGeometry_ = function(node, objectStack) { - var geometries = ol.xml.pushParseAndPop([], - ol.format.KML.MULTI_GEOMETRY_PARSERS_, node, objectStack); - if (!geometries) { - return null; - } - if (geometries.length === 0) { - return new ol.geom.GeometryCollection(geometries); - } - /** @type {ol.geom.Geometry} */ - var multiGeometry; - var homogeneous = true; - var type = geometries[0].getType(); - var geometry, i, ii; - for (i = 1, ii = geometries.length; i < ii; ++i) { - geometry = geometries[i]; - if (geometry.getType() != type) { - homogeneous = false; - break; - } - } - if (homogeneous) { - var layout; - var flatCoordinates; - if (type == ol.geom.GeometryType.POINT) { - var point = geometries[0]; - layout = point.getLayout(); - flatCoordinates = point.getFlatCoordinates(); - for (i = 1, ii = geometries.length; i < ii; ++i) { - geometry = geometries[i]; - ol.array.extend(flatCoordinates, geometry.getFlatCoordinates()); - } - multiGeometry = new ol.geom.MultiPoint(null); - multiGeometry.setFlatCoordinates(layout, flatCoordinates); - ol.format.KML.setCommonGeometryProperties_(multiGeometry, geometries); - } else if (type == ol.geom.GeometryType.LINE_STRING) { - multiGeometry = new ol.geom.MultiLineString(null); - multiGeometry.setLineStrings(geometries); - ol.format.KML.setCommonGeometryProperties_(multiGeometry, geometries); - } else if (type == ol.geom.GeometryType.POLYGON) { - multiGeometry = new ol.geom.MultiPolygon(null); - multiGeometry.setPolygons(geometries); - ol.format.KML.setCommonGeometryProperties_(multiGeometry, geometries); - } else if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) { - multiGeometry = new ol.geom.GeometryCollection(geometries); - } else { - ol.asserts.assert(false, 37); // Unknown geometry type found - } - } else { - multiGeometry = new ol.geom.GeometryCollection(geometries); - } - return /** @type {ol.geom.Geometry} */ (multiGeometry); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.Point|undefined} Point. - */ -ol.format.KML.readPoint_ = function(node, objectStack) { - var properties = ol.xml.pushParseAndPop({}, - ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, - objectStack); - var flatCoordinates = - ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - var point = new ol.geom.Point(null); - point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); - point.setProperties(properties); - return point; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.geom.Polygon|undefined} Polygon. - */ -ol.format.KML.readPolygon_ = function(node, objectStack) { - var properties = ol.xml.pushParseAndPop(/** @type {Object<string,*>} */ ({}), - ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, - objectStack); - var flatLinearRings = ol.xml.pushParseAndPop([null], - ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack); - if (flatLinearRings && flatLinearRings[0]) { - var polygon = new ol.geom.Polygon(null); - var flatCoordinates = flatLinearRings[0]; - var ends = [flatCoordinates.length]; - var i, ii; - for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { - ol.array.extend(flatCoordinates, flatLinearRings[i]); - ends.push(flatCoordinates.length); - } - polygon.setFlatCoordinates( - ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); - polygon.setProperties(properties); - return polygon; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<ol.style.Style>} Style. - */ -ol.format.KML.readStyle_ = function(node, objectStack) { - var styleObject = ol.xml.pushParseAndPop( - {}, ol.format.KML.STYLE_PARSERS_, node, objectStack); - if (!styleObject) { - return null; - } - var fillStyle = /** @type {ol.style.Fill} */ - ('fillStyle' in styleObject ? - styleObject['fillStyle'] : ol.format.KML.DEFAULT_FILL_STYLE_); - var fill = /** @type {boolean|undefined} */ (styleObject['fill']); - if (fill !== undefined && !fill) { - fillStyle = null; - } - var imageStyle = /** @type {ol.style.Image} */ - ('imageStyle' in styleObject ? - styleObject['imageStyle'] : ol.format.KML.DEFAULT_IMAGE_STYLE_); - if (imageStyle == ol.format.KML.DEFAULT_NO_IMAGE_STYLE_) { - imageStyle = undefined; - } - var textStyle = /** @type {ol.style.Text} */ - ('textStyle' in styleObject ? - styleObject['textStyle'] : ol.format.KML.DEFAULT_TEXT_STYLE_); - var strokeStyle = /** @type {ol.style.Stroke} */ - ('strokeStyle' in styleObject ? - styleObject['strokeStyle'] : ol.format.KML.DEFAULT_STROKE_STYLE_); - var outline = /** @type {boolean|undefined} */ - (styleObject['outline']); - if (outline !== undefined && !outline) { - strokeStyle = null; - } - return [new ol.style.Style({ - fill: fillStyle, - image: imageStyle, - stroke: strokeStyle, - text: textStyle, - zIndex: undefined // FIXME - })]; -}; - - -/** - * Reads an array of geometries and creates arrays for common geometry - * properties. Then sets them to the multi geometry. - * @param {ol.geom.MultiPoint|ol.geom.MultiLineString|ol.geom.MultiPolygon} - * multiGeometry A multi-geometry. - * @param {Array.<ol.geom.Geometry>} geometries List of geometries. - * @private - */ -ol.format.KML.setCommonGeometryProperties_ = function(multiGeometry, - geometries) { - var ii = geometries.length; - var extrudes = new Array(geometries.length); - var altitudeModes = new Array(geometries.length); - var geometry, i, hasExtrude, hasAltitudeMode; - hasExtrude = hasAltitudeMode = false; - for (i = 0; i < ii; ++i) { - geometry = geometries[i]; - extrudes[i] = geometry.get('extrude'); - altitudeModes[i] = geometry.get('altitudeMode'); - hasExtrude = hasExtrude || extrudes[i] !== undefined; - hasAltitudeMode = hasAltitudeMode || altitudeModes[i]; - } - if (hasExtrude) { - multiGeometry.set('extrude', extrudes); - } - if (hasAltitudeMode) { - multiGeometry.set('altitudeMode', altitudeModes); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.DataParser_ = function(node, objectStack) { - var name = node.getAttribute('name'); - ol.xml.parseNode(ol.format.KML.DATA_PARSERS_, node, objectStack); - var featureObject = - /** @type {Object} */ (objectStack[objectStack.length - 1]); - if (name !== null) { - featureObject[name] = featureObject.value; - } else if (featureObject.displayName !== null) { - featureObject[featureObject.displayName] = featureObject.value; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.ExtendedDataParser_ = function(node, objectStack) { - ol.xml.parseNode(ol.format.KML.EXTENDED_DATA_PARSERS_, node, objectStack); -}; - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.RegionParser_ = function(node, objectStack) { - ol.xml.parseNode(ol.format.KML.REGION_PARSERS_, node, objectStack); -}; - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.PairDataParser_ = function(node, objectStack) { - var pairObject = ol.xml.pushParseAndPop( - {}, ol.format.KML.PAIR_PARSERS_, node, objectStack); - if (!pairObject) { - return; - } - var key = /** @type {string|undefined} */ - (pairObject['key']); - if (key && key == 'normal') { - var styleUrl = /** @type {string|undefined} */ - (pairObject['styleUrl']); - if (styleUrl) { - objectStack[objectStack.length - 1] = styleUrl; - } - var Style = /** @type {ol.style.Style} */ - (pairObject['Style']); - if (Style) { - objectStack[objectStack.length - 1] = Style; - } - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.PlacemarkStyleMapParser_ = function(node, objectStack) { - var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack); - if (!styleMapValue) { - return; - } - var placemarkObject = objectStack[objectStack.length - 1]; - if (Array.isArray(styleMapValue)) { - placemarkObject['Style'] = styleMapValue; - } else if (typeof styleMapValue === 'string') { - placemarkObject['styleUrl'] = styleMapValue; - } else { - ol.asserts.assert(false, 38); // `styleMapValue` has an unknown type - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.SchemaDataParser_ = function(node, objectStack) { - ol.xml.parseNode(ol.format.KML.SCHEMA_DATA_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.SimpleDataParser_ = function(node, objectStack) { - var name = node.getAttribute('name'); - if (name !== null) { - var data = ol.format.XSD.readString(node); - var featureObject = - /** @type {Object} */ (objectStack[objectStack.length - 1]); - featureObject[name] = data; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.LatLonAltBoxParser_ = function(node, objectStack) { - var object = ol.xml.pushParseAndPop({}, ol.format.KML.LAT_LON_ALT_BOX_PARSERS_, node, objectStack); - if (!object) { - return; - } - var regionObject = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var extent = [ - parseFloat(object['west']), - parseFloat(object['south']), - parseFloat(object['east']), - parseFloat(object['north']) - ]; - regionObject['extent'] = extent; - regionObject['altitudeMode'] = object['altitudeMode']; - regionObject['minAltitude'] = parseFloat(object['minAltitude']); - regionObject['maxAltitude'] = parseFloat(object['maxAltitude']); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.LodParser_ = function(node, objectStack) { - var object = ol.xml.pushParseAndPop({}, ol.format.KML.LOD_PARSERS_, node, objectStack); - if (!object) { - return; - } - var lodObject = /** @type {Object} */ (objectStack[objectStack.length - 1]); - lodObject['minLodPixels'] = parseFloat(object['minLodPixels']); - lodObject['maxLodPixels'] = parseFloat(object['maxLodPixels']); - lodObject['minFadeExtent'] = parseFloat(object['minFadeExtent']); - lodObject['maxFadeExtent'] = parseFloat(object['maxFadeExtent']); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.innerBoundaryIsParser_ = function(node, objectStack) { - /** @type {Array.<number>|undefined} */ - var flatLinearRing = ol.xml.pushParseAndPop(undefined, - ol.format.KML.INNER_BOUNDARY_IS_PARSERS_, node, objectStack); - if (flatLinearRing) { - var flatLinearRings = /** @type {Array.<Array.<number>>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings.push(flatLinearRing); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.outerBoundaryIsParser_ = function(node, objectStack) { - /** @type {Array.<number>|undefined} */ - var flatLinearRing = ol.xml.pushParseAndPop(undefined, - ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_, node, objectStack); - if (flatLinearRing) { - var flatLinearRings = /** @type {Array.<Array.<number>>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings[0] = flatLinearRing; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.LinkParser_ = function(node, objectStack) { - ol.xml.parseNode(ol.format.KML.LINK_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.whenParser_ = function(node, objectStack) { - var gxTrackObject = /** @type {ol.KMLGxTrackObject_} */ - (objectStack[objectStack.length - 1]); - var whens = gxTrackObject.whens; - var s = ol.xml.getAllTextContent(node, false); - var when = Date.parse(s); - whens.push(isNaN(when) ? 0 : when); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.DATA_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'displayName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'value': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.EXTENDED_DATA_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Data': ol.format.KML.DataParser_, - 'SchemaData': ol.format.KML.SchemaDataParser_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.REGION_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'LatLonAltBox': ol.format.KML.LatLonAltBoxParser_, - 'Lod': ol.format.KML.LodParser_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.LAT_LON_ALT_BOX_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'altitudeMode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'minAltitude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'maxAltitude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'north': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'south': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'east': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'west': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.LOD_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'minLodPixels': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'maxLodPixels': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'minFadeExtent': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'maxFadeExtent': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'extrude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), - 'altitudeMode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.FLAT_LINEAR_RING_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'innerBoundaryIs': ol.format.KML.innerBoundaryIsParser_, - 'outerBoundaryIs': ol.format.KML.outerBoundaryIsParser_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.GX_TRACK_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'when': ol.format.KML.whenParser_ - }, ol.xml.makeStructureNS( - ol.format.KML.GX_NAMESPACE_URIS_, { - 'coord': ol.format.KML.gxCoordParser_ - })); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.ICON_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_) - }, ol.xml.makeStructureNS( - ol.format.KML.GX_NAMESPACE_URIS_, { - 'x': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'y': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'w': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'h': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal) - })); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.ICON_STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Icon': ol.xml.makeObjectPropertySetter(ol.format.KML.readIcon_), - 'heading': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), - 'hotSpot': ol.xml.makeObjectPropertySetter(ol.format.KML.readVec2_), - 'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.INNER_BOUNDARY_IS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.LABEL_STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_), - 'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.LINE_STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_), - 'width': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.MULTI_GEOMETRY_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'LineString': ol.xml.makeArrayPusher(ol.format.KML.readLineString_), - 'LinearRing': ol.xml.makeArrayPusher(ol.format.KML.readLinearRing_), - 'MultiGeometry': ol.xml.makeArrayPusher(ol.format.KML.readMultiGeometry_), - 'Point': ol.xml.makeArrayPusher(ol.format.KML.readPoint_), - 'Polygon': ol.xml.makeArrayPusher(ol.format.KML.readPolygon_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.GX_NAMESPACE_URIS_, { - 'Track': ol.xml.makeArrayPusher(ol.format.KML.readGxTrack_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.NETWORK_LINK_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'ExtendedData': ol.format.KML.ExtendedDataParser_, - 'Region': ol.format.KML.RegionParser_, - 'Link': ol.format.KML.LinkParser_, - 'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), - 'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.LINK_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.PAIR_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_), - 'key': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.PLACEMARK_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'ExtendedData': ol.format.KML.ExtendedDataParser_, - 'Region': ol.format.KML.RegionParser_, - 'MultiGeometry': ol.xml.makeObjectPropertySetter( - ol.format.KML.readMultiGeometry_, 'geometry'), - 'LineString': ol.xml.makeObjectPropertySetter( - ol.format.KML.readLineString_, 'geometry'), - 'LinearRing': ol.xml.makeObjectPropertySetter( - ol.format.KML.readLinearRing_, 'geometry'), - 'Point': ol.xml.makeObjectPropertySetter( - ol.format.KML.readPoint_, 'geometry'), - 'Polygon': ol.xml.makeObjectPropertySetter( - ol.format.KML.readPolygon_, 'geometry'), - 'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_), - 'StyleMap': ol.format.KML.PlacemarkStyleMapParser_, - 'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), - 'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_), - 'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean) - }, ol.xml.makeStructureNS( - ol.format.KML.GX_NAMESPACE_URIS_, { - 'MultiTrack': ol.xml.makeObjectPropertySetter( - ol.format.KML.readGxMultiTrack_, 'geometry'), - 'Track': ol.xml.makeObjectPropertySetter( - ol.format.KML.readGxTrack_, 'geometry') - } - )); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.POLY_STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_), - 'fill': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), - 'outline': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.SCHEMA_DATA_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'SimpleData': ol.format.KML.SimpleDataParser_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'IconStyle': ol.format.KML.IconStyleParser_, - 'LabelStyle': ol.format.KML.LabelStyleParser_, - 'LineStyle': ol.format.KML.LineStyleParser_, - 'PolyStyle': ol.format.KML.PolyStyleParser_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.KML.STYLE_MAP_PARSERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Pair': ol.format.KML.PairDataParser_ - }); - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<ol.Feature>|undefined} Features. - */ -ol.format.KML.prototype.readDocumentOrFolder_ = function(node, objectStack) { - // FIXME use scope somehow - var parsersNS = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Document': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this), - 'Folder': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this), - 'Placemark': ol.xml.makeArrayPusher(this.readPlacemark_, this), - 'Style': this.readSharedStyle_.bind(this), - 'StyleMap': this.readSharedStyleMap_.bind(this) - }); - /** @type {Array.<ol.Feature>} */ - var features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack, this); - if (features) { - return features; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {ol.Feature|undefined} Feature. - */ -ol.format.KML.prototype.readPlacemark_ = function(node, objectStack) { - var object = ol.xml.pushParseAndPop({'geometry': null}, - ol.format.KML.PLACEMARK_PARSERS_, node, objectStack); - if (!object) { - return undefined; - } - var feature = new ol.Feature(); - var id = node.getAttribute('id'); - if (id !== null) { - feature.setId(id); - } - var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); - - var geometry = object['geometry']; - if (geometry) { - ol.format.Feature.transformWithOptions(geometry, false, options); - } - feature.setGeometry(geometry); - delete object['geometry']; - - if (this.extractStyles_) { - var style = object['Style']; - var styleUrl = object['styleUrl']; - var styleFunction = ol.format.KML.createFeatureStyleFunction_( - style, styleUrl, this.defaultStyle_, this.sharedStyles_, - this.showPointNames_); - feature.setStyle(styleFunction); - } - delete object['Style']; - // we do not remove the styleUrl property from the object, so it - // gets stored on feature when setProperties is called - - feature.setProperties(object); - - return feature; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.prototype.readSharedStyle_ = function(node, objectStack) { - var id = node.getAttribute('id'); - if (id !== null) { - var style = ol.format.KML.readStyle_(node, objectStack); - if (style) { - var styleUri; - if (node.baseURI && node.baseURI !== 'about:blank') { - var url = new URL('#' + id, node.baseURI); - styleUri = url.href; - } else { - styleUri = '#' + id; - } - this.sharedStyles_[styleUri] = style; - } - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.prototype.readSharedStyleMap_ = function(node, objectStack) { - var id = node.getAttribute('id'); - if (id === null) { - return; - } - var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack); - if (!styleMapValue) { - return; - } - var styleUri; - if (node.baseURI && node.baseURI !== 'about:blank') { - var url = new URL('#' + id, node.baseURI); - styleUri = url.href; - } else { - styleUri = '#' + id; - } - this.sharedStyles_[styleUri] = styleMapValue; -}; - - -/** - * Read the first feature from a KML source. MultiGeometries are converted into - * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ - * MultiLineString/MultiPolygon if they are all of the same type. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.KML.prototype.readFeature; - - -/** - * @inheritDoc - */ -ol.format.KML.prototype.readFeatureFromNode = function(node, opt_options) { - if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) { - return null; - } - var feature = this.readPlacemark_( - node, [this.getReadOptions(node, opt_options)]); - if (feature) { - return feature; - } else { - return null; - } -}; - - -/** - * Read all features from a KML source. MultiGeometries are converted into - * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ - * MultiLineString/MultiPolygon if they are all of the same type. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.KML.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.KML.prototype.readFeaturesFromNode = function(node, opt_options) { - if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) { - return []; - } - var features; - var localName = node.localName; - if (localName == 'Document' || localName == 'Folder') { - features = this.readDocumentOrFolder_( - node, [this.getReadOptions(node, opt_options)]); - if (features) { - return features; - } else { - return []; - } - } else if (localName == 'Placemark') { - var feature = this.readPlacemark_( - node, [this.getReadOptions(node, opt_options)]); - if (feature) { - return [feature]; - } else { - return []; - } - } else if (localName == 'kml') { - features = []; - var n; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - var fs = this.readFeaturesFromNode(n, opt_options); - if (fs) { - ol.array.extend(features, fs); - } - } - return features; - } else { - return []; - } -}; - - -/** - * Read the name of the KML. - * - * @param {Document|Node|string} source Souce. - * @return {string|undefined} Name. - * @api - */ -ol.format.KML.prototype.readName = function(source) { - if (ol.xml.isDocument(source)) { - return this.readNameFromDocument(/** @type {Document} */ (source)); - } else if (ol.xml.isNode(source)) { - return this.readNameFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readNameFromDocument(doc); - } else { - return undefined; - } -}; - - -/** - * @param {Document} doc Document. - * @return {string|undefined} Name. - */ -ol.format.KML.prototype.readNameFromDocument = function(doc) { - var n; - for (n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - var name = this.readNameFromNode(n); - if (name) { - return name; - } - } - } - return undefined; -}; - - -/** - * @param {Node} node Node. - * @return {string|undefined} Name. - */ -ol.format.KML.prototype.readNameFromNode = function(node) { - var n; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && - n.localName == 'name') { - return ol.format.XSD.readString(n); - } - } - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - var localName = n.localName; - if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && - (localName == 'Document' || - localName == 'Folder' || - localName == 'Placemark' || - localName == 'kml')) { - var name = this.readNameFromNode(n); - if (name) { - return name; - } - } - } - return undefined; -}; - - -/** - * Read the network links of the KML. - * - * @param {Document|Node|string} source Source. - * @return {Array.<Object>} Network links. - * @api - */ -ol.format.KML.prototype.readNetworkLinks = function(source) { - var networkLinks = []; - if (ol.xml.isDocument(source)) { - ol.array.extend(networkLinks, this.readNetworkLinksFromDocument( - /** @type {Document} */ (source))); - } else if (ol.xml.isNode(source)) { - ol.array.extend(networkLinks, this.readNetworkLinksFromNode( - /** @type {Node} */ (source))); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - ol.array.extend(networkLinks, this.readNetworkLinksFromDocument(doc)); - } - return networkLinks; -}; - - -/** - * @param {Document} doc Document. - * @return {Array.<Object>} Network links. - */ -ol.format.KML.prototype.readNetworkLinksFromDocument = function(doc) { - var n, networkLinks = []; - for (n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - ol.array.extend(networkLinks, this.readNetworkLinksFromNode(n)); - } - } - return networkLinks; -}; - - -/** - * @param {Node} node Node. - * @return {Array.<Object>} Network links. - */ -ol.format.KML.prototype.readNetworkLinksFromNode = function(node) { - var n, networkLinks = []; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && - n.localName == 'NetworkLink') { - var obj = ol.xml.pushParseAndPop({}, ol.format.KML.NETWORK_LINK_PARSERS_, - n, []); - networkLinks.push(obj); - } - } - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - var localName = n.localName; - if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && - (localName == 'Document' || - localName == 'Folder' || - localName == 'kml')) { - ol.array.extend(networkLinks, this.readNetworkLinksFromNode(n)); - } - } - return networkLinks; -}; - - -/** - * Read the regions of the KML. - * - * @param {Document|Node|string} source Source. - * @return {Array.<Object>} Regions. - * @api - */ -ol.format.KML.prototype.readRegion = function(source) { - var regions = []; - if (ol.xml.isDocument(source)) { - ol.array.extend(regions, this.readRegionFromDocument( - /** @type {Document} */ (source))); - } else if (ol.xml.isNode(source)) { - ol.array.extend(regions, this.readRegionFromNode( - /** @type {Node} */ (source))); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - ol.array.extend(regions, this.readRegionFromDocument(doc)); - } - return regions; -}; - - -/** - * @param {Document} doc Document. - * @return {Array.<Object>} Region. - */ -ol.format.KML.prototype.readRegionFromDocument = function(doc) { - var n, regions = []; - for (n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - ol.array.extend(regions, this.readRegionFromNode(n)); - } - } - return regions; -}; - - -/** - * @param {Node} node Node. - * @return {Array.<Object>} Region. - * @api - */ -ol.format.KML.prototype.readRegionFromNode = function(node) { - var n, regions = []; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && - n.localName == 'Region') { - var obj = ol.xml.pushParseAndPop({}, ol.format.KML.REGION_PARSERS_, - n, []); - regions.push(obj); - } - } - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - var localName = n.localName; - if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && - (localName == 'Document' || - localName == 'Folder' || - localName == 'kml')) { - ol.array.extend(regions, this.readRegionFromNode(n)); - } - } - return regions; -}; - - -/** - * Read the projection from a KML source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.KML.prototype.readProjection; - - -/** - * @param {Node} node Node to append a TextNode with the color to. - * @param {ol.Color|string} color Color. - * @private - */ -ol.format.KML.writeColorTextNode_ = function(node, color) { - var rgba = ol.color.asArray(color); - var opacity = (rgba.length == 4) ? rgba[3] : 1; - var abgr = [opacity * 255, rgba[2], rgba[1], rgba[0]]; - var i; - for (i = 0; i < 4; ++i) { - var hex = parseInt(abgr[i], 10).toString(16); - abgr[i] = (hex.length == 1) ? '0' + hex : hex; - } - ol.format.XSD.writeStringTextNode(node, abgr.join('')); -}; - - -/** - * @param {Node} node Node to append a TextNode with the coordinates to. - * @param {Array.<number>} coordinates Coordinates. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeCoordinatesTextNode_ = function(node, coordinates, objectStack) { - var context = objectStack[objectStack.length - 1]; - - var layout = context['layout']; - var stride = context['stride']; - - var dimension; - if (layout == ol.geom.GeometryLayout.XY || - layout == ol.geom.GeometryLayout.XYM) { - dimension = 2; - } else if (layout == ol.geom.GeometryLayout.XYZ || - layout == ol.geom.GeometryLayout.XYZM) { - dimension = 3; - } else { - ol.asserts.assert(false, 34); // Invalid geometry layout - } - - var d, i; - var ii = coordinates.length; - var text = ''; - if (ii > 0) { - text += coordinates[0]; - for (d = 1; d < dimension; ++d) { - text += ',' + coordinates[d]; - } - for (i = stride; i < ii; i += stride) { - text += ' ' + coordinates[i]; - for (d = 1; d < dimension; ++d) { - text += ',' + coordinates[i + d]; - } - } - } - ol.format.XSD.writeStringTextNode(node, text); -}; - - -/** - * @param {Node} node Node. - * @param {{name: *, value: *}} pair Name value pair. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeDataNode_ = function(node, pair, objectStack) { - node.setAttribute('name', pair.name); - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var value = pair.value; - - if (typeof value == 'object') { - if (value !== null && value.displayName) { - ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, [value.displayName], objectStack, ['displayName']); - } - - if (value !== null && value.value) { - ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, [value.value], objectStack, ['value']); - } - } else { - ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, [value], objectStack, ['value']); - } -}; - - -/** - * @param {Node} node Node to append a TextNode with the name to. - * @param {string} name DisplayName. - * @private - */ -ol.format.KML.writeDataNodeName_ = function(node, name) { - ol.format.XSD.writeCDATASection(node, name); -}; - - -/** - * @param {Node} node Node to append a CDATA Section with the value to. - * @param {string} value Value. - * @private - */ -ol.format.KML.writeDataNodeValue_ = function(node, value) { - ol.format.XSD.writeStringTextNode(node, value); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<ol.Feature>} features Features. - * @param {Array.<*>} objectStack Object stack. - * @this {ol.format.KML} - * @private - */ -ol.format.KML.writeDocument_ = function(node, features, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - ol.xml.pushSerializeAndPop(context, ol.format.KML.DOCUMENT_SERIALIZERS_, - ol.format.KML.DOCUMENT_NODE_FACTORY_, features, objectStack, undefined, - this); -}; - - -/** - * @param {Node} node Node. - * @param {{names: Array<string>, values: (Array<*>)}} namesAndValues Names and values. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeExtendedData_ = function(node, namesAndValues, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var names = namesAndValues.names, values = namesAndValues.values; - var length = names.length; - - for (var i = 0; i < length; i++) { - ol.xml.pushSerializeAndPop(context, ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_, - ol.format.KML.DATA_NODE_FACTORY_, [{name: names[i], value: values[i]}], objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {Object} icon Icon object. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeIcon_ = function(node, icon, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.KML.ICON_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(icon, orderedKeys); - ol.xml.pushSerializeAndPop(context, - ol.format.KML.ICON_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, - values, objectStack, orderedKeys); - orderedKeys = - ol.format.KML.ICON_SEQUENCE_[ol.format.KML.GX_NAMESPACE_URIS_[0]]; - values = ol.xml.makeSequence(icon, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_SERIALIZERS_, - ol.format.KML.GX_NODE_FACTORY_, values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.style.Icon} style Icon style. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeIconStyle_ = function(node, style, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var properties = {}; - var src = style.getSrc(); - var size = style.getSize(); - var iconImageSize = style.getImageSize(); - var iconProperties = { - 'href': src - }; - - if (size) { - iconProperties['w'] = size[0]; - iconProperties['h'] = size[1]; - var anchor = style.getAnchor(); // top-left - var origin = style.getOrigin(); // top-left - - if (origin && iconImageSize && origin[0] !== 0 && origin[1] !== size[1]) { - iconProperties['x'] = origin[0]; - iconProperties['y'] = iconImageSize[1] - (origin[1] + size[1]); - } - - if (anchor && anchor[0] !== 0 && anchor[1] !== size[1]) { - var /** @type {ol.KMLVec2_} */ hotSpot = { - x: anchor[0], - xunits: ol.style.IconAnchorUnits.PIXELS, - y: size[1] - anchor[1], - yunits: ol.style.IconAnchorUnits.PIXELS - }; - properties['hotSpot'] = hotSpot; - } - } - - properties['Icon'] = iconProperties; - - var scale = style.getScale(); - if (scale !== 1) { - properties['scale'] = scale; - } - - var rotation = style.getRotation(); - if (rotation !== 0) { - properties['heading'] = rotation; // 0-360 - } - - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.KML.ICON_STYLE_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_STYLE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.style.Text} style style. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeLabelStyle_ = function(node, style, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var properties = {}; - var fill = style.getFill(); - if (fill) { - properties['color'] = fill.getColor(); - } - var scale = style.getScale(); - if (scale && scale !== 1) { - properties['scale'] = scale; - } - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = - ol.format.KML.LABEL_STYLE_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.LABEL_STYLE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.style.Stroke} style style. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeLineStyle_ = function(node, style, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var properties = { - 'color': style.getColor(), - 'width': style.getWidth() - }; - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.KML.LINE_STYLE_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.LINE_STYLE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Geometry} geometry Geometry. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeMultiGeometry_ = function(node, geometry, objectStack) { - /** @type {ol.XmlNodeStackItem} */ - var context = {node: node}; - var type = geometry.getType(); - /** @type {Array.<ol.geom.Geometry>} */ - var geometries; - /** @type {function(*, Array.<*>, string=): (Node|undefined)} */ - var factory; - if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) { - geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries(); - factory = ol.format.KML.GEOMETRY_NODE_FACTORY_; - } else if (type == ol.geom.GeometryType.MULTI_POINT) { - geometries = /** @type {ol.geom.MultiPoint} */ (geometry).getPoints(); - factory = ol.format.KML.POINT_NODE_FACTORY_; - } else if (type == ol.geom.GeometryType.MULTI_LINE_STRING) { - geometries = - (/** @type {ol.geom.MultiLineString} */ (geometry)).getLineStrings(); - factory = ol.format.KML.LINE_STRING_NODE_FACTORY_; - } else if (type == ol.geom.GeometryType.MULTI_POLYGON) { - geometries = - (/** @type {ol.geom.MultiPolygon} */ (geometry)).getPolygons(); - factory = ol.format.KML.POLYGON_NODE_FACTORY_; - } else { - ol.asserts.assert(false, 39); // Unknown geometry type - } - ol.xml.pushSerializeAndPop(context, - ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_, factory, - geometries, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.LinearRing} linearRing Linear ring. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeBoundaryIs_ = function(node, linearRing, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - ol.xml.pushSerializeAndPop(context, - ol.format.KML.BOUNDARY_IS_SERIALIZERS_, - ol.format.KML.LINEAR_RING_NODE_FACTORY_, [linearRing], objectStack); -}; - - -/** - * FIXME currently we do serialize arbitrary/custom feature properties - * (ExtendedData). - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Object stack. - * @this {ol.format.KML} - * @private - */ -ol.format.KML.writePlacemark_ = function(node, feature, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - - // set id - if (feature.getId()) { - node.setAttribute('id', feature.getId()); - } - - // serialize properties (properties unknown to KML are not serialized) - var properties = feature.getProperties(); - - // don't export these to ExtendedData - var filter = {'address': 1, 'description': 1, 'name': 1, 'open': 1, - 'phoneNumber': 1, 'styleUrl': 1, 'visibility': 1}; - filter[feature.getGeometryName()] = 1; - var keys = Object.keys(properties || {}).sort().filter(function(v) { - return !filter[v]; - }); - - if (keys.length > 0) { - var sequence = ol.xml.makeSequence(properties, keys); - var namesAndValues = {names: keys, values: sequence}; - ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, - ol.format.KML.EXTENDEDDATA_NODE_FACTORY_, [namesAndValues], objectStack); - } - - var styleFunction = feature.getStyleFunction(); - if (styleFunction) { - // FIXME the styles returned by the style function are supposed to be - // resolution-independent here - var styles = styleFunction.call(feature, 0); - if (styles) { - var style = Array.isArray(styles) ? styles[0] : styles; - if (this.writeStyles_) { - properties['Style'] = style; - } - var textStyle = style.getText(); - if (textStyle) { - properties['name'] = textStyle.getText(); - } - } - } - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.KML.PLACEMARK_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); - - // serialize geometry - var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); - var geometry = feature.getGeometry(); - if (geometry) { - geometry = - ol.format.Feature.transformWithOptions(geometry, true, options); - } - ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, - ol.format.KML.GEOMETRY_NODE_FACTORY_, [geometry], objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.SimpleGeometry} geometry Geometry. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writePrimitiveGeometry_ = function(node, geometry, objectStack) { - var flatCoordinates = geometry.getFlatCoordinates(); - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - context['layout'] = geometry.getLayout(); - context['stride'] = geometry.getStride(); - ol.xml.pushSerializeAndPop(context, - ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_, - ol.format.KML.COORDINATES_NODE_FACTORY_, - [flatCoordinates], objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.geom.Polygon} polygon Polygon. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writePolygon_ = function(node, polygon, objectStack) { - var linearRings = polygon.getLinearRings(); - var outerRing = linearRings.shift(); - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - // inner rings - ol.xml.pushSerializeAndPop(context, - ol.format.KML.POLYGON_SERIALIZERS_, - ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_, - linearRings, objectStack); - // outer ring - ol.xml.pushSerializeAndPop(context, - ol.format.KML.POLYGON_SERIALIZERS_, - ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_, - [outerRing], objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.style.Fill} style Style. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writePolyStyle_ = function(node, style, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - ol.xml.pushSerializeAndPop(context, ol.format.KML.POLY_STYLE_SERIALIZERS_, - ol.format.KML.COLOR_NODE_FACTORY_, [style.getColor()], objectStack); -}; - - -/** - * @param {Node} node Node to append a TextNode with the scale to. - * @param {number|undefined} scale Scale. - * @private - */ -ol.format.KML.writeScaleTextNode_ = function(node, scale) { - // the Math is to remove any excess decimals created by float arithmetic - ol.format.XSD.writeDecimalTextNode(node, - Math.round(scale * 1e6) / 1e6); -}; - - -/** - * @param {Node} node Node. - * @param {ol.style.Style} style Style. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.KML.writeStyle_ = function(node, style, objectStack) { - var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; - var properties = {}; - var fillStyle = style.getFill(); - var strokeStyle = style.getStroke(); - var imageStyle = style.getImage(); - var textStyle = style.getText(); - if (imageStyle instanceof ol.style.Icon) { - properties['IconStyle'] = imageStyle; - } - if (textStyle) { - properties['LabelStyle'] = textStyle; - } - if (strokeStyle) { - properties['LineStyle'] = strokeStyle; - } - if (fillStyle) { - properties['PolyStyle'] = fillStyle; - } - var parentNode = objectStack[objectStack.length - 1].node; - var orderedKeys = ol.format.KML.STYLE_SEQUENCE_[parentNode.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.STYLE_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); -}; - - -/** - * @param {Node} node Node to append a TextNode with the Vec2 to. - * @param {ol.KMLVec2_} vec2 Vec2. - * @private - */ -ol.format.KML.writeVec2_ = function(node, vec2) { - node.setAttribute('x', vec2.x); - node.setAttribute('y', vec2.y); - node.setAttribute('xunits', vec2.xunits); - node.setAttribute('yunits', vec2.yunits); -}; - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.KML_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'Document', 'Placemark' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.KML_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Document': ol.xml.makeChildAppender(ol.format.KML.writeDocument_), - 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.DOCUMENT_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.EXTENDEDDATA_NODE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Data': ol.xml.makeChildAppender(ol.format.KML.writeDataNode_), - 'value': ol.xml.makeChildAppender(ol.format.KML.writeDataNodeValue_), - 'displayName': ol.xml.makeChildAppender(ol.format.KML.writeDataNodeName_) - }); - - -/** - * @const - * @type {Object.<string, string>} - * @private - */ -ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_ = { - 'Point': 'Point', - 'LineString': 'LineString', - 'LinearRing': 'LinearRing', - 'Polygon': 'Polygon', - 'MultiPoint': 'MultiGeometry', - 'MultiLineString': 'MultiGeometry', - 'MultiPolygon': 'MultiGeometry', - 'GeometryCollection': 'MultiGeometry' -}; - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.ICON_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'href' - ], - ol.xml.makeStructureNS(ol.format.KML.GX_NAMESPACE_URIS_, [ - 'x', 'y', 'w', 'h' - ])); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.ICON_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'href': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) - }, ol.xml.makeStructureNS( - ol.format.KML.GX_NAMESPACE_URIS_, { - 'x': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'y': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'w': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'h': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode) - })); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.ICON_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'scale', 'heading', 'Icon', 'hotSpot' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.ICON_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'Icon': ol.xml.makeChildAppender(ol.format.KML.writeIcon_), - 'heading': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), - 'hotSpot': ol.xml.makeChildAppender(ol.format.KML.writeVec2_), - 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.LABEL_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'color', 'scale' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.LABEL_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_), - 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.LINE_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'color', 'width' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.LINE_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_), - 'width': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.BOUNDARY_IS_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'LinearRing': ol.xml.makeChildAppender( - ol.format.KML.writePrimitiveGeometry_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'LineString': ol.xml.makeChildAppender( - ol.format.KML.writePrimitiveGeometry_), - 'Point': ol.xml.makeChildAppender( - ol.format.KML.writePrimitiveGeometry_), - 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_), - 'GeometryCollection': ol.xml.makeChildAppender( - ol.format.KML.writeMultiGeometry_) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.PLACEMARK_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'name', 'open', 'visibility', 'address', 'phoneNumber', 'description', - 'styleUrl', 'Style' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.PLACEMARK_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'ExtendedData': ol.xml.makeChildAppender( - ol.format.KML.writeExtendedData_), - 'MultiGeometry': ol.xml.makeChildAppender( - ol.format.KML.writeMultiGeometry_), - 'LineString': ol.xml.makeChildAppender( - ol.format.KML.writePrimitiveGeometry_), - 'LinearRing': ol.xml.makeChildAppender( - ol.format.KML.writePrimitiveGeometry_), - 'Point': ol.xml.makeChildAppender( - ol.format.KML.writePrimitiveGeometry_), - 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_), - 'Style': ol.xml.makeChildAppender(ol.format.KML.writeStyle_), - 'address': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'description': ol.xml.makeChildAppender( - ol.format.XSD.writeStringTextNode), - 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'open': ol.xml.makeChildAppender(ol.format.XSD.writeBooleanTextNode), - 'phoneNumber': ol.xml.makeChildAppender( - ol.format.XSD.writeStringTextNode), - 'styleUrl': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), - 'visibility': ol.xml.makeChildAppender( - ol.format.XSD.writeBooleanTextNode) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'coordinates': ol.xml.makeChildAppender( - ol.format.KML.writeCoordinatesTextNode_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.POLYGON_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'outerBoundaryIs': ol.xml.makeChildAppender( - ol.format.KML.writeBoundaryIs_), - 'innerBoundaryIs': ol.xml.makeChildAppender( - ol.format.KML.writeBoundaryIs_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.POLY_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_) - }); - - -/** - * @const - * @type {Object.<string, Array.<string>>} - * @private - */ -ol.format.KML.STYLE_SEQUENCE_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, [ - 'IconStyle', 'LabelStyle', 'LineStyle', 'PolyStyle' - ]); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.KML.STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( - ol.format.KML.NAMESPACE_URIS_, { - 'IconStyle': ol.xml.makeChildAppender(ol.format.KML.writeIconStyle_), - 'LabelStyle': ol.xml.makeChildAppender(ol.format.KML.writeLabelStyle_), - 'LineStyle': ol.xml.makeChildAppender(ol.format.KML.writeLineStyle_), - 'PolyStyle': ol.xml.makeChildAppender(ol.format.KML.writePolyStyle_) - }); - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.KML.GX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - return ol.xml.createElementNS(ol.format.KML.GX_NAMESPACE_URIS_[0], - 'gx:' + opt_nodeName); -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.KML.DOCUMENT_NODE_FACTORY_ = function(value, objectStack, - opt_nodeName) { - var parentNode = objectStack[objectStack.length - 1].node; - return ol.xml.createElementNS(parentNode.namespaceURI, 'Placemark'); -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -ol.format.KML.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, - opt_nodeName) { - if (value) { - var parentNode = objectStack[objectStack.length - 1].node; - return ol.xml.createElementNS(parentNode.namespaceURI, - ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_[/** @type {ol.geom.Geometry} */ (value).getType()]); - } -}; - - -/** - * A factory for creating coordinates nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.COLOR_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('color'); - - -/** - * A factory for creating coordinates nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.COORDINATES_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('coordinates'); - - -/** - * A factory for creating Data nodes. - * @const - * @type {function(*, Array.<*>): (Node|undefined)} - * @private - */ -ol.format.KML.DATA_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('Data'); - - -/** - * A factory for creating ExtendedData nodes. - * @const - * @type {function(*, Array.<*>): (Node|undefined)} - * @private - */ -ol.format.KML.EXTENDEDDATA_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('ExtendedData'); - - -/** - * A factory for creating innerBoundaryIs nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('innerBoundaryIs'); - - -/** - * A factory for creating Point nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.POINT_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('Point'); - - -/** - * A factory for creating LineString nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.LINE_STRING_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('LineString'); - - -/** - * A factory for creating LinearRing nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.LINEAR_RING_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('LinearRing'); - - -/** - * A factory for creating Polygon nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.POLYGON_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('Polygon'); - - -/** - * A factory for creating outerBoundaryIs nodes. - * @const - * @type {function(*, Array.<*>, string=): (Node|undefined)} - * @private - */ -ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_ = - ol.xml.makeSimpleNodeFactory('outerBoundaryIs'); - - -/** - * Encode an array of features in the KML format. GeometryCollections, MultiPoints, - * MultiLineStrings, and MultiPolygons are output as MultiGeometries. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {string} Result. - * @api - */ -ol.format.KML.prototype.writeFeatures; - - -/** - * Encode an array of features in the KML format as an XML node. GeometryCollections, - * MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries. - * - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -ol.format.KML.prototype.writeFeaturesNode = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - var kml = ol.xml.createElementNS(ol.format.KML.NAMESPACE_URIS_[4], 'kml'); - var xmlnsUri = 'http://www.w3.org/2000/xmlns/'; - var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance'; - ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:gx', - ol.format.KML.GX_NAMESPACE_URIS_[0]); - ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri); - ol.xml.setAttributeNS(kml, xmlSchemaInstanceUri, 'xsi:schemaLocation', - ol.format.KML.SCHEMA_LOCATION_); - - var /** @type {ol.XmlNodeStackItem} */ context = {node: kml}; - var properties = {}; - if (features.length > 1) { - properties['Document'] = features; - } else if (features.length == 1) { - properties['Placemark'] = features[0]; - } - var orderedKeys = ol.format.KML.KML_SEQUENCE_[kml.namespaceURI]; - var values = ol.xml.makeSequence(properties, orderedKeys); - ol.xml.pushSerializeAndPop(context, ol.format.KML.KML_SERIALIZERS_, - ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys, - this); - return kml; -}; - - -/** - * @fileoverview - * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} - */ -goog.provide('ol.ext.PBF'); - -/** @typedef {function(*)} */ -ol.ext.PBF = function() {}; - -(function() {(function (exports) { -'use strict'; - -var read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m; - var eLen = nBytes * 8 - mLen - 1; - var eMax = (1 << eLen) - 1; - var eBias = eMax >> 1; - var nBits = -7; - var i = isLE ? (nBytes - 1) : 0; - var d = isLE ? -1 : 1; - var s = buffer[offset + i]; - i += d; - e = s & ((1 << (-nBits)) - 1); - s >>= (-nBits); - nBits += eLen; - for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} - m = e & ((1 << (-nBits)) - 1); - e >>= (-nBits); - nBits += mLen; - for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} - if (e === 0) { - e = 1 - eBias; - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen); - e = e - eBias; - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -}; -var write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c; - var eLen = nBytes * 8 - mLen - 1; - var eMax = (1 << eLen) - 1; - var eBias = eMax >> 1; - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0); - var i = isLE ? 0 : (nBytes - 1); - var d = isLE ? 1 : -1; - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; - value = Math.abs(value); - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0; - e = eMax; - } else { - e = Math.floor(Math.log(value) / Math.LN2); - if (value * (c = Math.pow(2, -e)) < 1) { - e--; - c *= 2; - } - if (e + eBias >= 1) { - value += rt / c; - } else { - value += rt * Math.pow(2, 1 - eBias); - } - if (value * c >= 2) { - e++; - c /= 2; - } - if (e + eBias >= eMax) { - m = 0; - e = eMax; - } else if (e + eBias >= 1) { - m = (value * c - 1) * Math.pow(2, mLen); - e = e + eBias; - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); - e = 0; - } - } - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - e = (e << mLen) | m; - eLen += mLen; - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - buffer[offset + i - d] |= s * 128; -}; -var index$2 = { - read: read, - write: write -}; - -var index = Pbf; -function Pbf(buf) { - this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0); - this.pos = 0; - this.type = 0; - this.length = this.buf.length; -} -Pbf.Varint = 0; -Pbf.Fixed64 = 1; -Pbf.Bytes = 2; -Pbf.Fixed32 = 5; -var SHIFT_LEFT_32 = (1 << 16) * (1 << 16); -var SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; -Pbf.prototype = { - destroy: function() { - this.buf = null; - }, - readFields: function(readField, result, end) { - end = end || this.length; - while (this.pos < end) { - var val = this.readVarint(), - tag = val >> 3, - startPos = this.pos; - this.type = val & 0x7; - readField(tag, result, this); - if (this.pos === startPos) this.skip(val); - } - return result; - }, - readMessage: function(readField, result) { - return this.readFields(readField, result, this.readVarint() + this.pos); - }, - readFixed32: function() { - var val = readUInt32(this.buf, this.pos); - this.pos += 4; - return val; - }, - readSFixed32: function() { - var val = readInt32(this.buf, this.pos); - this.pos += 4; - return val; - }, - readFixed64: function() { - var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; - this.pos += 8; - return val; - }, - readSFixed64: function() { - var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; - this.pos += 8; - return val; - }, - readFloat: function() { - var val = index$2.read(this.buf, this.pos, true, 23, 4); - this.pos += 4; - return val; - }, - readDouble: function() { - var val = index$2.read(this.buf, this.pos, true, 52, 8); - this.pos += 8; - return val; - }, - readVarint: function(isSigned) { - var buf = this.buf, - val, b; - b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val; - b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val; - b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val; - b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val; - b = buf[this.pos]; val |= (b & 0x0f) << 28; - return readVarintRemainder(val, isSigned, this); - }, - readVarint64: function() { - return this.readVarint(true); - }, - readSVarint: function() { - var num = this.readVarint(); - return num % 2 === 1 ? (num + 1) / -2 : num / 2; - }, - readBoolean: function() { - return Boolean(this.readVarint()); - }, - readString: function() { - var end = this.readVarint() + this.pos, - str = readUtf8(this.buf, this.pos, end); - this.pos = end; - return str; - }, - readBytes: function() { - var end = this.readVarint() + this.pos, - buffer = this.buf.subarray(this.pos, end); - this.pos = end; - return buffer; - }, - readPackedVarint: function(arr, isSigned) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readVarint(isSigned)); - return arr; - }, - readPackedSVarint: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readSVarint()); - return arr; - }, - readPackedBoolean: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readBoolean()); - return arr; - }, - readPackedFloat: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readFloat()); - return arr; - }, - readPackedDouble: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readDouble()); - return arr; - }, - readPackedFixed32: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readFixed32()); - return arr; - }, - readPackedSFixed32: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readSFixed32()); - return arr; - }, - readPackedFixed64: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readFixed64()); - return arr; - }, - readPackedSFixed64: function(arr) { - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readSFixed64()); - return arr; - }, - skip: function(val) { - var type = val & 0x7; - if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} - else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos; - else if (type === Pbf.Fixed32) this.pos += 4; - else if (type === Pbf.Fixed64) this.pos += 8; - else throw new Error('Unimplemented type: ' + type); - }, - writeTag: function(tag, type) { - this.writeVarint((tag << 3) | type); - }, - realloc: function(min) { - var length = this.length || 16; - while (length < this.pos + min) length *= 2; - if (length !== this.length) { - var buf = new Uint8Array(length); - buf.set(this.buf); - this.buf = buf; - this.length = length; - } - }, - finish: function() { - this.length = this.pos; - this.pos = 0; - return this.buf.subarray(0, this.length); - }, - writeFixed32: function(val) { - this.realloc(4); - writeInt32(this.buf, val, this.pos); - this.pos += 4; - }, - writeSFixed32: function(val) { - this.realloc(4); - writeInt32(this.buf, val, this.pos); - this.pos += 4; - }, - writeFixed64: function(val) { - this.realloc(8); - writeInt32(this.buf, val & -1, this.pos); - writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); - this.pos += 8; - }, - writeSFixed64: function(val) { - this.realloc(8); - writeInt32(this.buf, val & -1, this.pos); - writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); - this.pos += 8; - }, - writeVarint: function(val) { - val = +val || 0; - if (val > 0xfffffff || val < 0) { - writeBigVarint(val, this); - return; - } - this.realloc(4); - this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; - this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; - this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; - this.buf[this.pos++] = (val >>> 7) & 0x7f; - }, - writeSVarint: function(val) { - this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2); - }, - writeBoolean: function(val) { - this.writeVarint(Boolean(val)); - }, - writeString: function(str) { - str = String(str); - this.realloc(str.length * 4); - this.pos++; - var startPos = this.pos; - this.pos = writeUtf8(this.buf, str, this.pos); - var len = this.pos - startPos; - if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); - this.pos = startPos - 1; - this.writeVarint(len); - this.pos += len; - }, - writeFloat: function(val) { - this.realloc(4); - index$2.write(this.buf, val, this.pos, true, 23, 4); - this.pos += 4; - }, - writeDouble: function(val) { - this.realloc(8); - index$2.write(this.buf, val, this.pos, true, 52, 8); - this.pos += 8; - }, - writeBytes: function(buffer) { - var len = buffer.length; - this.writeVarint(len); - this.realloc(len); - for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i]; - }, - writeRawMessage: function(fn, obj) { - this.pos++; - var startPos = this.pos; - fn(obj, this); - var len = this.pos - startPos; - if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); - this.pos = startPos - 1; - this.writeVarint(len); - this.pos += len; - }, - writeMessage: function(tag, fn, obj) { - this.writeTag(tag, Pbf.Bytes); - this.writeRawMessage(fn, obj); - }, - writePackedVarint: function(tag, arr) { this.writeMessage(tag, writePackedVarint, arr); }, - writePackedSVarint: function(tag, arr) { this.writeMessage(tag, writePackedSVarint, arr); }, - writePackedBoolean: function(tag, arr) { this.writeMessage(tag, writePackedBoolean, arr); }, - writePackedFloat: function(tag, arr) { this.writeMessage(tag, writePackedFloat, arr); }, - writePackedDouble: function(tag, arr) { this.writeMessage(tag, writePackedDouble, arr); }, - writePackedFixed32: function(tag, arr) { this.writeMessage(tag, writePackedFixed32, arr); }, - writePackedSFixed32: function(tag, arr) { this.writeMessage(tag, writePackedSFixed32, arr); }, - writePackedFixed64: function(tag, arr) { this.writeMessage(tag, writePackedFixed64, arr); }, - writePackedSFixed64: function(tag, arr) { this.writeMessage(tag, writePackedSFixed64, arr); }, - writeBytesField: function(tag, buffer) { - this.writeTag(tag, Pbf.Bytes); - this.writeBytes(buffer); - }, - writeFixed32Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed32); - this.writeFixed32(val); - }, - writeSFixed32Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed32); - this.writeSFixed32(val); - }, - writeFixed64Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed64); - this.writeFixed64(val); - }, - writeSFixed64Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed64); - this.writeSFixed64(val); - }, - writeVarintField: function(tag, val) { - this.writeTag(tag, Pbf.Varint); - this.writeVarint(val); - }, - writeSVarintField: function(tag, val) { - this.writeTag(tag, Pbf.Varint); - this.writeSVarint(val); - }, - writeStringField: function(tag, str) { - this.writeTag(tag, Pbf.Bytes); - this.writeString(str); - }, - writeFloatField: function(tag, val) { - this.writeTag(tag, Pbf.Fixed32); - this.writeFloat(val); - }, - writeDoubleField: function(tag, val) { - this.writeTag(tag, Pbf.Fixed64); - this.writeDouble(val); - }, - writeBooleanField: function(tag, val) { - this.writeVarintField(tag, Boolean(val)); - } -}; -function readVarintRemainder(l, s, p) { - var buf = p.buf, - h, b; - b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s); - throw new Error('Expected varint not more than 10 bytes'); -} -function readPackedEnd(pbf) { - return pbf.type === Pbf.Bytes ? - pbf.readVarint() + pbf.pos : pbf.pos + 1; -} -function toNum(low, high, isSigned) { - if (isSigned) { - return high * 0x100000000 + (low >>> 0); - } - return ((high >>> 0) * 0x100000000) + (low >>> 0); -} -function writeBigVarint(val, pbf) { - var low, high; - if (val >= 0) { - low = (val % 0x100000000) | 0; - high = (val / 0x100000000) | 0; - } else { - low = ~(-val % 0x100000000); - high = ~(-val / 0x100000000); - if (low ^ 0xffffffff) { - low = (low + 1) | 0; - } else { - low = 0; - high = (high + 1) | 0; - } - } - if (val >= 0x10000000000000000 || val < -0x10000000000000000) { - throw new Error('Given varint doesn\'t fit into 10 bytes'); - } - pbf.realloc(10); - writeBigVarintLow(low, high, pbf); - writeBigVarintHigh(high, pbf); -} -function writeBigVarintLow(low, high, pbf) { - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos] = low & 0x7f; -} -function writeBigVarintHigh(high, pbf) { - var lsb = (high & 0x07) << 4; - pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f; -} -function makeRoomForExtraLength(startPos, len, pbf) { - var extraLen = - len <= 0x3fff ? 1 : - len <= 0x1fffff ? 2 : - len <= 0xfffffff ? 3 : Math.ceil(Math.log(len) / (Math.LN2 * 7)); - pbf.realloc(extraLen); - for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i]; -} -function writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); } -function writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); } -function writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); } -function writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); } -function writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); } -function writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); } -function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); } -function writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); } -function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); } -function readUInt32(buf, pos) { - return ((buf[pos]) | - (buf[pos + 1] << 8) | - (buf[pos + 2] << 16)) + - (buf[pos + 3] * 0x1000000); -} -function writeInt32(buf, val, pos) { - buf[pos] = val; - buf[pos + 1] = (val >>> 8); - buf[pos + 2] = (val >>> 16); - buf[pos + 3] = (val >>> 24); -} -function readInt32(buf, pos) { - return ((buf[pos]) | - (buf[pos + 1] << 8) | - (buf[pos + 2] << 16)) + - (buf[pos + 3] << 24); -} -function readUtf8(buf, pos, end) { - var str = ''; - var i = pos; - while (i < end) { - var b0 = buf[i]; - var c = null; - var bytesPerSequence = - b0 > 0xEF ? 4 : - b0 > 0xDF ? 3 : - b0 > 0xBF ? 2 : 1; - if (i + bytesPerSequence > end) break; - var b1, b2, b3; - if (bytesPerSequence === 1) { - if (b0 < 0x80) { - c = b0; - } - } else if (bytesPerSequence === 2) { - b1 = buf[i + 1]; - if ((b1 & 0xC0) === 0x80) { - c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F); - if (c <= 0x7F) { - c = null; - } - } - } else if (bytesPerSequence === 3) { - b1 = buf[i + 1]; - b2 = buf[i + 2]; - if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) { - c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F); - if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) { - c = null; - } - } - } else if (bytesPerSequence === 4) { - b1 = buf[i + 1]; - b2 = buf[i + 2]; - b3 = buf[i + 3]; - if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { - c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F); - if (c <= 0xFFFF || c >= 0x110000) { - c = null; - } - } - } - if (c === null) { - c = 0xFFFD; - bytesPerSequence = 1; - } else if (c > 0xFFFF) { - c -= 0x10000; - str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800); - c = 0xDC00 | c & 0x3FF; - } - str += String.fromCharCode(c); - i += bytesPerSequence; - } - return str; -} -function writeUtf8(buf, str, pos) { - for (var i = 0, c, lead; i < str.length; i++) { - c = str.charCodeAt(i); - if (c > 0xD7FF && c < 0xE000) { - if (lead) { - if (c < 0xDC00) { - buf[pos++] = 0xEF; - buf[pos++] = 0xBF; - buf[pos++] = 0xBD; - lead = c; - continue; - } else { - c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000; - lead = null; - } - } else { - if (c > 0xDBFF || (i + 1 === str.length)) { - buf[pos++] = 0xEF; - buf[pos++] = 0xBF; - buf[pos++] = 0xBD; - } else { - lead = c; - } - continue; - } - } else if (lead) { - buf[pos++] = 0xEF; - buf[pos++] = 0xBF; - buf[pos++] = 0xBD; - lead = null; - } - if (c < 0x80) { - buf[pos++] = c; - } else { - if (c < 0x800) { - buf[pos++] = c >> 0x6 | 0xC0; - } else { - if (c < 0x10000) { - buf[pos++] = c >> 0xC | 0xE0; - } else { - buf[pos++] = c >> 0x12 | 0xF0; - buf[pos++] = c >> 0xC & 0x3F | 0x80; - } - buf[pos++] = c >> 0x6 & 0x3F | 0x80; - } - buf[pos++] = c & 0x3F | 0x80; - } - } - return pos; -} - -exports['default'] = index; - -}((this.PBF = this.PBF || {})));}).call(ol.ext); -ol.ext.PBF = ol.ext.PBF.default; - - -/** - * @fileoverview - * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} - */ -goog.provide('ol.ext.vectortile.VectorTile'); - -/** @typedef {function(*)} */ -ol.ext.vectortile.VectorTile = function() {}; - -(function() {(function (exports) { -'use strict'; - -var index$2 = Point; -function Point(x, y) { - this.x = x; - this.y = y; -} -Point.prototype = { - clone: function() { return new Point(this.x, this.y); }, - add: function(p) { return this.clone()._add(p); }, - sub: function(p) { return this.clone()._sub(p); }, - multByPoint: function(p) { return this.clone()._multByPoint(p); }, - divByPoint: function(p) { return this.clone()._divByPoint(p); }, - mult: function(k) { return this.clone()._mult(k); }, - div: function(k) { return this.clone()._div(k); }, - rotate: function(a) { return this.clone()._rotate(a); }, - rotateAround: function(a,p) { return this.clone()._rotateAround(a,p); }, - matMult: function(m) { return this.clone()._matMult(m); }, - unit: function() { return this.clone()._unit(); }, - perp: function() { return this.clone()._perp(); }, - round: function() { return this.clone()._round(); }, - mag: function() { - return Math.sqrt(this.x * this.x + this.y * this.y); - }, - equals: function(other) { - return this.x === other.x && - this.y === other.y; - }, - dist: function(p) { - return Math.sqrt(this.distSqr(p)); - }, - distSqr: function(p) { - var dx = p.x - this.x, - dy = p.y - this.y; - return dx * dx + dy * dy; - }, - angle: function() { - return Math.atan2(this.y, this.x); - }, - angleTo: function(b) { - return Math.atan2(this.y - b.y, this.x - b.x); - }, - angleWith: function(b) { - return this.angleWithSep(b.x, b.y); - }, - angleWithSep: function(x, y) { - return Math.atan2( - this.x * y - this.y * x, - this.x * x + this.y * y); - }, - _matMult: function(m) { - var x = m[0] * this.x + m[1] * this.y, - y = m[2] * this.x + m[3] * this.y; - this.x = x; - this.y = y; - return this; - }, - _add: function(p) { - this.x += p.x; - this.y += p.y; - return this; - }, - _sub: function(p) { - this.x -= p.x; - this.y -= p.y; - return this; - }, - _mult: function(k) { - this.x *= k; - this.y *= k; - return this; - }, - _div: function(k) { - this.x /= k; - this.y /= k; - return this; - }, - _multByPoint: function(p) { - this.x *= p.x; - this.y *= p.y; - return this; - }, - _divByPoint: function(p) { - this.x /= p.x; - this.y /= p.y; - return this; - }, - _unit: function() { - this._div(this.mag()); - return this; - }, - _perp: function() { - var y = this.y; - this.y = this.x; - this.x = -y; - return this; - }, - _rotate: function(angle) { - var cos = Math.cos(angle), - sin = Math.sin(angle), - x = cos * this.x - sin * this.y, - y = sin * this.x + cos * this.y; - this.x = x; - this.y = y; - return this; - }, - _rotateAround: function(angle, p) { - var cos = Math.cos(angle), - sin = Math.sin(angle), - x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y), - y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y); - this.x = x; - this.y = y; - return this; - }, - _round: function() { - this.x = Math.round(this.x); - this.y = Math.round(this.y); - return this; - } -}; -Point.convert = function (a) { - if (a instanceof Point) { - return a; - } - if (Array.isArray(a)) { - return new Point(a[0], a[1]); - } - return a; -}; - -var vectortilefeature = VectorTileFeature$1; -function VectorTileFeature$1(pbf, end, extent, keys, values) { - this.properties = {}; - this.extent = extent; - this.type = 0; - this._pbf = pbf; - this._geometry = -1; - this._keys = keys; - this._values = values; - pbf.readFields(readFeature, this, end); -} -function readFeature(tag, feature, pbf) { - if (tag == 1) feature.id = pbf.readVarint(); - else if (tag == 2) readTag(pbf, feature); - else if (tag == 3) feature.type = pbf.readVarint(); - else if (tag == 4) feature._geometry = pbf.pos; -} -function readTag(pbf, feature) { - var end = pbf.readVarint() + pbf.pos; - while (pbf.pos < end) { - var key = feature._keys[pbf.readVarint()], - value = feature._values[pbf.readVarint()]; - feature.properties[key] = value; - } -} -VectorTileFeature$1.types = ['Unknown', 'Point', 'LineString', 'Polygon']; -VectorTileFeature$1.prototype.loadGeometry = function() { - var pbf = this._pbf; - pbf.pos = this._geometry; - var end = pbf.readVarint() + pbf.pos, - cmd = 1, - length = 0, - x = 0, - y = 0, - lines = [], - line; - while (pbf.pos < end) { - if (!length) { - var cmdLen = pbf.readVarint(); - cmd = cmdLen & 0x7; - length = cmdLen >> 3; - } - length--; - if (cmd === 1 || cmd === 2) { - x += pbf.readSVarint(); - y += pbf.readSVarint(); - if (cmd === 1) { - if (line) lines.push(line); - line = []; - } - line.push(new index$2(x, y)); - } else if (cmd === 7) { - if (line) { - line.push(line[0].clone()); - } - } else { - throw new Error('unknown command ' + cmd); - } - } - if (line) lines.push(line); - return lines; -}; -VectorTileFeature$1.prototype.bbox = function() { - var pbf = this._pbf; - pbf.pos = this._geometry; - var end = pbf.readVarint() + pbf.pos, - cmd = 1, - length = 0, - x = 0, - y = 0, - x1 = Infinity, - x2 = -Infinity, - y1 = Infinity, - y2 = -Infinity; - while (pbf.pos < end) { - if (!length) { - var cmdLen = pbf.readVarint(); - cmd = cmdLen & 0x7; - length = cmdLen >> 3; - } - length--; - if (cmd === 1 || cmd === 2) { - x += pbf.readSVarint(); - y += pbf.readSVarint(); - if (x < x1) x1 = x; - if (x > x2) x2 = x; - if (y < y1) y1 = y; - if (y > y2) y2 = y; - } else if (cmd !== 7) { - throw new Error('unknown command ' + cmd); - } - } - return [x1, y1, x2, y2]; -}; -VectorTileFeature$1.prototype.toGeoJSON = function(x, y, z) { - var size = this.extent * Math.pow(2, z), - x0 = this.extent * x, - y0 = this.extent * y, - coords = this.loadGeometry(), - type = VectorTileFeature$1.types[this.type], - i, j; - function project(line) { - for (var j = 0; j < line.length; j++) { - var p = line[j], y2 = 180 - (p.y + y0) * 360 / size; - line[j] = [ - (p.x + x0) * 360 / size - 180, - 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90 - ]; - } - } - switch (this.type) { - case 1: - var points = []; - for (i = 0; i < coords.length; i++) { - points[i] = coords[i][0]; - } - coords = points; - project(coords); - break; - case 2: - for (i = 0; i < coords.length; i++) { - project(coords[i]); - } - break; - case 3: - coords = classifyRings(coords); - for (i = 0; i < coords.length; i++) { - for (j = 0; j < coords[i].length; j++) { - project(coords[i][j]); - } - } - break; - } - if (coords.length === 1) { - coords = coords[0]; - } else { - type = 'Multi' + type; - } - var result = { - type: "Feature", - geometry: { - type: type, - coordinates: coords - }, - properties: this.properties - }; - if ('id' in this) { - result.id = this.id; - } - return result; -}; -function classifyRings(rings) { - var len = rings.length; - if (len <= 1) return [rings]; - var polygons = [], - polygon, - ccw; - for (var i = 0; i < len; i++) { - var area = signedArea(rings[i]); - if (area === 0) continue; - if (ccw === undefined) ccw = area < 0; - if (ccw === area < 0) { - if (polygon) polygons.push(polygon); - polygon = [rings[i]]; - } else { - polygon.push(rings[i]); - } - } - if (polygon) polygons.push(polygon); - return polygons; -} -function signedArea(ring) { - var sum = 0; - for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { - p1 = ring[i]; - p2 = ring[j]; - sum += (p2.x - p1.x) * (p1.y + p2.y); - } - return sum; -} - -var vectortilelayer = VectorTileLayer$1; -function VectorTileLayer$1(pbf, end) { - this.version = 1; - this.name = null; - this.extent = 4096; - this.length = 0; - this._pbf = pbf; - this._keys = []; - this._values = []; - this._features = []; - pbf.readFields(readLayer, this, end); - this.length = this._features.length; -} -function readLayer(tag, layer, pbf) { - if (tag === 15) layer.version = pbf.readVarint(); - else if (tag === 1) layer.name = pbf.readString(); - else if (tag === 5) layer.extent = pbf.readVarint(); - else if (tag === 2) layer._features.push(pbf.pos); - else if (tag === 3) layer._keys.push(pbf.readString()); - else if (tag === 4) layer._values.push(readValueMessage(pbf)); -} -function readValueMessage(pbf) { - var value = null, - end = pbf.readVarint() + pbf.pos; - while (pbf.pos < end) { - var tag = pbf.readVarint() >> 3; - value = tag === 1 ? pbf.readString() : - tag === 2 ? pbf.readFloat() : - tag === 3 ? pbf.readDouble() : - tag === 4 ? pbf.readVarint64() : - tag === 5 ? pbf.readVarint() : - tag === 6 ? pbf.readSVarint() : - tag === 7 ? pbf.readBoolean() : null; - } - return value; -} -VectorTileLayer$1.prototype.feature = function(i) { - if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); - this._pbf.pos = this._features[i]; - var end = this._pbf.readVarint() + this._pbf.pos; - return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values); -}; - -var vectortile = VectorTile$1; -function VectorTile$1(pbf, end) { - this.layers = pbf.readFields(readTile, {}, end); -} -function readTile(tag, layers, pbf) { - if (tag === 3) { - var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos); - if (layer.length) layers[layer.name] = layer; - } -} - -var VectorTile = vectortile; -var VectorTileFeature = vectortilefeature; -var VectorTileLayer = vectortilelayer; -var index = { - VectorTile: VectorTile, - VectorTileFeature: VectorTileFeature, - VectorTileLayer: VectorTileLayer -}; - -exports['default'] = index; -exports.VectorTile = VectorTile; -exports.VectorTileFeature = VectorTileFeature; -exports.VectorTileLayer = VectorTileLayer; - -}((this.vectortile = this.vectortile || {})));}).call(ol.ext); - -goog.provide('ol.render.Feature'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); - - -/** - * Lightweight, read-only, {@link ol.Feature} and {@link ol.geom.Geometry} like - * structure, optimized for rendering and styling. Geometry access through the - * API is limited to getting the type and extent of the geometry. - * - * @constructor - * @param {ol.geom.GeometryType} type Geometry type. - * @param {Array.<number>} flatCoordinates Flat coordinates. These always need - * to be right-handed for polygons. - * @param {Array.<number>|Array.<Array.<number>>} ends Ends or Endss. - * @param {Object.<string, *>} properties Properties. - * @param {number|string|undefined} id Feature id. - */ -ol.render.Feature = function(type, flatCoordinates, ends, properties, id) { - /** - * @private - * @type {ol.Extent|undefined} - */ - this.extent_; - - /** - * @private - * @type {number|string|undefined} - */ - this.id_ = id; - - /** - * @private - * @type {ol.geom.GeometryType} - */ - this.type_ = type; - - /** - * @private - * @type {Array.<number>} - */ - this.flatCoordinates_ = flatCoordinates; - - /** - * @private - * @type {Array.<number>|Array.<Array.<number>>} - */ - this.ends_ = ends; - - /** - * @private - * @type {Object.<string, *>} - */ - this.properties_ = properties; -}; - - -/** - * Get a feature property by its key. - * @param {string} key Key - * @return {*} Value for the requested key. - * @api - */ -ol.render.Feature.prototype.get = function(key) { - return this.properties_[key]; -}; - - -/** - * @return {Array.<number>|Array.<Array.<number>>} Ends or endss. - */ -ol.render.Feature.prototype.getEnds = function() { - return this.ends_; -}; - - -/** - * Get the extent of this feature's geometry. - * @return {ol.Extent} Extent. - * @api - */ -ol.render.Feature.prototype.getExtent = function() { - if (!this.extent_) { - this.extent_ = this.type_ === ol.geom.GeometryType.POINT ? - ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates_) : - ol.extent.createOrUpdateFromFlatCoordinates( - this.flatCoordinates_, 0, this.flatCoordinates_.length, 2); - - } - return this.extent_; -}; - -/** - * Get the feature identifier. This is a stable identifier for the feature and - * is set when reading data from a remote source. - * @return {number|string|undefined} Id. - * @api - */ -ol.render.Feature.prototype.getId = function() { - return this.id_; -}; - - -/** - * @return {Array.<number>} Flat coordinates. - */ -ol.render.Feature.prototype.getOrientedFlatCoordinates = function() { - return this.flatCoordinates_; -}; - - -/** - * @return {Array.<number>} Flat coordinates. - */ -ol.render.Feature.prototype.getFlatCoordinates = - ol.render.Feature.prototype.getOrientedFlatCoordinates; - - -/** - * For API compatibility with {@link ol.Feature}, this method is useful when - * determining the geometry type in style function (see {@link #getType}). - * @return {ol.render.Feature} Feature. - * @api - */ -ol.render.Feature.prototype.getGeometry = function() { - return this; -}; - - -/** - * Get the feature properties. - * @return {Object.<string, *>} Feature properties. - * @api - */ -ol.render.Feature.prototype.getProperties = function() { - return this.properties_; -}; - - -/** - * Get the feature for working with its geometry. - * @return {ol.render.Feature} Feature. - */ -ol.render.Feature.prototype.getSimplifiedGeometry = - ol.render.Feature.prototype.getGeometry; - - -/** - * @return {number} Stride. - */ -ol.render.Feature.prototype.getStride = function() { - return 2; -}; - - -/** - * @return {undefined} - */ -ol.render.Feature.prototype.getStyleFunction = ol.nullFunction; - - -/** - * Get the type of this feature's geometry. - * @return {ol.geom.GeometryType} Geometry type. - * @api - */ -ol.render.Feature.prototype.getType = function() { - return this.type_; -}; - -//FIXME Implement projection handling - -goog.provide('ol.format.MVT'); - -goog.require('ol'); -goog.require('ol.ext.PBF'); -goog.require('ol.ext.vectortile.VectorTile'); -goog.require('ol.format.Feature'); -goog.require('ol.format.FormatType'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.proj.Projection'); -goog.require('ol.proj.Units'); -goog.require('ol.render.Feature'); - - -/** - * @classdesc - * Feature format for reading data in the Mapbox MVT format. - * - * @constructor - * @extends {ol.format.Feature} - * @param {olx.format.MVTOptions=} opt_options Options. - * @api - */ -ol.format.MVT = function(opt_options) { - - ol.format.Feature.call(this); - - var options = opt_options ? opt_options : {}; - - /** - * @type {ol.proj.Projection} - */ - this.defaultDataProjection = new ol.proj.Projection({ - code: '', - units: ol.proj.Units.TILE_PIXELS - }); - - /** - * @private - * @type {function((ol.geom.Geometry|Object.<string,*>)=)| - * function(ol.geom.GeometryType,Array.<number>, - * (Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)} - */ - this.featureClass_ = options.featureClass ? - options.featureClass : ol.render.Feature; - - /** - * @private - * @type {string|undefined} - */ - this.geometryName_ = options.geometryName; - - /** - * @private - * @type {string} - */ - this.layerName_ = options.layerName ? options.layerName : 'layer'; - - /** - * @private - * @type {Array.<string>} - */ - this.layers_ = options.layers ? options.layers : null; - -}; -ol.inherits(ol.format.MVT, ol.format.Feature); - - -/** - * @inheritDoc - */ -ol.format.MVT.prototype.getType = function() { - return ol.format.FormatType.ARRAY_BUFFER; -}; - - -/** - * @private - * @param {Object} rawFeature Raw Mapbox feature. - * @param {string} layer Layer. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - */ -ol.format.MVT.prototype.readFeature_ = function( - rawFeature, layer, opt_options) { - var feature = new this.featureClass_(); - var id = rawFeature.id; - var values = rawFeature.properties; - values[this.layerName_] = layer; - if (this.geometryName_) { - feature.setGeometryName(this.geometryName_); - } - var geometry = ol.format.Feature.transformWithOptions( - ol.format.MVT.readGeometry_(rawFeature), false, - this.adaptOptions(opt_options)); - feature.setGeometry(geometry); - feature.setId(id); - feature.setProperties(values); - return feature; -}; - - -/** - * @private - * @param {Object} rawFeature Raw Mapbox feature. - * @param {string} layer Layer. - * @return {ol.render.Feature} Feature. - */ -ol.format.MVT.prototype.readRenderFeature_ = function(rawFeature, layer) { - var coords = rawFeature.loadGeometry(); - var ends = []; - var flatCoordinates = []; - ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends); - - var type = rawFeature.type; - /** @type {ol.geom.GeometryType} */ - var geometryType; - if (type === 1) { - geometryType = coords.length === 1 ? - ol.geom.GeometryType.POINT : ol.geom.GeometryType.MULTI_POINT; - } else if (type === 2) { - if (coords.length === 1) { - geometryType = ol.geom.GeometryType.LINE_STRING; - } else { - geometryType = ol.geom.GeometryType.MULTI_LINE_STRING; - } - } else if (type === 3) { - geometryType = ol.geom.GeometryType.POLYGON; - } - - var values = rawFeature.properties; - values[this.layerName_] = layer; - var id = rawFeature.id; - - return new this.featureClass_(geometryType, flatCoordinates, ends, values, id); -}; - - -/** - * @inheritDoc - * @api - */ -ol.format.MVT.prototype.readFeatures = function(source, opt_options) { - var layers = this.layers_; - - var pbf = new ol.ext.PBF(/** @type {ArrayBuffer} */ (source)); - var tile = new ol.ext.vectortile.VectorTile(pbf); - var features = []; - var featureClass = this.featureClass_; - var layer, feature; - for (var name in tile.layers) { - if (layers && layers.indexOf(name) == -1) { - continue; - } - layer = tile.layers[name]; - - for (var i = 0, ii = layer.length; i < ii; ++i) { - if (featureClass === ol.render.Feature) { - feature = this.readRenderFeature_(layer.feature(i), name); - } else { - feature = this.readFeature_(layer.feature(i), name, opt_options); - } - features.push(feature); - } - } - - return features; -}; - - -/** - * @inheritDoc - * @api - */ -ol.format.MVT.prototype.readProjection = function(source) { - return this.defaultDataProjection; -}; - - -/** - * Sets the layers that features will be read from. - * @param {Array.<string>} layers Layers. - * @api - */ -ol.format.MVT.prototype.setLayers = function(layers) { - this.layers_ = layers; -}; - - -/** - * @private - * @param {Object} coords Raw feature coordinates. - * @param {Array.<number>} flatCoordinates Flat coordinates to be populated by - * this function. - * @param {Array.<number>} ends Ends to be populated by this function. - */ -ol.format.MVT.calculateFlatCoordinates_ = function( - coords, flatCoordinates, ends) { - var end = 0; - for (var i = 0, ii = coords.length; i < ii; ++i) { - var line = coords[i]; - var j, jj; - for (j = 0, jj = line.length; j < jj; ++j) { - var coord = line[j]; - // Non-tilespace coords can be calculated here when a TileGrid and - // TileCoord are known. - flatCoordinates.push(coord.x, coord.y); - } - end += 2 * j; - ends.push(end); - } -}; - - -/** - * @private - * @param {Object} rawFeature Raw Mapbox feature. - * @return {ol.geom.Geometry} Geometry. - */ -ol.format.MVT.readGeometry_ = function(rawFeature) { - var type = rawFeature.type; - if (type === 0) { - return null; - } - - var coords = rawFeature.loadGeometry(); - var ends = []; - var flatCoordinates = []; - ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends); - - var geom; - if (type === 1) { - geom = coords.length === 1 ? - new ol.geom.Point(null) : new ol.geom.MultiPoint(null); - } else if (type === 2) { - if (coords.length === 1) { - geom = new ol.geom.LineString(null); - } else { - geom = new ol.geom.MultiLineString(null); - } - } else if (type === 3) { - geom = new ol.geom.Polygon(null); - } - - geom.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates, - ends); - - return geom; -}; - - -/** - * Not implemented. - * @override - */ -ol.format.MVT.prototype.readFeature = function() {}; - - -/** - * Not implemented. - * @override - */ -ol.format.MVT.prototype.readGeometry = function() {}; - - -/** - * Not implemented. - * @override - */ -ol.format.MVT.prototype.writeFeature = function() {}; - - -/** - * Not implemented. - * @override - */ -ol.format.MVT.prototype.writeGeometry = function() {}; - - -/** - * Not implemented. - * @override - */ -ol.format.MVT.prototype.writeFeatures = function() {}; - -// FIXME add typedef for stack state objects -goog.provide('ol.format.OSMXML'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.XMLFeature'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Feature format for reading data in the - * [OSMXML format](http://wiki.openstreetmap.org/wiki/OSM_XML). - * - * @constructor - * @extends {ol.format.XMLFeature} - * @api - */ -ol.format.OSMXML = function() { - ol.format.XMLFeature.call(this); - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get('EPSG:4326'); -}; -ol.inherits(ol.format.OSMXML, ol.format.XMLFeature); - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.OSMXML.readNode_ = function(node, objectStack) { - var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); - var state = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var id = node.getAttribute('id'); - /** @type {ol.Coordinate} */ - var coordinates = [ - parseFloat(node.getAttribute('lon')), - parseFloat(node.getAttribute('lat')) - ]; - state.nodes[id] = coordinates; - - var values = ol.xml.pushParseAndPop({ - tags: {} - }, ol.format.OSMXML.NODE_PARSERS_, node, objectStack); - if (!ol.obj.isEmpty(values.tags)) { - var geometry = new ol.geom.Point(coordinates); - ol.format.Feature.transformWithOptions(geometry, false, options); - var feature = new ol.Feature(geometry); - feature.setId(id); - feature.setProperties(values.tags); - state.features.push(feature); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.OSMXML.readWay_ = function(node, objectStack) { - var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); - var id = node.getAttribute('id'); - var values = ol.xml.pushParseAndPop({ - ndrefs: [], - tags: {} - }, ol.format.OSMXML.WAY_PARSERS_, node, objectStack); - var state = /** @type {Object} */ (objectStack[objectStack.length - 1]); - /** @type {Array.<number>} */ - var flatCoordinates = []; - for (var i = 0, ii = values.ndrefs.length; i < ii; i++) { - var point = state.nodes[values.ndrefs[i]]; - ol.array.extend(flatCoordinates, point); - } - var geometry; - if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) { - // closed way - geometry = new ol.geom.Polygon(null); - geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates, - [flatCoordinates.length]); - } else { - geometry = new ol.geom.LineString(null); - geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates); - } - ol.format.Feature.transformWithOptions(geometry, false, options); - var feature = new ol.Feature(geometry); - feature.setId(id); - feature.setProperties(values.tags); - state.features.push(feature); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.OSMXML.readNd_ = function(node, objectStack) { - var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); - values.ndrefs.push(node.getAttribute('ref')); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.OSMXML.readTag_ = function(node, objectStack) { - var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); - values.tags[node.getAttribute('k')] = node.getAttribute('v'); -}; - - -/** - * @const - * @private - * @type {Array.<string>} - */ -ol.format.OSMXML.NAMESPACE_URIS_ = [ - null -]; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OSMXML.WAY_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OSMXML.NAMESPACE_URIS_, { - 'nd': ol.format.OSMXML.readNd_, - 'tag': ol.format.OSMXML.readTag_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OSMXML.PARSERS_ = ol.xml.makeStructureNS( - ol.format.OSMXML.NAMESPACE_URIS_, { - 'node': ol.format.OSMXML.readNode_, - 'way': ol.format.OSMXML.readWay_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OSMXML.NODE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OSMXML.NAMESPACE_URIS_, { - 'tag': ol.format.OSMXML.readTag_ - }); - - -/** - * Read all features from an OSM source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.OSMXML.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) { - var options = this.getReadOptions(node, opt_options); - if (node.localName == 'osm') { - var state = ol.xml.pushParseAndPop({ - nodes: {}, - features: [] - }, ol.format.OSMXML.PARSERS_, node, [options]); - if (state.features) { - return state.features; - } - } - return []; -}; - - -/** - * Read the projection from an OSM source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.OSMXML.prototype.readProjection; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.OSMXML.prototype.writeFeatureNode = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.OSMXML.prototype.writeFeaturesNode = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.OSMXML.prototype.writeGeometryNode = function(geometry, opt_options) {}; - -goog.provide('ol.format.XLink'); - - -/** - * @const - * @type {string} - */ -ol.format.XLink.NAMESPACE_URI = 'http://www.w3.org/1999/xlink'; - - -/** - * @param {Node} node Node. - * @return {boolean|undefined} Boolean. - */ -ol.format.XLink.readHref = function(node) { - return node.getAttributeNS(ol.format.XLink.NAMESPACE_URI, 'href'); -}; - -goog.provide('ol.format.XML'); - -goog.require('ol.xml'); - - -/** - * @classdesc - * Generic format for reading non-feature XML data - * - * @constructor - * @abstract - * @struct - */ -ol.format.XML = function() { -}; - - -/** - * @param {Document|Node|string} source Source. - * @return {Object} The parsed result. - */ -ol.format.XML.prototype.read = function(source) { - if (ol.xml.isDocument(source)) { - return this.readFromDocument(/** @type {Document} */ (source)); - } else if (ol.xml.isNode(source)) { - return this.readFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readFromDocument(doc); - } else { - return null; - } -}; - - -/** - * @abstract - * @param {Document} doc Document. - * @return {Object} Object - */ -ol.format.XML.prototype.readFromDocument = function(doc) {}; - - -/** - * @abstract - * @param {Node} node Node. - * @return {Object} Object - */ -ol.format.XML.prototype.readFromNode = function(node) {}; - -goog.provide('ol.format.OWS'); - -goog.require('ol'); -goog.require('ol.format.XLink'); -goog.require('ol.format.XML'); -goog.require('ol.format.XSD'); -goog.require('ol.xml'); - - -/** - * @constructor - * @extends {ol.format.XML} - */ -ol.format.OWS = function() { - ol.format.XML.call(this); -}; -ol.inherits(ol.format.OWS, ol.format.XML); - - -/** - * @inheritDoc - */ -ol.format.OWS.prototype.readFromDocument = function(doc) { - for (var n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -ol.format.OWS.prototype.readFromNode = function(node) { - var owsObject = ol.xml.pushParseAndPop({}, - ol.format.OWS.PARSERS_, node, []); - return owsObject ? owsObject : null; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The address. - */ -ol.format.OWS.readAddress_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.OWS.ADDRESS_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The values. - */ -ol.format.OWS.readAllowedValues_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.OWS.ALLOWED_VALUES_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The constraint. - */ -ol.format.OWS.readConstraint_ = function(node, objectStack) { - var name = node.getAttribute('name'); - if (!name) { - return undefined; - } - return ol.xml.pushParseAndPop({'name': name}, - ol.format.OWS.CONSTRAINT_PARSERS_, node, - objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The contact info. - */ -ol.format.OWS.readContactInfo_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.OWS.CONTACT_INFO_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The DCP. - */ -ol.format.OWS.readDcp_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.OWS.DCP_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The GET object. - */ -ol.format.OWS.readGet_ = function(node, objectStack) { - var href = ol.format.XLink.readHref(node); - if (!href) { - return undefined; - } - return ol.xml.pushParseAndPop({'href': href}, - ol.format.OWS.REQUEST_METHOD_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The HTTP object. - */ -ol.format.OWS.readHttp_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, ol.format.OWS.HTTP_PARSERS_, - node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The operation. - */ -ol.format.OWS.readOperation_ = function(node, objectStack) { - var name = node.getAttribute('name'); - var value = ol.xml.pushParseAndPop({}, - ol.format.OWS.OPERATION_PARSERS_, node, objectStack); - if (!value) { - return undefined; - } - var object = /** @type {Object} */ - (objectStack[objectStack.length - 1]); - object[name] = value; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The operations metadata. - */ -ol.format.OWS.readOperationsMetadata_ = function(node, - objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.OWS.OPERATIONS_METADATA_PARSERS_, node, - objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The phone. - */ -ol.format.OWS.readPhone_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.OWS.PHONE_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The service identification. - */ -ol.format.OWS.readServiceIdentification_ = function(node, - objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_, node, - objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The service contact. - */ -ol.format.OWS.readServiceContact_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.OWS.SERVICE_CONTACT_PARSERS_, node, - objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} The service provider. - */ -ol.format.OWS.readServiceProvider_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.OWS.SERVICE_PROVIDER_PARSERS_, node, - objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {string|undefined} The value. - */ -ol.format.OWS.readValue_ = function(node, objectStack) { - return ol.format.XSD.readString(node); -}; - - -/** - * @const - * @type {Array.<string>} - * @private - */ -ol.format.OWS.NAMESPACE_URIS_ = [ - null, - 'http://www.opengis.net/ows/1.1' -]; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'ServiceIdentification': ol.xml.makeObjectPropertySetter( - ol.format.OWS.readServiceIdentification_), - 'ServiceProvider': ol.xml.makeObjectPropertySetter( - ol.format.OWS.readServiceProvider_), - 'OperationsMetadata': ol.xml.makeObjectPropertySetter( - ol.format.OWS.readOperationsMetadata_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.ADDRESS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'DeliveryPoint': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'AdministrativeArea': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'PostalCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'ElectronicMailAddress': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.ALLOWED_VALUES_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Value': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readValue_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.CONSTRAINT_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'AllowedValues': ol.xml.makeObjectPropertySetter( - ol.format.OWS.readAllowedValues_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.CONTACT_INFO_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Phone': ol.xml.makeObjectPropertySetter(ol.format.OWS.readPhone_), - 'Address': ol.xml.makeObjectPropertySetter(ol.format.OWS.readAddress_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.DCP_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'HTTP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readHttp_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.HTTP_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Get': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readGet_), - 'Post': undefined // TODO - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.OPERATION_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'DCP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readDcp_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.OPERATIONS_METADATA_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Operation': ol.format.OWS.readOperation_ - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.PHONE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Voice': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Facsimile': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.REQUEST_METHOD_PARSERS_ = ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Constraint': ol.xml.makeObjectPropertyPusher( - ol.format.OWS.readConstraint_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.SERVICE_CONTACT_PARSERS_ = - ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'IndividualName': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'PositionName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'ContactInfo': ol.xml.makeObjectPropertySetter( - ol.format.OWS.readContactInfo_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_ = - ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'ServiceTypeVersion': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'ServiceType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.OWS.SERVICE_PROVIDER_PARSERS_ = - ol.xml.makeStructureNS( - ol.format.OWS.NAMESPACE_URIS_, { - 'ProviderName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'ProviderSite': ol.xml.makeObjectPropertySetter(ol.format.XLink.readHref), - 'ServiceContact': ol.xml.makeObjectPropertySetter( - ol.format.OWS.readServiceContact_) - }); - -goog.provide('ol.geom.flat.flip'); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {Array.<number>=} opt_dest Destination. - * @param {number=} opt_destOffset Destination offset. - * @return {Array.<number>} Flat coordinates. - */ -ol.geom.flat.flip.flipXY = function(flatCoordinates, offset, end, stride, opt_dest, opt_destOffset) { - var dest, destOffset; - if (opt_dest !== undefined) { - dest = opt_dest; - destOffset = opt_destOffset !== undefined ? opt_destOffset : 0; - } else { - dest = []; - destOffset = 0; - } - var j = offset; - while (j < end) { - var x = flatCoordinates[j++]; - dest[destOffset++] = flatCoordinates[j++]; - dest[destOffset++] = x; - for (var k = 2; k < stride; ++k) { - dest[destOffset++] = flatCoordinates[j++]; - } - } - dest.length = destOffset; - return dest; -}; - -goog.provide('ol.format.Polyline'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.TextFeature'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.flip'); -goog.require('ol.geom.flat.inflate'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Feature format for reading and writing data in the Encoded - * Polyline Algorithm Format. - * - * @constructor - * @extends {ol.format.TextFeature} - * @param {olx.format.PolylineOptions=} opt_options - * Optional configuration object. - * @api - */ -ol.format.Polyline = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.TextFeature.call(this); - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get('EPSG:4326'); - - /** - * @private - * @type {number} - */ - this.factor_ = options.factor ? options.factor : 1e5; - - /** - * @private - * @type {ol.geom.GeometryLayout} - */ - this.geometryLayout_ = options.geometryLayout ? - options.geometryLayout : ol.geom.GeometryLayout.XY; -}; -ol.inherits(ol.format.Polyline, ol.format.TextFeature); - - -/** - * Encode a list of n-dimensional points and return an encoded string - * - * Attention: This function will modify the passed array! - * - * @param {Array.<number>} numbers A list of n-dimensional points. - * @param {number} stride The number of dimension of the points in the list. - * @param {number=} opt_factor The factor by which the numbers will be - * multiplied. The remaining decimal places will get rounded away. - * Default is `1e5`. - * @return {string} The encoded string. - * @api - */ -ol.format.Polyline.encodeDeltas = function(numbers, stride, opt_factor) { - var factor = opt_factor ? opt_factor : 1e5; - var d; - - var lastNumbers = new Array(stride); - for (d = 0; d < stride; ++d) { - lastNumbers[d] = 0; - } - - var i, ii; - for (i = 0, ii = numbers.length; i < ii;) { - for (d = 0; d < stride; ++d, ++i) { - var num = numbers[i]; - var delta = num - lastNumbers[d]; - lastNumbers[d] = num; - - numbers[i] = delta; - } - } - - return ol.format.Polyline.encodeFloats(numbers, factor); -}; - - -/** - * Decode a list of n-dimensional points from an encoded string - * - * @param {string} encoded An encoded string. - * @param {number} stride The number of dimension of the points in the - * encoded string. - * @param {number=} opt_factor The factor by which the resulting numbers will - * be divided. Default is `1e5`. - * @return {Array.<number>} A list of n-dimensional points. - * @api - */ -ol.format.Polyline.decodeDeltas = function(encoded, stride, opt_factor) { - var factor = opt_factor ? opt_factor : 1e5; - var d; - - /** @type {Array.<number>} */ - var lastNumbers = new Array(stride); - for (d = 0; d < stride; ++d) { - lastNumbers[d] = 0; - } - - var numbers = ol.format.Polyline.decodeFloats(encoded, factor); - - var i, ii; - for (i = 0, ii = numbers.length; i < ii;) { - for (d = 0; d < stride; ++d, ++i) { - lastNumbers[d] += numbers[i]; - - numbers[i] = lastNumbers[d]; - } - } - - return numbers; -}; - - -/** - * Encode a list of floating point numbers and return an encoded string - * - * Attention: This function will modify the passed array! - * - * @param {Array.<number>} numbers A list of floating point numbers. - * @param {number=} opt_factor The factor by which the numbers will be - * multiplied. The remaining decimal places will get rounded away. - * Default is `1e5`. - * @return {string} The encoded string. - * @api - */ -ol.format.Polyline.encodeFloats = function(numbers, opt_factor) { - var factor = opt_factor ? opt_factor : 1e5; - var i, ii; - for (i = 0, ii = numbers.length; i < ii; ++i) { - numbers[i] = Math.round(numbers[i] * factor); - } - - return ol.format.Polyline.encodeSignedIntegers(numbers); -}; - - -/** - * Decode a list of floating point numbers from an encoded string - * - * @param {string} encoded An encoded string. - * @param {number=} opt_factor The factor by which the result will be divided. - * Default is `1e5`. - * @return {Array.<number>} A list of floating point numbers. - * @api - */ -ol.format.Polyline.decodeFloats = function(encoded, opt_factor) { - var factor = opt_factor ? opt_factor : 1e5; - var numbers = ol.format.Polyline.decodeSignedIntegers(encoded); - var i, ii; - for (i = 0, ii = numbers.length; i < ii; ++i) { - numbers[i] /= factor; - } - return numbers; -}; - - -/** - * Encode a list of signed integers and return an encoded string - * - * Attention: This function will modify the passed array! - * - * @param {Array.<number>} numbers A list of signed integers. - * @return {string} The encoded string. - */ -ol.format.Polyline.encodeSignedIntegers = function(numbers) { - var i, ii; - for (i = 0, ii = numbers.length; i < ii; ++i) { - var num = numbers[i]; - numbers[i] = (num < 0) ? ~(num << 1) : (num << 1); - } - return ol.format.Polyline.encodeUnsignedIntegers(numbers); -}; - - -/** - * Decode a list of signed integers from an encoded string - * - * @param {string} encoded An encoded string. - * @return {Array.<number>} A list of signed integers. - */ -ol.format.Polyline.decodeSignedIntegers = function(encoded) { - var numbers = ol.format.Polyline.decodeUnsignedIntegers(encoded); - var i, ii; - for (i = 0, ii = numbers.length; i < ii; ++i) { - var num = numbers[i]; - numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); - } - return numbers; -}; - - -/** - * Encode a list of unsigned integers and return an encoded string - * - * @param {Array.<number>} numbers A list of unsigned integers. - * @return {string} The encoded string. - */ -ol.format.Polyline.encodeUnsignedIntegers = function(numbers) { - var encoded = ''; - var i, ii; - for (i = 0, ii = numbers.length; i < ii; ++i) { - encoded += ol.format.Polyline.encodeUnsignedInteger(numbers[i]); - } - return encoded; -}; - - -/** - * Decode a list of unsigned integers from an encoded string - * - * @param {string} encoded An encoded string. - * @return {Array.<number>} A list of unsigned integers. - */ -ol.format.Polyline.decodeUnsignedIntegers = function(encoded) { - var numbers = []; - var current = 0; - var shift = 0; - var i, ii; - for (i = 0, ii = encoded.length; i < ii; ++i) { - var b = encoded.charCodeAt(i) - 63; - current |= (b & 0x1f) << shift; - if (b < 0x20) { - numbers.push(current); - current = 0; - shift = 0; - } else { - shift += 5; - } - } - return numbers; -}; - - -/** - * Encode one single unsigned integer and return an encoded string - * - * @param {number} num Unsigned integer that should be encoded. - * @return {string} The encoded string. - */ -ol.format.Polyline.encodeUnsignedInteger = function(num) { - var value, encoded = ''; - while (num >= 0x20) { - value = (0x20 | (num & 0x1f)) + 63; - encoded += String.fromCharCode(value); - num >>= 5; - } - value = num + 63; - encoded += String.fromCharCode(value); - return encoded; -}; - - -/** - * Read the feature from the Polyline source. The coordinates are assumed to be - * in two dimensions and in latitude, longitude order. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.Polyline.prototype.readFeature; - - -/** - * @inheritDoc - */ -ol.format.Polyline.prototype.readFeatureFromText = function(text, opt_options) { - var geometry = this.readGeometryFromText(text, opt_options); - return new ol.Feature(geometry); -}; - - -/** - * Read the feature from the source. As Polyline sources contain a single - * feature, this will return the feature in an array. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.Polyline.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.Polyline.prototype.readFeaturesFromText = function(text, opt_options) { - var feature = this.readFeatureFromText(text, opt_options); - return [feature]; -}; - - -/** - * Read the geometry from the source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.geom.Geometry} Geometry. - * @api - */ -ol.format.Polyline.prototype.readGeometry; - - -/** - * @inheritDoc - */ -ol.format.Polyline.prototype.readGeometryFromText = function(text, opt_options) { - var stride = ol.geom.SimpleGeometry.getStrideForLayout(this.geometryLayout_); - var flatCoordinates = ol.format.Polyline.decodeDeltas( - text, stride, this.factor_); - ol.geom.flat.flip.flipXY( - flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); - var coordinates = ol.geom.flat.inflate.coordinates( - flatCoordinates, 0, flatCoordinates.length, stride); - - return /** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions( - new ol.geom.LineString(coordinates, this.geometryLayout_), false, - this.adaptOptions(opt_options))); -}; - - -/** - * Read the projection from a Polyline source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.format.Polyline.prototype.readProjection; - - -/** - * @inheritDoc - */ -ol.format.Polyline.prototype.writeFeatureText = function(feature, opt_options) { - var geometry = feature.getGeometry(); - if (geometry) { - return this.writeGeometryText(geometry, opt_options); - } else { - ol.asserts.assert(false, 40); // Expected `feature` to have a geometry - return ''; - } -}; - - -/** - * @inheritDoc - */ -ol.format.Polyline.prototype.writeFeaturesText = function(features, opt_options) { - return this.writeFeatureText(features[0], opt_options); -}; - - -/** - * Write a single geometry in Polyline format. - * - * @function - * @param {ol.geom.Geometry} geometry Geometry. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} Geometry. - * @api - */ -ol.format.Polyline.prototype.writeGeometry; - - -/** - * @inheritDoc - */ -ol.format.Polyline.prototype.writeGeometryText = function(geometry, opt_options) { - geometry = /** @type {ol.geom.LineString} */ - (ol.format.Feature.transformWithOptions( - geometry, true, this.adaptOptions(opt_options))); - var flatCoordinates = geometry.getFlatCoordinates(); - var stride = geometry.getStride(); - ol.geom.flat.flip.flipXY( - flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); - return ol.format.Polyline.encodeDeltas(flatCoordinates, stride, this.factor_); -}; - -goog.provide('ol.format.TopoJSON'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.JSONFeature'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Feature format for reading data in the TopoJSON format. - * - * @constructor - * @extends {ol.format.JSONFeature} - * @param {olx.format.TopoJSONOptions=} opt_options Options. - * @api - */ -ol.format.TopoJSON = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.JSONFeature.call(this); - - /** - * @private - * @type {string|undefined} - */ - this.layerName_ = options.layerName; - - /** - * @private - * @type {Array.<string>} - */ - this.layers_ = options.layers ? options.layers : null; - - /** - * @inheritDoc - */ - this.defaultDataProjection = ol.proj.get( - options.defaultDataProjection ? - options.defaultDataProjection : 'EPSG:4326'); - -}; -ol.inherits(ol.format.TopoJSON, ol.format.JSONFeature); - - -/** - * Concatenate arcs into a coordinate array. - * @param {Array.<number>} indices Indices of arcs to concatenate. Negative - * values indicate arcs need to be reversed. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs (already - * transformed). - * @return {Array.<ol.Coordinate>} Coordinates array. - * @private - */ -ol.format.TopoJSON.concatenateArcs_ = function(indices, arcs) { - /** @type {Array.<ol.Coordinate>} */ - var coordinates = []; - var index, arc; - var i, ii; - var j, jj; - for (i = 0, ii = indices.length; i < ii; ++i) { - index = indices[i]; - if (i > 0) { - // splicing together arcs, discard last point - coordinates.pop(); - } - if (index >= 0) { - // forward arc - arc = arcs[index]; - } else { - // reverse arc - arc = arcs[~index].slice().reverse(); - } - coordinates.push.apply(coordinates, arc); - } - // provide fresh copies of coordinate arrays - for (j = 0, jj = coordinates.length; j < jj; ++j) { - coordinates[j] = coordinates[j].slice(); - } - return coordinates; -}; - - -/** - * Create a point from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON object. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @return {ol.geom.Point} Geometry. - * @private - */ -ol.format.TopoJSON.readPointGeometry_ = function(object, scale, translate) { - var coordinates = object.coordinates; - if (scale && translate) { - ol.format.TopoJSON.transformVertex_(coordinates, scale, translate); - } - return new ol.geom.Point(coordinates); -}; - - -/** - * Create a multi-point from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON object. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @return {ol.geom.MultiPoint} Geometry. - * @private - */ -ol.format.TopoJSON.readMultiPointGeometry_ = function(object, scale, - translate) { - var coordinates = object.coordinates; - var i, ii; - if (scale && translate) { - for (i = 0, ii = coordinates.length; i < ii; ++i) { - ol.format.TopoJSON.transformVertex_(coordinates[i], scale, translate); - } - } - return new ol.geom.MultiPoint(coordinates); -}; - - -/** - * Create a linestring from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON object. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @return {ol.geom.LineString} Geometry. - * @private - */ -ol.format.TopoJSON.readLineStringGeometry_ = function(object, arcs) { - var coordinates = ol.format.TopoJSON.concatenateArcs_(object.arcs, arcs); - return new ol.geom.LineString(coordinates); -}; - - -/** - * Create a multi-linestring from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON object. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @return {ol.geom.MultiLineString} Geometry. - * @private - */ -ol.format.TopoJSON.readMultiLineStringGeometry_ = function(object, arcs) { - var coordinates = []; - var i, ii; - for (i = 0, ii = object.arcs.length; i < ii; ++i) { - coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs); - } - return new ol.geom.MultiLineString(coordinates); -}; - - -/** - * Create a polygon from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON object. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @return {ol.geom.Polygon} Geometry. - * @private - */ -ol.format.TopoJSON.readPolygonGeometry_ = function(object, arcs) { - var coordinates = []; - var i, ii; - for (i = 0, ii = object.arcs.length; i < ii; ++i) { - coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs); - } - return new ol.geom.Polygon(coordinates); -}; - - -/** - * Create a multi-polygon from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON object. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @return {ol.geom.MultiPolygon} Geometry. - * @private - */ -ol.format.TopoJSON.readMultiPolygonGeometry_ = function(object, arcs) { - var coordinates = []; - var polyArray, ringCoords, j, jj; - var i, ii; - for (i = 0, ii = object.arcs.length; i < ii; ++i) { - // for each polygon - polyArray = object.arcs[i]; - ringCoords = []; - for (j = 0, jj = polyArray.length; j < jj; ++j) { - // for each ring - ringCoords[j] = ol.format.TopoJSON.concatenateArcs_(polyArray[j], arcs); - } - coordinates[i] = ringCoords; - } - return new ol.geom.MultiPolygon(coordinates); -}; - - -/** - * Create features from a TopoJSON GeometryCollection object. - * - * @param {TopoJSONGeometryCollection} collection TopoJSON Geometry - * object. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @param {string|undefined} property Property to set the `GeometryCollection`'s parent - * object to. - * @param {string} name Name of the `Topology`'s child object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Array of features. - * @private - */ -ol.format.TopoJSON.readFeaturesFromGeometryCollection_ = function( - collection, arcs, scale, translate, property, name, opt_options) { - var geometries = collection.geometries; - var features = []; - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - features[i] = ol.format.TopoJSON.readFeatureFromGeometry_( - geometries[i], arcs, scale, translate, property, name, opt_options); - } - return features; -}; - - -/** - * Create a feature from a TopoJSON geometry object. - * - * @param {TopoJSONGeometry} object TopoJSON geometry object. - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @param {string|undefined} property Property to set the `GeometryCollection`'s parent - * object to. - * @param {string} name Name of the `Topology`'s child object. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @private - */ -ol.format.TopoJSON.readFeatureFromGeometry_ = function(object, arcs, - scale, translate, property, name, opt_options) { - var geometry; - var type = object.type; - var geometryReader = ol.format.TopoJSON.GEOMETRY_READERS_[type]; - if ((type === 'Point') || (type === 'MultiPoint')) { - geometry = geometryReader(object, scale, translate); - } else { - geometry = geometryReader(object, arcs); - } - var feature = new ol.Feature(); - feature.setGeometry(/** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, false, opt_options))); - if (object.id !== undefined) { - feature.setId(object.id); - } - var properties = object.properties; - if (property) { - if (!properties) { - properties = {}; - } - properties[property] = name; - } - if (properties) { - feature.setProperties(properties); - } - return feature; -}; - - -/** - * Read all features from a TopoJSON source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.TopoJSON.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.TopoJSON.prototype.readFeaturesFromObject = function( - object, opt_options) { - if (object.type == 'Topology') { - var topoJSONTopology = /** @type {TopoJSONTopology} */ (object); - var transform, scale = null, translate = null; - if (topoJSONTopology.transform) { - transform = topoJSONTopology.transform; - scale = transform.scale; - translate = transform.translate; - } - var arcs = topoJSONTopology.arcs; - if (transform) { - ol.format.TopoJSON.transformArcs_(arcs, scale, translate); - } - /** @type {Array.<ol.Feature>} */ - var features = []; - var topoJSONFeatures = topoJSONTopology.objects; - var property = this.layerName_; - var objectName, feature; - for (objectName in topoJSONFeatures) { - if (this.layers_ && this.layers_.indexOf(objectName) == -1) { - continue; - } - if (topoJSONFeatures[objectName].type === 'GeometryCollection') { - feature = /** @type {TopoJSONGeometryCollection} */ - (topoJSONFeatures[objectName]); - features.push.apply(features, - ol.format.TopoJSON.readFeaturesFromGeometryCollection_( - feature, arcs, scale, translate, property, objectName, opt_options)); - } else { - feature = /** @type {TopoJSONGeometry} */ - (topoJSONFeatures[objectName]); - features.push(ol.format.TopoJSON.readFeatureFromGeometry_( - feature, arcs, scale, translate, property, objectName, opt_options)); - } - } - return features; - } else { - return []; - } -}; - - -/** - * Apply a linear transform to array of arcs. The provided array of arcs is - * modified in place. - * - * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @private - */ -ol.format.TopoJSON.transformArcs_ = function(arcs, scale, translate) { - var i, ii; - for (i = 0, ii = arcs.length; i < ii; ++i) { - ol.format.TopoJSON.transformArc_(arcs[i], scale, translate); - } -}; - - -/** - * Apply a linear transform to an arc. The provided arc is modified in place. - * - * @param {Array.<ol.Coordinate>} arc Arc. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @private - */ -ol.format.TopoJSON.transformArc_ = function(arc, scale, translate) { - var x = 0; - var y = 0; - var vertex; - var i, ii; - for (i = 0, ii = arc.length; i < ii; ++i) { - vertex = arc[i]; - x += vertex[0]; - y += vertex[1]; - vertex[0] = x; - vertex[1] = y; - ol.format.TopoJSON.transformVertex_(vertex, scale, translate); - } -}; - - -/** - * Apply a linear transform to a vertex. The provided vertex is modified in - * place. - * - * @param {ol.Coordinate} vertex Vertex. - * @param {Array.<number>} scale Scale for each dimension. - * @param {Array.<number>} translate Translation for each dimension. - * @private - */ -ol.format.TopoJSON.transformVertex_ = function(vertex, scale, translate) { - vertex[0] = vertex[0] * scale[0] + translate[0]; - vertex[1] = vertex[1] * scale[1] + translate[1]; -}; - - -/** - * Read the projection from a TopoJSON source. - * - * @param {Document|Node|Object|string} object Source. - * @return {ol.proj.Projection} Projection. - * @override - * @api - */ -ol.format.TopoJSON.prototype.readProjection; - - -/** - * @inheritDoc - */ -ol.format.TopoJSON.prototype.readProjectionFromObject = function(object) { - return this.defaultDataProjection; -}; - - -/** - * @const - * @private - * @type {Object.<string, function(TopoJSONGeometry, Array, ...Array): ol.geom.Geometry>} - */ -ol.format.TopoJSON.GEOMETRY_READERS_ = { - 'Point': ol.format.TopoJSON.readPointGeometry_, - 'LineString': ol.format.TopoJSON.readLineStringGeometry_, - 'Polygon': ol.format.TopoJSON.readPolygonGeometry_, - 'MultiPoint': ol.format.TopoJSON.readMultiPointGeometry_, - 'MultiLineString': ol.format.TopoJSON.readMultiLineStringGeometry_, - 'MultiPolygon': ol.format.TopoJSON.readMultiPolygonGeometry_ -}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.TopoJSON.prototype.writeFeatureObject = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.TopoJSON.prototype.writeFeaturesObject = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.TopoJSON.prototype.writeGeometryObject = function(geometry, opt_options) {}; - - -/** - * Not implemented. - * @override - */ -ol.format.TopoJSON.prototype.readGeometryFromObject = function() {}; - - -/** - * Not implemented. - * @override - */ -ol.format.TopoJSON.prototype.readFeatureFromObject = function() {}; - -goog.provide('ol.format.WFS'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.format.GML2'); -goog.require('ol.format.GML3'); -goog.require('ol.format.GMLBase'); -goog.require('ol.format.filter'); -goog.require('ol.format.XMLFeature'); -goog.require('ol.format.XSD'); -goog.require('ol.geom.Geometry'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Feature format for reading and writing data in the WFS format. - * By default, supports WFS version 1.1.0. You can pass a GML format - * as option if you want to read a WFS that contains GML2 (WFS 1.0.0). - * Also see {@link ol.format.GMLBase} which is used by this format. - * - * @constructor - * @param {olx.format.WFSOptions=} opt_options - * Optional configuration object. - * @extends {ol.format.XMLFeature} - * @api - */ -ol.format.WFS = function(opt_options) { - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {Array.<string>|string|undefined} - */ - this.featureType_ = options.featureType; - - /** - * @private - * @type {Object.<string, string>|string|undefined} - */ - this.featureNS_ = options.featureNS; - - /** - * @private - * @type {ol.format.GMLBase} - */ - this.gmlFormat_ = options.gmlFormat ? - options.gmlFormat : new ol.format.GML3(); - - /** - * @private - * @type {string} - */ - this.schemaLocation_ = options.schemaLocation ? - options.schemaLocation : - ol.format.WFS.SCHEMA_LOCATIONS[ol.format.WFS.DEFAULT_VERSION]; - - ol.format.XMLFeature.call(this); -}; -ol.inherits(ol.format.WFS, ol.format.XMLFeature); - - -/** - * @const - * @type {string} - */ -ol.format.WFS.FEATURE_PREFIX = 'feature'; - - -/** - * @const - * @type {string} - */ -ol.format.WFS.XMLNS = 'http://www.w3.org/2000/xmlns/'; - - -/** - * @const - * @type {string} - */ -ol.format.WFS.OGCNS = 'http://www.opengis.net/ogc'; - - -/** - * @const - * @type {string} - */ -ol.format.WFS.WFSNS = 'http://www.opengis.net/wfs'; - - -/** - * @const - * @type {string} - */ -ol.format.WFS.FESNS = 'http://www.opengis.net/fes'; - - -/** - * @const - * @type {Object.<string, string>} - */ -ol.format.WFS.SCHEMA_LOCATIONS = { - '1.1.0': 'http://www.opengis.net/wfs ' + - 'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd', - '1.0.0': 'http://www.opengis.net/wfs ' + - 'http://schemas.opengis.net/wfs/1.0.0/wfs.xsd' -}; - - -/** - * @const - * @type {string} - */ -ol.format.WFS.DEFAULT_VERSION = '1.1.0'; - - -/** - * Read all features from a WFS FeatureCollection. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.WFS.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.WFS.prototype.readFeaturesFromNode = function(node, opt_options) { - var context = /** @type {ol.XmlNodeStackItem} */ ({ - 'featureType': this.featureType_, - 'featureNS': this.featureNS_ - }); - ol.obj.assign(context, this.getReadOptions(node, - opt_options ? opt_options : {})); - var objectStack = [context]; - this.gmlFormat_.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][ - 'featureMember'] = - ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal); - var features = ol.xml.pushParseAndPop([], - this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, - objectStack, this.gmlFormat_); - if (!features) { - features = []; - } - return features; -}; - - -/** - * Read transaction response of the source. - * - * @param {Document|Node|Object|string} source Source. - * @return {ol.WFSTransactionResponse|undefined} Transaction response. - * @api - */ -ol.format.WFS.prototype.readTransactionResponse = function(source) { - if (ol.xml.isDocument(source)) { - return this.readTransactionResponseFromDocument( - /** @type {Document} */ (source)); - } else if (ol.xml.isNode(source)) { - return this.readTransactionResponseFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readTransactionResponseFromDocument(doc); - } else { - return undefined; - } -}; - - -/** - * Read feature collection metadata of the source. - * - * @param {Document|Node|Object|string} source Source. - * @return {ol.WFSFeatureCollectionMetadata|undefined} - * FeatureCollection metadata. - * @api - */ -ol.format.WFS.prototype.readFeatureCollectionMetadata = function(source) { - if (ol.xml.isDocument(source)) { - return this.readFeatureCollectionMetadataFromDocument( - /** @type {Document} */ (source)); - } else if (ol.xml.isNode(source)) { - return this.readFeatureCollectionMetadataFromNode( - /** @type {Node} */ (source)); - } else if (typeof source === 'string') { - var doc = ol.xml.parse(source); - return this.readFeatureCollectionMetadataFromDocument(doc); - } else { - return undefined; - } -}; - - -/** - * @param {Document} doc Document. - * @return {ol.WFSFeatureCollectionMetadata|undefined} - * FeatureCollection metadata. - */ -ol.format.WFS.prototype.readFeatureCollectionMetadataFromDocument = function(doc) { - for (var n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFeatureCollectionMetadataFromNode(n); - } - } - return undefined; -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WFS.FEATURE_COLLECTION_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'boundedBy': ol.xml.makeObjectPropertySetter( - ol.format.GMLBase.prototype.readGeometryElement, 'bounds') - } -}; - - -/** - * @param {Node} node Node. - * @return {ol.WFSFeatureCollectionMetadata|undefined} - * FeatureCollection metadata. - */ -ol.format.WFS.prototype.readFeatureCollectionMetadataFromNode = function(node) { - var result = {}; - var value = ol.format.XSD.readNonNegativeIntegerString( - node.getAttribute('numberOfFeatures')); - result['numberOfFeatures'] = value; - return ol.xml.pushParseAndPop( - /** @type {ol.WFSFeatureCollectionMetadata} */ (result), - ol.format.WFS.FEATURE_COLLECTION_PARSERS_, node, [], this.gmlFormat_); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_ = { - 'http://www.opengis.net/wfs': { - 'totalInserted': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'totalUpdated': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'totalDeleted': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger) - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Transaction Summary. - * @private - */ -ol.format.WFS.readTransactionSummary_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_, node, objectStack); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WFS.OGC_FID_PARSERS_ = { - 'http://www.opengis.net/ogc': { - 'FeatureId': ol.xml.makeArrayPusher(function(node, objectStack) { - return node.getAttribute('fid'); - }) - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -ol.format.WFS.fidParser_ = function(node, objectStack) { - ol.xml.parseNode(ol.format.WFS.OGC_FID_PARSERS_, node, objectStack); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WFS.INSERT_RESULTS_PARSERS_ = { - 'http://www.opengis.net/wfs': { - 'Feature': ol.format.WFS.fidParser_ - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Array.<string>|undefined} Insert results. - * @private - */ -ol.format.WFS.readInsertResults_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - [], ol.format.WFS.INSERT_RESULTS_PARSERS_, node, objectStack); -}; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_ = { - 'http://www.opengis.net/wfs': { - 'TransactionSummary': ol.xml.makeObjectPropertySetter( - ol.format.WFS.readTransactionSummary_, 'transactionSummary'), - 'InsertResults': ol.xml.makeObjectPropertySetter( - ol.format.WFS.readInsertResults_, 'insertIds') - } -}; - - -/** - * @param {Document} doc Document. - * @return {ol.WFSTransactionResponse|undefined} Transaction response. - */ -ol.format.WFS.prototype.readTransactionResponseFromDocument = function(doc) { - for (var n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readTransactionResponseFromNode(n); - } - } - return undefined; -}; - - -/** - * @param {Node} node Node. - * @return {ol.WFSTransactionResponse|undefined} Transaction response. - */ -ol.format.WFS.prototype.readTransactionResponseFromNode = function(node) { - return ol.xml.pushParseAndPop( - /** @type {ol.WFSTransactionResponse} */({}), - ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_, node, []); -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.WFS.QUERY_SERIALIZERS_ = { - 'http://www.opengis.net/wfs': { - 'PropertyName': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeFeature_ = function(node, feature, objectStack) { - var context = objectStack[objectStack.length - 1]; - var featureType = context['featureType']; - var featureNS = context['featureNS']; - var gmlVersion = context['gmlVersion']; - var child = ol.xml.createElementNS(featureNS, featureType); - node.appendChild(child); - if (gmlVersion === 2) { - ol.format.GML2.prototype.writeFeatureElement(child, feature, objectStack); - } else { - ol.format.GML3.prototype.writeFeatureElement(child, feature, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {number|string} fid Feature identifier. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeOgcFidFilter_ = function(node, fid, objectStack) { - var filter = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'Filter'); - var child = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'FeatureId'); - filter.appendChild(child); - child.setAttribute('fid', fid); - node.appendChild(filter); -}; - - -/** - * @param {string|undefined} featurePrefix The prefix of the feature. - * @param {string} featureType The type of the feature. - * @returns {string} The value of the typeName property. - * @private - */ -ol.format.WFS.getTypeName_ = function(featurePrefix, featureType) { - featurePrefix = featurePrefix ? featurePrefix : - ol.format.WFS.FEATURE_PREFIX; - var prefix = featurePrefix + ':'; - // The featureType already contains the prefix. - if (featureType.indexOf(prefix) === 0) { - return featureType; - } else { - return prefix + featureType; - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeDelete_ = function(node, feature, objectStack) { - var context = objectStack[objectStack.length - 1]; - ol.asserts.assert(feature.getId() !== undefined, 26); // Features must have an id set - var featureType = context['featureType']; - var featurePrefix = context['featurePrefix']; - var featureNS = context['featureNS']; - var typeName = ol.format.WFS.getTypeName_(featurePrefix, featureType); - node.setAttribute('typeName', typeName); - ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix, - featureNS); - var fid = feature.getId(); - if (fid !== undefined) { - ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeUpdate_ = function(node, feature, objectStack) { - var context = objectStack[objectStack.length - 1]; - ol.asserts.assert(feature.getId() !== undefined, 27); // Features must have an id set - var featureType = context['featureType']; - var featurePrefix = context['featurePrefix']; - var featureNS = context['featureNS']; - var typeName = ol.format.WFS.getTypeName_(featurePrefix, featureType); - node.setAttribute('typeName', typeName); - ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix, - featureNS); - var fid = feature.getId(); - if (fid !== undefined) { - var keys = feature.getKeys(); - var values = []; - for (var i = 0, ii = keys.length; i < ii; i++) { - var value = feature.get(keys[i]); - if (value !== undefined) { - values.push({name: keys[i], value: value}); - } - } - ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ( - {'gmlVersion': context['gmlVersion'], node: node, - 'hasZ': context['hasZ'], 'srsName': context['srsName']}), - ol.format.WFS.TRANSACTION_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('Property'), values, - objectStack); - ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {Object} pair Property name and value. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeProperty_ = function(node, pair, objectStack) { - var name = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Name'); - var context = objectStack[objectStack.length - 1]; - var gmlVersion = context['gmlVersion']; - node.appendChild(name); - ol.format.XSD.writeStringTextNode(name, pair.name); - if (pair.value !== undefined && pair.value !== null) { - var value = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Value'); - node.appendChild(value); - if (pair.value instanceof ol.geom.Geometry) { - if (gmlVersion === 2) { - ol.format.GML2.prototype.writeGeometryElement(value, - pair.value, objectStack); - } else { - ol.format.GML3.prototype.writeGeometryElement(value, - pair.value, objectStack); - } - } else { - ol.format.XSD.writeStringTextNode(value, pair.value); - } - } -}; - - -/** - * @param {Node} node Node. - * @param {{vendorId: string, safeToIgnore: boolean, value: string}} - * nativeElement The native element. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeNative_ = function(node, nativeElement, objectStack) { - if (nativeElement.vendorId) { - node.setAttribute('vendorId', nativeElement.vendorId); - } - if (nativeElement.safeToIgnore !== undefined) { - node.setAttribute('safeToIgnore', nativeElement.safeToIgnore); - } - if (nativeElement.value !== undefined) { - ol.format.XSD.writeStringTextNode(node, nativeElement.value); - } -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.WFS.TRANSACTION_SERIALIZERS_ = { - 'http://www.opengis.net/wfs': { - 'Insert': ol.xml.makeChildAppender(ol.format.WFS.writeFeature_), - 'Update': ol.xml.makeChildAppender(ol.format.WFS.writeUpdate_), - 'Delete': ol.xml.makeChildAppender(ol.format.WFS.writeDelete_), - 'Property': ol.xml.makeChildAppender(ol.format.WFS.writeProperty_), - 'Native': ol.xml.makeChildAppender(ol.format.WFS.writeNative_) - } -}; - - -/** - * @param {Node} node Node. - * @param {string} featureType Feature type. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeQuery_ = function(node, featureType, objectStack) { - var context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var featurePrefix = context['featurePrefix']; - var featureNS = context['featureNS']; - var propertyNames = context['propertyNames']; - var srsName = context['srsName']; - var typeName; - // If feature prefix is not defined, we must not use the default prefix. - if (featurePrefix) { - typeName = ol.format.WFS.getTypeName_(featurePrefix, featureType); - } else { - typeName = featureType; - } - node.setAttribute('typeName', typeName); - if (srsName) { - node.setAttribute('srsName', srsName); - } - if (featureNS) { - ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix, - featureNS); - } - var item = /** @type {ol.XmlNodeStackItem} */ (ol.obj.assign({}, context)); - item.node = node; - ol.xml.pushSerializeAndPop(item, - ol.format.WFS.QUERY_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('PropertyName'), propertyNames, - objectStack); - var filter = context['filter']; - if (filter) { - var child = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'Filter'); - node.appendChild(child); - ol.format.WFS.writeFilterCondition_(child, filter, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.Filter} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeFilterCondition_ = function(node, filter, objectStack) { - /** @type {ol.XmlNodeStackItem} */ - var item = {node: node}; - ol.xml.pushSerializeAndPop(item, - ol.format.WFS.GETFEATURE_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory(filter.getTagName()), - [filter], objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.Bbox} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeBboxFilter_ = function(node, filter, objectStack) { - var context = objectStack[objectStack.length - 1]; - context['srsName'] = filter.srsName; - - ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName); - ol.format.GML3.prototype.writeGeometryElement(node, filter.extent, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.Intersects} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeIntersectsFilter_ = function(node, filter, objectStack) { - var context = objectStack[objectStack.length - 1]; - context['srsName'] = filter.srsName; - - ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName); - ol.format.GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.Within} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeWithinFilter_ = function(node, filter, objectStack) { - var context = objectStack[objectStack.length - 1]; - context['srsName'] = filter.srsName; - - ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName); - ol.format.GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.During} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeDuringFilter_ = function(node, filter, objectStack) { - - var valueReference = ol.xml.createElementNS(ol.format.WFS.FESNS, 'ValueReference'); - ol.format.XSD.writeStringTextNode(valueReference, filter.propertyName); - node.appendChild(valueReference); - - var timePeriod = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'TimePeriod'); - - node.appendChild(timePeriod); - - var begin = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'begin'); - timePeriod.appendChild(begin); - ol.format.WFS.writeTimeInstant_(begin, filter.begin); - - var end = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'end'); - timePeriod.appendChild(end); - ol.format.WFS.writeTimeInstant_(end, filter.end); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.LogicalNary} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeLogicalFilter_ = function(node, filter, objectStack) { - /** @type {ol.XmlNodeStackItem} */ - var item = {node: node}; - filter.conditions.forEach(function(condition) { - ol.xml.pushSerializeAndPop(item, - ol.format.WFS.GETFEATURE_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory(condition.getTagName()), - [condition], objectStack); - }); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.Not} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeNotFilter_ = function(node, filter, objectStack) { - /** @type {ol.XmlNodeStackItem} */ - var item = {node: node}; - var condition = filter.condition; - ol.xml.pushSerializeAndPop(item, - ol.format.WFS.GETFEATURE_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory(condition.getTagName()), - [condition], objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.ComparisonBinary} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeComparisonFilter_ = function(node, filter, objectStack) { - if (filter.matchCase !== undefined) { - node.setAttribute('matchCase', filter.matchCase.toString()); - } - ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); - ol.format.WFS.writeOgcLiteral_(node, '' + filter.expression); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.IsNull} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeIsNullFilter_ = function(node, filter, objectStack) { - ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.IsBetween} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeIsBetweenFilter_ = function(node, filter, objectStack) { - ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); - - var lowerBoundary = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'LowerBoundary'); - node.appendChild(lowerBoundary); - ol.format.WFS.writeOgcLiteral_(lowerBoundary, '' + filter.lowerBoundary); - - var upperBoundary = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'UpperBoundary'); - node.appendChild(upperBoundary); - ol.format.WFS.writeOgcLiteral_(upperBoundary, '' + filter.upperBoundary); -}; - - -/** - * @param {Node} node Node. - * @param {ol.format.filter.IsLike} filter Filter. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeIsLikeFilter_ = function(node, filter, objectStack) { - node.setAttribute('wildCard', filter.wildCard); - node.setAttribute('singleChar', filter.singleChar); - node.setAttribute('escapeChar', filter.escapeChar); - if (filter.matchCase !== undefined) { - node.setAttribute('matchCase', filter.matchCase.toString()); - } - ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); - ol.format.WFS.writeOgcLiteral_(node, '' + filter.pattern); -}; - - -/** - * @param {string} tagName Tag name. - * @param {Node} node Node. - * @param {string} value Value. - * @private - */ -ol.format.WFS.writeOgcExpression_ = function(tagName, node, value) { - var property = ol.xml.createElementNS(ol.format.WFS.OGCNS, tagName); - ol.format.XSD.writeStringTextNode(property, value); - node.appendChild(property); -}; - - -/** - * @param {Node} node Node. - * @param {string} value PropertyName value. - * @private - */ -ol.format.WFS.writeOgcPropertyName_ = function(node, value) { - ol.format.WFS.writeOgcExpression_('PropertyName', node, value); -}; - - -/** - * @param {Node} node Node. - * @param {string} value PropertyName value. - * @private - */ -ol.format.WFS.writeOgcLiteral_ = function(node, value) { - ol.format.WFS.writeOgcExpression_('Literal', node, value); -}; - - -/** - * @param {Node} node Node. - * @param {string} time PropertyName value. - * @private - */ -ol.format.WFS.writeTimeInstant_ = function(node, time) { - var timeInstant = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'TimeInstant'); - node.appendChild(timeInstant); - - var timePosition = ol.xml.createElementNS(ol.format.GMLBase.GMLNS, 'timePosition'); - timeInstant.appendChild(timePosition); - ol.format.XSD.writeStringTextNode(timePosition, time); -}; - - -/** - * @type {Object.<string, Object.<string, ol.XmlSerializer>>} - * @private - */ -ol.format.WFS.GETFEATURE_SERIALIZERS_ = { - 'http://www.opengis.net/wfs': { - 'Query': ol.xml.makeChildAppender(ol.format.WFS.writeQuery_) - }, - 'http://www.opengis.net/ogc': { - 'During': ol.xml.makeChildAppender(ol.format.WFS.writeDuringFilter_), - 'And': ol.xml.makeChildAppender(ol.format.WFS.writeLogicalFilter_), - 'Or': ol.xml.makeChildAppender(ol.format.WFS.writeLogicalFilter_), - 'Not': ol.xml.makeChildAppender(ol.format.WFS.writeNotFilter_), - 'BBOX': ol.xml.makeChildAppender(ol.format.WFS.writeBboxFilter_), - 'Intersects': ol.xml.makeChildAppender(ol.format.WFS.writeIntersectsFilter_), - 'Within': ol.xml.makeChildAppender(ol.format.WFS.writeWithinFilter_), - 'PropertyIsEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), - 'PropertyIsNotEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), - 'PropertyIsLessThan': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), - 'PropertyIsLessThanOrEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), - 'PropertyIsGreaterThan': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), - 'PropertyIsGreaterThanOrEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), - 'PropertyIsNull': ol.xml.makeChildAppender(ol.format.WFS.writeIsNullFilter_), - 'PropertyIsBetween': ol.xml.makeChildAppender(ol.format.WFS.writeIsBetweenFilter_), - 'PropertyIsLike': ol.xml.makeChildAppender(ol.format.WFS.writeIsLikeFilter_) - } -}; - - -/** - * Encode filter as WFS `Filter` and return the Node. - * - * @param {ol.format.filter.Filter} filter Filter. - * @return {Node} Result. - * @api - */ -ol.format.WFS.writeFilter = function(filter) { - var child = ol.xml.createElementNS(ol.format.WFS.OGCNS, 'Filter'); - ol.format.WFS.writeFilterCondition_(child, filter, []); - return child; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<string>} featureTypes Feature types. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -ol.format.WFS.writeGetFeature_ = function(node, featureTypes, objectStack) { - var context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - var item = /** @type {ol.XmlNodeStackItem} */ (ol.obj.assign({}, context)); - item.node = node; - ol.xml.pushSerializeAndPop(item, - ol.format.WFS.GETFEATURE_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('Query'), featureTypes, - objectStack); -}; - - -/** - * Encode format as WFS `GetFeature` and return the Node. - * - * @param {olx.format.WFSWriteGetFeatureOptions} options Options. - * @return {Node} Result. - * @api - */ -ol.format.WFS.prototype.writeGetFeature = function(options) { - var node = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'GetFeature'); - node.setAttribute('service', 'WFS'); - node.setAttribute('version', '1.1.0'); - var filter; - if (options) { - if (options.handle) { - node.setAttribute('handle', options.handle); - } - if (options.outputFormat) { - node.setAttribute('outputFormat', options.outputFormat); - } - if (options.maxFeatures !== undefined) { - node.setAttribute('maxFeatures', options.maxFeatures); - } - if (options.resultType) { - node.setAttribute('resultType', options.resultType); - } - if (options.startIndex !== undefined) { - node.setAttribute('startIndex', options.startIndex); - } - if (options.count !== undefined) { - node.setAttribute('count', options.count); - } - filter = options.filter; - if (options.bbox) { - ol.asserts.assert(options.geometryName, - 12); // `options.geometryName` must also be provided when `options.bbox` is set - var bbox = ol.format.filter.bbox( - /** @type {string} */ (options.geometryName), options.bbox, options.srsName); - if (filter) { - // if bbox and filter are both set, combine the two into a single filter - filter = ol.format.filter.and(filter, bbox); - } else { - filter = bbox; - } - } - } - ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation', this.schemaLocation_); - /** @type {ol.XmlNodeStackItem} */ - var context = { - node: node, - 'srsName': options.srsName, - 'featureNS': options.featureNS ? options.featureNS : this.featureNS_, - 'featurePrefix': options.featurePrefix, - 'geometryName': options.geometryName, - 'filter': filter, - 'propertyNames': options.propertyNames ? options.propertyNames : [] - }; - ol.asserts.assert(Array.isArray(options.featureTypes), - 11); // `options.featureTypes` should be an Array - ol.format.WFS.writeGetFeature_(node, /** @type {!Array.<string>} */ (options.featureTypes), [context]); - return node; -}; - - -/** - * Encode format as WFS `Transaction` and return the Node. - * - * @param {Array.<ol.Feature>} inserts The features to insert. - * @param {Array.<ol.Feature>} updates The features to update. - * @param {Array.<ol.Feature>} deletes The features to delete. - * @param {olx.format.WFSWriteTransactionOptions} options Write options. - * @return {Node} Result. - * @api - */ -ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes, - options) { - var objectStack = []; - var node = ol.xml.createElementNS(ol.format.WFS.WFSNS, 'Transaction'); - var version = options.version ? - options.version : ol.format.WFS.DEFAULT_VERSION; - var gmlVersion = version === '1.0.0' ? 2 : 3; - node.setAttribute('service', 'WFS'); - node.setAttribute('version', version); - var baseObj; - /** @type {ol.XmlNodeStackItem} */ - var obj; - if (options) { - baseObj = options.gmlOptions ? options.gmlOptions : {}; - if (options.handle) { - node.setAttribute('handle', options.handle); - } - } - var schemaLocation = ol.format.WFS.SCHEMA_LOCATIONS[version]; - ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation', schemaLocation); - if (inserts) { - obj = {node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, - 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName}; - ol.obj.assign(obj, baseObj); - ol.xml.pushSerializeAndPop(obj, - ol.format.WFS.TRANSACTION_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('Insert'), inserts, - objectStack); - } - if (updates) { - obj = {node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, - 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName}; - ol.obj.assign(obj, baseObj); - ol.xml.pushSerializeAndPop(obj, - ol.format.WFS.TRANSACTION_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('Update'), updates, - objectStack); - } - if (deletes) { - ol.xml.pushSerializeAndPop({node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, - 'gmlVersion': gmlVersion, 'srsName': options.srsName}, - ol.format.WFS.TRANSACTION_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('Delete'), deletes, - objectStack); - } - if (options.nativeElements) { - ol.xml.pushSerializeAndPop({node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, - 'gmlVersion': gmlVersion, 'srsName': options.srsName}, - ol.format.WFS.TRANSACTION_SERIALIZERS_, - ol.xml.makeSimpleNodeFactory('Native'), options.nativeElements, - objectStack); - } - return node; -}; - - -/** - * Read the projection from a WFS source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {?ol.proj.Projection} Projection. - * @api - */ -ol.format.WFS.prototype.readProjection; - - -/** - * @inheritDoc - */ -ol.format.WFS.prototype.readProjectionFromDocument = function(doc) { - for (var n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readProjectionFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -ol.format.WFS.prototype.readProjectionFromNode = function(node) { - if (node.firstElementChild && - node.firstElementChild.firstElementChild) { - node = node.firstElementChild.firstElementChild; - for (var n = node.firstElementChild; n; n = n.nextElementSibling) { - if (!(n.childNodes.length === 0 || - (n.childNodes.length === 1 && - n.firstChild.nodeType === 3))) { - var objectStack = [{}]; - this.gmlFormat_.readGeometryElement(n, objectStack); - return ol.proj.get(objectStack.pop().srsName); - } - } - } - - return null; -}; - -goog.provide('ol.format.WKT'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.format.Feature'); -goog.require('ol.format.TextFeature'); -goog.require('ol.geom.GeometryCollection'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.geom.SimpleGeometry'); - - -/** - * @classdesc - * Geometry format for reading and writing data in the `WellKnownText` (WKT) - * format. - * - * @constructor - * @extends {ol.format.TextFeature} - * @param {olx.format.WKTOptions=} opt_options Options. - * @api - */ -ol.format.WKT = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.format.TextFeature.call(this); - - /** - * Split GeometryCollection into multiple features. - * @type {boolean} - * @private - */ - this.splitCollection_ = options.splitCollection !== undefined ? - options.splitCollection : false; - -}; -ol.inherits(ol.format.WKT, ol.format.TextFeature); - - -/** - * @const - * @type {string} - */ -ol.format.WKT.EMPTY = 'EMPTY'; - - -/** - * @const - * @type {string} - */ -ol.format.WKT.Z = 'Z'; - - -/** - * @const - * @type {string} - */ -ol.format.WKT.M = 'M'; - - -/** - * @const - * @type {string} - */ -ol.format.WKT.ZM = 'ZM'; - - -/** - * @param {ol.geom.Point} geom Point geometry. - * @return {string} Coordinates part of Point as WKT. - * @private - */ -ol.format.WKT.encodePointGeometry_ = function(geom) { - var coordinates = geom.getCoordinates(); - if (coordinates.length === 0) { - return ''; - } - return coordinates.join(' '); -}; - - -/** - * @param {ol.geom.MultiPoint} geom MultiPoint geometry. - * @return {string} Coordinates part of MultiPoint as WKT. - * @private - */ -ol.format.WKT.encodeMultiPointGeometry_ = function(geom) { - var array = []; - var components = geom.getPoints(); - for (var i = 0, ii = components.length; i < ii; ++i) { - array.push('(' + ol.format.WKT.encodePointGeometry_(components[i]) + ')'); - } - return array.join(','); -}; - - -/** - * @param {ol.geom.GeometryCollection} geom GeometryCollection geometry. - * @return {string} Coordinates part of GeometryCollection as WKT. - * @private - */ -ol.format.WKT.encodeGeometryCollectionGeometry_ = function(geom) { - var array = []; - var geoms = geom.getGeometries(); - for (var i = 0, ii = geoms.length; i < ii; ++i) { - array.push(ol.format.WKT.encode_(geoms[i])); - } - return array.join(','); -}; - - -/** - * @param {ol.geom.LineString|ol.geom.LinearRing} geom LineString geometry. - * @return {string} Coordinates part of LineString as WKT. - * @private - */ -ol.format.WKT.encodeLineStringGeometry_ = function(geom) { - var coordinates = geom.getCoordinates(); - var array = []; - for (var i = 0, ii = coordinates.length; i < ii; ++i) { - array.push(coordinates[i].join(' ')); - } - return array.join(','); -}; - - -/** - * @param {ol.geom.MultiLineString} geom MultiLineString geometry. - * @return {string} Coordinates part of MultiLineString as WKT. - * @private - */ -ol.format.WKT.encodeMultiLineStringGeometry_ = function(geom) { - var array = []; - var components = geom.getLineStrings(); - for (var i = 0, ii = components.length; i < ii; ++i) { - array.push('(' + ol.format.WKT.encodeLineStringGeometry_( - components[i]) + ')'); - } - return array.join(','); -}; - - -/** - * @param {ol.geom.Polygon} geom Polygon geometry. - * @return {string} Coordinates part of Polygon as WKT. - * @private - */ -ol.format.WKT.encodePolygonGeometry_ = function(geom) { - var array = []; - var rings = geom.getLinearRings(); - for (var i = 0, ii = rings.length; i < ii; ++i) { - array.push('(' + ol.format.WKT.encodeLineStringGeometry_( - rings[i]) + ')'); - } - return array.join(','); -}; - - -/** - * @param {ol.geom.MultiPolygon} geom MultiPolygon geometry. - * @return {string} Coordinates part of MultiPolygon as WKT. - * @private - */ -ol.format.WKT.encodeMultiPolygonGeometry_ = function(geom) { - var array = []; - var components = geom.getPolygons(); - for (var i = 0, ii = components.length; i < ii; ++i) { - array.push('(' + ol.format.WKT.encodePolygonGeometry_( - components[i]) + ')'); - } - return array.join(','); -}; - -/** - * @param {ol.geom.SimpleGeometry} geom SimpleGeometry geometry. - * @return {string} Potential dimensional information for WKT type. - * @private - */ -ol.format.WKT.encodeGeometryLayout_ = function(geom) { - var layout = geom.getLayout(); - var dimInfo = ''; - if (layout === ol.geom.GeometryLayout.XYZ || layout === ol.geom.GeometryLayout.XYZM) { - dimInfo += ol.format.WKT.Z; - } - if (layout === ol.geom.GeometryLayout.XYM || layout === ol.geom.GeometryLayout.XYZM) { - dimInfo += ol.format.WKT.M; - } - return dimInfo; -}; - - -/** - * Encode a geometry as WKT. - * @param {ol.geom.Geometry} geom The geometry to encode. - * @return {string} WKT string for the geometry. - * @private - */ -ol.format.WKT.encode_ = function(geom) { - var type = geom.getType(); - var geometryEncoder = ol.format.WKT.GeometryEncoder_[type]; - var enc = geometryEncoder(geom); - type = type.toUpperCase(); - if (geom instanceof ol.geom.SimpleGeometry) { - var dimInfo = ol.format.WKT.encodeGeometryLayout_(geom); - if (dimInfo.length > 0) { - type += ' ' + dimInfo; - } - } - if (enc.length === 0) { - return type + ' ' + ol.format.WKT.EMPTY; - } - return type + '(' + enc + ')'; -}; - - -/** - * @const - * @type {Object.<string, function(ol.geom.Geometry): string>} - * @private - */ -ol.format.WKT.GeometryEncoder_ = { - 'Point': ol.format.WKT.encodePointGeometry_, - 'LineString': ol.format.WKT.encodeLineStringGeometry_, - 'Polygon': ol.format.WKT.encodePolygonGeometry_, - 'MultiPoint': ol.format.WKT.encodeMultiPointGeometry_, - 'MultiLineString': ol.format.WKT.encodeMultiLineStringGeometry_, - 'MultiPolygon': ol.format.WKT.encodeMultiPolygonGeometry_, - 'GeometryCollection': ol.format.WKT.encodeGeometryCollectionGeometry_ -}; - - -/** - * Parse a WKT string. - * @param {string} wkt WKT string. - * @return {ol.geom.Geometry|undefined} - * The geometry created. - * @private - */ -ol.format.WKT.prototype.parse_ = function(wkt) { - var lexer = new ol.format.WKT.Lexer(wkt); - var parser = new ol.format.WKT.Parser(lexer); - return parser.parse(); -}; - - -/** - * Read a feature from a WKT source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.Feature} Feature. - * @api - */ -ol.format.WKT.prototype.readFeature; - - -/** - * @inheritDoc - */ -ol.format.WKT.prototype.readFeatureFromText = function(text, opt_options) { - var geom = this.readGeometryFromText(text, opt_options); - if (geom) { - var feature = new ol.Feature(); - feature.setGeometry(geom); - return feature; - } - return null; -}; - - -/** - * Read all features from a WKT source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.WKT.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.WKT.prototype.readFeaturesFromText = function(text, opt_options) { - var geometries = []; - var geometry = this.readGeometryFromText(text, opt_options); - if (this.splitCollection_ && - geometry.getType() == ol.geom.GeometryType.GEOMETRY_COLLECTION) { - geometries = (/** @type {ol.geom.GeometryCollection} */ (geometry)) - .getGeometriesArray(); - } else { - geometries = [geometry]; - } - var feature, features = []; - for (var i = 0, ii = geometries.length; i < ii; ++i) { - feature = new ol.Feature(); - feature.setGeometry(geometries[i]); - features.push(feature); - } - return features; -}; - - -/** - * Read a single geometry from a WKT source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Read options. - * @return {ol.geom.Geometry} Geometry. - * @api - */ -ol.format.WKT.prototype.readGeometry; - - -/** - * @inheritDoc - */ -ol.format.WKT.prototype.readGeometryFromText = function(text, opt_options) { - var geometry = this.parse_(text); - if (geometry) { - return /** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, false, opt_options)); - } else { - return null; - } -}; - - -/** - * Encode a feature as a WKT string. - * - * @function - * @param {ol.Feature} feature Feature. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} WKT string. - * @api - */ -ol.format.WKT.prototype.writeFeature; - - -/** - * @inheritDoc - */ -ol.format.WKT.prototype.writeFeatureText = function(feature, opt_options) { - var geometry = feature.getGeometry(); - if (geometry) { - return this.writeGeometryText(geometry, opt_options); - } - return ''; -}; - - -/** - * Encode an array of features as a WKT string. - * - * @function - * @param {Array.<ol.Feature>} features Features. - * @param {olx.format.WriteOptions=} opt_options Write options. - * @return {string} WKT string. - * @api - */ -ol.format.WKT.prototype.writeFeatures; - - -/** - * @inheritDoc - */ -ol.format.WKT.prototype.writeFeaturesText = function(features, opt_options) { - if (features.length == 1) { - return this.writeFeatureText(features[0], opt_options); - } - var geometries = []; - for (var i = 0, ii = features.length; i < ii; ++i) { - geometries.push(features[i].getGeometry()); - } - var collection = new ol.geom.GeometryCollection(geometries); - return this.writeGeometryText(collection, opt_options); -}; - - -/** - * Write a single geometry as a WKT string. - * - * @function - * @param {ol.geom.Geometry} geometry Geometry. - * @return {string} WKT string. - * @api - */ -ol.format.WKT.prototype.writeGeometry; - - -/** - * @inheritDoc - */ -ol.format.WKT.prototype.writeGeometryText = function(geometry, opt_options) { - return ol.format.WKT.encode_(/** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, true, opt_options))); -}; - - -/** - * @const - * @enum {number} - * @private - */ -ol.format.WKT.TokenType_ = { - TEXT: 1, - LEFT_PAREN: 2, - RIGHT_PAREN: 3, - NUMBER: 4, - COMMA: 5, - EOF: 6 -}; - - -/** - * Class to tokenize a WKT string. - * @param {string} wkt WKT string. - * @constructor - * @protected - */ -ol.format.WKT.Lexer = function(wkt) { - - /** - * @type {string} - */ - this.wkt = wkt; - - /** - * @type {number} - * @private - */ - this.index_ = -1; -}; - - -/** - * @param {string} c Character. - * @return {boolean} Whether the character is alphabetic. - * @private - */ -ol.format.WKT.Lexer.prototype.isAlpha_ = function(c) { - return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; -}; - - -/** - * @param {string} c Character. - * @param {boolean=} opt_decimal Whether the string number - * contains a dot, i.e. is a decimal number. - * @return {boolean} Whether the character is numeric. - * @private - */ -ol.format.WKT.Lexer.prototype.isNumeric_ = function(c, opt_decimal) { - var decimal = opt_decimal !== undefined ? opt_decimal : false; - return c >= '0' && c <= '9' || c == '.' && !decimal; -}; - - -/** - * @param {string} c Character. - * @return {boolean} Whether the character is whitespace. - * @private - */ -ol.format.WKT.Lexer.prototype.isWhiteSpace_ = function(c) { - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -}; - - -/** - * @return {string} Next string character. - * @private - */ -ol.format.WKT.Lexer.prototype.nextChar_ = function() { - return this.wkt.charAt(++this.index_); -}; - - -/** - * Fetch and return the next token. - * @return {!ol.WKTToken} Next string token. - */ -ol.format.WKT.Lexer.prototype.nextToken = function() { - var c = this.nextChar_(); - var token = {position: this.index_, value: c}; - - if (c == '(') { - token.type = ol.format.WKT.TokenType_.LEFT_PAREN; - } else if (c == ',') { - token.type = ol.format.WKT.TokenType_.COMMA; - } else if (c == ')') { - token.type = ol.format.WKT.TokenType_.RIGHT_PAREN; - } else if (this.isNumeric_(c) || c == '-') { - token.type = ol.format.WKT.TokenType_.NUMBER; - token.value = this.readNumber_(); - } else if (this.isAlpha_(c)) { - token.type = ol.format.WKT.TokenType_.TEXT; - token.value = this.readText_(); - } else if (this.isWhiteSpace_(c)) { - return this.nextToken(); - } else if (c === '') { - token.type = ol.format.WKT.TokenType_.EOF; - } else { - throw new Error('Unexpected character: ' + c); - } - - return token; -}; - - -/** - * @return {number} Numeric token value. - * @private - */ -ol.format.WKT.Lexer.prototype.readNumber_ = function() { - var c, index = this.index_; - var decimal = false; - var scientificNotation = false; - do { - if (c == '.') { - decimal = true; - } else if (c == 'e' || c == 'E') { - scientificNotation = true; - } - c = this.nextChar_(); - } while ( - this.isNumeric_(c, decimal) || - // if we haven't detected a scientific number before, 'e' or 'E' - // hint that we should continue to read - !scientificNotation && (c == 'e' || c == 'E') || - // once we know that we have a scientific number, both '-' and '+' - // are allowed - scientificNotation && (c == '-' || c == '+') - ); - return parseFloat(this.wkt.substring(index, this.index_--)); -}; - - -/** - * @return {string} String token value. - * @private - */ -ol.format.WKT.Lexer.prototype.readText_ = function() { - var c, index = this.index_; - do { - c = this.nextChar_(); - } while (this.isAlpha_(c)); - return this.wkt.substring(index, this.index_--).toUpperCase(); -}; - - -/** - * Class to parse the tokens from the WKT string. - * @param {ol.format.WKT.Lexer} lexer The lexer. - * @constructor - * @protected - */ -ol.format.WKT.Parser = function(lexer) { - - /** - * @type {ol.format.WKT.Lexer} - * @private - */ - this.lexer_ = lexer; - - /** - * @type {ol.WKTToken} - * @private - */ - this.token_; - - /** - * @type {ol.geom.GeometryLayout} - * @private - */ - this.layout_ = ol.geom.GeometryLayout.XY; -}; - - -/** - * Fetch the next token form the lexer and replace the active token. - * @private - */ -ol.format.WKT.Parser.prototype.consume_ = function() { - this.token_ = this.lexer_.nextToken(); -}; - -/** - * Tests if the given type matches the type of the current token. - * @param {ol.format.WKT.TokenType_} type Token type. - * @return {boolean} Whether the token matches the given type. - */ -ol.format.WKT.Parser.prototype.isTokenType = function(type) { - var isMatch = this.token_.type == type; - return isMatch; -}; - - -/** - * If the given type matches the current token, consume it. - * @param {ol.format.WKT.TokenType_} type Token type. - * @return {boolean} Whether the token matches the given type. - */ -ol.format.WKT.Parser.prototype.match = function(type) { - var isMatch = this.isTokenType(type); - if (isMatch) { - this.consume_(); - } - return isMatch; -}; - - -/** - * Try to parse the tokens provided by the lexer. - * @return {ol.geom.Geometry} The geometry. - */ -ol.format.WKT.Parser.prototype.parse = function() { - this.consume_(); - var geometry = this.parseGeometry_(); - return geometry; -}; - - -/** - * Try to parse the dimensional info. - * @return {ol.geom.GeometryLayout} The layout. - * @private - */ -ol.format.WKT.Parser.prototype.parseGeometryLayout_ = function() { - var layout = ol.geom.GeometryLayout.XY; - var dimToken = this.token_; - if (this.isTokenType(ol.format.WKT.TokenType_.TEXT)) { - var dimInfo = dimToken.value; - if (dimInfo === ol.format.WKT.Z) { - layout = ol.geom.GeometryLayout.XYZ; - } else if (dimInfo === ol.format.WKT.M) { - layout = ol.geom.GeometryLayout.XYM; - } else if (dimInfo === ol.format.WKT.ZM) { - layout = ol.geom.GeometryLayout.XYZM; - } - if (layout !== ol.geom.GeometryLayout.XY) { - this.consume_(); - } - } - return layout; -}; - - -/** - * @return {!ol.geom.Geometry} The geometry. - * @private - */ -ol.format.WKT.Parser.prototype.parseGeometry_ = function() { - var token = this.token_; - if (this.match(ol.format.WKT.TokenType_.TEXT)) { - var geomType = token.value; - this.layout_ = this.parseGeometryLayout_(); - if (geomType == ol.geom.GeometryType.GEOMETRY_COLLECTION.toUpperCase()) { - var geometries = this.parseGeometryCollectionText_(); - return new ol.geom.GeometryCollection(geometries); - } else { - var parser = ol.format.WKT.Parser.GeometryParser_[geomType]; - var ctor = ol.format.WKT.Parser.GeometryConstructor_[geomType]; - if (!parser || !ctor) { - throw new Error('Invalid geometry type: ' + geomType); - } - var coordinates = parser.call(this); - return new ctor(coordinates, this.layout_); - } - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<ol.geom.Geometry>} A collection of geometries. - * @private - */ -ol.format.WKT.Parser.prototype.parseGeometryCollectionText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var geometries = []; - do { - geometries.push(this.parseGeometry_()); - } while (this.match(ol.format.WKT.TokenType_.COMMA)); - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return geometries; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {Array.<number>} All values in a point. - * @private - */ -ol.format.WKT.Parser.prototype.parsePointText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var coordinates = this.parsePoint_(); - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return null; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<!Array.<number>>} All points in a linestring. - * @private - */ -ol.format.WKT.Parser.prototype.parseLineStringText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var coordinates = this.parsePointList_(); - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<!Array.<number>>} All points in a polygon. - * @private - */ -ol.format.WKT.Parser.prototype.parsePolygonText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var coordinates = this.parseLineStringTextList_(); - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<!Array.<number>>} All points in a multipoint. - * @private - */ -ol.format.WKT.Parser.prototype.parseMultiPointText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var coordinates; - if (this.token_.type == ol.format.WKT.TokenType_.LEFT_PAREN) { - coordinates = this.parsePointTextList_(); - } else { - coordinates = this.parsePointList_(); - } - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<!Array.<number>>} All linestring points - * in a multilinestring. - * @private - */ -ol.format.WKT.Parser.prototype.parseMultiLineStringText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var coordinates = this.parseLineStringTextList_(); - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<!Array.<number>>} All polygon points in a multipolygon. - * @private - */ -ol.format.WKT.Parser.prototype.parseMultiPolygonText_ = function() { - if (this.match(ol.format.WKT.TokenType_.LEFT_PAREN)) { - var coordinates = this.parsePolygonTextList_(); - if (this.match(ol.format.WKT.TokenType_.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<number>} A point. - * @private - */ -ol.format.WKT.Parser.prototype.parsePoint_ = function() { - var coordinates = []; - var dimensions = this.layout_.length; - for (var i = 0; i < dimensions; ++i) { - var token = this.token_; - if (this.match(ol.format.WKT.TokenType_.NUMBER)) { - coordinates.push(token.value); - } else { - break; - } - } - if (coordinates.length == dimensions) { - return coordinates; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.<!Array.<number>>} An array of points. - * @private - */ -ol.format.WKT.Parser.prototype.parsePointList_ = function() { - var coordinates = [this.parsePoint_()]; - while (this.match(ol.format.WKT.TokenType_.COMMA)) { - coordinates.push(this.parsePoint_()); - } - return coordinates; -}; - - -/** - * @return {!Array.<!Array.<number>>} An array of points. - * @private - */ -ol.format.WKT.Parser.prototype.parsePointTextList_ = function() { - var coordinates = [this.parsePointText_()]; - while (this.match(ol.format.WKT.TokenType_.COMMA)) { - coordinates.push(this.parsePointText_()); - } - return coordinates; -}; - - -/** - * @return {!Array.<!Array.<number>>} An array of points. - * @private - */ -ol.format.WKT.Parser.prototype.parseLineStringTextList_ = function() { - var coordinates = [this.parseLineStringText_()]; - while (this.match(ol.format.WKT.TokenType_.COMMA)) { - coordinates.push(this.parseLineStringText_()); - } - return coordinates; -}; - - -/** - * @return {!Array.<!Array.<number>>} An array of points. - * @private - */ -ol.format.WKT.Parser.prototype.parsePolygonTextList_ = function() { - var coordinates = [this.parsePolygonText_()]; - while (this.match(ol.format.WKT.TokenType_.COMMA)) { - coordinates.push(this.parsePolygonText_()); - } - return coordinates; -}; - - -/** - * @return {boolean} Whether the token implies an empty geometry. - * @private - */ -ol.format.WKT.Parser.prototype.isEmptyGeometry_ = function() { - var isEmpty = this.isTokenType(ol.format.WKT.TokenType_.TEXT) && - this.token_.value == ol.format.WKT.EMPTY; - if (isEmpty) { - this.consume_(); - } - return isEmpty; -}; - - -/** - * Create an error message for an unexpected token error. - * @return {string} Error message. - * @private - */ -ol.format.WKT.Parser.prototype.formatErrorMessage_ = function() { - return 'Unexpected `' + this.token_.value + '` at position ' + - this.token_.position + ' in `' + this.lexer_.wkt + '`'; -}; - - -/** - * @enum {function (new:ol.geom.Geometry, Array, ol.geom.GeometryLayout)} - * @private - */ -ol.format.WKT.Parser.GeometryConstructor_ = { - 'POINT': ol.geom.Point, - 'LINESTRING': ol.geom.LineString, - 'POLYGON': ol.geom.Polygon, - 'MULTIPOINT': ol.geom.MultiPoint, - 'MULTILINESTRING': ol.geom.MultiLineString, - 'MULTIPOLYGON': ol.geom.MultiPolygon -}; - - -/** - * @enum {(function(): Array)} - * @private - */ -ol.format.WKT.Parser.GeometryParser_ = { - 'POINT': ol.format.WKT.Parser.prototype.parsePointText_, - 'LINESTRING': ol.format.WKT.Parser.prototype.parseLineStringText_, - 'POLYGON': ol.format.WKT.Parser.prototype.parsePolygonText_, - 'MULTIPOINT': ol.format.WKT.Parser.prototype.parseMultiPointText_, - 'MULTILINESTRING': ol.format.WKT.Parser.prototype.parseMultiLineStringText_, - 'MULTIPOLYGON': ol.format.WKT.Parser.prototype.parseMultiPolygonText_ -}; - -goog.provide('ol.format.WMSCapabilities'); - -goog.require('ol'); -goog.require('ol.format.XLink'); -goog.require('ol.format.XML'); -goog.require('ol.format.XSD'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Format for reading WMS capabilities data - * - * @constructor - * @extends {ol.format.XML} - * @api - */ -ol.format.WMSCapabilities = function() { - - ol.format.XML.call(this); - - /** - * @type {string|undefined} - */ - this.version = undefined; -}; -ol.inherits(ol.format.WMSCapabilities, ol.format.XML); - - -/** - * Read a WMS capabilities document. - * - * @function - * @param {Document|Node|string} source The XML source. - * @return {Object} An object representing the WMS capabilities. - * @api - */ -ol.format.WMSCapabilities.prototype.read; - - -/** - * @inheritDoc - */ -ol.format.WMSCapabilities.prototype.readFromDocument = function(doc) { - for (var n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -ol.format.WMSCapabilities.prototype.readFromNode = function(node) { - this.version = node.getAttribute('version').trim(); - var wmsCapabilityObject = ol.xml.pushParseAndPop({ - 'version': this.version - }, ol.format.WMSCapabilities.PARSERS_, node, []); - return wmsCapabilityObject ? wmsCapabilityObject : null; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Attribution object. - */ -ol.format.WMSCapabilities.readAttribution_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object} Bounding box object. - */ -ol.format.WMSCapabilities.readBoundingBox_ = function(node, objectStack) { - var extent = [ - ol.format.XSD.readDecimalString(node.getAttribute('minx')), - ol.format.XSD.readDecimalString(node.getAttribute('miny')), - ol.format.XSD.readDecimalString(node.getAttribute('maxx')), - ol.format.XSD.readDecimalString(node.getAttribute('maxy')) - ]; - - var resolutions = [ - ol.format.XSD.readDecimalString(node.getAttribute('resx')), - ol.format.XSD.readDecimalString(node.getAttribute('resy')) - ]; - - return { - 'crs': node.getAttribute('CRS'), - 'extent': extent, - 'res': resolutions - }; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {ol.Extent|undefined} Bounding box object. - */ -ol.format.WMSCapabilities.readEXGeographicBoundingBox_ = function(node, objectStack) { - var geographicBoundingBox = ol.xml.pushParseAndPop( - {}, - ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_, - node, objectStack); - if (!geographicBoundingBox) { - return undefined; - } - var westBoundLongitude = /** @type {number|undefined} */ - (geographicBoundingBox['westBoundLongitude']); - var southBoundLatitude = /** @type {number|undefined} */ - (geographicBoundingBox['southBoundLatitude']); - var eastBoundLongitude = /** @type {number|undefined} */ - (geographicBoundingBox['eastBoundLongitude']); - var northBoundLatitude = /** @type {number|undefined} */ - (geographicBoundingBox['northBoundLatitude']); - if (westBoundLongitude === undefined || southBoundLatitude === undefined || - eastBoundLongitude === undefined || northBoundLatitude === undefined) { - return undefined; - } - return [ - westBoundLongitude, southBoundLatitude, - eastBoundLongitude, northBoundLatitude - ]; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} Capability object. - */ -ol.format.WMSCapabilities.readCapability_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.CAPABILITY_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} Service object. - */ -ol.format.WMSCapabilities.readService_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.SERVICE_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} Contact information object. - */ -ol.format.WMSCapabilities.readContactInformation_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_, - node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} Contact person object. - */ -ol.format.WMSCapabilities.readContactPersonPrimary_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_, - node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} Contact address object. - */ -ol.format.WMSCapabilities.readContactAddress_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_, - node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<string>|undefined} Format array. - */ -ol.format.WMSCapabilities.readException_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - [], ol.format.WMSCapabilities.EXCEPTION_PARSERS_, node, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Object|undefined} Layer object. - */ -ol.format.WMSCapabilities.readCapabilityLayer_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Layer object. - */ -ol.format.WMSCapabilities.readLayer_ = function(node, objectStack) { - var parentLayerObject = /** @type {Object.<string,*>} */ - (objectStack[objectStack.length - 1]); - - var layerObject = ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack); - - if (!layerObject) { - return undefined; - } - var queryable = - ol.format.XSD.readBooleanString(node.getAttribute('queryable')); - if (queryable === undefined) { - queryable = parentLayerObject['queryable']; - } - layerObject['queryable'] = queryable !== undefined ? queryable : false; - - var cascaded = ol.format.XSD.readNonNegativeIntegerString( - node.getAttribute('cascaded')); - if (cascaded === undefined) { - cascaded = parentLayerObject['cascaded']; - } - layerObject['cascaded'] = cascaded; - - var opaque = ol.format.XSD.readBooleanString(node.getAttribute('opaque')); - if (opaque === undefined) { - opaque = parentLayerObject['opaque']; - } - layerObject['opaque'] = opaque !== undefined ? opaque : false; - - var noSubsets = - ol.format.XSD.readBooleanString(node.getAttribute('noSubsets')); - if (noSubsets === undefined) { - noSubsets = parentLayerObject['noSubsets']; - } - layerObject['noSubsets'] = noSubsets !== undefined ? noSubsets : false; - - var fixedWidth = - ol.format.XSD.readDecimalString(node.getAttribute('fixedWidth')); - if (!fixedWidth) { - fixedWidth = parentLayerObject['fixedWidth']; - } - layerObject['fixedWidth'] = fixedWidth; - - var fixedHeight = - ol.format.XSD.readDecimalString(node.getAttribute('fixedHeight')); - if (!fixedHeight) { - fixedHeight = parentLayerObject['fixedHeight']; - } - layerObject['fixedHeight'] = fixedHeight; - - // See 7.2.4.8 - var addKeys = ['Style', 'CRS', 'AuthorityURL']; - addKeys.forEach(function(key) { - if (key in parentLayerObject) { - var childValue = layerObject[key] || []; - layerObject[key] = childValue.concat(parentLayerObject[key]); - } - }); - - var replaceKeys = ['EX_GeographicBoundingBox', 'BoundingBox', 'Dimension', - 'Attribution', 'MinScaleDenominator', 'MaxScaleDenominator']; - replaceKeys.forEach(function(key) { - if (!(key in layerObject)) { - var parentValue = parentLayerObject[key]; - layerObject[key] = parentValue; - } - }); - - return layerObject; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object} Dimension object. - */ -ol.format.WMSCapabilities.readDimension_ = function(node, objectStack) { - var dimensionObject = { - 'name': node.getAttribute('name'), - 'units': node.getAttribute('units'), - 'unitSymbol': node.getAttribute('unitSymbol'), - 'default': node.getAttribute('default'), - 'multipleValues': ol.format.XSD.readBooleanString( - node.getAttribute('multipleValues')), - 'nearestValue': ol.format.XSD.readBooleanString( - node.getAttribute('nearestValue')), - 'current': ol.format.XSD.readBooleanString(node.getAttribute('current')), - 'values': ol.format.XSD.readString(node) - }; - return dimensionObject; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Online resource object. - */ -ol.format.WMSCapabilities.readFormatOnlineresource_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_, - node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Request object. - */ -ol.format.WMSCapabilities.readRequest_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.REQUEST_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} DCP type object. - */ -ol.format.WMSCapabilities.readDCPType_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.DCPTYPE_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} HTTP object. - */ -ol.format.WMSCapabilities.readHTTP_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.HTTP_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Operation type object. - */ -ol.format.WMSCapabilities.readOperationType_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Online resource object. - */ -ol.format.WMSCapabilities.readSizedFormatOnlineresource_ = function(node, objectStack) { - var formatOnlineresource = - ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack); - if (formatOnlineresource) { - var size = [ - ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('width')), - ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('height')) - ]; - formatOnlineresource['size'] = size; - return formatOnlineresource; - } - return undefined; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Authority URL object. - */ -ol.format.WMSCapabilities.readAuthorityURL_ = function(node, objectStack) { - var authorityObject = - ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack); - if (authorityObject) { - authorityObject['name'] = node.getAttribute('name'); - return authorityObject; - } - return undefined; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Metadata URL object. - */ -ol.format.WMSCapabilities.readMetadataURL_ = function(node, objectStack) { - var metadataObject = - ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack); - if (metadataObject) { - metadataObject['type'] = node.getAttribute('type'); - return metadataObject; - } - return undefined; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Style object. - */ -ol.format.WMSCapabilities.readStyle_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - {}, ol.format.WMSCapabilities.STYLE_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Array.<string>|undefined} Keyword list. - */ -ol.format.WMSCapabilities.readKeywordList_ = function(node, objectStack) { - return ol.xml.pushParseAndPop( - [], ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_, node, objectStack); -}; - - -/** - * @const - * @private - * @type {Array.<string>} - */ -ol.format.WMSCapabilities.NAMESPACE_URIS_ = [ - null, - 'http://www.opengis.net/wms' -]; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Service': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readService_), - 'Capability': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readCapability_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.CAPABILITY_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Request': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readRequest_), - 'Exception': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readException_), - 'Layer': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readCapabilityLayer_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.SERVICE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'KeywordList': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readKeywordList_), - 'OnlineResource': ol.xml.makeObjectPropertySetter( - ol.format.XLink.readHref), - 'ContactInformation': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readContactInformation_), - 'Fees': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'AccessConstraints': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'LayerLimit': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MaxWidth': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MaxHeight': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'ContactPersonPrimary': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readContactPersonPrimary_), - 'ContactPosition': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'ContactAddress': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readContactAddress_), - 'ContactVoiceTelephone': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'ContactFacsimileTelephone': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'ContactElectronicMailAddress': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'ContactPerson': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'ContactOrganization': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'AddressType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'StateOrProvince': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'PostCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.EXCEPTION_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Format': ol.xml.makeArrayPusher(ol.format.XSD.readString) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.LAYER_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'KeywordList': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readKeywordList_), - 'CRS': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString), - 'EX_GeographicBoundingBox': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readEXGeographicBoundingBox_), - 'BoundingBox': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readBoundingBox_), - 'Dimension': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readDimension_), - 'Attribution': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readAttribution_), - 'AuthorityURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readAuthorityURL_), - 'Identifier': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString), - 'MetadataURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readMetadataURL_), - 'DataURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readFormatOnlineresource_), - 'FeatureListURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readFormatOnlineresource_), - 'Style': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readStyle_), - 'MinScaleDenominator': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal), - 'MaxScaleDenominator': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal), - 'Layer': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readLayer_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'OnlineResource': ol.xml.makeObjectPropertySetter( - ol.format.XLink.readHref), - 'LogoURL': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readSizedFormatOnlineresource_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_ = - ol.xml.makeStructureNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'westBoundLongitude': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal), - 'eastBoundLongitude': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal), - 'southBoundLatitude': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal), - 'northBoundLatitude': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.REQUEST_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'GetCapabilities': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readOperationType_), - 'GetMap': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readOperationType_), - 'GetFeatureInfo': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readOperationType_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Format': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString), - 'DCPType': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readDCPType_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.DCPTYPE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'HTTP': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readHTTP_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.HTTP_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Get': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readFormatOnlineresource_), - 'Post': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readFormatOnlineresource_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'LegendURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMSCapabilities.readSizedFormatOnlineresource_), - 'StyleSheetURL': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readFormatOnlineresource_), - 'StyleURL': ol.xml.makeObjectPropertySetter( - ol.format.WMSCapabilities.readFormatOnlineresource_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_ = - ol.xml.makeStructureNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Format': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), - 'OnlineResource': ol.xml.makeObjectPropertySetter( - ol.format.XLink.readHref) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMSCapabilities.NAMESPACE_URIS_, { - 'Keyword': ol.xml.makeArrayPusher(ol.format.XSD.readString) - }); - -goog.provide('ol.format.WMSGetFeatureInfo'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.format.GML2'); -goog.require('ol.format.XMLFeature'); -goog.require('ol.obj'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Format for reading WMSGetFeatureInfo format. It uses - * {@link ol.format.GML2} to read features. - * - * @constructor - * @extends {ol.format.XMLFeature} - * @param {olx.format.WMSGetFeatureInfoOptions=} opt_options Options. - * @api - */ -ol.format.WMSGetFeatureInfo = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {string} - */ - this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver'; - - - /** - * @private - * @type {ol.format.GML2} - */ - this.gmlFormat_ = new ol.format.GML2(); - - - /** - * @private - * @type {Array.<string>} - */ - this.layers_ = options.layers ? options.layers : null; - - ol.format.XMLFeature.call(this); -}; -ol.inherits(ol.format.WMSGetFeatureInfo, ol.format.XMLFeature); - - -/** - * @const - * @type {string} - * @private - */ -ol.format.WMSGetFeatureInfo.featureIdentifier_ = '_feature'; - - -/** - * @const - * @type {string} - * @private - */ -ol.format.WMSGetFeatureInfo.layerIdentifier_ = '_layer'; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Array.<ol.Feature>} Features. - * @private - */ -ol.format.WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) { - node.setAttribute('namespaceURI', this.featureNS_); - var localName = node.localName; - /** @type {Array.<ol.Feature>} */ - var features = []; - if (node.childNodes.length === 0) { - return features; - } - if (localName == 'msGMLOutput') { - for (var i = 0, ii = node.childNodes.length; i < ii; i++) { - var layer = node.childNodes[i]; - if (layer.nodeType !== Node.ELEMENT_NODE) { - continue; - } - var context = objectStack[0]; - - var toRemove = ol.format.WMSGetFeatureInfo.layerIdentifier_; - var layerName = layer.localName.replace(toRemove, ''); - - if (this.layers_ && !ol.array.includes(this.layers_, layerName)) { - continue; - } - - var featureType = layerName + - ol.format.WMSGetFeatureInfo.featureIdentifier_; - - context['featureType'] = featureType; - context['featureNS'] = this.featureNS_; - - var parsers = {}; - parsers[featureType] = ol.xml.makeArrayPusher( - this.gmlFormat_.readFeatureElement, this.gmlFormat_); - var parsersNS = ol.xml.makeStructureNS( - [context['featureNS'], null], parsers); - layer.setAttribute('namespaceURI', this.featureNS_); - var layerFeatures = ol.xml.pushParseAndPop( - [], parsersNS, layer, objectStack, this.gmlFormat_); - if (layerFeatures) { - ol.array.extend(features, layerFeatures); - } - } - } - if (localName == 'FeatureCollection') { - var gmlFeatures = ol.xml.pushParseAndPop([], - this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, - [{}], this.gmlFormat_); - if (gmlFeatures) { - features = gmlFeatures; - } - } - return features; -}; - - -/** - * Read all features from a WMSGetFeatureInfo response. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {olx.format.ReadOptions=} opt_options Options. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.format.WMSGetFeatureInfo.prototype.readFeatures; - - -/** - * @inheritDoc - */ -ol.format.WMSGetFeatureInfo.prototype.readFeaturesFromNode = function(node, opt_options) { - var options = {}; - if (opt_options) { - ol.obj.assign(options, this.getReadOptions(node, opt_options)); - } - return this.readFeatures_(node, [options]); -}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.WMSGetFeatureInfo.prototype.writeFeatureNode = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.WMSGetFeatureInfo.prototype.writeFeaturesNode = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -ol.format.WMSGetFeatureInfo.prototype.writeGeometryNode = function(geometry, opt_options) {}; - -goog.provide('ol.format.WMTSCapabilities'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.format.OWS'); -goog.require('ol.format.XLink'); -goog.require('ol.format.XML'); -goog.require('ol.format.XSD'); -goog.require('ol.xml'); - - -/** - * @classdesc - * Format for reading WMTS capabilities data. - * - * @constructor - * @extends {ol.format.XML} - * @api - */ -ol.format.WMTSCapabilities = function() { - ol.format.XML.call(this); - - /** - * @type {ol.format.OWS} - * @private - */ - this.owsParser_ = new ol.format.OWS(); -}; -ol.inherits(ol.format.WMTSCapabilities, ol.format.XML); - - -/** - * Read a WMTS capabilities document. - * - * @function - * @param {Document|Node|string} source The XML source. - * @return {Object} An object representing the WMTS capabilities. - * @api - */ -ol.format.WMTSCapabilities.prototype.read; - - -/** - * @inheritDoc - */ -ol.format.WMTSCapabilities.prototype.readFromDocument = function(doc) { - for (var n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -ol.format.WMTSCapabilities.prototype.readFromNode = function(node) { - var version = node.getAttribute('version').trim(); - var WMTSCapabilityObject = this.owsParser_.readFromNode(node); - if (!WMTSCapabilityObject) { - return null; - } - WMTSCapabilityObject['version'] = version; - WMTSCapabilityObject = ol.xml.pushParseAndPop(WMTSCapabilityObject, - ol.format.WMTSCapabilities.PARSERS_, node, []); - return WMTSCapabilityObject ? WMTSCapabilityObject : null; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Attribution object. - */ -ol.format.WMTSCapabilities.readContents_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.CONTENTS_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Layers object. - */ -ol.format.WMTSCapabilities.readLayer_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.LAYER_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Tile Matrix Set object. - */ -ol.format.WMTSCapabilities.readTileMatrixSet_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.TMS_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Style object. - */ -ol.format.WMTSCapabilities.readStyle_ = function(node, objectStack) { - var style = ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.STYLE_PARSERS_, node, objectStack); - if (!style) { - return undefined; - } - var isDefault = node.getAttribute('isDefault') === 'true'; - style['isDefault'] = isDefault; - return style; - -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Tile Matrix Set Link object. - */ -ol.format.WMTSCapabilities.readTileMatrixSetLink_ = function(node, - objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Dimension object. - */ -ol.format.WMTSCapabilities.readDimensions_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.DIMENSION_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Resource URL object. - */ -ol.format.WMTSCapabilities.readResourceUrl_ = function(node, objectStack) { - var format = node.getAttribute('format'); - var template = node.getAttribute('template'); - var resourceType = node.getAttribute('resourceType'); - var resource = {}; - if (format) { - resource['format'] = format; - } - if (template) { - resource['template'] = template; - } - if (resourceType) { - resource['resourceType'] = resourceType; - } - return resource; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} WGS84 BBox object. - */ -ol.format.WMTSCapabilities.readWgs84BoundingBox_ = function(node, objectStack) { - var coordinates = ol.xml.pushParseAndPop([], - ol.format.WMTSCapabilities.WGS84_BBOX_READERS_, node, objectStack); - if (coordinates.length != 2) { - return undefined; - } - return ol.extent.boundingExtent(coordinates); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Legend object. - */ -ol.format.WMTSCapabilities.readLegendUrl_ = function(node, objectStack) { - var legend = {}; - legend['format'] = node.getAttribute('format'); - legend['href'] = ol.format.XLink.readHref(node); - return legend; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} Coordinates object. - */ -ol.format.WMTSCapabilities.readCoordinates_ = function(node, objectStack) { - var coordinates = ol.format.XSD.readString(node).split(' '); - if (!coordinates || coordinates.length != 2) { - return undefined; - } - var x = +coordinates[0]; - var y = +coordinates[1]; - if (isNaN(x) || isNaN(y)) { - return undefined; - } - return [x, y]; -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} TileMatrix object. - */ -ol.format.WMTSCapabilities.readTileMatrix_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.TM_PARSERS_, node, objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} TileMatrixSetLimits Object. - */ -ol.format.WMTSCapabilities.readTileMatrixLimitsList_ = function(node, - objectStack) { - return ol.xml.pushParseAndPop([], - ol.format.WMTSCapabilities.TMS_LIMITS_LIST_PARSERS_, node, - objectStack); -}; - - -/** - * @private - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Object|undefined} TileMatrixLimits Array. - */ -ol.format.WMTSCapabilities.readTileMatrixLimits_ = function(node, objectStack) { - return ol.xml.pushParseAndPop({}, - ol.format.WMTSCapabilities.TMS_LIMITS_PARSERS_, node, objectStack); -}; - - -/** - * @const - * @private - * @type {Array.<string>} - */ -ol.format.WMTSCapabilities.NAMESPACE_URIS_ = [ - null, - 'http://www.opengis.net/wmts/1.0' -]; - - -/** - * @const - * @private - * @type {Array.<string>} - */ -ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_ = [ - null, - 'http://www.opengis.net/ows/1.1' -]; - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'Contents': ol.xml.makeObjectPropertySetter( - ol.format.WMTSCapabilities.readContents_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.CONTENTS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'Layer': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readLayer_), - 'TileMatrixSet': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readTileMatrixSet_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.LAYER_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'Style': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readStyle_), - 'Format': ol.xml.makeObjectPropertyPusher( - ol.format.XSD.readString), - 'TileMatrixSetLink': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readTileMatrixSetLink_), - 'Dimension': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readDimensions_), - 'ResourceURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readResourceUrl_) - }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { - 'Title': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'Abstract': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'WGS84BoundingBox': ol.xml.makeObjectPropertySetter( - ol.format.WMTSCapabilities.readWgs84BoundingBox_), - 'Identifier': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - })); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.STYLE_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'LegendURL': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readLegendUrl_) - }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { - 'Title': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'Identifier': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - })); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'TileMatrixSet': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'TileMatrixSetLimits': ol.xml.makeObjectPropertySetter( - ol.format.WMTSCapabilities.readTileMatrixLimitsList_) - }); - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.TMS_LIMITS_LIST_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'TileMatrixLimits': ol.xml.makeArrayPusher( - ol.format.WMTSCapabilities.readTileMatrixLimits_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.TMS_LIMITS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'TileMatrix': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'MinTileRow': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MaxTileRow': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MinTileCol': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MaxTileCol': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.DIMENSION_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'Default': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'Value': ol.xml.makeObjectPropertyPusher( - ol.format.XSD.readString) - }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { - 'Identifier': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - })); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.WGS84_BBOX_READERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { - 'LowerCorner': ol.xml.makeArrayPusher( - ol.format.WMTSCapabilities.readCoordinates_), - 'UpperCorner': ol.xml.makeArrayPusher( - ol.format.WMTSCapabilities.readCoordinates_) - }); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.TMS_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'WellKnownScaleSet': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'TileMatrix': ol.xml.makeObjectPropertyPusher( - ol.format.WMTSCapabilities.readTileMatrix_) - }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { - 'SupportedCRS': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString), - 'Identifier': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - })); - - -/** - * @const - * @type {Object.<string, Object.<string, ol.XmlParser>>} - * @private - */ -ol.format.WMTSCapabilities.TM_PARSERS_ = ol.xml.makeStructureNS( - ol.format.WMTSCapabilities.NAMESPACE_URIS_, { - 'TopLeftCorner': ol.xml.makeObjectPropertySetter( - ol.format.WMTSCapabilities.readCoordinates_), - 'ScaleDenominator': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readDecimal), - 'TileWidth': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'TileHeight': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MatrixWidth': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger), - 'MatrixHeight': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readNonNegativeInteger) - }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { - 'Identifier': ol.xml.makeObjectPropertySetter( - ol.format.XSD.readString) - })); - -goog.provide('ol.GeolocationProperty'); - - -/** - * @enum {string} - */ -ol.GeolocationProperty = { - ACCURACY: 'accuracy', - ACCURACY_GEOMETRY: 'accuracyGeometry', - ALTITUDE: 'altitude', - ALTITUDE_ACCURACY: 'altitudeAccuracy', - HEADING: 'heading', - POSITION: 'position', - PROJECTION: 'projection', - SPEED: 'speed', - TRACKING: 'tracking', - TRACKING_OPTIONS: 'trackingOptions' -}; - -// FIXME handle geolocation not supported - -goog.provide('ol.Geolocation'); - -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.GeolocationProperty'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.geom.Polygon'); -goog.require('ol.has'); -goog.require('ol.math'); -goog.require('ol.proj'); -goog.require('ol.sphere.WGS84'); - - -/** - * @classdesc - * Helper class for providing HTML5 Geolocation capabilities. - * The [Geolocation API](http://www.w3.org/TR/geolocation-API/) - * is used to locate a user's position. - * - * To get notified of position changes, register a listener for the generic - * `change` event on your instance of `ol.Geolocation`. - * - * Example: - * - * var geolocation = new ol.Geolocation({ - * // take the projection to use from the map's view - * projection: view.getProjection() - * }); - * // listen to changes in position - * geolocation.on('change', function(evt) { - * window.console.log(geolocation.getPosition()); - * }); - * - * @fires error - * @constructor - * @extends {ol.Object} - * @param {olx.GeolocationOptions=} opt_options Options. - * @api - */ -ol.Geolocation = function(opt_options) { - - ol.Object.call(this); - - var options = opt_options || {}; - - /** - * The unprojected (EPSG:4326) device position. - * @private - * @type {ol.Coordinate} - */ - this.position_ = null; - - /** - * @private - * @type {ol.TransformFunction} - */ - this.transform_ = ol.proj.identityTransform; - - /** - * @private - * @type {number|undefined} - */ - this.watchId_ = undefined; - - ol.events.listen( - this, ol.Object.getChangeEventType(ol.GeolocationProperty.PROJECTION), - this.handleProjectionChanged_, this); - ol.events.listen( - this, ol.Object.getChangeEventType(ol.GeolocationProperty.TRACKING), - this.handleTrackingChanged_, this); - - if (options.projection !== undefined) { - this.setProjection(options.projection); - } - if (options.trackingOptions !== undefined) { - this.setTrackingOptions(options.trackingOptions); - } - - this.setTracking(options.tracking !== undefined ? options.tracking : false); - -}; -ol.inherits(ol.Geolocation, ol.Object); - - -/** - * @inheritDoc - */ -ol.Geolocation.prototype.disposeInternal = function() { - this.setTracking(false); - ol.Object.prototype.disposeInternal.call(this); -}; - - -/** - * @private - */ -ol.Geolocation.prototype.handleProjectionChanged_ = function() { - var projection = this.getProjection(); - if (projection) { - this.transform_ = ol.proj.getTransformFromProjections( - ol.proj.get('EPSG:4326'), projection); - if (this.position_) { - this.set( - ol.GeolocationProperty.POSITION, this.transform_(this.position_)); - } - } -}; - - -/** - * @private - */ -ol.Geolocation.prototype.handleTrackingChanged_ = function() { - if (ol.has.GEOLOCATION) { - var tracking = this.getTracking(); - if (tracking && this.watchId_ === undefined) { - this.watchId_ = navigator.geolocation.watchPosition( - this.positionChange_.bind(this), - this.positionError_.bind(this), - this.getTrackingOptions()); - } else if (!tracking && this.watchId_ !== undefined) { - navigator.geolocation.clearWatch(this.watchId_); - this.watchId_ = undefined; - } - } -}; - - -/** - * @private - * @param {GeolocationPosition} position position event. - */ -ol.Geolocation.prototype.positionChange_ = function(position) { - var coords = position.coords; - this.set(ol.GeolocationProperty.ACCURACY, coords.accuracy); - this.set(ol.GeolocationProperty.ALTITUDE, - coords.altitude === null ? undefined : coords.altitude); - this.set(ol.GeolocationProperty.ALTITUDE_ACCURACY, - coords.altitudeAccuracy === null ? - undefined : coords.altitudeAccuracy); - this.set(ol.GeolocationProperty.HEADING, coords.heading === null ? - undefined : ol.math.toRadians(coords.heading)); - if (!this.position_) { - this.position_ = [coords.longitude, coords.latitude]; - } else { - this.position_[0] = coords.longitude; - this.position_[1] = coords.latitude; - } - var projectedPosition = this.transform_(this.position_); - this.set(ol.GeolocationProperty.POSITION, projectedPosition); - this.set(ol.GeolocationProperty.SPEED, - coords.speed === null ? undefined : coords.speed); - var geometry = ol.geom.Polygon.circular( - ol.sphere.WGS84, this.position_, coords.accuracy); - geometry.applyTransform(this.transform_); - this.set(ol.GeolocationProperty.ACCURACY_GEOMETRY, geometry); - this.changed(); -}; - -/** - * Triggered when the Geolocation returns an error. - * @event error - * @api - */ - -/** - * @private - * @param {GeolocationPositionError} error error object. - */ -ol.Geolocation.prototype.positionError_ = function(error) { - error.type = ol.events.EventType.ERROR; - this.setTracking(false); - this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error)); -}; - - -/** - * Get the accuracy of the position in meters. - * @return {number|undefined} The accuracy of the position measurement in - * meters. - * @observable - * @api - */ -ol.Geolocation.prototype.getAccuracy = function() { - return /** @type {number|undefined} */ ( - this.get(ol.GeolocationProperty.ACCURACY)); -}; - - -/** - * Get a geometry of the position accuracy. - * @return {?ol.geom.Polygon} A geometry of the position accuracy. - * @observable - * @api - */ -ol.Geolocation.prototype.getAccuracyGeometry = function() { - return /** @type {?ol.geom.Polygon} */ ( - this.get(ol.GeolocationProperty.ACCURACY_GEOMETRY) || null); -}; - - -/** - * Get the altitude associated with the position. - * @return {number|undefined} The altitude of the position in meters above mean - * sea level. - * @observable - * @api - */ -ol.Geolocation.prototype.getAltitude = function() { - return /** @type {number|undefined} */ ( - this.get(ol.GeolocationProperty.ALTITUDE)); -}; - - -/** - * Get the altitude accuracy of the position. - * @return {number|undefined} The accuracy of the altitude measurement in - * meters. - * @observable - * @api - */ -ol.Geolocation.prototype.getAltitudeAccuracy = function() { - return /** @type {number|undefined} */ ( - this.get(ol.GeolocationProperty.ALTITUDE_ACCURACY)); -}; - - -/** - * Get the heading as radians clockwise from North. - * @return {number|undefined} The heading of the device in radians from north. - * @observable - * @api - */ -ol.Geolocation.prototype.getHeading = function() { - return /** @type {number|undefined} */ ( - this.get(ol.GeolocationProperty.HEADING)); -}; - - -/** - * Get the position of the device. - * @return {ol.Coordinate|undefined} The current position of the device reported - * in the current projection. - * @observable - * @api - */ -ol.Geolocation.prototype.getPosition = function() { - return /** @type {ol.Coordinate|undefined} */ ( - this.get(ol.GeolocationProperty.POSITION)); -}; - - -/** - * Get the projection associated with the position. - * @return {ol.proj.Projection|undefined} The projection the position is - * reported in. - * @observable - * @api - */ -ol.Geolocation.prototype.getProjection = function() { - return /** @type {ol.proj.Projection|undefined} */ ( - this.get(ol.GeolocationProperty.PROJECTION)); -}; - - -/** - * Get the speed in meters per second. - * @return {number|undefined} The instantaneous speed of the device in meters - * per second. - * @observable - * @api - */ -ol.Geolocation.prototype.getSpeed = function() { - return /** @type {number|undefined} */ ( - this.get(ol.GeolocationProperty.SPEED)); -}; - - -/** - * Determine if the device location is being tracked. - * @return {boolean} The device location is being tracked. - * @observable - * @api - */ -ol.Geolocation.prototype.getTracking = function() { - return /** @type {boolean} */ ( - this.get(ol.GeolocationProperty.TRACKING)); -}; - - -/** - * Get the tracking options. - * @see http://www.w3.org/TR/geolocation-API/#position-options - * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by - * the [HTML5 Geolocation spec - * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). - * @observable - * @api - */ -ol.Geolocation.prototype.getTrackingOptions = function() { - return /** @type {GeolocationPositionOptions|undefined} */ ( - this.get(ol.GeolocationProperty.TRACKING_OPTIONS)); -}; - - -/** - * Set the projection to use for transforming the coordinates. - * @param {ol.ProjectionLike} projection The projection the position is - * reported in. - * @observable - * @api - */ -ol.Geolocation.prototype.setProjection = function(projection) { - this.set(ol.GeolocationProperty.PROJECTION, ol.proj.get(projection)); -}; - - -/** - * Enable or disable tracking. - * @param {boolean} tracking Enable tracking. - * @observable - * @api - */ -ol.Geolocation.prototype.setTracking = function(tracking) { - this.set(ol.GeolocationProperty.TRACKING, tracking); -}; - - -/** - * Set the tracking options. - * @see http://www.w3.org/TR/geolocation-API/#position-options - * @param {GeolocationPositionOptions} options PositionOptions as defined by the - * [HTML5 Geolocation spec - * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). - * @observable - * @api - */ -ol.Geolocation.prototype.setTrackingOptions = function(options) { - this.set(ol.GeolocationProperty.TRACKING_OPTIONS, options); -}; - -goog.provide('ol.geom.Circle'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.geom.flat.deflate'); - - -/** - * @classdesc - * Circle geometry. - * - * @constructor - * @extends {ol.geom.SimpleGeometry} - * @param {ol.Coordinate} center Center. - * @param {number=} opt_radius Radius. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.Circle = function(center, opt_radius, opt_layout) { - ol.geom.SimpleGeometry.call(this); - var radius = opt_radius ? opt_radius : 0; - this.setCenterAndRadius(center, radius, opt_layout); -}; -ol.inherits(ol.geom.Circle, ol.geom.SimpleGeometry); - - -/** - * Make a complete copy of the geometry. - * @return {!ol.geom.Circle} Clone. - * @override - * @api - */ -ol.geom.Circle.prototype.clone = function() { - var circle = new ol.geom.Circle(null); - circle.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); - return circle; -}; - - -/** - * @inheritDoc - */ -ol.geom.Circle.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - var flatCoordinates = this.flatCoordinates; - var dx = x - flatCoordinates[0]; - var dy = y - flatCoordinates[1]; - var squaredDistance = dx * dx + dy * dy; - if (squaredDistance < minSquaredDistance) { - var i; - if (squaredDistance === 0) { - for (i = 0; i < this.stride; ++i) { - closestPoint[i] = flatCoordinates[i]; - } - } else { - var delta = this.getRadius() / Math.sqrt(squaredDistance); - closestPoint[0] = flatCoordinates[0] + delta * dx; - closestPoint[1] = flatCoordinates[1] + delta * dy; - for (i = 2; i < this.stride; ++i) { - closestPoint[i] = flatCoordinates[i]; - } - } - closestPoint.length = this.stride; - return squaredDistance; - } else { - return minSquaredDistance; - } -}; - - -/** - * @inheritDoc - */ -ol.geom.Circle.prototype.containsXY = function(x, y) { - var flatCoordinates = this.flatCoordinates; - var dx = x - flatCoordinates[0]; - var dy = y - flatCoordinates[1]; - return dx * dx + dy * dy <= this.getRadiusSquared_(); -}; - - -/** - * Return the center of the circle as {@link ol.Coordinate coordinate}. - * @return {ol.Coordinate} Center. - * @api - */ -ol.geom.Circle.prototype.getCenter = function() { - return this.flatCoordinates.slice(0, this.stride); -}; - - -/** - * @inheritDoc - */ -ol.geom.Circle.prototype.computeExtent = function(extent) { - var flatCoordinates = this.flatCoordinates; - var radius = flatCoordinates[this.stride] - flatCoordinates[0]; - return ol.extent.createOrUpdate( - flatCoordinates[0] - radius, flatCoordinates[1] - radius, - flatCoordinates[0] + radius, flatCoordinates[1] + radius, - extent); -}; - - -/** - * Return the radius of the circle. - * @return {number} Radius. - * @api - */ -ol.geom.Circle.prototype.getRadius = function() { - return Math.sqrt(this.getRadiusSquared_()); -}; - - -/** - * @private - * @return {number} Radius squared. - */ -ol.geom.Circle.prototype.getRadiusSquared_ = function() { - var dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0]; - var dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1]; - return dx * dx + dy * dy; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Circle.prototype.getType = function() { - return ol.geom.GeometryType.CIRCLE; -}; - - -/** - * @inheritDoc - * @api - */ -ol.geom.Circle.prototype.intersectsExtent = function(extent) { - var circleExtent = this.getExtent(); - if (ol.extent.intersects(extent, circleExtent)) { - var center = this.getCenter(); - - if (extent[0] <= center[0] && extent[2] >= center[0]) { - return true; - } - if (extent[1] <= center[1] && extent[3] >= center[1]) { - return true; - } - - return ol.extent.forEachCorner(extent, this.intersectsCoordinate, this); - } - return false; - -}; - - -/** - * Set the center of the circle as {@link ol.Coordinate coordinate}. - * @param {ol.Coordinate} center Center. - * @api - */ -ol.geom.Circle.prototype.setCenter = function(center) { - var stride = this.stride; - var radius = this.flatCoordinates[stride] - this.flatCoordinates[0]; - var flatCoordinates = center.slice(); - flatCoordinates[stride] = flatCoordinates[0] + radius; - var i; - for (i = 1; i < stride; ++i) { - flatCoordinates[stride + i] = center[i]; - } - this.setFlatCoordinates(this.layout, flatCoordinates); -}; - - -/** - * Set the center (as {@link ol.Coordinate coordinate}) and the radius (as - * number) of the circle. - * @param {ol.Coordinate} center Center. - * @param {number} radius Radius. - * @param {ol.geom.GeometryLayout=} opt_layout Layout. - * @api - */ -ol.geom.Circle.prototype.setCenterAndRadius = function(center, radius, opt_layout) { - if (!center) { - this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); - } else { - this.setLayout(opt_layout, center, 0); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - /** @type {Array.<number>} */ - var flatCoordinates = this.flatCoordinates; - var offset = ol.geom.flat.deflate.coordinate( - flatCoordinates, 0, center, this.stride); - flatCoordinates[offset++] = flatCoordinates[0] + radius; - var i, ii; - for (i = 1, ii = this.stride; i < ii; ++i) { - flatCoordinates[offset++] = flatCoordinates[i]; - } - flatCoordinates.length = offset; - this.changed(); - } -}; - - -/** - * @inheritDoc - */ -ol.geom.Circle.prototype.getCoordinates = function() {}; - - -/** - * @inheritDoc - */ -ol.geom.Circle.prototype.setCoordinates = function(coordinates, opt_layout) {}; - - -/** - * @param {ol.geom.GeometryLayout} layout Layout. - * @param {Array.<number>} flatCoordinates Flat coordinates. - */ -ol.geom.Circle.prototype.setFlatCoordinates = function(layout, flatCoordinates) { - this.setFlatCoordinatesInternal(layout, flatCoordinates); - this.changed(); -}; - - -/** - * Set the radius of the circle. The radius is in the units of the projection. - * @param {number} radius Radius. - * @api - */ -ol.geom.Circle.prototype.setRadius = function(radius) { - this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius; - this.changed(); -}; - - -/** - * Transform each coordinate of the circle from one coordinate reference system - * to another. The geometry is modified in place. - * If you do not want the geometry modified in place, first clone() it and - * then use this function on the clone. - * - * Internally a circle is currently represented by two points: the center of - * the circle `[cx, cy]`, and the point to the right of the circle - * `[cx + r, cy]`. This `transform` function just transforms these two points. - * So the resulting geometry is also a circle, and that circle does not - * correspond to the shape that would be obtained by transforming every point - * of the original circle. - * - * @param {ol.ProjectionLike} source The current projection. Can be a - * string identifier or a {@link ol.proj.Projection} object. - * @param {ol.ProjectionLike} destination The desired projection. Can be a - * string identifier or a {@link ol.proj.Projection} object. - * @return {ol.geom.Circle} This geometry. Note that original geometry is - * modified in place. - * @function - * @api - */ -ol.geom.Circle.prototype.transform; - -goog.provide('ol.geom.flat.geodesic'); - -goog.require('ol.math'); -goog.require('ol.proj'); - - -/** - * @private - * @param {function(number): ol.Coordinate} interpolate Interpolate function. - * @param {ol.TransformFunction} transform Transform from longitude/latitude to - * projected coordinates. - * @param {number} squaredTolerance Squared tolerance. - * @return {Array.<number>} Flat coordinates. - */ -ol.geom.flat.geodesic.line_ = function(interpolate, transform, squaredTolerance) { - // FIXME reduce garbage generation - // FIXME optimize stack operations - - /** @type {Array.<number>} */ - var flatCoordinates = []; - - var geoA = interpolate(0); - var geoB = interpolate(1); - - var a = transform(geoA); - var b = transform(geoB); - - /** @type {Array.<ol.Coordinate>} */ - var geoStack = [geoB, geoA]; - /** @type {Array.<ol.Coordinate>} */ - var stack = [b, a]; - /** @type {Array.<number>} */ - var fractionStack = [1, 0]; - - /** @type {Object.<string, boolean>} */ - var fractions = {}; - - var maxIterations = 1e5; - var geoM, m, fracA, fracB, fracM, key; - - while (--maxIterations > 0 && fractionStack.length > 0) { - // Pop the a coordinate off the stack - fracA = fractionStack.pop(); - geoA = geoStack.pop(); - a = stack.pop(); - // Add the a coordinate if it has not been added yet - key = fracA.toString(); - if (!(key in fractions)) { - flatCoordinates.push(a[0], a[1]); - fractions[key] = true; - } - // Pop the b coordinate off the stack - fracB = fractionStack.pop(); - geoB = geoStack.pop(); - b = stack.pop(); - // Find the m point between the a and b coordinates - fracM = (fracA + fracB) / 2; - geoM = interpolate(fracM); - m = transform(geoM); - if (ol.math.squaredSegmentDistance(m[0], m[1], a[0], a[1], - b[0], b[1]) < squaredTolerance) { - // If the m point is sufficiently close to the straight line, then we - // discard it. Just use the b coordinate and move on to the next line - // segment. - flatCoordinates.push(b[0], b[1]); - key = fracB.toString(); - fractions[key] = true; - } else { - // Otherwise, we need to subdivide the current line segment. Split it - // into two and push the two line segments onto the stack. - fractionStack.push(fracB, fracM, fracM, fracA); - stack.push(b, m, m, a); - geoStack.push(geoB, geoM, geoM, geoA); - } - } - - return flatCoordinates; -}; - - -/** -* Generate a great-circle arcs between two lat/lon points. -* @param {number} lon1 Longitude 1 in degrees. -* @param {number} lat1 Latitude 1 in degrees. -* @param {number} lon2 Longitude 2 in degrees. -* @param {number} lat2 Latitude 2 in degrees. - * @param {ol.proj.Projection} projection Projection. -* @param {number} squaredTolerance Squared tolerance. -* @return {Array.<number>} Flat coordinates. -*/ -ol.geom.flat.geodesic.greatCircleArc = function( - lon1, lat1, lon2, lat2, projection, squaredTolerance) { - - var geoProjection = ol.proj.get('EPSG:4326'); - - var cosLat1 = Math.cos(ol.math.toRadians(lat1)); - var sinLat1 = Math.sin(ol.math.toRadians(lat1)); - var cosLat2 = Math.cos(ol.math.toRadians(lat2)); - var sinLat2 = Math.sin(ol.math.toRadians(lat2)); - var cosDeltaLon = Math.cos(ol.math.toRadians(lon2 - lon1)); - var sinDeltaLon = Math.sin(ol.math.toRadians(lon2 - lon1)); - var d = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosDeltaLon; - - return ol.geom.flat.geodesic.line_( - /** - * @param {number} frac Fraction. - * @return {ol.Coordinate} Coordinate. - */ - function(frac) { - if (1 <= d) { - return [lon2, lat2]; - } - var D = frac * Math.acos(d); - var cosD = Math.cos(D); - var sinD = Math.sin(D); - var y = sinDeltaLon * cosLat2; - var x = cosLat1 * sinLat2 - sinLat1 * cosLat2 * cosDeltaLon; - var theta = Math.atan2(y, x); - var lat = Math.asin(sinLat1 * cosD + cosLat1 * sinD * Math.cos(theta)); - var lon = ol.math.toRadians(lon1) + - Math.atan2(Math.sin(theta) * sinD * cosLat1, - cosD - sinLat1 * Math.sin(lat)); - return [ol.math.toDegrees(lon), ol.math.toDegrees(lat)]; - }, ol.proj.getTransform(geoProjection, projection), squaredTolerance); -}; - - -/** - * Generate a meridian (line at constant longitude). - * @param {number} lon Longitude. - * @param {number} lat1 Latitude 1. - * @param {number} lat2 Latitude 2. - * @param {ol.proj.Projection} projection Projection. - * @param {number} squaredTolerance Squared tolerance. - * @return {Array.<number>} Flat coordinates. - */ -ol.geom.flat.geodesic.meridian = function(lon, lat1, lat2, projection, squaredTolerance) { - var epsg4326Projection = ol.proj.get('EPSG:4326'); - return ol.geom.flat.geodesic.line_( - /** - * @param {number} frac Fraction. - * @return {ol.Coordinate} Coordinate. - */ - function(frac) { - return [lon, lat1 + ((lat2 - lat1) * frac)]; - }, - ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance); -}; - - -/** - * Generate a parallel (line at constant latitude). - * @param {number} lat Latitude. - * @param {number} lon1 Longitude 1. - * @param {number} lon2 Longitude 2. - * @param {ol.proj.Projection} projection Projection. - * @param {number} squaredTolerance Squared tolerance. - * @return {Array.<number>} Flat coordinates. - */ -ol.geom.flat.geodesic.parallel = function(lat, lon1, lon2, projection, squaredTolerance) { - var epsg4326Projection = ol.proj.get('EPSG:4326'); - return ol.geom.flat.geodesic.line_( - /** - * @param {number} frac Fraction. - * @return {ol.Coordinate} Coordinate. - */ - function(frac) { - return [lon1 + ((lon2 - lon1) * frac), lat]; - }, - ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance); -}; - -goog.provide('ol.Graticule'); - -goog.require('ol.coordinate'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryLayout'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.flat.geodesic'); -goog.require('ol.math'); -goog.require('ol.proj'); -goog.require('ol.render.EventType'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Text'); - - -/** - * Render a grid for a coordinate system on a map. - * @constructor - * @param {olx.GraticuleOptions=} opt_options Options. - * @api - */ -ol.Graticule = function(opt_options) { - var options = opt_options || {}; - - /** - * @type {ol.Map} - * @private - */ - this.map_ = null; - - /** - * @type {ol.proj.Projection} - * @private - */ - this.projection_ = null; - - /** - * @type {number} - * @private - */ - this.maxLat_ = Infinity; - - /** - * @type {number} - * @private - */ - this.maxLon_ = Infinity; - - /** - * @type {number} - * @private - */ - this.minLat_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.minLon_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.maxLatP_ = Infinity; - - /** - * @type {number} - * @private - */ - this.maxLonP_ = Infinity; - - /** - * @type {number} - * @private - */ - this.minLatP_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.minLonP_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.targetSize_ = options.targetSize !== undefined ? - options.targetSize : 100; - - /** - * @type {number} - * @private - */ - this.maxLines_ = options.maxLines !== undefined ? options.maxLines : 100; - - /** - * @type {Array.<ol.geom.LineString>} - * @private - */ - this.meridians_ = []; - - /** - * @type {Array.<ol.geom.LineString>} - * @private - */ - this.parallels_ = []; - - /** - * @type {ol.style.Stroke} - * @private - */ - this.strokeStyle_ = options.strokeStyle !== undefined ? - options.strokeStyle : ol.Graticule.DEFAULT_STROKE_STYLE_; - - /** - * @type {ol.TransformFunction|undefined} - * @private - */ - this.fromLonLatTransform_ = undefined; - - /** - * @type {ol.TransformFunction|undefined} - * @private - */ - this.toLonLatTransform_ = undefined; - - /** - * @type {ol.Coordinate} - * @private - */ - this.projectionCenterLonLat_ = null; - - /** - * @type {Array.<ol.GraticuleLabelDataType>} - * @private - */ - this.meridiansLabels_ = null; - - /** - * @type {Array.<ol.GraticuleLabelDataType>} - * @private - */ - this.parallelsLabels_ = null; - - if (options.showLabels == true) { - var degreesToString = ol.coordinate.degreesToStringHDMS; - - /** - * @type {null|function(number):string} - * @private - */ - this.lonLabelFormatter_ = options.lonLabelFormatter == undefined ? - degreesToString.bind(this, 'EW') : options.lonLabelFormatter; - - /** - * @type {function(number):string} - * @private - */ - this.latLabelFormatter_ = options.latLabelFormatter == undefined ? - degreesToString.bind(this, 'NS') : options.latLabelFormatter; - - /** - * Longitude label position in fractions (0..1) of view extent. 0 means - * bottom, 1 means top. - * @type {number} - * @private - */ - this.lonLabelPosition_ = options.lonLabelPosition == undefined ? 0 : - options.lonLabelPosition; - - /** - * Latitude Label position in fractions (0..1) of view extent. 0 means left, 1 - * means right. - * @type {number} - * @private - */ - this.latLabelPosition_ = options.latLabelPosition == undefined ? 1 : - options.latLabelPosition; - - /** - * @type {ol.style.Text} - * @private - */ - this.lonLabelStyle_ = options.lonLabelStyle !== undefined ? options.lonLabelStyle : - new ol.style.Text({ - font: '12px Calibri,sans-serif', - textBaseline: 'bottom', - fill: new ol.style.Fill({ - color: 'rgba(0,0,0,1)' - }), - stroke: new ol.style.Stroke({ - color: 'rgba(255,255,255,1)', - width: 3 - }) - }); - - /** - * @type {ol.style.Text} - * @private - */ - this.latLabelStyle_ = options.latLabelStyle !== undefined ? options.latLabelStyle : - new ol.style.Text({ - font: '12px Calibri,sans-serif', - textAlign: 'end', - fill: new ol.style.Fill({ - color: 'rgba(0,0,0,1)' - }), - stroke: new ol.style.Stroke({ - color: 'rgba(255,255,255,1)', - width: 3 - }) - }); - - this.meridiansLabels_ = []; - this.parallelsLabels_ = []; - } - - this.setMap(options.map !== undefined ? options.map : null); -}; - - -/** - * @type {ol.style.Stroke} - * @private - * @const - */ -ol.Graticule.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({ - color: 'rgba(0,0,0,0.2)' -}); - - -/** - * TODO can be configurable - * @type {Array.<number>} - * @private - */ -ol.Graticule.intervals_ = [90, 45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, - 0.01, 0.005, 0.002, 0.001]; - - -/** - * @param {number} lon Longitude. - * @param {number} minLat Minimal latitude. - * @param {number} maxLat Maximal latitude. - * @param {number} squaredTolerance Squared tolerance. - * @param {ol.Extent} extent Extent. - * @param {number} index Index. - * @return {number} Index. - * @private - */ -ol.Graticule.prototype.addMeridian_ = function(lon, minLat, maxLat, squaredTolerance, extent, index) { - var lineString = this.getMeridian_(lon, minLat, maxLat, - squaredTolerance, index); - if (ol.extent.intersects(lineString.getExtent(), extent)) { - if (this.meridiansLabels_) { - var textPoint = this.getMeridianPoint_(lineString, extent, index); - this.meridiansLabels_[index] = { - geom: textPoint, - text: this.lonLabelFormatter_(lon) - }; - } - this.meridians_[index++] = lineString; - } - return index; -}; - -/** - * @param {ol.geom.LineString} lineString Meridian - * @param {ol.Extent} extent Extent. - * @param {number} index Index. - * @return {ol.geom.Point} Meridian point. - * @private - */ -ol.Graticule.prototype.getMeridianPoint_ = function(lineString, extent, index) { - var flatCoordinates = lineString.getFlatCoordinates(); - var clampedBottom = Math.max(extent[1], flatCoordinates[1]); - var clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]); - var lat = ol.math.clamp( - extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_, - clampedBottom, clampedTop); - var coordinate = [flatCoordinates[0], lat]; - var point = this.meridiansLabels_[index] !== undefined ? - this.meridiansLabels_[index].geom : new ol.geom.Point(null); - point.setCoordinates(coordinate); - return point; -}; - - -/** - * @param {number} lat Latitude. - * @param {number} minLon Minimal longitude. - * @param {number} maxLon Maximal longitude. - * @param {number} squaredTolerance Squared tolerance. - * @param {ol.Extent} extent Extent. - * @param {number} index Index. - * @return {number} Index. - * @private - */ -ol.Graticule.prototype.addParallel_ = function(lat, minLon, maxLon, squaredTolerance, extent, index) { - var lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, - index); - if (ol.extent.intersects(lineString.getExtent(), extent)) { - if (this.parallelsLabels_) { - var textPoint = this.getParallelPoint_(lineString, extent, index); - this.parallelsLabels_[index] = { - geom: textPoint, - text: this.latLabelFormatter_(lat) - }; - } - this.parallels_[index++] = lineString; - } - return index; -}; - - -/** - * @param {ol.geom.LineString} lineString Parallels. - * @param {ol.Extent} extent Extent. - * @param {number} index Index. - * @return {ol.geom.Point} Parallel point. - * @private - */ -ol.Graticule.prototype.getParallelPoint_ = function(lineString, extent, index) { - var flatCoordinates = lineString.getFlatCoordinates(); - var clampedLeft = Math.max(extent[0], flatCoordinates[0]); - var clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]); - var lon = ol.math.clamp( - extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_, - clampedLeft, clampedRight); - var coordinate = [lon, flatCoordinates[1]]; - var point = this.parallelsLabels_[index] !== undefined ? - this.parallelsLabels_[index].geom : new ol.geom.Point(null); - point.setCoordinates(coordinate); - return point; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} squaredTolerance Squared tolerance. - * @private - */ -ol.Graticule.prototype.createGraticule_ = function(extent, center, resolution, squaredTolerance) { - - var interval = this.getInterval_(resolution); - if (interval == -1) { - this.meridians_.length = this.parallels_.length = 0; - if (this.meridiansLabels_) { - this.meridiansLabels_.length = 0; - } - if (this.parallelsLabels_) { - this.parallelsLabels_.length = 0; - } - return; - } - - var centerLonLat = this.toLonLatTransform_(center); - var centerLon = centerLonLat[0]; - var centerLat = centerLonLat[1]; - var maxLines = this.maxLines_; - var cnt, idx, lat, lon; - - var validExtent = [ - Math.max(extent[0], this.minLonP_), - Math.max(extent[1], this.minLatP_), - Math.min(extent[2], this.maxLonP_), - Math.min(extent[3], this.maxLatP_) - ]; - - validExtent = ol.proj.transformExtent(validExtent, this.projection_, - 'EPSG:4326'); - var maxLat = validExtent[3]; - var maxLon = validExtent[2]; - var minLat = validExtent[1]; - var minLon = validExtent[0]; - - // Create meridians - - centerLon = Math.floor(centerLon / interval) * interval; - lon = ol.math.clamp(centerLon, this.minLon_, this.maxLon_); - - idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0); - - cnt = 0; - while (lon != this.minLon_ && cnt++ < maxLines) { - lon = Math.max(lon - interval, this.minLon_); - idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); - } - - lon = ol.math.clamp(centerLon, this.minLon_, this.maxLon_); - - cnt = 0; - while (lon != this.maxLon_ && cnt++ < maxLines) { - lon = Math.min(lon + interval, this.maxLon_); - idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); - } - - this.meridians_.length = idx; - if (this.meridiansLabels_) { - this.meridiansLabels_.length = idx; - } - - // Create parallels - - centerLat = Math.floor(centerLat / interval) * interval; - lat = ol.math.clamp(centerLat, this.minLat_, this.maxLat_); - - idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, 0); - - cnt = 0; - while (lat != this.minLat_ && cnt++ < maxLines) { - lat = Math.max(lat - interval, this.minLat_); - idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); - } - - lat = ol.math.clamp(centerLat, this.minLat_, this.maxLat_); - - cnt = 0; - while (lat != this.maxLat_ && cnt++ < maxLines) { - lat = Math.min(lat + interval, this.maxLat_); - idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); - } - - this.parallels_.length = idx; - if (this.parallelsLabels_) { - this.parallelsLabels_.length = idx; - } - -}; - - -/** - * @param {number} resolution Resolution. - * @return {number} The interval in degrees. - * @private - */ -ol.Graticule.prototype.getInterval_ = function(resolution) { - var centerLon = this.projectionCenterLonLat_[0]; - var centerLat = this.projectionCenterLonLat_[1]; - var interval = -1; - var i, ii, delta, dist; - var target = Math.pow(this.targetSize_ * resolution, 2); - /** @type {Array.<number>} **/ - var p1 = []; - /** @type {Array.<number>} **/ - var p2 = []; - for (i = 0, ii = ol.Graticule.intervals_.length; i < ii; ++i) { - delta = ol.Graticule.intervals_[i] / 2; - p1[0] = centerLon - delta; - p1[1] = centerLat - delta; - p2[0] = centerLon + delta; - p2[1] = centerLat + delta; - this.fromLonLatTransform_(p1, p1); - this.fromLonLatTransform_(p2, p2); - dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2); - if (dist <= target) { - break; - } - interval = ol.Graticule.intervals_[i]; - } - return interval; -}; - - -/** - * Get the map associated with this graticule. - * @return {ol.Map} The map. - * @api - */ -ol.Graticule.prototype.getMap = function() { - return this.map_; -}; - - -/** - * @param {number} lon Longitude. - * @param {number} minLat Minimal latitude. - * @param {number} maxLat Maximal latitude. - * @param {number} squaredTolerance Squared tolerance. - * @return {ol.geom.LineString} The meridian line string. - * @param {number} index Index. - * @private - */ -ol.Graticule.prototype.getMeridian_ = function(lon, minLat, maxLat, - squaredTolerance, index) { - var flatCoordinates = ol.geom.flat.geodesic.meridian(lon, - minLat, maxLat, this.projection_, squaredTolerance); - var lineString = this.meridians_[index] !== undefined ? - this.meridians_[index] : new ol.geom.LineString(null); - lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates); - return lineString; -}; - - -/** - * Get the list of meridians. Meridians are lines of equal longitude. - * @return {Array.<ol.geom.LineString>} The meridians. - * @api - */ -ol.Graticule.prototype.getMeridians = function() { - return this.meridians_; -}; - - -/** - * @param {number} lat Latitude. - * @param {number} minLon Minimal longitude. - * @param {number} maxLon Maximal longitude. - * @param {number} squaredTolerance Squared tolerance. - * @return {ol.geom.LineString} The parallel line string. - * @param {number} index Index. - * @private - */ -ol.Graticule.prototype.getParallel_ = function(lat, minLon, maxLon, - squaredTolerance, index) { - var flatCoordinates = ol.geom.flat.geodesic.parallel(lat, - this.minLon_, this.maxLon_, this.projection_, squaredTolerance); - var lineString = this.parallels_[index] !== undefined ? - this.parallels_[index] : new ol.geom.LineString(null); - lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates); - return lineString; -}; - - -/** - * Get the list of parallels. Pallels are lines of equal latitude. - * @return {Array.<ol.geom.LineString>} The parallels. - * @api - */ -ol.Graticule.prototype.getParallels = function() { - return this.parallels_; -}; - - -/** - * @param {ol.render.Event} e Event. - * @private - */ -ol.Graticule.prototype.handlePostCompose_ = function(e) { - var vectorContext = e.vectorContext; - var frameState = e.frameState; - var extent = frameState.extent; - var viewState = frameState.viewState; - var center = viewState.center; - var projection = viewState.projection; - var resolution = viewState.resolution; - var pixelRatio = frameState.pixelRatio; - var squaredTolerance = - resolution * resolution / (4 * pixelRatio * pixelRatio); - - var updateProjectionInfo = !this.projection_ || - !ol.proj.equivalent(this.projection_, projection); - - if (updateProjectionInfo) { - this.updateProjectionInfo_(projection); - } - - //Fix the extent if wrapped. - //(note: this is the same extent as vectorContext.extent_) - var offsetX = 0; - if (projection.canWrapX()) { - var projectionExtent = projection.getExtent(); - var worldWidth = ol.extent.getWidth(projectionExtent); - var x = frameState.focus[0]; - if (x < projectionExtent[0] || x > projectionExtent[2]) { - var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); - offsetX = worldWidth * worldsAway; - extent = [ - extent[0] + offsetX, extent[1], - extent[2] + offsetX, extent[3] - ]; - } - } - - this.createGraticule_(extent, center, resolution, squaredTolerance); - - // Draw the lines - vectorContext.setFillStrokeStyle(null, this.strokeStyle_); - var i, l, line; - for (i = 0, l = this.meridians_.length; i < l; ++i) { - line = this.meridians_[i]; - vectorContext.drawGeometry(line); - } - for (i = 0, l = this.parallels_.length; i < l; ++i) { - line = this.parallels_[i]; - vectorContext.drawGeometry(line); - } - var labelData; - if (this.meridiansLabels_) { - for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) { - labelData = this.meridiansLabels_[i]; - this.lonLabelStyle_.setText(labelData.text); - vectorContext.setTextStyle(this.lonLabelStyle_); - vectorContext.drawGeometry(labelData.geom); - } - } - if (this.parallelsLabels_) { - for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) { - labelData = this.parallelsLabels_[i]; - this.latLabelStyle_.setText(labelData.text); - vectorContext.setTextStyle(this.latLabelStyle_); - vectorContext.drawGeometry(labelData.geom); - } - } -}; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @private - */ -ol.Graticule.prototype.updateProjectionInfo_ = function(projection) { - var epsg4326Projection = ol.proj.get('EPSG:4326'); - - var extent = projection.getExtent(); - var worldExtent = projection.getWorldExtent(); - var worldExtentP = ol.proj.transformExtent(worldExtent, - epsg4326Projection, projection); - - var maxLat = worldExtent[3]; - var maxLon = worldExtent[2]; - var minLat = worldExtent[1]; - var minLon = worldExtent[0]; - - var maxLatP = worldExtentP[3]; - var maxLonP = worldExtentP[2]; - var minLatP = worldExtentP[1]; - var minLonP = worldExtentP[0]; - - this.maxLat_ = maxLat; - this.maxLon_ = maxLon; - this.minLat_ = minLat; - this.minLon_ = minLon; - - this.maxLatP_ = maxLatP; - this.maxLonP_ = maxLonP; - this.minLatP_ = minLatP; - this.minLonP_ = minLonP; - - - this.fromLonLatTransform_ = ol.proj.getTransform( - epsg4326Projection, projection); - - this.toLonLatTransform_ = ol.proj.getTransform( - projection, epsg4326Projection); - - this.projectionCenterLonLat_ = this.toLonLatTransform_( - ol.extent.getCenter(extent)); - - this.projection_ = projection; -}; - - -/** - * Set the map for this graticule. The graticule will be rendered on the - * provided map. - * @param {ol.Map} map Map. - * @api - */ -ol.Graticule.prototype.setMap = function(map) { - if (this.map_) { - this.map_.un(ol.render.EventType.POSTCOMPOSE, - this.handlePostCompose_, this); - this.map_.render(); - } - if (map) { - map.on(ol.render.EventType.POSTCOMPOSE, - this.handlePostCompose_, this); - map.render(); - } - this.map_ = map; -}; - -goog.provide('ol.ImageBase'); - -goog.require('ol'); -goog.require('ol.events.EventTarget'); -goog.require('ol.events.EventType'); - - -/** - * @constructor - * @abstract - * @extends {ol.events.EventTarget} - * @param {ol.Extent} extent Extent. - * @param {number|undefined} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.ImageState} state State. - * @param {Array.<ol.Attribution>} attributions Attributions. - */ -ol.ImageBase = function(extent, resolution, pixelRatio, state, attributions) { - - ol.events.EventTarget.call(this); - - /** - * @private - * @type {Array.<ol.Attribution>} - */ - this.attributions_ = attributions; - - /** - * @protected - * @type {ol.Extent} - */ - this.extent = extent; - - /** - * @private - * @type {number} - */ - this.pixelRatio_ = pixelRatio; - - /** - * @protected - * @type {number|undefined} - */ - this.resolution = resolution; - - /** - * @protected - * @type {ol.ImageState} - */ - this.state = state; - -}; -ol.inherits(ol.ImageBase, ol.events.EventTarget); - - -/** - * @protected - */ -ol.ImageBase.prototype.changed = function() { - this.dispatchEvent(ol.events.EventType.CHANGE); -}; - - -/** - * @return {Array.<ol.Attribution>} Attributions. - */ -ol.ImageBase.prototype.getAttributions = function() { - return this.attributions_; -}; - - -/** - * @return {ol.Extent} Extent. - */ -ol.ImageBase.prototype.getExtent = function() { - return this.extent; -}; - - -/** - * @abstract - * @param {Object=} opt_context Object. - * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image. - */ -ol.ImageBase.prototype.getImage = function(opt_context) {}; - - -/** - * @return {number} PixelRatio. - */ -ol.ImageBase.prototype.getPixelRatio = function() { - return this.pixelRatio_; -}; - - -/** - * @return {number} Resolution. - */ -ol.ImageBase.prototype.getResolution = function() { - return /** @type {number} */ (this.resolution); -}; - - -/** - * @return {ol.ImageState} State. - */ -ol.ImageBase.prototype.getState = function() { - return this.state; -}; - - -/** - * Load not yet loaded URI. - * @abstract - */ -ol.ImageBase.prototype.load = function() {}; - -goog.provide('ol.Image'); - -goog.require('ol'); -goog.require('ol.ImageBase'); -goog.require('ol.ImageState'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.obj'); - - -/** - * @constructor - * @extends {ol.ImageBase} - * @param {ol.Extent} extent Extent. - * @param {number|undefined} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {Array.<ol.Attribution>} attributions Attributions. - * @param {string} src Image source URI. - * @param {?string} crossOrigin Cross origin. - * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. - */ -ol.Image = function(extent, resolution, pixelRatio, attributions, src, - crossOrigin, imageLoadFunction) { - - ol.ImageBase.call(this, extent, resolution, pixelRatio, ol.ImageState.IDLE, - attributions); - - /** - * @private - * @type {string} - */ - this.src_ = src; - - /** - * @private - * @type {HTMLCanvasElement|Image|HTMLVideoElement} - */ - this.image_ = new Image(); - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } - - /** - * @private - * @type {Object.<number, (HTMLCanvasElement|Image|HTMLVideoElement)>} - */ - this.imageByContext_ = {}; - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.imageListenerKeys_ = null; - - /** - * @protected - * @type {ol.ImageState} - */ - this.state = ol.ImageState.IDLE; - - /** - * @private - * @type {ol.ImageLoadFunctionType} - */ - this.imageLoadFunction_ = imageLoadFunction; - -}; -ol.inherits(ol.Image, ol.ImageBase); - - -/** - * @inheritDoc - * @api - */ -ol.Image.prototype.getImage = function(opt_context) { - if (opt_context !== undefined) { - var image; - var key = ol.getUid(opt_context); - if (key in this.imageByContext_) { - return this.imageByContext_[key]; - } else if (ol.obj.isEmpty(this.imageByContext_)) { - image = this.image_; - } else { - image = /** @type {Image} */ (this.image_.cloneNode(false)); - } - this.imageByContext_[key] = image; - return image; - } else { - return this.image_; - } -}; - - -/** - * Tracks loading or read errors. - * - * @private - */ -ol.Image.prototype.handleImageError_ = function() { - this.state = ol.ImageState.ERROR; - this.unlistenImage_(); - this.changed(); -}; - - -/** - * Tracks successful image load. - * - * @private - */ -ol.Image.prototype.handleImageLoad_ = function() { - if (this.resolution === undefined) { - this.resolution = ol.extent.getHeight(this.extent) / this.image_.height; - } - this.state = ol.ImageState.LOADED; - this.unlistenImage_(); - this.changed(); -}; - - -/** - * Load the image or retry if loading previously failed. - * Loading is taken care of by the tile queue, and calling this method is - * only needed for preloading or for reloading in case of an error. - * @override - * @api - */ -ol.Image.prototype.load = function() { - if (this.state == ol.ImageState.IDLE || this.state == ol.ImageState.ERROR) { - this.state = ol.ImageState.LOADING; - this.changed(); - this.imageListenerKeys_ = [ - ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, - this.handleImageError_, this), - ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, - this.handleImageLoad_, this) - ]; - this.imageLoadFunction_(this, this.src_); - } -}; - - -/** - * @param {HTMLCanvasElement|Image|HTMLVideoElement} image Image. - */ -ol.Image.prototype.setImage = function(image) { - this.image_ = image; -}; - - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -ol.Image.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(ol.events.unlistenByKey); - this.imageListenerKeys_ = null; -}; - -goog.provide('ol.ImageCanvas'); - -goog.require('ol'); -goog.require('ol.ImageBase'); -goog.require('ol.ImageState'); - - -/** - * @constructor - * @extends {ol.ImageBase} - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {Array.<ol.Attribution>} attributions Attributions. - * @param {HTMLCanvasElement} canvas Canvas. - * @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to - * support asynchronous canvas drawing. - */ -ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions, - canvas, opt_loader) { - - /** - * Optional canvas loader function. - * @type {?ol.ImageCanvasLoader} - * @private - */ - this.loader_ = opt_loader !== undefined ? opt_loader : null; - - var state = opt_loader !== undefined ? - ol.ImageState.IDLE : ol.ImageState.LOADED; - - ol.ImageBase.call(this, extent, resolution, pixelRatio, state, attributions); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = canvas; - - /** - * @private - * @type {Error} - */ - this.error_ = null; - -}; -ol.inherits(ol.ImageCanvas, ol.ImageBase); - - -/** - * Get any error associated with asynchronous rendering. - * @return {Error} Any error that occurred during rendering. - */ -ol.ImageCanvas.prototype.getError = function() { - return this.error_; -}; - - -/** - * Handle async drawing complete. - * @param {Error} err Any error during drawing. - * @private - */ -ol.ImageCanvas.prototype.handleLoad_ = function(err) { - if (err) { - this.error_ = err; - this.state = ol.ImageState.ERROR; - } else { - this.state = ol.ImageState.LOADED; - } - this.changed(); -}; - - -/** - * @inheritDoc - */ -ol.ImageCanvas.prototype.load = function() { - if (this.state == ol.ImageState.IDLE) { - this.state = ol.ImageState.LOADING; - this.changed(); - this.loader_(this.handleLoad_.bind(this)); - } -}; - - -/** - * @inheritDoc - */ -ol.ImageCanvas.prototype.getImage = function(opt_context) { - return this.canvas_; -}; - -goog.provide('ol.Tile'); - -goog.require('ol'); -goog.require('ol.TileState'); -goog.require('ol.events.EventTarget'); -goog.require('ol.events.EventType'); - - -/** - * @classdesc - * Base class for tiles. - * - * @constructor - * @abstract - * @extends {ol.events.EventTarget} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileState} state State. - */ -ol.Tile = function(tileCoord, state) { - - ol.events.EventTarget.call(this); - - /** - * @type {ol.TileCoord} - */ - this.tileCoord = tileCoord; - - /** - * @protected - * @type {ol.TileState} - */ - this.state = state; - - /** - * An "interim" tile for this tile. The interim tile may be used while this - * one is loading, for "smooth" transitions when changing params/dimensions - * on the source. - * @type {ol.Tile} - */ - this.interimTile = null; - - /** - * A key assigned to the tile. This is used by the tile source to determine - * if this tile can effectively be used, or if a new tile should be created - * and this one be used as an interim tile for this new tile. - * @type {string} - */ - this.key = ''; - -}; -ol.inherits(ol.Tile, ol.events.EventTarget); - - -/** - * @protected - */ -ol.Tile.prototype.changed = function() { - this.dispatchEvent(ol.events.EventType.CHANGE); -}; - - -/** - * @return {string} Key. - */ -ol.Tile.prototype.getKey = function() { - return this.key + '/' + this.tileCoord; -}; - -/** - * Get the interim tile most suitable for rendering using the chain of interim - * tiles. This corresponds to the most recent tile that has been loaded, if no - * such tile exists, the original tile is returned. - * @return {!ol.Tile} Best tile for rendering. - */ -ol.Tile.prototype.getInterimTile = function() { - if (!this.interimTile) { - //empty chain - return this; - } - var tile = this.interimTile; - - // find the first loaded tile and return it. Since the chain is sorted in - // decreasing order of creation time, there is no need to search the remainder - // of the list (all those tiles correspond to older requests and will be - // cleaned up by refreshInterimChain) - do { - if (tile.getState() == ol.TileState.LOADED) { - return tile; - } - tile = tile.interimTile; - } while (tile); - - // we can not find a better tile - return this; -}; - -/** - * Goes through the chain of interim tiles and discards sections of the chain - * that are no longer relevant. - */ -ol.Tile.prototype.refreshInterimChain = function() { - if (!this.interimTile) { - return; - } - - var tile = this.interimTile; - var prev = this; - - do { - if (tile.getState() == ol.TileState.LOADED) { - //we have a loaded tile, we can discard the rest of the list - //we would could abort any LOADING tile request - //older than this tile (i.e. any LOADING tile following this entry in the chain) - tile.interimTile = null; - break; - } else if (tile.getState() == ol.TileState.LOADING) { - //keep this LOADING tile any loaded tiles later in the chain are - //older than this tile, so we're still interested in the request - prev = tile; - } else if (tile.getState() == ol.TileState.IDLE) { - //the head of the list is the most current tile, we don't need - //to start any other requests for this chain - prev.interimTile = tile.interimTile; - } else { - prev = tile; - } - tile = prev.interimTile; - } while (tile); -}; - -/** - * Get the tile coordinate for this tile. - * @return {ol.TileCoord} The tile coordinate. - * @api - */ -ol.Tile.prototype.getTileCoord = function() { - return this.tileCoord; -}; - - -/** - * @return {ol.TileState} State. - */ -ol.Tile.prototype.getState = function() { - return this.state; -}; - -/** - * @param {ol.TileState} state State. - */ -ol.Tile.prototype.setState = function(state) { - this.state = state; - this.changed(); -}; - -/** - * Load the image or retry if loading previously failed. - * Loading is taken care of by the tile queue, and calling this method is - * only needed for preloading or for reloading in case of an error. - * @abstract - * @api - */ -ol.Tile.prototype.load = function() {}; - -goog.provide('ol.ImageTile'); - -goog.require('ol'); -goog.require('ol.Tile'); -goog.require('ol.TileState'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); - - -/** - * @constructor - * @extends {ol.Tile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileState} state State. - * @param {string} src Image source URI. - * @param {?string} crossOrigin Cross origin. - * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. - */ -ol.ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction) { - - ol.Tile.call(this, tileCoord, state); - - /** - * Image URI - * - * @private - * @type {string} - */ - this.src_ = src; - - /** - * @private - * @type {Image} - */ - this.image_ = new Image(); - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.imageListenerKeys_ = null; - - /** - * @private - * @type {ol.TileLoadFunctionType} - */ - this.tileLoadFunction_ = tileLoadFunction; - -}; -ol.inherits(ol.ImageTile, ol.Tile); - - -/** - * @inheritDoc - */ -ol.ImageTile.prototype.disposeInternal = function() { - if (this.state == ol.TileState.LOADING) { - this.unlistenImage_(); - } - if (this.interimTile) { - this.interimTile.dispose(); - } - this.state = ol.TileState.ABORT; - this.changed(); - ol.Tile.prototype.disposeInternal.call(this); -}; - - -/** - * Get the HTML image element for this tile (may be a Canvas, Image, or Video). - * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. - * @api - */ -ol.ImageTile.prototype.getImage = function() { - return this.image_; -}; - - -/** - * @inheritDoc - */ -ol.ImageTile.prototype.getKey = function() { - return this.src_; -}; - - -/** - * Tracks loading or read errors. - * - * @private - */ -ol.ImageTile.prototype.handleImageError_ = function() { - this.state = ol.TileState.ERROR; - this.image_ = ol.ImageTile.blankImage; - this.unlistenImage_(); - this.changed(); -}; - - -/** - * Tracks successful image load. - * - * @private - */ -ol.ImageTile.prototype.handleImageLoad_ = function() { - if (this.image_.naturalWidth && this.image_.naturalHeight) { - this.state = ol.TileState.LOADED; - } else { - this.state = ol.TileState.EMPTY; - } - this.unlistenImage_(); - this.changed(); -}; - - -/** - * @inheritDoc - * @api - */ -ol.ImageTile.prototype.load = function() { - if (this.state == ol.TileState.IDLE || this.state == ol.TileState.ERROR) { - this.state = ol.TileState.LOADING; - this.changed(); - this.imageListenerKeys_ = [ - ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, - this.handleImageError_, this), - ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, - this.handleImageLoad_, this) - ]; - this.tileLoadFunction_(this, this.src_); - } -}; - - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -ol.ImageTile.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(ol.events.unlistenByKey); - this.imageListenerKeys_ = null; -}; - - -/** - * A blank image. - * @type {Image} - */ -ol.ImageTile.blankImage = new Image(); -ol.ImageTile.blankImage.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; - -// FIXME should handle all geo-referenced data, not just vector data - -goog.provide('ol.interaction.DragAndDrop'); - -goog.require('ol'); -goog.require('ol.functions'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.EventType'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Handles input of vector data by drag and drop. - * - * @constructor - * @extends {ol.interaction.Interaction} - * @fires ol.interaction.DragAndDrop.Event - * @param {olx.interaction.DragAndDropOptions=} opt_options Options. - * @api - */ -ol.interaction.DragAndDrop = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.interaction.Interaction.call(this, { - handleEvent: ol.interaction.DragAndDrop.handleEvent - }); - - /** - * @private - * @type {Array.<function(new: ol.format.Feature)>} - */ - this.formatConstructors_ = options.formatConstructors ? - options.formatConstructors : []; - - /** - * @private - * @type {ol.proj.Projection} - */ - this.projection_ = options.projection ? - ol.proj.get(options.projection) : null; - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.dropListenKeys_ = null; - - /** - * @private - * @type {Element} - */ - this.target = options.target ? options.target : null; - -}; -ol.inherits(ol.interaction.DragAndDrop, ol.interaction.Interaction); - - -/** - * @param {Event} event Event. - * @this {ol.interaction.DragAndDrop} - * @private - */ -ol.interaction.DragAndDrop.handleDrop_ = function(event) { - var files = event.dataTransfer.files; - var i, ii, file; - for (i = 0, ii = files.length; i < ii; ++i) { - file = files.item(i); - var reader = new FileReader(); - reader.addEventListener(ol.events.EventType.LOAD, - this.handleResult_.bind(this, file)); - reader.readAsText(file); - } -}; - - -/** - * @param {Event} event Event. - * @private - */ -ol.interaction.DragAndDrop.handleStop_ = function(event) { - event.stopPropagation(); - event.preventDefault(); - event.dataTransfer.dropEffect = 'copy'; -}; - - -/** - * @param {File} file File. - * @param {Event} event Load event. - * @private - */ -ol.interaction.DragAndDrop.prototype.handleResult_ = function(file, event) { - var result = event.target.result; - var map = this.getMap(); - var projection = this.projection_; - if (!projection) { - var view = map.getView(); - projection = view.getProjection(); - } - - var formatConstructors = this.formatConstructors_; - var features = []; - var i, ii; - for (i = 0, ii = formatConstructors.length; i < ii; ++i) { - /** - * Avoid "cannot instantiate abstract class" error. - * @type {Function} - */ - var formatConstructor = formatConstructors[i]; - /** - * @type {ol.format.Feature} - */ - var format = new formatConstructor(); - features = this.tryReadFeatures_(format, result, { - featureProjection: projection - }); - if (features && features.length > 0) { - break; - } - } - this.dispatchEvent( - new ol.interaction.DragAndDrop.Event( - ol.interaction.DragAndDrop.EventType_.ADD_FEATURES, file, - features, projection)); -}; - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} unconditionally and - * neither prevents the browser default nor stops event propagation. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.DragAndDrop} - * @api - */ -ol.interaction.DragAndDrop.handleEvent = ol.functions.TRUE; - - -/** - * @private - */ -ol.interaction.DragAndDrop.prototype.registerListeners_ = function() { - var map = this.getMap(); - if (map) { - var dropArea = this.target ? this.target : map.getViewport(); - this.dropListenKeys_ = [ - ol.events.listen(dropArea, ol.events.EventType.DROP, - ol.interaction.DragAndDrop.handleDrop_, this), - ol.events.listen(dropArea, ol.events.EventType.DRAGENTER, - ol.interaction.DragAndDrop.handleStop_, this), - ol.events.listen(dropArea, ol.events.EventType.DRAGOVER, - ol.interaction.DragAndDrop.handleStop_, this), - ol.events.listen(dropArea, ol.events.EventType.DROP, - ol.interaction.DragAndDrop.handleStop_, this) - ]; - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.DragAndDrop.prototype.setActive = function(active) { - ol.interaction.Interaction.prototype.setActive.call(this, active); - if (active) { - this.registerListeners_(); - } else { - this.unregisterListeners_(); - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.DragAndDrop.prototype.setMap = function(map) { - this.unregisterListeners_(); - ol.interaction.Interaction.prototype.setMap.call(this, map); - if (this.getActive()) { - this.registerListeners_(); - } -}; - - -/** - * @param {ol.format.Feature} format Format. - * @param {string} text Text. - * @param {olx.format.ReadOptions} options Read options. - * @private - * @return {Array.<ol.Feature>} Features. - */ -ol.interaction.DragAndDrop.prototype.tryReadFeatures_ = function(format, text, options) { - try { - return format.readFeatures(text, options); - } catch (e) { - return null; - } -}; - - -/** - * @private - */ -ol.interaction.DragAndDrop.prototype.unregisterListeners_ = function() { - if (this.dropListenKeys_) { - this.dropListenKeys_.forEach(ol.events.unlistenByKey); - this.dropListenKeys_ = null; - } -}; - - -/** - * @enum {string} - * @private - */ -ol.interaction.DragAndDrop.EventType_ = { - /** - * Triggered when features are added - * @event ol.interaction.DragAndDrop.Event#addfeatures - * @api - */ - ADD_FEATURES: 'addfeatures' -}; - - -/** - * @classdesc - * Events emitted by {@link ol.interaction.DragAndDrop} instances are instances - * of this type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.interaction.DragAndDropEvent} - * @param {ol.interaction.DragAndDrop.EventType_} type Type. - * @param {File} file File. - * @param {Array.<ol.Feature>=} opt_features Features. - * @param {ol.proj.Projection=} opt_projection Projection. - */ -ol.interaction.DragAndDrop.Event = function(type, file, opt_features, opt_projection) { - - ol.events.Event.call(this, type); - - /** - * The features parsed from dropped data. - * @type {Array.<ol.Feature>|undefined} - * @api - */ - this.features = opt_features; - - /** - * The dropped file. - * @type {File} - * @api - */ - this.file = file; - - /** - * The feature projection. - * @type {ol.proj.Projection|undefined} - * @api - */ - this.projection = opt_projection; - -}; -ol.inherits(ol.interaction.DragAndDrop.Event, ol.events.Event); - -goog.provide('ol.interaction.DragRotateAndZoom'); - -goog.require('ol'); -goog.require('ol.RotationConstraint'); -goog.require('ol.ViewHint'); -goog.require('ol.events.condition'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.interaction.Pointer'); - - -/** - * @classdesc - * Allows the user to zoom and rotate the map by clicking and dragging - * on the map. By default, this interaction is limited to when the shift - * key is held down. - * - * This interaction is only supported for mouse devices. - * - * And this interaction is not included in the default interactions. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.DragRotateAndZoomOptions=} opt_options Options. - * @api - */ -ol.interaction.DragRotateAndZoom = function(opt_options) { - - var options = opt_options ? opt_options : {}; - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.DragRotateAndZoom.handleDownEvent_, - handleDragEvent: ol.interaction.DragRotateAndZoom.handleDragEvent_, - handleUpEvent: ol.interaction.DragRotateAndZoom.handleUpEvent_ - }); - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.shiftKeyOnly; - - /** - * @private - * @type {number|undefined} - */ - this.lastAngle_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.lastMagnitude_ = undefined; - - /** - * @private - * @type {number} - */ - this.lastScaleDelta_ = 0; - - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 400; - -}; -ol.inherits(ol.interaction.DragRotateAndZoom, ol.interaction.Pointer); - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @this {ol.interaction.DragRotateAndZoom} - * @private - */ -ol.interaction.DragRotateAndZoom.handleDragEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return; - } - - var map = mapBrowserEvent.map; - var size = map.getSize(); - var offset = mapBrowserEvent.pixel; - var deltaX = offset[0] - size[0] / 2; - var deltaY = size[1] / 2 - offset[1]; - var theta = Math.atan2(deltaY, deltaX); - var magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY); - var view = map.getView(); - if (view.getConstraints().rotation !== ol.RotationConstraint.disable && this.lastAngle_ !== undefined) { - var angleDelta = theta - this.lastAngle_; - ol.interaction.Interaction.rotateWithoutConstraints( - view, view.getRotation() - angleDelta); - } - this.lastAngle_ = theta; - if (this.lastMagnitude_ !== undefined) { - var resolution = this.lastMagnitude_ * (view.getResolution() / magnitude); - ol.interaction.Interaction.zoomWithoutConstraints(view, resolution); - } - if (this.lastMagnitude_ !== undefined) { - this.lastScaleDelta_ = this.lastMagnitude_ / magnitude; - } - this.lastMagnitude_ = magnitude; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.DragRotateAndZoom} - * @private - */ -ol.interaction.DragRotateAndZoom.handleUpEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return true; - } - - var map = mapBrowserEvent.map; - var view = map.getView(); - view.setHint(ol.ViewHint.INTERACTING, -1); - var direction = this.lastScaleDelta_ - 1; - ol.interaction.Interaction.rotate(view, view.getRotation()); - ol.interaction.Interaction.zoom(view, view.getResolution(), - undefined, this.duration_, direction); - this.lastScaleDelta_ = 0; - return false; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.DragRotateAndZoom} - * @private - */ -ol.interaction.DragRotateAndZoom.handleDownEvent_ = function(mapBrowserEvent) { - if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { - return false; - } - - if (this.condition_(mapBrowserEvent)) { - mapBrowserEvent.map.getView().setHint(ol.ViewHint.INTERACTING, 1); - this.lastAngle_ = undefined; - this.lastMagnitude_ = undefined; - return true; - } else { - return false; - } -}; - -goog.provide('ol.interaction.DrawEventType'); - - -/** - * @enum {string} - */ -ol.interaction.DrawEventType = { - /** - * Triggered upon feature draw start - * @event ol.interaction.Draw.Event#drawstart - * @api - */ - DRAWSTART: 'drawstart', - /** - * Triggered upon feature draw end - * @event ol.interaction.Draw.Event#drawend - * @api - */ - DRAWEND: 'drawend' -}; - -goog.provide('ol.render.canvas.Instruction'); - -/** - * @enum {number} - */ -ol.render.canvas.Instruction = { - BEGIN_GEOMETRY: 0, - BEGIN_PATH: 1, - CIRCLE: 2, - CLOSE_PATH: 3, - DRAW_IMAGE: 4, - DRAW_TEXT: 5, - END_GEOMETRY: 6, - FILL: 7, - MOVE_TO_LINE_TO: 8, - SET_FILL_STYLE: 9, - SET_STROKE_STYLE: 10, - SET_TEXT_STYLE: 11, - STROKE: 12 -}; - -goog.provide('ol.render.canvas.Replay'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.extent.Relationship'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.has'); -goog.require('ol.obj'); -goog.require('ol.render.VectorContext'); -goog.require('ol.render.canvas.Instruction'); -goog.require('ol.transform'); - - -/** - * @constructor - * @extends {ol.render.VectorContext} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @struct - */ -ol.render.canvas.Replay = function(tolerance, maxExtent, resolution, overlaps) { - ol.render.VectorContext.call(this); - - /** - * @protected - * @type {number} - */ - this.tolerance = tolerance; - - /** - * @protected - * @const - * @type {ol.Extent} - */ - this.maxExtent = maxExtent; - - /** - * @protected - * @type {boolean} - */ - this.overlaps = overlaps; - - /** - * @protected - * @type {number} - */ - this.maxLineWidth = 0; - - /** - * @protected - * @const - * @type {number} - */ - this.resolution = resolution; - - /** - * @private - * @type {ol.Coordinate} - */ - this.fillOrigin_; - - /** - * @private - * @type {Array.<*>} - */ - this.beginGeometryInstruction1_ = null; - - /** - * @private - * @type {Array.<*>} - */ - this.beginGeometryInstruction2_ = null; - - /** - * @protected - * @type {Array.<*>} - */ - this.instructions = []; - - /** - * @protected - * @type {Array.<number>} - */ - this.coordinates = []; - - /** - * @private - * @type {!ol.Transform} - */ - this.renderedTransform_ = ol.transform.create(); - - /** - * @protected - * @type {Array.<*>} - */ - this.hitDetectionInstructions = []; - - /** - * @private - * @type {Array.<number>} - */ - this.pixelCoordinates_ = null; - - /** - * @private - * @type {!ol.Transform} - */ - this.tmpLocalTransform_ = ol.transform.create(); - - /** - * @private - * @type {!ol.Transform} - */ - this.resetTransform_ = ol.transform.create(); -}; -ol.inherits(ol.render.canvas.Replay, ol.render.VectorContext); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {boolean} closed Last input coordinate equals first. - * @param {boolean} skipFirst Skip first coordinate. - * @protected - * @return {number} My end. - */ -ol.render.canvas.Replay.prototype.appendFlatCoordinates = function(flatCoordinates, offset, end, stride, closed, skipFirst) { - - var myEnd = this.coordinates.length; - var extent = this.getBufferedMaxExtent(); - if (skipFirst) { - offset += stride; - } - var lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]]; - var nextCoord = [NaN, NaN]; - var skipped = true; - - var i, lastRel, nextRel; - for (i = offset + stride; i < end; i += stride) { - nextCoord[0] = flatCoordinates[i]; - nextCoord[1] = flatCoordinates[i + 1]; - nextRel = ol.extent.coordinateRelationship(extent, nextCoord); - if (nextRel !== lastRel) { - if (skipped) { - this.coordinates[myEnd++] = lastCoord[0]; - this.coordinates[myEnd++] = lastCoord[1]; - } - this.coordinates[myEnd++] = nextCoord[0]; - this.coordinates[myEnd++] = nextCoord[1]; - skipped = false; - } else if (nextRel === ol.extent.Relationship.INTERSECTING) { - this.coordinates[myEnd++] = nextCoord[0]; - this.coordinates[myEnd++] = nextCoord[1]; - skipped = false; - } else { - skipped = true; - } - lastCoord[0] = nextCoord[0]; - lastCoord[1] = nextCoord[1]; - lastRel = nextRel; - } - - // Last coordinate equals first or only one point to append: - if ((closed && skipped) || i === offset + stride) { - this.coordinates[myEnd++] = lastCoord[0]; - this.coordinates[myEnd++] = lastCoord[1]; - } - return myEnd; -}; - - -/** - * @protected - * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) { - this.beginGeometryInstruction1_ = - [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0]; - this.instructions.push(this.beginGeometryInstruction1_); - this.beginGeometryInstruction2_ = - [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0]; - this.hitDetectionInstructions.push(this.beginGeometryInstruction2_); -}; - - -/** - * @private - * @param {CanvasRenderingContext2D} context Context. - * @param {number} rotation Rotation. - */ -ol.render.canvas.Replay.prototype.fill_ = function(context, rotation) { - if (this.fillOrigin_) { - var origin = ol.transform.apply(this.renderedTransform_, this.fillOrigin_.slice()); - context.translate(origin[0], origin[1]); - context.rotate(rotation); - } - context.fill(); - if (this.fillOrigin_) { - context.setTransform.apply(context, this.resetTransform_); - } -}; - - -/** - * @private - * @param {CanvasRenderingContext2D} context Context. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {Array.<*>} instructions Instructions array. - * @param {function((ol.Feature|ol.render.Feature)): T|undefined} - * featureCallback Feature callback. - * @param {ol.Extent=} opt_hitExtent Only check features that intersect this - * extent. - * @return {T|undefined} Callback result. - * @template T - */ -ol.render.canvas.Replay.prototype.replay_ = function( - context, pixelRatio, transform, viewRotation, skippedFeaturesHash, - instructions, featureCallback, opt_hitExtent) { - /** @type {Array.<number>} */ - var pixelCoordinates; - if (this.pixelCoordinates_ && ol.array.equals(transform, this.renderedTransform_)) { - pixelCoordinates = this.pixelCoordinates_; - } else { - if (!this.pixelCoordinates_) { - this.pixelCoordinates_ = []; - } - pixelCoordinates = ol.geom.flat.transform.transform2D( - this.coordinates, 0, this.coordinates.length, 2, - transform, this.pixelCoordinates_); - ol.transform.setFromArray(this.renderedTransform_, transform); - } - var skipFeatures = !ol.obj.isEmpty(skippedFeaturesHash); - var i = 0; // instruction index - var ii = instructions.length; // end of instructions - var d = 0; // data index - var dd; // end of per-instruction data - var localTransform = this.tmpLocalTransform_; - var resetTransform = this.resetTransform_; - var prevX, prevY, roundX, roundY; - var pendingFill = 0; - var pendingStroke = 0; - // When the batch size gets too big, performance decreases. 200 is a good - // balance between batch size and number of fill/stroke instructions. - var batchSize = - this.instructions != instructions || this.overlaps ? 0 : 200; - while (i < ii) { - var instruction = instructions[i]; - var type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]); - var feature, fill, stroke, text, x, y; - switch (type) { - case ol.render.canvas.Instruction.BEGIN_GEOMETRY: - feature = /** @type {ol.Feature|ol.render.Feature} */ (instruction[1]); - if ((skipFeatures && - skippedFeaturesHash[ol.getUid(feature).toString()]) || - !feature.getGeometry()) { - i = /** @type {number} */ (instruction[2]); - } else if (opt_hitExtent !== undefined && !ol.extent.intersects( - opt_hitExtent, feature.getGeometry().getExtent())) { - i = /** @type {number} */ (instruction[2]) + 1; - } else { - ++i; - } - break; - case ol.render.canvas.Instruction.BEGIN_PATH: - if (pendingFill > batchSize) { - this.fill_(context, viewRotation); - pendingFill = 0; - } - if (pendingStroke > batchSize) { - context.stroke(); - pendingStroke = 0; - } - if (!pendingFill && !pendingStroke) { - context.beginPath(); - prevX = prevY = NaN; - } - ++i; - break; - case ol.render.canvas.Instruction.CIRCLE: - d = /** @type {number} */ (instruction[1]); - var x1 = pixelCoordinates[d]; - var y1 = pixelCoordinates[d + 1]; - var x2 = pixelCoordinates[d + 2]; - var y2 = pixelCoordinates[d + 3]; - var dx = x2 - x1; - var dy = y2 - y1; - var r = Math.sqrt(dx * dx + dy * dy); - context.moveTo(x1 + r, y1); - context.arc(x1, y1, r, 0, 2 * Math.PI, true); - ++i; - break; - case ol.render.canvas.Instruction.CLOSE_PATH: - context.closePath(); - ++i; - break; - case ol.render.canvas.Instruction.DRAW_IMAGE: - d = /** @type {number} */ (instruction[1]); - dd = /** @type {number} */ (instruction[2]); - var image = /** @type {HTMLCanvasElement|HTMLVideoElement|Image} */ - (instruction[3]); - // Remaining arguments in DRAW_IMAGE are in alphabetical order - var anchorX = /** @type {number} */ (instruction[4]) * pixelRatio; - var anchorY = /** @type {number} */ (instruction[5]) * pixelRatio; - var height = /** @type {number} */ (instruction[6]); - var opacity = /** @type {number} */ (instruction[7]); - var originX = /** @type {number} */ (instruction[8]); - var originY = /** @type {number} */ (instruction[9]); - var rotateWithView = /** @type {boolean} */ (instruction[10]); - var rotation = /** @type {number} */ (instruction[11]); - var scale = /** @type {number} */ (instruction[12]); - var snapToPixel = /** @type {boolean} */ (instruction[13]); - var width = /** @type {number} */ (instruction[14]); - if (rotateWithView) { - rotation += viewRotation; - } - for (; d < dd; d += 2) { - x = pixelCoordinates[d] - anchorX; - y = pixelCoordinates[d + 1] - anchorY; - if (snapToPixel) { - x = Math.round(x); - y = Math.round(y); - } - if (scale != 1 || rotation !== 0) { - var centerX = x + anchorX; - var centerY = y + anchorY; - ol.transform.compose(localTransform, - centerX, centerY, scale, scale, rotation, -centerX, -centerY); - context.setTransform.apply(context, localTransform); - } - var alpha = context.globalAlpha; - if (opacity != 1) { - context.globalAlpha = alpha * opacity; - } - - var w = (width + originX > image.width) ? image.width - originX : width; - var h = (height + originY > image.height) ? image.height - originY : height; - - context.drawImage(image, originX, originY, w, h, - x, y, w * pixelRatio, h * pixelRatio); - - if (opacity != 1) { - context.globalAlpha = alpha; - } - if (scale != 1 || rotation !== 0) { - context.setTransform.apply(context, resetTransform); - } - } - ++i; - break; - case ol.render.canvas.Instruction.DRAW_TEXT: - d = /** @type {number} */ (instruction[1]); - dd = /** @type {number} */ (instruction[2]); - text = /** @type {string} */ (instruction[3]); - var offsetX = /** @type {number} */ (instruction[4]) * pixelRatio; - var offsetY = /** @type {number} */ (instruction[5]) * pixelRatio; - rotation = /** @type {number} */ (instruction[6]); - scale = /** @type {number} */ (instruction[7]) * pixelRatio; - fill = /** @type {boolean} */ (instruction[8]); - stroke = /** @type {boolean} */ (instruction[9]); - rotateWithView = /** @type {boolean} */ (instruction[10]); - if (rotateWithView) { - rotation += viewRotation; - } - for (; d < dd; d += 2) { - x = pixelCoordinates[d] + offsetX; - y = pixelCoordinates[d + 1] + offsetY; - if (scale != 1 || rotation !== 0) { - ol.transform.compose(localTransform, x, y, scale, scale, rotation, -x, -y); - context.setTransform.apply(context, localTransform); - } - - // Support multiple lines separated by \n - var lines = text.split('\n'); - var numLines = lines.length; - var fontSize, lineY; - if (numLines > 1) { - // Estimate line height using width of capital M, and add padding - fontSize = Math.round(context.measureText('M').width * 1.5); - lineY = y - (((numLines - 1) / 2) * fontSize); - } else { - // No need to calculate line height/offset for a single line - fontSize = 0; - lineY = y; - } - - for (var lineIndex = 0; lineIndex < numLines; lineIndex++) { - var line = lines[lineIndex]; - if (stroke) { - context.strokeText(line, x, lineY); - } - if (fill) { - context.fillText(line, x, lineY); - } - - // Move next line down by fontSize px - lineY = lineY + fontSize; - } - - if (scale != 1 || rotation !== 0) { - context.setTransform.apply(context, resetTransform); - } - } - ++i; - break; - case ol.render.canvas.Instruction.END_GEOMETRY: - if (featureCallback !== undefined) { - feature = - /** @type {ol.Feature|ol.render.Feature} */ (instruction[1]); - var result = featureCallback(feature); - if (result) { - return result; - } - } - ++i; - break; - case ol.render.canvas.Instruction.FILL: - if (batchSize) { - pendingFill++; - } else { - this.fill_(context, viewRotation); - } - ++i; - break; - case ol.render.canvas.Instruction.MOVE_TO_LINE_TO: - d = /** @type {number} */ (instruction[1]); - dd = /** @type {number} */ (instruction[2]); - x = pixelCoordinates[d]; - y = pixelCoordinates[d + 1]; - roundX = (x + 0.5) | 0; - roundY = (y + 0.5) | 0; - if (roundX !== prevX || roundY !== prevY) { - context.moveTo(x, y); - prevX = roundX; - prevY = roundY; - } - for (d += 2; d < dd; d += 2) { - x = pixelCoordinates[d]; - y = pixelCoordinates[d + 1]; - roundX = (x + 0.5) | 0; - roundY = (y + 0.5) | 0; - if (d == dd - 2 || roundX !== prevX || roundY !== prevY) { - context.lineTo(x, y); - prevX = roundX; - prevY = roundY; - } - } - ++i; - break; - case ol.render.canvas.Instruction.SET_FILL_STYLE: - this.fillOrigin_ = instruction[2]; - - if (pendingFill) { - this.fill_(context, viewRotation); - pendingFill = 0; - if (pendingStroke) { - context.stroke(); - pendingStroke = 0; - } - } - - context.fillStyle = /** @type {ol.ColorLike} */ (instruction[1]); - ++i; - break; - case ol.render.canvas.Instruction.SET_STROKE_STYLE: - var usePixelRatio = instruction[8] !== undefined ? - instruction[8] : true; - var renderedPixelRatio = instruction[9]; - - var lineWidth = /** @type {number} */ (instruction[2]); - if (pendingStroke) { - context.stroke(); - pendingStroke = 0; - } - context.strokeStyle = /** @type {ol.ColorLike} */ (instruction[1]); - context.lineWidth = usePixelRatio ? lineWidth * pixelRatio : lineWidth; - context.lineCap = /** @type {string} */ (instruction[3]); - context.lineJoin = /** @type {string} */ (instruction[4]); - context.miterLimit = /** @type {number} */ (instruction[5]); - if (ol.has.CANVAS_LINE_DASH) { - var lineDash = /** @type {Array.<number>} */ (instruction[6]); - var lineDashOffset = /** @type {number} */ (instruction[7]); - if (usePixelRatio && pixelRatio !== renderedPixelRatio) { - lineDash = lineDash.map(function(dash) { - return dash * pixelRatio / renderedPixelRatio; - }); - lineDashOffset *= pixelRatio / renderedPixelRatio; - instruction[6] = lineDash; - instruction[7] = lineDashOffset; - instruction[9] = pixelRatio; - } - context.lineDashOffset = lineDashOffset; - context.setLineDash(lineDash); - } - ++i; - break; - case ol.render.canvas.Instruction.SET_TEXT_STYLE: - context.font = /** @type {string} */ (instruction[1]); - context.textAlign = /** @type {string} */ (instruction[2]); - context.textBaseline = /** @type {string} */ (instruction[3]); - ++i; - break; - case ol.render.canvas.Instruction.STROKE: - if (batchSize) { - pendingStroke++; - } else { - context.stroke(); - } - ++i; - break; - default: - ++i; // consume the instruction anyway, to avoid an infinite loop - break; - } - } - if (pendingFill) { - this.fill_(context, viewRotation); - } - if (pendingStroke) { - context.stroke(); - } - return undefined; -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - */ -ol.render.canvas.Replay.prototype.replay = function( - context, pixelRatio, transform, viewRotation, skippedFeaturesHash) { - var instructions = this.instructions; - this.replay_(context, pixelRatio, transform, viewRotation, - skippedFeaturesHash, instructions, undefined, undefined); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {ol.Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T=} opt_featureCallback - * Feature callback. - * @param {ol.Extent=} opt_hitExtent Only check features that intersect this - * extent. - * @return {T|undefined} Callback result. - * @template T - */ -ol.render.canvas.Replay.prototype.replayHitDetection = function( - context, transform, viewRotation, skippedFeaturesHash, - opt_featureCallback, opt_hitExtent) { - var instructions = this.hitDetectionInstructions; - return this.replay_(context, 1, transform, viewRotation, - skippedFeaturesHash, instructions, opt_featureCallback, opt_hitExtent); -}; - - -/** - * Reverse the hit detection instructions. - */ -ol.render.canvas.Replay.prototype.reverseHitDetectionInstructions = function() { - var hitDetectionInstructions = this.hitDetectionInstructions; - // step 1 - reverse array - hitDetectionInstructions.reverse(); - // step 2 - reverse instructions within geometry blocks - var i; - var n = hitDetectionInstructions.length; - var instruction; - var type; - var begin = -1; - for (i = 0; i < n; ++i) { - instruction = hitDetectionInstructions[i]; - type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]); - if (type == ol.render.canvas.Instruction.END_GEOMETRY) { - begin = i; - } else if (type == ol.render.canvas.Instruction.BEGIN_GEOMETRY) { - instruction[2] = i; - ol.array.reverseSubArray(this.hitDetectionInstructions, begin, i); - begin = -1; - } - } -}; - - -/** - * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. - * @param {ol.Feature|ol.render.Feature} feature Feature. - */ -ol.render.canvas.Replay.prototype.endGeometry = function(geometry, feature) { - this.beginGeometryInstruction1_[2] = this.instructions.length; - this.beginGeometryInstruction1_ = null; - this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length; - this.beginGeometryInstruction2_ = null; - var endGeometryInstruction = - [ol.render.canvas.Instruction.END_GEOMETRY, feature]; - this.instructions.push(endGeometryInstruction); - this.hitDetectionInstructions.push(endGeometryInstruction); -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.render.canvas.Replay.prototype.finish = ol.nullFunction; - - -/** - * Get the buffered rendering extent. Rendering will be clipped to the extent - * provided to the constructor. To account for symbolizers that may intersect - * this extent, we calculate a buffered extent (e.g. based on stroke width). - * @return {ol.Extent} The buffered rendering extent. - * @protected - */ -ol.render.canvas.Replay.prototype.getBufferedMaxExtent = function() { - return this.maxExtent; -}; - -goog.provide('ol.render.canvas.ImageReplay'); - -goog.require('ol'); -goog.require('ol.render.canvas.Instruction'); -goog.require('ol.render.canvas.Replay'); - - -/** - * @constructor - * @extends {ol.render.canvas.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @struct - */ -ol.render.canvas.ImageReplay = function(tolerance, maxExtent, resolution, overlaps) { - ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps); - - /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|Image} - */ - this.hitDetectionImage_ = null; - - /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|Image} - */ - this.image_ = null; - - /** - * @private - * @type {number|undefined} - */ - this.anchorX_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.anchorY_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.height_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.opacity_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.originX_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.originY_ = undefined; - - /** - * @private - * @type {boolean|undefined} - */ - this.rotateWithView_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.scale_ = undefined; - - /** - * @private - * @type {boolean|undefined} - */ - this.snapToPixel_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.width_ = undefined; - -}; -ol.inherits(ol.render.canvas.ImageReplay, ol.render.canvas.Replay); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - * @return {number} My end. - */ -ol.render.canvas.ImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { - return this.appendFlatCoordinates( - flatCoordinates, offset, end, stride, false, false); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.ImageReplay.prototype.drawPoint = function(pointGeometry, feature) { - if (!this.image_) { - return; - } - this.beginGeometry(pointGeometry, feature); - var flatCoordinates = pointGeometry.getFlatCoordinates(); - var stride = pointGeometry.getStride(); - var myBegin = this.coordinates.length; - var myEnd = this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - this.instructions.push([ - ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, - this.hitDetectionImage_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.endGeometry(pointGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.ImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) { - if (!this.image_) { - return; - } - this.beginGeometry(multiPointGeometry, feature); - var flatCoordinates = multiPointGeometry.getFlatCoordinates(); - var stride = multiPointGeometry.getStride(); - var myBegin = this.coordinates.length; - var myEnd = this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - this.instructions.push([ - ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, - this.hitDetectionImage_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.endGeometry(multiPointGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.ImageReplay.prototype.finish = function() { - this.reverseHitDetectionInstructions(); - // FIXME this doesn't really protect us against further calls to draw*Geometry - this.anchorX_ = undefined; - this.anchorY_ = undefined; - this.hitDetectionImage_ = null; - this.image_ = null; - this.height_ = undefined; - this.scale_ = undefined; - this.opacity_ = undefined; - this.originX_ = undefined; - this.originY_ = undefined; - this.rotateWithView_ = undefined; - this.rotation_ = undefined; - this.snapToPixel_ = undefined; - this.width_ = undefined; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) { - var anchor = imageStyle.getAnchor(); - var size = imageStyle.getSize(); - var hitDetectionImage = imageStyle.getHitDetectionImage(1); - var image = imageStyle.getImage(1); - var origin = imageStyle.getOrigin(); - this.anchorX_ = anchor[0]; - this.anchorY_ = anchor[1]; - this.hitDetectionImage_ = hitDetectionImage; - this.image_ = image; - this.height_ = size[1]; - this.opacity_ = imageStyle.getOpacity(); - this.originX_ = origin[0]; - this.originY_ = origin[1]; - this.rotateWithView_ = imageStyle.getRotateWithView(); - this.rotation_ = imageStyle.getRotation(); - this.scale_ = imageStyle.getScale(); - this.snapToPixel_ = imageStyle.getSnapToPixel(); - this.width_ = size[0]; -}; - -goog.provide('ol.render.canvas.LineStringReplay'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.colorlike'); -goog.require('ol.extent'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.Instruction'); -goog.require('ol.render.canvas.Replay'); - - -/** - * @constructor - * @extends {ol.render.canvas.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @struct - */ -ol.render.canvas.LineStringReplay = function(tolerance, maxExtent, resolution, overlaps) { - - ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps); - - /** - * @private - * @type {ol.Extent} - */ - this.bufferedMaxExtent_ = null; - - /** - * @private - * @type {{currentStrokeStyle: (ol.ColorLike|undefined), - * currentLineCap: (string|undefined), - * currentLineDash: Array.<number>, - * currentLineDashOffset: (number|undefined), - * currentLineJoin: (string|undefined), - * currentLineWidth: (number|undefined), - * currentMiterLimit: (number|undefined), - * lastStroke: number, - * strokeStyle: (ol.ColorLike|undefined), - * lineCap: (string|undefined), - * lineDash: Array.<number>, - * lineDashOffset: (number|undefined), - * lineJoin: (string|undefined), - * lineWidth: (number|undefined), - * miterLimit: (number|undefined)}|null} - */ - this.state_ = { - currentStrokeStyle: undefined, - currentLineCap: undefined, - currentLineDash: null, - currentLineDashOffset: undefined, - currentLineJoin: undefined, - currentLineWidth: undefined, - currentMiterLimit: undefined, - lastStroke: 0, - strokeStyle: undefined, - lineCap: undefined, - lineDash: null, - lineDashOffset: undefined, - lineJoin: undefined, - lineWidth: undefined, - miterLimit: undefined - }; - -}; -ol.inherits(ol.render.canvas.LineStringReplay, ol.render.canvas.Replay); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - * @return {number} end. - */ -ol.render.canvas.LineStringReplay.prototype.drawFlatCoordinates_ = function(flatCoordinates, offset, end, stride) { - var myBegin = this.coordinates.length; - var myEnd = this.appendFlatCoordinates( - flatCoordinates, offset, end, stride, false, false); - var moveToLineToInstruction = - [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd]; - this.instructions.push(moveToLineToInstruction); - this.hitDetectionInstructions.push(moveToLineToInstruction); - return end; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.LineStringReplay.prototype.getBufferedMaxExtent = function() { - if (!this.bufferedMaxExtent_) { - this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent); - if (this.maxLineWidth > 0) { - var width = this.resolution * (this.maxLineWidth + 1) / 2; - ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_); - } - } - return this.bufferedMaxExtent_; -}; - - -/** - * @private - */ -ol.render.canvas.LineStringReplay.prototype.setStrokeStyle_ = function() { - var state = this.state_; - var strokeStyle = state.strokeStyle; - var lineCap = state.lineCap; - var lineDash = state.lineDash; - var lineDashOffset = state.lineDashOffset; - var lineJoin = state.lineJoin; - var lineWidth = state.lineWidth; - var miterLimit = state.miterLimit; - if (state.currentStrokeStyle != strokeStyle || - state.currentLineCap != lineCap || - !ol.array.equals(state.currentLineDash, lineDash) || - state.currentLineDashOffset != lineDashOffset || - state.currentLineJoin != lineJoin || - state.currentLineWidth != lineWidth || - state.currentMiterLimit != miterLimit) { - if (state.lastStroke != this.coordinates.length) { - this.instructions.push([ol.render.canvas.Instruction.STROKE]); - state.lastStroke = this.coordinates.length; - } - this.instructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash, lineDashOffset, true, 1 - ], [ - ol.render.canvas.Instruction.BEGIN_PATH - ]); - state.currentStrokeStyle = strokeStyle; - state.currentLineCap = lineCap; - state.currentLineDash = lineDash; - state.currentLineDashOffset = lineDashOffset; - state.currentLineJoin = lineJoin; - state.currentLineWidth = lineWidth; - state.currentMiterLimit = miterLimit; - } -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.LineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) { - var state = this.state_; - var strokeStyle = state.strokeStyle; - var lineWidth = state.lineWidth; - if (strokeStyle === undefined || lineWidth === undefined) { - return; - } - this.setStrokeStyle_(); - this.beginGeometry(lineStringGeometry, feature); - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset, true, 1 - ], [ - ol.render.canvas.Instruction.BEGIN_PATH - ]); - var flatCoordinates = lineStringGeometry.getFlatCoordinates(); - var stride = lineStringGeometry.getStride(); - this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); - this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]); - this.endGeometry(lineStringGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.LineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) { - var state = this.state_; - var strokeStyle = state.strokeStyle; - var lineWidth = state.lineWidth; - if (strokeStyle === undefined || lineWidth === undefined) { - return; - } - this.setStrokeStyle_(); - this.beginGeometry(multiLineStringGeometry, feature); - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset, true, 1 - ], [ - ol.render.canvas.Instruction.BEGIN_PATH - ]); - var ends = multiLineStringGeometry.getEnds(); - var flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); - var stride = multiLineStringGeometry.getStride(); - var offset = 0; - var i, ii; - for (i = 0, ii = ends.length; i < ii; ++i) { - offset = this.drawFlatCoordinates_( - flatCoordinates, offset, ends[i], stride); - } - this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]); - this.endGeometry(multiLineStringGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.LineStringReplay.prototype.finish = function() { - var state = this.state_; - if (state.lastStroke != this.coordinates.length) { - this.instructions.push([ol.render.canvas.Instruction.STROKE]); - } - this.reverseHitDetectionInstructions(); - this.state_ = null; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - var strokeStyleColor = strokeStyle.getColor(); - this.state_.strokeStyle = ol.colorlike.asColorLike(strokeStyleColor ? - strokeStyleColor : ol.render.canvas.defaultStrokeStyle); - var strokeStyleLineCap = strokeStyle.getLineCap(); - this.state_.lineCap = strokeStyleLineCap !== undefined ? - strokeStyleLineCap : ol.render.canvas.defaultLineCap; - var strokeStyleLineDash = strokeStyle.getLineDash(); - this.state_.lineDash = strokeStyleLineDash ? - strokeStyleLineDash : ol.render.canvas.defaultLineDash; - var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - this.state_.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset; - var strokeStyleLineJoin = strokeStyle.getLineJoin(); - this.state_.lineJoin = strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : ol.render.canvas.defaultLineJoin; - var strokeStyleWidth = strokeStyle.getWidth(); - this.state_.lineWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : ol.render.canvas.defaultLineWidth; - var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - this.state_.miterLimit = strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; - - if (this.state_.lineWidth > this.maxLineWidth) { - this.maxLineWidth = this.state_.lineWidth; - // invalidate the buffered max extent cache - this.bufferedMaxExtent_ = null; - } -}; - -goog.provide('ol.render.canvas.PolygonReplay'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.color'); -goog.require('ol.colorlike'); -goog.require('ol.extent'); -goog.require('ol.geom.flat.simplify'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.Instruction'); -goog.require('ol.render.canvas.Replay'); - - -/** - * @constructor - * @extends {ol.render.canvas.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @struct - */ -ol.render.canvas.PolygonReplay = function(tolerance, maxExtent, resolution, overlaps) { - - ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps); - - /** - * @private - * @type {ol.Extent} - */ - this.bufferedMaxExtent_ = null; - - /** - * @private - * @type {{currentFillStyle: (ol.ColorLike|undefined), - * currentStrokeStyle: (ol.ColorLike|undefined), - * currentLineCap: (string|undefined), - * currentLineDash: Array.<number>, - * currentLineDashOffset: (number|undefined), - * currentLineJoin: (string|undefined), - * currentLineWidth: (number|undefined), - * currentMiterLimit: (number|undefined), - * fillStyle: (ol.ColorLike|undefined), - * strokeStyle: (ol.ColorLike|undefined), - * lineCap: (string|undefined), - * lineDash: Array.<number>, - * lineDashOffset: (number|undefined), - * lineJoin: (string|undefined), - * lineWidth: (number|undefined), - * miterLimit: (number|undefined)}|null} - */ - this.state_ = { - currentFillStyle: undefined, - currentStrokeStyle: undefined, - currentLineCap: undefined, - currentLineDash: null, - currentLineDashOffset: undefined, - currentLineJoin: undefined, - currentLineWidth: undefined, - currentMiterLimit: undefined, - fillStyle: undefined, - strokeStyle: undefined, - lineCap: undefined, - lineDash: null, - lineDashOffset: undefined, - lineJoin: undefined, - lineWidth: undefined, - miterLimit: undefined - }; - -}; -ol.inherits(ol.render.canvas.PolygonReplay, ol.render.canvas.Replay); - - -/** - * @param {Array.<number>} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.<number>} ends Ends. - * @param {number} stride Stride. - * @private - * @return {number} End. - */ -ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCoordinates, offset, ends, stride) { - var state = this.state_; - var fill = state.fillStyle !== undefined; - var stroke = state.strokeStyle != undefined; - var numEnds = ends.length; - var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH]; - this.instructions.push(beginPathInstruction); - this.hitDetectionInstructions.push(beginPathInstruction); - for (var i = 0; i < numEnds; ++i) { - var end = ends[i]; - var myBegin = this.coordinates.length; - var myEnd = this.appendFlatCoordinates( - flatCoordinates, offset, end, stride, true, !stroke); - var moveToLineToInstruction = - [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd]; - this.instructions.push(moveToLineToInstruction); - this.hitDetectionInstructions.push(moveToLineToInstruction); - if (stroke) { - // Performance optimization: only call closePath() when we have a stroke. - // Otherwise the ring is closed already (see appendFlatCoordinates above). - var closePathInstruction = [ol.render.canvas.Instruction.CLOSE_PATH]; - this.instructions.push(closePathInstruction); - this.hitDetectionInstructions.push(closePathInstruction); - } - offset = end; - } - var fillInstruction = [ol.render.canvas.Instruction.FILL]; - this.hitDetectionInstructions.push(fillInstruction); - if (fill) { - this.instructions.push(fillInstruction); - } - if (stroke) { - var strokeInstruction = [ol.render.canvas.Instruction.STROKE]; - this.instructions.push(strokeInstruction); - this.hitDetectionInstructions.push(strokeInstruction); - } - return offset; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.PolygonReplay.prototype.drawCircle = function(circleGeometry, feature) { - var state = this.state_; - var fillStyle = state.fillStyle; - var strokeStyle = state.strokeStyle; - if (fillStyle === undefined && strokeStyle === undefined) { - return; - } - this.setFillStrokeStyles_(circleGeometry); - this.beginGeometry(circleGeometry, feature); - // always fill the circle for hit detection - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_FILL_STYLE, - ol.color.asString(ol.render.canvas.defaultFillStyle) - ]); - if (state.strokeStyle !== undefined) { - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset, true, 1 - ]); - } - var flatCoordinates = circleGeometry.getFlatCoordinates(); - var stride = circleGeometry.getStride(); - var myBegin = this.coordinates.length; - this.appendFlatCoordinates( - flatCoordinates, 0, flatCoordinates.length, stride, false, false); - var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH]; - var circleInstruction = [ol.render.canvas.Instruction.CIRCLE, myBegin]; - this.instructions.push(beginPathInstruction, circleInstruction); - this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction); - var fillInstruction = [ol.render.canvas.Instruction.FILL]; - this.hitDetectionInstructions.push(fillInstruction); - if (state.fillStyle !== undefined) { - this.instructions.push(fillInstruction); - } - if (state.strokeStyle !== undefined) { - var strokeInstruction = [ol.render.canvas.Instruction.STROKE]; - this.instructions.push(strokeInstruction); - this.hitDetectionInstructions.push(strokeInstruction); - } - this.endGeometry(circleGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) { - var state = this.state_; - this.setFillStrokeStyles_(polygonGeometry); - this.beginGeometry(polygonGeometry, feature); - // always fill the polygon for hit detection - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_FILL_STYLE, - ol.color.asString(ol.render.canvas.defaultFillStyle)] - ); - if (state.strokeStyle !== undefined) { - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset, true, 1 - ]); - } - var ends = polygonGeometry.getEnds(); - var flatCoordinates = polygonGeometry.getOrientedFlatCoordinates(); - var stride = polygonGeometry.getStride(); - this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride); - this.endGeometry(polygonGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) { - var state = this.state_; - var fillStyle = state.fillStyle; - var strokeStyle = state.strokeStyle; - if (fillStyle === undefined && strokeStyle === undefined) { - return; - } - this.setFillStrokeStyles_(multiPolygonGeometry); - this.beginGeometry(multiPolygonGeometry, feature); - // always fill the multi-polygon for hit detection - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_FILL_STYLE, - ol.color.asString(ol.render.canvas.defaultFillStyle) - ]); - if (state.strokeStyle !== undefined) { - this.hitDetectionInstructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset, true, 1 - ]); - } - var endss = multiPolygonGeometry.getEndss(); - var flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates(); - var stride = multiPolygonGeometry.getStride(); - var offset = 0; - var i, ii; - for (i = 0, ii = endss.length; i < ii; ++i) { - offset = this.drawFlatCoordinatess_( - flatCoordinates, offset, endss[i], stride); - } - this.endGeometry(multiPolygonGeometry, feature); -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.PolygonReplay.prototype.finish = function() { - this.reverseHitDetectionInstructions(); - this.state_ = null; - // We want to preserve topology when drawing polygons. Polygons are - // simplified using quantization and point elimination. However, we might - // have received a mix of quantized and non-quantized geometries, so ensure - // that all are quantized by quantizing all coordinates in the batch. - var tolerance = this.tolerance; - if (tolerance !== 0) { - var coordinates = this.coordinates; - var i, ii; - for (i = 0, ii = coordinates.length; i < ii; ++i) { - coordinates[i] = ol.geom.flat.simplify.snap(coordinates[i], tolerance); - } - } -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.PolygonReplay.prototype.getBufferedMaxExtent = function() { - if (!this.bufferedMaxExtent_) { - this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent); - if (this.maxLineWidth > 0) { - var width = this.resolution * (this.maxLineWidth + 1) / 2; - ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_); - } - } - return this.bufferedMaxExtent_; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - var state = this.state_; - if (fillStyle) { - var fillStyleColor = fillStyle.getColor(); - state.fillStyle = ol.colorlike.asColorLike(fillStyleColor ? - fillStyleColor : ol.render.canvas.defaultFillStyle); - } else { - state.fillStyle = undefined; - } - if (strokeStyle) { - var strokeStyleColor = strokeStyle.getColor(); - state.strokeStyle = ol.colorlike.asColorLike(strokeStyleColor ? - strokeStyleColor : ol.render.canvas.defaultStrokeStyle); - var strokeStyleLineCap = strokeStyle.getLineCap(); - state.lineCap = strokeStyleLineCap !== undefined ? - strokeStyleLineCap : ol.render.canvas.defaultLineCap; - var strokeStyleLineDash = strokeStyle.getLineDash(); - state.lineDash = strokeStyleLineDash ? - strokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash; - var strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - state.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset; - var strokeStyleLineJoin = strokeStyle.getLineJoin(); - state.lineJoin = strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : ol.render.canvas.defaultLineJoin; - var strokeStyleWidth = strokeStyle.getWidth(); - state.lineWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : ol.render.canvas.defaultLineWidth; - var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - state.miterLimit = strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; - - if (state.lineWidth > this.maxLineWidth) { - this.maxLineWidth = state.lineWidth; - // invalidate the buffered max extent cache - this.bufferedMaxExtent_ = null; - } - } else { - state.strokeStyle = undefined; - state.lineCap = undefined; - state.lineDash = null; - state.lineDashOffset = undefined; - state.lineJoin = undefined; - state.lineWidth = undefined; - state.miterLimit = undefined; - } -}; - - -/** - * @private - * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. - */ -ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function(geometry) { - var state = this.state_; - var fillStyle = state.fillStyle; - var strokeStyle = state.strokeStyle; - var lineCap = state.lineCap; - var lineDash = state.lineDash; - var lineDashOffset = state.lineDashOffset; - var lineJoin = state.lineJoin; - var lineWidth = state.lineWidth; - var miterLimit = state.miterLimit; - if (fillStyle !== undefined && (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle)) { - var fillInstruction = [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle]; - if (typeof fillStyle !== 'string') { - var fillExtent = geometry.getExtent(); - fillInstruction.push([fillExtent[0], fillExtent[3]]); - } - this.instructions.push(fillInstruction); - state.currentFillStyle = state.fillStyle; - } - if (strokeStyle !== undefined) { - if (state.currentStrokeStyle != strokeStyle || - state.currentLineCap != lineCap || - !ol.array.equals(state.currentLineDash, lineDash) || - state.currentLineDashOffset != lineDashOffset || - state.currentLineJoin != lineJoin || - state.currentLineWidth != lineWidth || - state.currentMiterLimit != miterLimit) { - this.instructions.push([ - ol.render.canvas.Instruction.SET_STROKE_STYLE, - strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash, lineDashOffset, true, 1 - ]); - state.currentStrokeStyle = strokeStyle; - state.currentLineCap = lineCap; - state.currentLineDash = lineDash; - state.currentLineDashOffset = lineDashOffset; - state.currentLineJoin = lineJoin; - state.currentLineWidth = lineWidth; - state.currentMiterLimit = miterLimit; - } - } -}; - -goog.provide('ol.render.canvas.TextReplay'); - -goog.require('ol'); -goog.require('ol.colorlike'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.Instruction'); -goog.require('ol.render.canvas.Replay'); - - -/** - * @constructor - * @extends {ol.render.canvas.Replay} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @struct - */ -ol.render.canvas.TextReplay = function(tolerance, maxExtent, resolution, overlaps) { - - ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution, overlaps); - - /** - * @private - * @type {?ol.CanvasFillState} - */ - this.replayFillState_ = null; - - /** - * @private - * @type {?ol.CanvasStrokeState} - */ - this.replayStrokeState_ = null; - - /** - * @private - * @type {?ol.CanvasTextState} - */ - this.replayTextState_ = null; - - /** - * @private - * @type {string} - */ - this.text_ = ''; - - /** - * @private - * @type {number} - */ - this.textOffsetX_ = 0; - - /** - * @private - * @type {number} - */ - this.textOffsetY_ = 0; - - /** - * @private - * @type {boolean|undefined} - */ - this.textRotateWithView_ = undefined; - - /** - * @private - * @type {number} - */ - this.textRotation_ = 0; - - /** - * @private - * @type {number} - */ - this.textScale_ = 0; - - /** - * @private - * @type {?ol.CanvasFillState} - */ - this.textFillState_ = null; - - /** - * @private - * @type {?ol.CanvasStrokeState} - */ - this.textStrokeState_ = null; - - /** - * @private - * @type {?ol.CanvasTextState} - */ - this.textState_ = null; - -}; -ol.inherits(ol.render.canvas.TextReplay, ol.render.canvas.Replay); - - -/** - * @inheritDoc - */ -ol.render.canvas.TextReplay.prototype.drawText = function(flatCoordinates, offset, end, stride, geometry, feature) { - if (this.text_ === '' || !this.textState_ || - (!this.textFillState_ && !this.textStrokeState_)) { - return; - } - if (this.textFillState_) { - this.setReplayFillState_(this.textFillState_); - } - if (this.textStrokeState_) { - this.setReplayStrokeState_(this.textStrokeState_); - } - this.setReplayTextState_(this.textState_); - this.beginGeometry(geometry, feature); - var myBegin = this.coordinates.length; - var myEnd = - this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); - var fill = !!this.textFillState_; - var stroke = !!this.textStrokeState_; - var drawTextInstruction = [ - ol.render.canvas.Instruction.DRAW_TEXT, myBegin, myEnd, this.text_, - this.textOffsetX_, this.textOffsetY_, this.textRotation_, this.textScale_, - fill, stroke, this.textRotateWithView_]; - this.instructions.push(drawTextInstruction); - this.hitDetectionInstructions.push(drawTextInstruction); - this.endGeometry(geometry, feature); -}; - - -/** - * @param {ol.CanvasFillState} fillState Fill state. - * @private - */ -ol.render.canvas.TextReplay.prototype.setReplayFillState_ = function(fillState) { - var replayFillState = this.replayFillState_; - if (replayFillState && - replayFillState.fillStyle == fillState.fillStyle) { - return; - } - var setFillStyleInstruction = - [ol.render.canvas.Instruction.SET_FILL_STYLE, fillState.fillStyle]; - this.instructions.push(setFillStyleInstruction); - this.hitDetectionInstructions.push(setFillStyleInstruction); - if (!replayFillState) { - this.replayFillState_ = { - fillStyle: fillState.fillStyle - }; - } else { - replayFillState.fillStyle = fillState.fillStyle; - } -}; - - -/** - * @param {ol.CanvasStrokeState} strokeState Stroke state. - * @private - */ -ol.render.canvas.TextReplay.prototype.setReplayStrokeState_ = function(strokeState) { - var replayStrokeState = this.replayStrokeState_; - if (replayStrokeState && - replayStrokeState.lineCap == strokeState.lineCap && - replayStrokeState.lineDash == strokeState.lineDash && - replayStrokeState.lineDashOffset == strokeState.lineDashOffset && - replayStrokeState.lineJoin == strokeState.lineJoin && - replayStrokeState.lineWidth == strokeState.lineWidth && - replayStrokeState.miterLimit == strokeState.miterLimit && - replayStrokeState.strokeStyle == strokeState.strokeStyle) { - return; - } - var setStrokeStyleInstruction = [ - ol.render.canvas.Instruction.SET_STROKE_STYLE, strokeState.strokeStyle, - strokeState.lineWidth, strokeState.lineCap, strokeState.lineJoin, - strokeState.miterLimit, strokeState.lineDash, strokeState.lineDashOffset, false, 1 - ]; - this.instructions.push(setStrokeStyleInstruction); - this.hitDetectionInstructions.push(setStrokeStyleInstruction); - if (!replayStrokeState) { - this.replayStrokeState_ = { - lineCap: strokeState.lineCap, - lineDash: strokeState.lineDash, - lineDashOffset: strokeState.lineDashOffset, - lineJoin: strokeState.lineJoin, - lineWidth: strokeState.lineWidth, - miterLimit: strokeState.miterLimit, - strokeStyle: strokeState.strokeStyle - }; - } else { - replayStrokeState.lineCap = strokeState.lineCap; - replayStrokeState.lineDash = strokeState.lineDash; - replayStrokeState.lineDashOffset = strokeState.lineDashOffset; - replayStrokeState.lineJoin = strokeState.lineJoin; - replayStrokeState.lineWidth = strokeState.lineWidth; - replayStrokeState.miterLimit = strokeState.miterLimit; - replayStrokeState.strokeStyle = strokeState.strokeStyle; - } -}; - - -/** - * @param {ol.CanvasTextState} textState Text state. - * @private - */ -ol.render.canvas.TextReplay.prototype.setReplayTextState_ = function(textState) { - var replayTextState = this.replayTextState_; - if (replayTextState && - replayTextState.font == textState.font && - replayTextState.textAlign == textState.textAlign && - replayTextState.textBaseline == textState.textBaseline) { - return; - } - var setTextStyleInstruction = [ol.render.canvas.Instruction.SET_TEXT_STYLE, - textState.font, textState.textAlign, textState.textBaseline]; - this.instructions.push(setTextStyleInstruction); - this.hitDetectionInstructions.push(setTextStyleInstruction); - if (!replayTextState) { - this.replayTextState_ = { - font: textState.font, - textAlign: textState.textAlign, - textBaseline: textState.textBaseline - }; - } else { - replayTextState.font = textState.font; - replayTextState.textAlign = textState.textAlign; - replayTextState.textBaseline = textState.textBaseline; - } -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) { - if (!textStyle) { - this.text_ = ''; - } else { - var textFillStyle = textStyle.getFill(); - if (!textFillStyle) { - this.textFillState_ = null; - } else { - var textFillStyleColor = textFillStyle.getColor(); - var fillStyle = ol.colorlike.asColorLike(textFillStyleColor ? - textFillStyleColor : ol.render.canvas.defaultFillStyle); - if (!this.textFillState_) { - this.textFillState_ = { - fillStyle: fillStyle - }; - } else { - var textFillState = this.textFillState_; - textFillState.fillStyle = fillStyle; - } - } - var textStrokeStyle = textStyle.getStroke(); - if (!textStrokeStyle) { - this.textStrokeState_ = null; - } else { - var textStrokeStyleColor = textStrokeStyle.getColor(); - var textStrokeStyleLineCap = textStrokeStyle.getLineCap(); - var textStrokeStyleLineDash = textStrokeStyle.getLineDash(); - var textStrokeStyleLineDashOffset = textStrokeStyle.getLineDashOffset(); - var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin(); - var textStrokeStyleWidth = textStrokeStyle.getWidth(); - var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit(); - var lineCap = textStrokeStyleLineCap !== undefined ? - textStrokeStyleLineCap : ol.render.canvas.defaultLineCap; - var lineDash = textStrokeStyleLineDash ? - textStrokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash; - var lineDashOffset = textStrokeStyleLineDashOffset !== undefined ? - textStrokeStyleLineDashOffset : ol.render.canvas.defaultLineDashOffset; - var lineJoin = textStrokeStyleLineJoin !== undefined ? - textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin; - var lineWidth = textStrokeStyleWidth !== undefined ? - textStrokeStyleWidth : ol.render.canvas.defaultLineWidth; - var miterLimit = textStrokeStyleMiterLimit !== undefined ? - textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; - var strokeStyle = ol.colorlike.asColorLike(textStrokeStyleColor ? - textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle); - if (!this.textStrokeState_) { - this.textStrokeState_ = { - lineCap: lineCap, - lineDash: lineDash, - lineDashOffset: lineDashOffset, - lineJoin: lineJoin, - lineWidth: lineWidth, - miterLimit: miterLimit, - strokeStyle: strokeStyle - }; - } else { - var textStrokeState = this.textStrokeState_; - textStrokeState.lineCap = lineCap; - textStrokeState.lineDash = lineDash; - textStrokeState.lineDashOffset = lineDashOffset; - textStrokeState.lineJoin = lineJoin; - textStrokeState.lineWidth = lineWidth; - textStrokeState.miterLimit = miterLimit; - textStrokeState.strokeStyle = strokeStyle; - } - } - var textFont = textStyle.getFont(); - var textOffsetX = textStyle.getOffsetX(); - var textOffsetY = textStyle.getOffsetY(); - var textRotateWithView = textStyle.getRotateWithView(); - var textRotation = textStyle.getRotation(); - var textScale = textStyle.getScale(); - var textText = textStyle.getText(); - var textTextAlign = textStyle.getTextAlign(); - var textTextBaseline = textStyle.getTextBaseline(); - var font = textFont !== undefined ? - textFont : ol.render.canvas.defaultFont; - var textAlign = textTextAlign !== undefined ? - textTextAlign : ol.render.canvas.defaultTextAlign; - var textBaseline = textTextBaseline !== undefined ? - textTextBaseline : ol.render.canvas.defaultTextBaseline; - if (!this.textState_) { - this.textState_ = { - font: font, - textAlign: textAlign, - textBaseline: textBaseline - }; - } else { - var textState = this.textState_; - textState.font = font; - textState.textAlign = textAlign; - textState.textBaseline = textBaseline; - } - this.text_ = textText !== undefined ? textText : ''; - this.textOffsetX_ = textOffsetX !== undefined ? textOffsetX : 0; - this.textOffsetY_ = textOffsetY !== undefined ? textOffsetY : 0; - this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false; - this.textRotation_ = textRotation !== undefined ? textRotation : 0; - this.textScale_ = textScale !== undefined ? textScale : 1; - } -}; - -goog.provide('ol.render.canvas.ReplayGroup'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.geom.flat.transform'); -goog.require('ol.obj'); -goog.require('ol.render.ReplayGroup'); -goog.require('ol.render.canvas.ImageReplay'); -goog.require('ol.render.canvas.LineStringReplay'); -goog.require('ol.render.canvas.PolygonReplay'); -goog.require('ol.render.canvas.TextReplay'); -goog.require('ol.render.replay'); -goog.require('ol.transform'); - - -/** - * @constructor - * @extends {ol.render.ReplayGroup} - * @param {number} tolerance Tolerance. - * @param {ol.Extent} maxExtent Max extent. - * @param {number} resolution Resolution. - * @param {boolean} overlaps The replay group can have overlapping geometries. - * @param {number=} opt_renderBuffer Optional rendering buffer. - * @struct - */ -ol.render.canvas.ReplayGroup = function( - tolerance, maxExtent, resolution, overlaps, opt_renderBuffer) { - ol.render.ReplayGroup.call(this); - - /** - * @private - * @type {number} - */ - this.tolerance_ = tolerance; - - /** - * @private - * @type {ol.Extent} - */ - this.maxExtent_ = maxExtent; - - /** - * @private - * @type {boolean} - */ - this.overlaps_ = overlaps; - - /** - * @private - * @type {number} - */ - this.resolution_ = resolution; - - /** - * @private - * @type {number|undefined} - */ - this.renderBuffer_ = opt_renderBuffer; - - /** - * @private - * @type {!Object.<string, - * Object.<ol.render.ReplayType, ol.render.canvas.Replay>>} - */ - this.replaysByZIndex_ = {}; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.hitDetectionContext_ = ol.dom.createCanvasContext2D(1, 1); - - /** - * @private - * @type {ol.Transform} - */ - this.hitDetectionTransform_ = ol.transform.create(); -}; -ol.inherits(ol.render.canvas.ReplayGroup, ol.render.ReplayGroup); - - -/** - * This cache is used for storing calculated pixel circles for increasing performance. - * It is a static property to allow each Replaygroup to access it. - * @type {Object.<number, Array.<Array.<(boolean|undefined)>>>} - * @private - */ -ol.render.canvas.ReplayGroup.circleArrayCache_ = { - 0: [[true]] -}; - - -/** - * This method fills a row in the array from the given coordinate to the - * middle with `true`. - * @param {Array.<Array.<(boolean|undefined)>>} array The array that will be altered. - * @param {number} x X coordinate. - * @param {number} y Y coordinate. - * @private - */ -ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_ = function(array, x, y) { - var i; - var radius = Math.floor(array.length / 2); - if (x >= radius) { - for (i = radius; i < x; i++) { - array[i][y] = true; - } - } else if (x < radius) { - for (i = x + 1; i < radius; i++) { - array[i][y] = true; - } - } -}; - - -/** - * This methods creates a circle inside a fitting array. Points inside the - * circle are marked by true, points on the outside are undefined. - * It uses the midpoint circle algorithm. - * A cache is used to increase performance. - * @param {number} radius Radius. - * @returns {Array.<Array.<(boolean|undefined)>>} An array with marked circle points. - * @private - */ -ol.render.canvas.ReplayGroup.getCircleArray_ = function(radius) { - if (ol.render.canvas.ReplayGroup.circleArrayCache_[radius] !== undefined) { - return ol.render.canvas.ReplayGroup.circleArrayCache_[radius]; - } - - var arraySize = radius * 2 + 1; - var arr = new Array(arraySize); - for (var i = 0; i < arraySize; i++) { - arr[i] = new Array(arraySize); - } - - var x = radius; - var y = 0; - var error = 0; - - while (x >= y) { - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + x, radius + y); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + y, radius + x); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - y, radius + x); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - x, radius + y); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - x, radius - y); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius - y, radius - x); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + y, radius - x); - ol.render.canvas.ReplayGroup.fillCircleArrayRowToMiddle_(arr, radius + x, radius - y); - - y++; - error += 1 + 2 * y; - if (2 * (error - x) + 1 > 0) { - x -= 1; - error += 1 - 2 * x; - } - } - - ol.render.canvas.ReplayGroup.circleArrayCache_[radius] = arr; - return arr; -}; - -/** - * FIXME empty description for jsdoc - */ -ol.render.canvas.ReplayGroup.prototype.finish = function() { - var zKey; - for (zKey in this.replaysByZIndex_) { - var replays = this.replaysByZIndex_[zKey]; - var replayKey; - for (replayKey in replays) { - replays[replayKey].finish(); - } - } -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T} callback Feature - * callback. - * @return {T|undefined} Callback result. - * @template T - */ -ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function( - coordinate, resolution, rotation, hitTolerance, skippedFeaturesHash, callback) { - - hitTolerance = Math.round(hitTolerance); - var contextSize = hitTolerance * 2 + 1; - var transform = ol.transform.compose(this.hitDetectionTransform_, - hitTolerance + 0.5, hitTolerance + 0.5, - 1 / resolution, -1 / resolution, - -rotation, - -coordinate[0], -coordinate[1]); - var context = this.hitDetectionContext_; - - if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) { - context.canvas.width = contextSize; - context.canvas.height = contextSize; - } else { - context.clearRect(0, 0, contextSize, contextSize); - } - - /** - * @type {ol.Extent} - */ - var hitExtent; - if (this.renderBuffer_ !== undefined) { - hitExtent = ol.extent.createEmpty(); - ol.extent.extendCoordinate(hitExtent, coordinate); - ol.extent.buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent); - } - - var mask = ol.render.canvas.ReplayGroup.getCircleArray_(hitTolerance); - - return this.replayHitDetection_(context, transform, rotation, - skippedFeaturesHash, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - var imageData = context.getImageData(0, 0, contextSize, contextSize).data; - for (var i = 0; i < contextSize; i++) { - for (var j = 0; j < contextSize; j++) { - if (mask[i][j]) { - if (imageData[(j * contextSize + i) * 4 + 3] > 0) { - var result = callback(feature); - if (result) { - return result; - } else { - context.clearRect(0, 0, contextSize, contextSize); - return undefined; - } - } - } - } - } - }, hitExtent); -}; - - -/** - * @param {ol.Transform} transform Transform. - * @return {Array.<number>} Clip coordinates. - */ -ol.render.canvas.ReplayGroup.prototype.getClipCoords = function(transform) { - var maxExtent = this.maxExtent_; - var minX = maxExtent[0]; - var minY = maxExtent[1]; - var maxX = maxExtent[2]; - var maxY = maxExtent[3]; - var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY]; - ol.geom.flat.transform.transform2D( - flatClipCoords, 0, 8, 2, transform, flatClipCoords); - return flatClipCoords; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.ReplayGroup.prototype.getReplay = function(zIndex, replayType) { - var zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; - var replays = this.replaysByZIndex_[zIndexKey]; - if (replays === undefined) { - replays = {}; - this.replaysByZIndex_[zIndexKey] = replays; - } - var replay = replays[replayType]; - if (replay === undefined) { - var Constructor = ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_[replayType]; - replay = new Constructor(this.tolerance_, this.maxExtent_, - this.resolution_, this.overlaps_); - replays[replayType] = replay; - } - return replay; -}; - - -/** - * @inheritDoc - */ -ol.render.canvas.ReplayGroup.prototype.isEmpty = function() { - return ol.obj.isEmpty(this.replaysByZIndex_); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {Array.<ol.render.ReplayType>=} opt_replayTypes Ordered replay types - * to replay. Default is {@link ol.render.replay.ORDER} - */ -ol.render.canvas.ReplayGroup.prototype.replay = function(context, pixelRatio, - transform, viewRotation, skippedFeaturesHash, opt_replayTypes) { - - /** @type {Array.<number>} */ - var zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(ol.array.numberSafeCompareFunction); - - // setup clipping so that the parts of over-simplified geometries are not - // visible outside the current extent when panning - var flatClipCoords = this.getClipCoords(transform); - context.save(); - context.beginPath(); - context.moveTo(flatClipCoords[0], flatClipCoords[1]); - context.lineTo(flatClipCoords[2], flatClipCoords[3]); - context.lineTo(flatClipCoords[4], flatClipCoords[5]); - context.lineTo(flatClipCoords[6], flatClipCoords[7]); - context.clip(); - - var replayTypes = opt_replayTypes ? opt_replayTypes : ol.render.replay.ORDER; - var i, ii, j, jj, replays, replay; - for (i = 0, ii = zs.length; i < ii; ++i) { - replays = this.replaysByZIndex_[zs[i].toString()]; - for (j = 0, jj = replayTypes.length; j < jj; ++j) { - replay = replays[replayTypes[j]]; - if (replay !== undefined) { - replay.replay(context, pixelRatio, transform, viewRotation, - skippedFeaturesHash); - } - } - } - - context.restore(); -}; - - -/** - * @private - * @param {CanvasRenderingContext2D} context Context. - * @param {ol.Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features - * to skip. - * @param {function((ol.Feature|ol.render.Feature)): T} featureCallback - * Feature callback. - * @param {ol.Extent=} opt_hitExtent Only check features that intersect this - * extent. - * @return {T|undefined} Callback result. - * @template T - */ -ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( - context, transform, viewRotation, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - /** @type {Array.<number>} */ - var zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(function(a, b) { - return b - a; - }); - - var i, ii, j, replays, replay, result; - for (i = 0, ii = zs.length; i < ii; ++i) { - replays = this.replaysByZIndex_[zs[i].toString()]; - for (j = ol.render.replay.ORDER.length - 1; j >= 0; --j) { - replay = replays[ol.render.replay.ORDER[j]]; - if (replay !== undefined) { - result = replay.replayHitDetection(context, transform, viewRotation, - skippedFeaturesHash, featureCallback, opt_hitExtent); - if (result) { - return result; - } - } - } - } - return undefined; -}; - - -/** - * @const - * @private - * @type {Object.<ol.render.ReplayType, - * function(new: ol.render.canvas.Replay, number, ol.Extent, - * number, boolean)>} - */ -ol.render.canvas.ReplayGroup.BATCH_CONSTRUCTORS_ = { - 'Circle': ol.render.canvas.PolygonReplay, - 'Image': ol.render.canvas.ImageReplay, - 'LineString': ol.render.canvas.LineStringReplay, - 'Polygon': ol.render.canvas.PolygonReplay, - 'Text': ol.render.canvas.TextReplay -}; - -goog.provide('ol.renderer.Layer'); - -goog.require('ol'); -goog.require('ol.ImageState'); -goog.require('ol.Observable'); -goog.require('ol.TileState'); -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.functions'); -goog.require('ol.source.State'); - - -/** - * @constructor - * @extends {ol.Observable} - * @param {ol.layer.Layer} layer Layer. - * @struct - */ -ol.renderer.Layer = function(layer) { - - ol.Observable.call(this); - - /** - * @private - * @type {ol.layer.Layer} - */ - this.layer_ = layer; - - -}; -ol.inherits(ol.renderer.Layer, ol.Observable); - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {olx.FrameState} frameState Frame state. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: S, (ol.Feature|ol.render.Feature), ol.layer.Layer): T} - * callback Feature callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T - */ -ol.renderer.Layer.prototype.forEachFeatureAtCoordinate = ol.nullFunction; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {olx.FrameState} frameState Frame state. - * @return {boolean} Is there a feature at the given coordinate? - */ -ol.renderer.Layer.prototype.hasFeatureAtCoordinate = ol.functions.FALSE; - - -/** - * Create a function that adds loaded tiles to the tile lookup. - * @param {ol.source.Tile} source Tile source. - * @param {ol.proj.Projection} projection Projection of the tiles. - * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded - * tiles by zoom level. - * @return {function(number, ol.TileRange):boolean} A function that can be - * called with a zoom level and a tile range to add loaded tiles to the - * lookup. - * @protected - */ -ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, projection, tiles) { - return ( - /** - * @param {number} zoom Zoom level. - * @param {ol.TileRange} tileRange Tile range. - * @return {boolean} The tile range is fully loaded. - */ - function(zoom, tileRange) { - function callback(tile) { - if (!tiles[zoom]) { - tiles[zoom] = {}; - } - tiles[zoom][tile.tileCoord.toString()] = tile; - } - return source.forEachLoadedTile(projection, zoom, tileRange, callback); - }); -}; - - -/** - * @return {ol.layer.Layer} Layer. - */ -ol.renderer.Layer.prototype.getLayer = function() { - return this.layer_; -}; - - -/** - * Handle changes in image state. - * @param {ol.events.Event} event Image change event. - * @private - */ -ol.renderer.Layer.prototype.handleImageChange_ = function(event) { - var image = /** @type {ol.Image} */ (event.target); - if (image.getState() === ol.ImageState.LOADED) { - this.renderIfReadyAndVisible(); - } -}; - - -/** - * Load the image if not already loaded, and register the image change - * listener if needed. - * @param {ol.ImageBase} image Image. - * @return {boolean} `true` if the image is already loaded, `false` - * otherwise. - * @protected - */ -ol.renderer.Layer.prototype.loadImage = function(image) { - var imageState = image.getState(); - if (imageState != ol.ImageState.LOADED && - imageState != ol.ImageState.ERROR) { - ol.events.listen(image, ol.events.EventType.CHANGE, - this.handleImageChange_, this); - } - if (imageState == ol.ImageState.IDLE) { - image.load(); - imageState = image.getState(); - } - return imageState == ol.ImageState.LOADED; -}; - - -/** - * @protected - */ -ol.renderer.Layer.prototype.renderIfReadyAndVisible = function() { - var layer = this.getLayer(); - if (layer.getVisible() && layer.getSourceState() == ol.source.State.READY) { - this.changed(); - } -}; - - -/** - * @param {olx.FrameState} frameState Frame state. - * @param {ol.source.Tile} tileSource Tile source. - * @protected - */ -ol.renderer.Layer.prototype.scheduleExpireCache = function(frameState, tileSource) { - if (tileSource.canExpireCache()) { - /** - * @param {ol.source.Tile} tileSource Tile source. - * @param {ol.Map} map Map. - * @param {olx.FrameState} frameState Frame state. - */ - var postRenderFunction = function(tileSource, map, frameState) { - var tileSourceKey = ol.getUid(tileSource).toString(); - tileSource.expireCache(frameState.viewState.projection, - frameState.usedTiles[tileSourceKey]); - }.bind(null, tileSource); - - frameState.postRenderFunctions.push( - /** @type {ol.PostRenderFunction} */ (postRenderFunction) - ); - } -}; - - -/** - * @param {Object.<string, ol.Attribution>} attributionsSet Attributions - * set (target). - * @param {Array.<ol.Attribution>} attributions Attributions (source). - * @protected - */ -ol.renderer.Layer.prototype.updateAttributions = function(attributionsSet, attributions) { - if (attributions) { - var attribution, i, ii; - for (i = 0, ii = attributions.length; i < ii; ++i) { - attribution = attributions[i]; - attributionsSet[ol.getUid(attribution).toString()] = attribution; - } - } -}; - - -/** - * @param {olx.FrameState} frameState Frame state. - * @param {ol.source.Source} source Source. - * @protected - */ -ol.renderer.Layer.prototype.updateLogos = function(frameState, source) { - var logo = source.getLogo(); - if (logo !== undefined) { - if (typeof logo === 'string') { - frameState.logos[logo] = ''; - } else if (logo) { - ol.asserts.assert(typeof logo.href == 'string', 44); // `logo.href` should be a string. - ol.asserts.assert(typeof logo.src == 'string', 45); // `logo.src` should be a string. - frameState.logos[logo.src] = logo.href; - } - } -}; - - -/** - * @param {Object.<string, Object.<string, ol.TileRange>>} usedTiles Used tiles. - * @param {ol.source.Tile} tileSource Tile source. - * @param {number} z Z. - * @param {ol.TileRange} tileRange Tile range. - * @protected - */ -ol.renderer.Layer.prototype.updateUsedTiles = function(usedTiles, tileSource, z, tileRange) { - // FIXME should we use tilesToDrawByZ instead? - var tileSourceKey = ol.getUid(tileSource).toString(); - var zKey = z.toString(); - if (tileSourceKey in usedTiles) { - if (zKey in usedTiles[tileSourceKey]) { - usedTiles[tileSourceKey][zKey].extend(tileRange); - } else { - usedTiles[tileSourceKey][zKey] = tileRange; - } - } else { - usedTiles[tileSourceKey] = {}; - usedTiles[tileSourceKey][zKey] = tileRange; - } -}; - - -/** - * Manage tile pyramid. - * This function performs a number of functions related to the tiles at the - * current zoom and lower zoom levels: - * - registers idle tiles in frameState.wantedTiles so that they are not - * discarded by the tile queue - * - enqueues missing tiles - * @param {olx.FrameState} frameState Frame state. - * @param {ol.source.Tile} tileSource Tile source. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {ol.Extent} extent Extent. - * @param {number} currentZ Current Z. - * @param {number} preload Load low resolution tiles up to 'preload' levels. - * @param {function(this: T, ol.Tile)=} opt_tileCallback Tile callback. - * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`. - * @protected - * @template T - */ -ol.renderer.Layer.prototype.manageTilePyramid = function( - frameState, tileSource, tileGrid, pixelRatio, projection, extent, - currentZ, preload, opt_tileCallback, opt_this) { - var tileSourceKey = ol.getUid(tileSource).toString(); - if (!(tileSourceKey in frameState.wantedTiles)) { - frameState.wantedTiles[tileSourceKey] = {}; - } - var wantedTiles = frameState.wantedTiles[tileSourceKey]; - var tileQueue = frameState.tileQueue; - var minZoom = tileGrid.getMinZoom(); - var tile, tileRange, tileResolution, x, y, z; - for (z = currentZ; z >= minZoom; --z) { - tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange); - tileResolution = tileGrid.getResolution(z); - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - if (currentZ - z <= preload) { - tile = tileSource.getTile(z, x, y, pixelRatio, projection); - if (tile.getState() == ol.TileState.IDLE) { - wantedTiles[tile.getKey()] = true; - if (!tileQueue.isKeyQueued(tile.getKey())) { - tileQueue.enqueue([tile, tileSourceKey, - tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]); - } - } - if (opt_tileCallback !== undefined) { - opt_tileCallback.call(opt_this, tile); - } - } else { - tileSource.useTile(z, x, y, projection); - } - } - } - } -}; - -goog.provide('ol.renderer.canvas.Layer'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.render.Event'); -goog.require('ol.render.EventType'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.Immediate'); -goog.require('ol.renderer.Layer'); -goog.require('ol.transform'); - - -/** - * @constructor - * @abstract - * @extends {ol.renderer.Layer} - * @param {ol.layer.Layer} layer Layer. - */ -ol.renderer.canvas.Layer = function(layer) { - - ol.renderer.Layer.call(this, layer); - - /** - * @protected - * @type {number} - */ - this.renderedResolution; - - /** - * @private - * @type {ol.Transform} - */ - this.transform_ = ol.transform.create(); - -}; -ol.inherits(ol.renderer.canvas.Layer, ol.renderer.Layer); - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.Extent} extent Clip extent. - * @protected - */ -ol.renderer.canvas.Layer.prototype.clip = function(context, frameState, extent) { - var pixelRatio = frameState.pixelRatio; - var width = frameState.size[0] * pixelRatio; - var height = frameState.size[1] * pixelRatio; - var rotation = frameState.viewState.rotation; - var topLeft = ol.extent.getTopLeft(/** @type {ol.Extent} */ (extent)); - var topRight = ol.extent.getTopRight(/** @type {ol.Extent} */ (extent)); - var bottomRight = ol.extent.getBottomRight(/** @type {ol.Extent} */ (extent)); - var bottomLeft = ol.extent.getBottomLeft(/** @type {ol.Extent} */ (extent)); - - ol.transform.apply(frameState.coordinateToPixelTransform, topLeft); - ol.transform.apply(frameState.coordinateToPixelTransform, topRight); - ol.transform.apply(frameState.coordinateToPixelTransform, bottomRight); - ol.transform.apply(frameState.coordinateToPixelTransform, bottomLeft); - - context.save(); - ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); - context.beginPath(); - context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio); - context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio); - context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio); - context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio); - context.clip(); - ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); -}; - - -/** - * @param {ol.render.EventType} type Event type. - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.Transform=} opt_transform Transform. - * @private - */ -ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) { - var layer = this.getLayer(); - if (layer.hasListener(type)) { - var width = frameState.size[0] * frameState.pixelRatio; - var height = frameState.size[1] * frameState.pixelRatio; - var rotation = frameState.viewState.rotation; - ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); - var transform = opt_transform !== undefined ? - opt_transform : this.getTransform(frameState, 0); - var render = new ol.render.canvas.Immediate( - context, frameState.pixelRatio, frameState.extent, transform, - frameState.viewState.rotation); - var composeEvent = new ol.render.Event(type, render, frameState, - context, null); - layer.dispatchEvent(composeEvent); - ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); - } -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {olx.FrameState} frameState FrameState. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -ol.renderer.canvas.Layer.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, callback, thisArg) { - var hasFeature = this.forEachFeatureAtCoordinate( - coordinate, frameState, 0, ol.functions.TRUE, this); - - if (hasFeature) { - return callback.call(thisArg, this.getLayer(), null); - } else { - return undefined; - } -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @param {ol.Transform=} opt_transform Transform. - * @protected - */ -ol.renderer.canvas.Layer.prototype.postCompose = function(context, frameState, layerState, opt_transform) { - this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, context, - frameState, opt_transform); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.Transform=} opt_transform Transform. - * @protected - */ -ol.renderer.canvas.Layer.prototype.preCompose = function(context, frameState, opt_transform) { - this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, context, - frameState, opt_transform); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.Transform=} opt_transform Transform. - * @protected - */ -ol.renderer.canvas.Layer.prototype.dispatchRenderEvent = function(context, frameState, opt_transform) { - this.dispatchComposeEvent_(ol.render.EventType.RENDER, context, - frameState, opt_transform); -}; - - -/** - * @param {olx.FrameState} frameState Frame state. - * @param {number} offsetX Offset on the x-axis in view coordinates. - * @protected - * @return {!ol.Transform} Transform. - */ -ol.renderer.canvas.Layer.prototype.getTransform = function(frameState, offsetX) { - var viewState = frameState.viewState; - var pixelRatio = frameState.pixelRatio; - var dx1 = pixelRatio * frameState.size[0] / 2; - var dy1 = pixelRatio * frameState.size[1] / 2; - var sx = pixelRatio / viewState.resolution; - var sy = -sx; - var angle = -viewState.rotation; - var dx2 = -viewState.center[0] + offsetX; - var dy2 = -viewState.center[1]; - return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); -}; - - -/** - * @abstract - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @param {CanvasRenderingContext2D} context Context. - */ -ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerState, context) {}; - -/** - * @abstract - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @return {boolean} whether composeFrame should be called. - */ -ol.renderer.canvas.Layer.prototype.prepareFrame = function(frameState, layerState) {}; - -goog.provide('ol.renderer.vector'); - -goog.require('ol'); -goog.require('ol.ImageState'); -goog.require('ol.render.ReplayType'); - - -/** - * @param {ol.Feature|ol.render.Feature} feature1 Feature 1. - * @param {ol.Feature|ol.render.Feature} feature2 Feature 2. - * @return {number} Order. - */ -ol.renderer.vector.defaultOrder = function(feature1, feature2) { - return ol.getUid(feature1) - ol.getUid(feature2); -}; - - -/** - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @return {number} Squared pixel tolerance. - */ -ol.renderer.vector.getSquaredTolerance = function(resolution, pixelRatio) { - var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio); - return tolerance * tolerance; -}; - - -/** - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @return {number} Pixel tolerance. - */ -ol.renderer.vector.getTolerance = function(resolution, pixelRatio) { - return ol.SIMPLIFY_TOLERANCE * resolution / pixelRatio; -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.Circle} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderCircleGeometry_ = function(replayGroup, geometry, style, feature) { - var fillStyle = style.getFill(); - var strokeStyle = style.getStroke(); - if (fillStyle || strokeStyle) { - var circleReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.CIRCLE); - circleReplay.setFillStrokeStyle(fillStyle, strokeStyle); - circleReplay.drawCircle(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - textReplay.drawText(geometry.getCenter(), 0, 2, 2, geometry, feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {ol.style.Style} style Style. - * @param {number} squaredTolerance Squared tolerance. - * @param {function(this: T, ol.events.Event)} listener Listener function. - * @param {T} thisArg Value to use as `this` when executing `listener`. - * @return {boolean} `true` if style is loading. - * @template T - */ -ol.renderer.vector.renderFeature = function( - replayGroup, feature, style, squaredTolerance, listener, thisArg) { - var loading = false; - var imageStyle, imageState; - imageStyle = style.getImage(); - if (imageStyle) { - imageState = imageStyle.getImageState(); - if (imageState == ol.ImageState.LOADED || - imageState == ol.ImageState.ERROR) { - imageStyle.unlistenImageChange(listener, thisArg); - } else { - if (imageState == ol.ImageState.IDLE) { - imageStyle.load(); - } - imageState = imageStyle.getImageState(); - imageStyle.listenImageChange(listener, thisArg); - loading = true; - } - } - ol.renderer.vector.renderFeature_(replayGroup, feature, style, - squaredTolerance); - return loading; -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {ol.style.Style} style Style. - * @param {number} squaredTolerance Squared tolerance. - * @private - */ -ol.renderer.vector.renderFeature_ = function( - replayGroup, feature, style, squaredTolerance) { - var geometry = style.getGeometryFunction()(feature); - if (!geometry) { - return; - } - var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); - var geometryRenderer = - ol.renderer.vector.GEOMETRY_RENDERERS_[simplifiedGeometry.getType()]; - geometryRenderer(replayGroup, simplifiedGeometry, style, feature); -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.GeometryCollection} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderGeometryCollectionGeometry_ = function(replayGroup, geometry, style, feature) { - var geometries = geometry.getGeometriesArray(); - var i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - var geometryRenderer = - ol.renderer.vector.GEOMETRY_RENDERERS_[geometries[i].getType()]; - geometryRenderer(replayGroup, geometries[i], style, feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.LineString|ol.render.Feature} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderLineStringGeometry_ = function(replayGroup, geometry, style, feature) { - var strokeStyle = style.getStroke(); - if (strokeStyle) { - var lineStringReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.LINE_STRING); - lineStringReplay.setFillStrokeStyle(null, strokeStyle); - lineStringReplay.drawLineString(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - textReplay.drawText(geometry.getFlatMidpoint(), 0, 2, 2, geometry, feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.MultiLineString|ol.render.Feature} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderMultiLineStringGeometry_ = function(replayGroup, geometry, style, feature) { - var strokeStyle = style.getStroke(); - if (strokeStyle) { - var lineStringReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.LINE_STRING); - lineStringReplay.setFillStrokeStyle(null, strokeStyle); - lineStringReplay.drawMultiLineString(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - var flatMidpointCoordinates = geometry.getFlatMidpoints(); - textReplay.drawText(flatMidpointCoordinates, 0, - flatMidpointCoordinates.length, 2, geometry, feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.MultiPolygon} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderMultiPolygonGeometry_ = function(replayGroup, geometry, style, feature) { - var fillStyle = style.getFill(); - var strokeStyle = style.getStroke(); - if (strokeStyle || fillStyle) { - var polygonReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.POLYGON); - polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle); - polygonReplay.drawMultiPolygon(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - var flatInteriorPointCoordinates = geometry.getFlatInteriorPoints(); - textReplay.drawText(flatInteriorPointCoordinates, 0, - flatInteriorPointCoordinates.length, 2, geometry, feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.Point|ol.render.Feature} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderPointGeometry_ = function(replayGroup, geometry, style, feature) { - var imageStyle = style.getImage(); - if (imageStyle) { - if (imageStyle.getImageState() != ol.ImageState.LOADED) { - return; - } - var imageReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.IMAGE); - imageReplay.setImageStyle(imageStyle); - imageReplay.drawPoint(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - textReplay.drawText(geometry.getFlatCoordinates(), 0, 2, 2, geometry, - feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.MultiPoint|ol.render.Feature} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderMultiPointGeometry_ = function(replayGroup, geometry, style, feature) { - var imageStyle = style.getImage(); - if (imageStyle) { - if (imageStyle.getImageState() != ol.ImageState.LOADED) { - return; - } - var imageReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.IMAGE); - imageReplay.setImageStyle(imageStyle); - imageReplay.drawMultiPoint(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - var flatCoordinates = geometry.getFlatCoordinates(); - textReplay.drawText(flatCoordinates, 0, flatCoordinates.length, - geometry.getStride(), geometry, feature); - } -}; - - -/** - * @param {ol.render.ReplayGroup} replayGroup Replay group. - * @param {ol.geom.Polygon|ol.render.Feature} geometry Geometry. - * @param {ol.style.Style} style Style. - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @private - */ -ol.renderer.vector.renderPolygonGeometry_ = function(replayGroup, geometry, style, feature) { - var fillStyle = style.getFill(); - var strokeStyle = style.getStroke(); - if (fillStyle || strokeStyle) { - var polygonReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.POLYGON); - polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle); - polygonReplay.drawPolygon(geometry, feature); - } - var textStyle = style.getText(); - if (textStyle) { - var textReplay = replayGroup.getReplay( - style.getZIndex(), ol.render.ReplayType.TEXT); - textReplay.setTextStyle(textStyle); - textReplay.drawText( - geometry.getFlatInteriorPoint(), 0, 2, 2, geometry, feature); - } -}; - - -/** - * @const - * @private - * @type {Object.<ol.geom.GeometryType, - * function(ol.render.ReplayGroup, ol.geom.Geometry, - * ol.style.Style, Object)>} - */ -ol.renderer.vector.GEOMETRY_RENDERERS_ = { - 'Point': ol.renderer.vector.renderPointGeometry_, - 'LineString': ol.renderer.vector.renderLineStringGeometry_, - 'Polygon': ol.renderer.vector.renderPolygonGeometry_, - 'MultiPoint': ol.renderer.vector.renderMultiPointGeometry_, - 'MultiLineString': ol.renderer.vector.renderMultiLineStringGeometry_, - 'MultiPolygon': ol.renderer.vector.renderMultiPolygonGeometry_, - 'GeometryCollection': ol.renderer.vector.renderGeometryCollectionGeometry_, - 'Circle': ol.renderer.vector.renderCircleGeometry_ -}; - -goog.provide('ol.renderer.canvas.VectorLayer'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.render.EventType'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.ReplayGroup'); -goog.require('ol.renderer.canvas.Layer'); -goog.require('ol.renderer.vector'); - - -/** - * @constructor - * @extends {ol.renderer.canvas.Layer} - * @param {ol.layer.Vector} vectorLayer Vector layer. - */ -ol.renderer.canvas.VectorLayer = function(vectorLayer) { - - ol.renderer.canvas.Layer.call(this, vectorLayer); - - /** - * @private - * @type {boolean} - */ - this.dirty_ = false; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = -1; - - /** - * @private - * @type {number} - */ - this.renderedResolution_ = NaN; - - /** - * @private - * @type {ol.Extent} - */ - this.renderedExtent_ = ol.extent.createEmpty(); - - /** - * @private - * @type {function(ol.Feature, ol.Feature): number|null} - */ - this.renderedRenderOrder_ = null; - - /** - * @private - * @type {ol.render.canvas.ReplayGroup} - */ - this.replayGroup_ = null; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = ol.dom.createCanvasContext2D(); - -}; -ol.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) { - - var extent = frameState.extent; - var pixelRatio = frameState.pixelRatio; - var skippedFeatureUids = layerState.managed ? - frameState.skippedFeatureUids : {}; - var viewState = frameState.viewState; - var projection = viewState.projection; - var rotation = viewState.rotation; - var projectionExtent = projection.getExtent(); - var vectorSource = /** @type {ol.source.Vector} */ (this.getLayer().getSource()); - - var transform = this.getTransform(frameState, 0); - - this.preCompose(context, frameState, transform); - - // clipped rendering if layer extent is set - var clipExtent = layerState.extent; - var clipped = clipExtent !== undefined; - if (clipped) { - this.clip(context, frameState, /** @type {ol.Extent} */ (clipExtent)); - } - var replayGroup = this.replayGroup_; - if (replayGroup && !replayGroup.isEmpty()) { - var layer = this.getLayer(); - var drawOffsetX = 0; - var drawOffsetY = 0; - var replayContext; - if (layer.hasListener(ol.render.EventType.RENDER)) { - var drawWidth = context.canvas.width; - var drawHeight = context.canvas.height; - if (rotation) { - var drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight)); - drawOffsetX = (drawSize - drawWidth) / 2; - drawOffsetY = (drawSize - drawHeight) / 2; - drawWidth = drawHeight = drawSize; - } - // resize and clear - this.context_.canvas.width = drawWidth; - this.context_.canvas.height = drawHeight; - replayContext = this.context_; - } else { - replayContext = context; - } - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - var alpha = replayContext.globalAlpha; - replayContext.globalAlpha = layerState.opacity; - if (replayContext != context) { - replayContext.translate(drawOffsetX, drawOffsetY); - } - - var width = frameState.size[0] * pixelRatio; - var height = frameState.size[1] * pixelRatio; - ol.render.canvas.rotateAtOffset(replayContext, -rotation, - width / 2, height / 2); - replayGroup.replay(replayContext, pixelRatio, transform, rotation, - skippedFeatureUids); - if (vectorSource.getWrapX() && projection.canWrapX() && - !ol.extent.containsExtent(projectionExtent, extent)) { - var startX = extent[0]; - var worldWidth = ol.extent.getWidth(projectionExtent); - var world = 0; - var offsetX; - while (startX < projectionExtent[0]) { - --world; - offsetX = worldWidth * world; - transform = this.getTransform(frameState, offsetX); - replayGroup.replay(replayContext, pixelRatio, transform, rotation, - skippedFeatureUids); - startX += worldWidth; - } - world = 0; - startX = extent[2]; - while (startX > projectionExtent[2]) { - ++world; - offsetX = worldWidth * world; - transform = this.getTransform(frameState, offsetX); - replayGroup.replay(replayContext, pixelRatio, transform, rotation, - skippedFeatureUids); - startX -= worldWidth; - } - // restore original transform for render and compose events - transform = this.getTransform(frameState, 0); - } - ol.render.canvas.rotateAtOffset(replayContext, rotation, - width / 2, height / 2); - - if (replayContext != context) { - this.dispatchRenderEvent(replayContext, frameState, transform); - context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); - replayContext.translate(-drawOffsetX, -drawOffsetY); - } - replayContext.globalAlpha = alpha; - } - - if (clipped) { - context.restore(); - } - this.postCompose(context, frameState, layerState, transform); - -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - if (!this.replayGroup_) { - return undefined; - } else { - var resolution = frameState.viewState.resolution; - var rotation = frameState.viewState.rotation; - var layer = this.getLayer(); - /** @type {Object.<string, boolean>} */ - var features = {}; - return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, - rotation, hitTolerance, {}, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - var key = ol.getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, layer); - } - }); - } -}; - - -/** - * Handle changes in image style state. - * @param {ol.events.Event} event Image style change event. - * @private - */ -ol.renderer.canvas.VectorLayer.prototype.handleStyleImageChange_ = function(event) { - this.renderIfReadyAndVisible(); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, layerState) { - - var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); - var vectorSource = vectorLayer.getSource(); - - this.updateAttributions( - frameState.attributions, vectorSource.getAttributions()); - this.updateLogos(frameState, vectorSource); - - var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; - var interacting = frameState.viewHints[ol.ViewHint.INTERACTING]; - var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); - var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); - - if (!this.dirty_ && (!updateWhileAnimating && animating) || - (!updateWhileInteracting && interacting)) { - return true; - } - - var frameStateExtent = frameState.extent; - var viewState = frameState.viewState; - var projection = viewState.projection; - var resolution = viewState.resolution; - var pixelRatio = frameState.pixelRatio; - var vectorLayerRevision = vectorLayer.getRevision(); - var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); - var vectorLayerRenderOrder = vectorLayer.getRenderOrder(); - - if (vectorLayerRenderOrder === undefined) { - vectorLayerRenderOrder = ol.renderer.vector.defaultOrder; - } - - var extent = ol.extent.buffer(frameStateExtent, - vectorLayerRenderBuffer * resolution); - var projectionExtent = viewState.projection.getExtent(); - - if (vectorSource.getWrapX() && viewState.projection.canWrapX() && - !ol.extent.containsExtent(projectionExtent, frameState.extent)) { - // For the replay group, we need an extent that intersects the real world - // (-180° to +180°). To support geometries in a coordinate range from -540° - // to +540°, we add at least 1 world width on each side of the projection - // extent. If the viewport is wider than the world, we need to add half of - // the viewport width to make sure we cover the whole viewport. - var worldWidth = ol.extent.getWidth(projectionExtent); - var buffer = Math.max(ol.extent.getWidth(extent) / 2, worldWidth); - extent[0] = projectionExtent[0] - buffer; - extent[2] = projectionExtent[2] + buffer; - } - - if (!this.dirty_ && - this.renderedResolution_ == resolution && - this.renderedRevision_ == vectorLayerRevision && - this.renderedRenderOrder_ == vectorLayerRenderOrder && - ol.extent.containsExtent(this.renderedExtent_, extent)) { - return true; - } - - this.replayGroup_ = null; - - this.dirty_ = false; - - var replayGroup = - new ol.render.canvas.ReplayGroup( - ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, - resolution, vectorSource.getOverlaps(), vectorLayer.getRenderBuffer()); - vectorSource.loadFeatures(extent, resolution, projection); - /** - * @param {ol.Feature} feature Feature. - * @this {ol.renderer.canvas.VectorLayer} - */ - var renderFeature = function(feature) { - var styles; - var styleFunction = feature.getStyleFunction(); - if (styleFunction) { - styles = styleFunction.call(feature, resolution); - } else { - styleFunction = vectorLayer.getStyleFunction(); - if (styleFunction) { - styles = styleFunction(feature, resolution); - } - } - if (styles) { - var dirty = this.renderFeature( - feature, resolution, pixelRatio, styles, replayGroup); - this.dirty_ = this.dirty_ || dirty; - } - }; - if (vectorLayerRenderOrder) { - /** @type {Array.<ol.Feature>} */ - var features = []; - vectorSource.forEachFeatureInExtent(extent, - /** - * @param {ol.Feature} feature Feature. - */ - function(feature) { - features.push(feature); - }, this); - features.sort(vectorLayerRenderOrder); - features.forEach(renderFeature, this); - } else { - vectorSource.forEachFeatureInExtent(extent, renderFeature, this); - } - replayGroup.finish(); - - this.renderedResolution_ = resolution; - this.renderedRevision_ = vectorLayerRevision; - this.renderedRenderOrder_ = vectorLayerRenderOrder; - this.renderedExtent_ = extent; - this.replayGroup_ = replayGroup; - - return true; -}; - - -/** - * @param {ol.Feature} feature Feature. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of - * styles. - * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - */ -ol.renderer.canvas.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { - if (!styles) { - return false; - } - var loading = false; - if (Array.isArray(styles)) { - for (var i = 0, ii = styles.length; i < ii; ++i) { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles[i], - ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - } else { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles, - ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - return loading; -}; - -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.renderer.webgl.defaultmapshader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.renderer.webgl.defaultmapshader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.renderer.webgl.defaultmapshader.Fragment.SOURCE); - }; - ol.inherits(ol.renderer.webgl.defaultmapshader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.defaultmapshader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform float u_opacity;\nuniform sampler2D u_texture;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_texture, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n gl_FragColor.a = texColor.a * u_opacity;\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.defaultmapshader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.defaultmapshader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.renderer.webgl.defaultmapshader.Fragment.DEBUG_SOURCE : - ol.renderer.webgl.defaultmapshader.Fragment.OPTIMIZED_SOURCE; - - - ol.renderer.webgl.defaultmapshader.fragment = new ol.renderer.webgl.defaultmapshader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.renderer.webgl.defaultmapshader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.renderer.webgl.defaultmapshader.Vertex.SOURCE); - }; - ol.inherits(ol.renderer.webgl.defaultmapshader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.defaultmapshader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\n\nuniform mat4 u_texCoordMatrix;\nuniform mat4 u_projectionMatrix;\n\nvoid main(void) {\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);\n v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.defaultmapshader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.defaultmapshader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.renderer.webgl.defaultmapshader.Vertex.DEBUG_SOURCE : - ol.renderer.webgl.defaultmapshader.Vertex.OPTIMIZED_SOURCE; - - - ol.renderer.webgl.defaultmapshader.vertex = new ol.renderer.webgl.defaultmapshader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.renderer.webgl.defaultmapshader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_opacity' : 'f'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_projectionMatrix' : 'e'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_texCoordMatrix = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_texCoordMatrix' : 'd'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_texture = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_texture' : 'g'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'b'); - - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'c'); - }; - -} - -goog.provide('ol.renderer.webgl.Layer'); - -goog.require('ol'); -goog.require('ol.render.Event'); -goog.require('ol.render.EventType'); -goog.require('ol.render.webgl.Immediate'); -goog.require('ol.renderer.Layer'); -goog.require('ol.renderer.webgl.defaultmapshader'); -goog.require('ol.transform'); -goog.require('ol.vec.Mat4'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Buffer'); -goog.require('ol.webgl.Context'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @abstract - * @extends {ol.renderer.Layer} - * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. - * @param {ol.layer.Layer} layer Layer. - */ - ol.renderer.webgl.Layer = function(mapRenderer, layer) { - - ol.renderer.Layer.call(this, layer); - - /** - * @protected - * @type {ol.renderer.webgl.Map} - */ - this.mapRenderer = mapRenderer; - - /** - * @private - * @type {ol.webgl.Buffer} - */ - this.arrayBuffer_ = new ol.webgl.Buffer([ - -1, -1, 0, 0, - 1, -1, 1, 0, - -1, 1, 0, 1, - 1, 1, 1, 1 - ]); - - /** - * @protected - * @type {WebGLTexture} - */ - this.texture = null; - - /** - * @protected - * @type {WebGLFramebuffer} - */ - this.framebuffer = null; - - /** - * @protected - * @type {number|undefined} - */ - this.framebufferDimension = undefined; - - /** - * @protected - * @type {ol.Transform} - */ - this.texCoordMatrix = ol.transform.create(); - - /** - * @protected - * @type {ol.Transform} - */ - this.projectionMatrix = ol.transform.create(); - - /** - * @type {Array.<number>} - * @private - */ - this.tmpMat4_ = ol.vec.Mat4.create(); - - /** - * @private - * @type {ol.renderer.webgl.defaultmapshader.Locations} - */ - this.defaultLocations_ = null; - - }; - ol.inherits(ol.renderer.webgl.Layer, ol.renderer.Layer); - - - /** - * @param {olx.FrameState} frameState Frame state. - * @param {number} framebufferDimension Framebuffer dimension. - * @protected - */ - ol.renderer.webgl.Layer.prototype.bindFramebuffer = function(frameState, framebufferDimension) { - - var gl = this.mapRenderer.getGL(); - - if (this.framebufferDimension === undefined || - this.framebufferDimension != framebufferDimension) { - /** - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLFramebuffer} framebuffer Framebuffer. - * @param {WebGLTexture} texture Texture. - */ - var postRenderFunction = function(gl, framebuffer, texture) { - if (!gl.isContextLost()) { - gl.deleteFramebuffer(framebuffer); - gl.deleteTexture(texture); - } - }.bind(null, gl, this.framebuffer, this.texture); - - frameState.postRenderFunctions.push( - /** @type {ol.PostRenderFunction} */ (postRenderFunction) - ); - - var texture = ol.webgl.Context.createEmptyTexture( - gl, framebufferDimension, framebufferDimension); - - var framebuffer = gl.createFramebuffer(); - gl.bindFramebuffer(ol.webgl.FRAMEBUFFER, framebuffer); - gl.framebufferTexture2D(ol.webgl.FRAMEBUFFER, - ol.webgl.COLOR_ATTACHMENT0, ol.webgl.TEXTURE_2D, texture, 0); - - this.texture = texture; - this.framebuffer = framebuffer; - this.framebufferDimension = framebufferDimension; - - } else { - gl.bindFramebuffer(ol.webgl.FRAMEBUFFER, this.framebuffer); - } - - }; - - - /** - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @param {ol.webgl.Context} context Context. - */ - ol.renderer.webgl.Layer.prototype.composeFrame = function(frameState, layerState, context) { - - this.dispatchComposeEvent_( - ol.render.EventType.PRECOMPOSE, context, frameState); - - context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.arrayBuffer_); - - var gl = context.getGL(); - - var fragmentShader = ol.renderer.webgl.defaultmapshader.fragment; - var vertexShader = ol.renderer.webgl.defaultmapshader.vertex; - - var program = context.getProgram(fragmentShader, vertexShader); - - var locations; - if (!this.defaultLocations_) { - // eslint-disable-next-line openlayers-internal/no-missing-requires - locations = new ol.renderer.webgl.defaultmapshader.Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - if (context.useProgram(program)) { - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer( - locations.a_position, 2, ol.webgl.FLOAT, false, 16, 0); - gl.enableVertexAttribArray(locations.a_texCoord); - gl.vertexAttribPointer( - locations.a_texCoord, 2, ol.webgl.FLOAT, false, 16, 8); - gl.uniform1i(locations.u_texture, 0); - } - - gl.uniformMatrix4fv(locations.u_texCoordMatrix, false, - ol.vec.Mat4.fromTransform(this.tmpMat4_, this.getTexCoordMatrix())); - gl.uniformMatrix4fv(locations.u_projectionMatrix, false, - ol.vec.Mat4.fromTransform(this.tmpMat4_, this.getProjectionMatrix())); - gl.uniform1f(locations.u_opacity, layerState.opacity); - gl.bindTexture(ol.webgl.TEXTURE_2D, this.getTexture()); - gl.drawArrays(ol.webgl.TRIANGLE_STRIP, 0, 4); - - this.dispatchComposeEvent_( - ol.render.EventType.POSTCOMPOSE, context, frameState); - - }; - - - /** - * @param {ol.render.EventType} type Event type. - * @param {ol.webgl.Context} context WebGL context. - * @param {olx.FrameState} frameState Frame state. - * @private - */ - ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState) { - var layer = this.getLayer(); - if (layer.hasListener(type)) { - var viewState = frameState.viewState; - var resolution = viewState.resolution; - var pixelRatio = frameState.pixelRatio; - var extent = frameState.extent; - var center = viewState.center; - var rotation = viewState.rotation; - var size = frameState.size; - - var render = new ol.render.webgl.Immediate( - context, center, resolution, rotation, size, extent, pixelRatio); - var composeEvent = new ol.render.Event( - type, render, frameState, null, context); - layer.dispatchEvent(composeEvent); - } - }; - - - /** - * @return {!ol.Transform} Matrix. - */ - ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = function() { - return this.texCoordMatrix; - }; - - - /** - * @return {WebGLTexture} Texture. - */ - ol.renderer.webgl.Layer.prototype.getTexture = function() { - return this.texture; - }; - - - /** - * @return {!ol.Transform} Matrix. - */ - ol.renderer.webgl.Layer.prototype.getProjectionMatrix = function() { - return this.projectionMatrix; - }; - - - /** - * Handle webglcontextlost. - */ - ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() { - this.texture = null; - this.framebuffer = null; - this.framebufferDimension = undefined; - }; - - - /** - * @abstract - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @param {ol.webgl.Context} context Context. - * @return {boolean} whether composeFrame should be called. - */ - ol.renderer.webgl.Layer.prototype.prepareFrame = function(frameState, layerState, context) {}; - - - /** - * @abstract - * @param {ol.Pixel} pixel Pixel. - * @param {olx.FrameState} frameState FrameState. - * @param {function(this: S, ol.layer.Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ - ol.renderer.webgl.Layer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {}; - -} - -goog.provide('ol.renderer.webgl.VectorLayer'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.extent'); -goog.require('ol.render.webgl.ReplayGroup'); -goog.require('ol.renderer.vector'); -goog.require('ol.renderer.webgl.Layer'); -goog.require('ol.transform'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.renderer.webgl.Layer} - * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. - * @param {ol.layer.Vector} vectorLayer Vector layer. - */ - ol.renderer.webgl.VectorLayer = function(mapRenderer, vectorLayer) { - - ol.renderer.webgl.Layer.call(this, mapRenderer, vectorLayer); - - /** - * @private - * @type {boolean} - */ - this.dirty_ = false; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = -1; - - /** - * @private - * @type {number} - */ - this.renderedResolution_ = NaN; - - /** - * @private - * @type {ol.Extent} - */ - this.renderedExtent_ = ol.extent.createEmpty(); - - /** - * @private - * @type {function(ol.Feature, ol.Feature): number|null} - */ - this.renderedRenderOrder_ = null; - - /** - * @private - * @type {ol.render.webgl.ReplayGroup} - */ - this.replayGroup_ = null; - - /** - * The last layer state. - * @private - * @type {?ol.LayerState} - */ - this.layerState_ = null; - - }; - ol.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer); - - - /** - * @inheritDoc - */ - ol.renderer.webgl.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) { - this.layerState_ = layerState; - var viewState = frameState.viewState; - var replayGroup = this.replayGroup_; - var size = frameState.size; - var pixelRatio = frameState.pixelRatio; - var gl = this.mapRenderer.getGL(); - if (replayGroup && !replayGroup.isEmpty()) { - gl.enable(gl.SCISSOR_TEST); - gl.scissor(0, 0, size[0] * pixelRatio, size[1] * pixelRatio); - replayGroup.replay(context, - viewState.center, viewState.resolution, viewState.rotation, - size, pixelRatio, layerState.opacity, - layerState.managed ? frameState.skippedFeatureUids : {}); - gl.disable(gl.SCISSOR_TEST); - } - - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.VectorLayer.prototype.disposeInternal = function() { - var replayGroup = this.replayGroup_; - if (replayGroup) { - var context = this.mapRenderer.getContext(); - replayGroup.getDeleteResourcesFunction(context)(); - this.replayGroup_ = null; - } - ol.renderer.webgl.Layer.prototype.disposeInternal.call(this); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - if (!this.replayGroup_ || !this.layerState_) { - return undefined; - } else { - var context = this.mapRenderer.getContext(); - var viewState = frameState.viewState; - var layer = this.getLayer(); - var layerState = this.layerState_; - /** @type {Object.<string, boolean>} */ - var features = {}; - return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, - context, viewState.center, viewState.resolution, viewState.rotation, - frameState.size, frameState.pixelRatio, layerState.opacity, - {}, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - var key = ol.getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, layer); - } - }); - } - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.VectorLayer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) { - if (!this.replayGroup_ || !this.layerState_) { - return false; - } else { - var context = this.mapRenderer.getContext(); - var viewState = frameState.viewState; - var layerState = this.layerState_; - return this.replayGroup_.hasFeatureAtCoordinate(coordinate, - context, viewState.center, viewState.resolution, viewState.rotation, - frameState.size, frameState.pixelRatio, layerState.opacity, - frameState.skippedFeatureUids); - } - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.VectorLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - var coordinate = ol.transform.apply( - frameState.pixelToCoordinateTransform, pixel.slice()); - var hasFeature = this.hasFeatureAtCoordinate(coordinate, frameState); - - if (hasFeature) { - return callback.call(thisArg, this.getLayer(), null); - } else { - return undefined; - } - }; - - - /** - * Handle changes in image style state. - * @param {ol.events.Event} event Image style change event. - * @private - */ - ol.renderer.webgl.VectorLayer.prototype.handleStyleImageChange_ = function(event) { - this.renderIfReadyAndVisible(); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.VectorLayer.prototype.prepareFrame = function(frameState, layerState, context) { - - var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); - var vectorSource = vectorLayer.getSource(); - - this.updateAttributions( - frameState.attributions, vectorSource.getAttributions()); - this.updateLogos(frameState, vectorSource); - - var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; - var interacting = frameState.viewHints[ol.ViewHint.INTERACTING]; - var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); - var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); - - if (!this.dirty_ && (!updateWhileAnimating && animating) || - (!updateWhileInteracting && interacting)) { - return true; - } - - var frameStateExtent = frameState.extent; - var viewState = frameState.viewState; - var projection = viewState.projection; - var resolution = viewState.resolution; - var pixelRatio = frameState.pixelRatio; - var vectorLayerRevision = vectorLayer.getRevision(); - var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); - var vectorLayerRenderOrder = vectorLayer.getRenderOrder(); - - if (vectorLayerRenderOrder === undefined) { - vectorLayerRenderOrder = ol.renderer.vector.defaultOrder; - } - - var extent = ol.extent.buffer(frameStateExtent, - vectorLayerRenderBuffer * resolution); - - if (!this.dirty_ && - this.renderedResolution_ == resolution && - this.renderedRevision_ == vectorLayerRevision && - this.renderedRenderOrder_ == vectorLayerRenderOrder && - ol.extent.containsExtent(this.renderedExtent_, extent)) { - return true; - } - - if (this.replayGroup_) { - frameState.postRenderFunctions.push( - this.replayGroup_.getDeleteResourcesFunction(context)); - } - - this.dirty_ = false; - - var replayGroup = new ol.render.webgl.ReplayGroup( - ol.renderer.vector.getTolerance(resolution, pixelRatio), - extent, vectorLayer.getRenderBuffer()); - vectorSource.loadFeatures(extent, resolution, projection); - /** - * @param {ol.Feature} feature Feature. - * @this {ol.renderer.webgl.VectorLayer} - */ - var renderFeature = function(feature) { - var styles; - var styleFunction = feature.getStyleFunction(); - if (styleFunction) { - styles = styleFunction.call(feature, resolution); - } else { - styleFunction = vectorLayer.getStyleFunction(); - if (styleFunction) { - styles = styleFunction(feature, resolution); - } - } - if (styles) { - var dirty = this.renderFeature( - feature, resolution, pixelRatio, styles, replayGroup); - this.dirty_ = this.dirty_ || dirty; - } - }; - if (vectorLayerRenderOrder) { - /** @type {Array.<ol.Feature>} */ - var features = []; - vectorSource.forEachFeatureInExtent(extent, - /** - * @param {ol.Feature} feature Feature. - */ - function(feature) { - features.push(feature); - }, this); - features.sort(vectorLayerRenderOrder); - features.forEach(renderFeature, this); - } else { - vectorSource.forEachFeatureInExtent(extent, renderFeature, this); - } - replayGroup.finish(context); - - this.renderedResolution_ = resolution; - this.renderedRevision_ = vectorLayerRevision; - this.renderedRenderOrder_ = vectorLayerRenderOrder; - this.renderedExtent_ = extent; - this.replayGroup_ = replayGroup; - - return true; - }; - - - /** - * @param {ol.Feature} feature Feature. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of - * styles. - * @param {ol.render.webgl.ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - */ - ol.renderer.webgl.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { - if (!styles) { - return false; - } - var loading = false; - if (Array.isArray(styles)) { - for (var i = styles.length - 1, ii = 0; i >= ii; --i) { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles[i], - ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - } else { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles, - ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - return loading; - }; - -} - -goog.provide('ol.layer.Vector'); - -goog.require('ol'); -goog.require('ol.layer.Layer'); -goog.require('ol.obj'); -goog.require('ol.renderer.Type'); -goog.require('ol.renderer.canvas.VectorLayer'); -goog.require('ol.renderer.webgl.VectorLayer'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * Vector data that is rendered client-side. - * Note that any property set in the options is set as a {@link ol.Object} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {ol.layer.Layer} - * @fires ol.render.Event - * @param {olx.layer.VectorOptions=} opt_options Options. - * @api - */ -ol.layer.Vector = function(opt_options) { - var options = opt_options ? - opt_options : /** @type {olx.layer.VectorOptions} */ ({}); - - var baseOptions = ol.obj.assign({}, options); - - delete baseOptions.style; - delete baseOptions.renderBuffer; - delete baseOptions.updateWhileAnimating; - delete baseOptions.updateWhileInteracting; - ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (baseOptions)); - - /** - * @type {number} - * @private - */ - this.renderBuffer_ = options.renderBuffer !== undefined ? - options.renderBuffer : 100; - - /** - * User provided style. - * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} - * @private - */ - this.style_ = null; - - /** - * Style function for use within the library. - * @type {ol.StyleFunction|undefined} - * @private - */ - this.styleFunction_ = undefined; - - this.setStyle(options.style); - - /** - * @type {boolean} - * @private - */ - this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? - options.updateWhileAnimating : false; - - /** - * @type {boolean} - * @private - */ - this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? - options.updateWhileInteracting : false; -}; -ol.inherits(ol.layer.Vector, ol.layer.Layer); - - -/** - * @inheritDoc - */ -ol.layer.Vector.prototype.createRenderer = function(mapRenderer) { - var renderer = null; - var type = mapRenderer.getType(); - if (ol.ENABLE_CANVAS && type === ol.renderer.Type.CANVAS) { - renderer = new ol.renderer.canvas.VectorLayer(this); - } else if (ol.ENABLE_WEBGL && type === ol.renderer.Type.WEBGL) { - renderer = new ol.renderer.webgl.VectorLayer(/** @type {ol.renderer.webgl.Map} */ (mapRenderer), this); - } - return renderer; -}; - - -/** - * @return {number|undefined} Render buffer. - */ -ol.layer.Vector.prototype.getRenderBuffer = function() { - return this.renderBuffer_; -}; - - -/** - * @return {function(ol.Feature, ol.Feature): number|null|undefined} Render - * order. - */ -ol.layer.Vector.prototype.getRenderOrder = function() { - return /** @type {ol.RenderOrderFunction|null|undefined} */ ( - this.get(ol.layer.Vector.Property_.RENDER_ORDER)); -}; - - -/** - * Return the associated {@link ol.source.Vector vectorsource} of the layer. - * @function - * @return {ol.source.Vector} Source. - * @api - */ -ol.layer.Vector.prototype.getSource; - - -/** - * Get the style for features. This returns whatever was passed to the `style` - * option at construction or to the `setStyle` method. - * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} - * Layer style. - * @api - */ -ol.layer.Vector.prototype.getStyle = function() { - return this.style_; -}; - - -/** - * Get the style function. - * @return {ol.StyleFunction|undefined} Layer style function. - * @api - */ -ol.layer.Vector.prototype.getStyleFunction = function() { - return this.styleFunction_; -}; - - -/** - * @return {boolean} Whether the rendered layer should be updated while - * animating. - */ -ol.layer.Vector.prototype.getUpdateWhileAnimating = function() { - return this.updateWhileAnimating_; -}; - - -/** - * @return {boolean} Whether the rendered layer should be updated while - * interacting. - */ -ol.layer.Vector.prototype.getUpdateWhileInteracting = function() { - return this.updateWhileInteracting_; -}; - - -/** - * @param {ol.RenderOrderFunction|null|undefined} renderOrder - * Render order. - */ -ol.layer.Vector.prototype.setRenderOrder = function(renderOrder) { - this.set(ol.layer.Vector.Property_.RENDER_ORDER, renderOrder); -}; - - -/** - * Set the style for features. This can be a single style object, an array - * of styles, or a function that takes a feature and resolution and returns - * an array of styles. If it is `undefined` the default style is used. If - * it is `null` the layer has no style (a `null` style), so only features - * that have their own styles will be rendered in the layer. See - * {@link ol.style} for information on the default style. - * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|null|undefined} - * style Layer style. - * @api - */ -ol.layer.Vector.prototype.setStyle = function(style) { - this.style_ = style !== undefined ? style : ol.style.Style.defaultFunction; - this.styleFunction_ = style === null ? - undefined : ol.style.Style.createFunction(this.style_); - this.changed(); -}; - - -/** - * @enum {string} - * @private - */ -ol.layer.Vector.Property_ = { - RENDER_ORDER: 'renderOrder' -}; - -goog.provide('ol.loadingstrategy'); - - -/** - * Strategy function for loading all features with a single request. - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @return {Array.<ol.Extent>} Extents. - * @api - */ -ol.loadingstrategy.all = function(extent, resolution) { - return [[-Infinity, -Infinity, Infinity, Infinity]]; -}; - - -/** - * Strategy function for loading features based on the view's extent and - * resolution. - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @return {Array.<ol.Extent>} Extents. - * @api - */ -ol.loadingstrategy.bbox = function(extent, resolution) { - return [extent]; -}; - - -/** - * Creates a strategy function for loading features based on a tile grid. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @return {function(ol.Extent, number): Array.<ol.Extent>} Loading strategy. - * @api - */ -ol.loadingstrategy.tile = function(tileGrid) { - return ( - /** - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @return {Array.<ol.Extent>} Extents. - */ - function(extent, resolution) { - var z = tileGrid.getZForResolution(resolution); - var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - /** @type {Array.<ol.Extent>} */ - var extents = []; - /** @type {ol.TileCoord} */ - var tileCoord = [z, 0, 0]; - for (tileCoord[1] = tileRange.minX; tileCoord[1] <= tileRange.maxX; - ++tileCoord[1]) { - for (tileCoord[2] = tileRange.minY; tileCoord[2] <= tileRange.maxY; - ++tileCoord[2]) { - extents.push(tileGrid.getTileCoordExtent(tileCoord)); - } - } - return extents; - }); -}; - -goog.provide('ol.source.Source'); - -goog.require('ol'); -goog.require('ol.Attribution'); -goog.require('ol.Object'); -goog.require('ol.proj'); -goog.require('ol.source.State'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for {@link ol.layer.Layer} sources. - * - * A generic `change` event is triggered when the state of the source changes. - * - * @constructor - * @abstract - * @extends {ol.Object} - * @param {ol.SourceSourceOptions} options Source options. - * @api - */ -ol.source.Source = function(options) { - - ol.Object.call(this); - - /** - * @private - * @type {ol.proj.Projection} - */ - this.projection_ = ol.proj.get(options.projection); - - /** - * @private - * @type {Array.<ol.Attribution>} - */ - this.attributions_ = ol.source.Source.toAttributionsArray_(options.attributions); - - /** - * @private - * @type {string|olx.LogoOptions|undefined} - */ - this.logo_ = options.logo; - - /** - * @private - * @type {ol.source.State} - */ - this.state_ = options.state !== undefined ? - options.state : ol.source.State.READY; - - /** - * @private - * @type {boolean} - */ - this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false; - -}; -ol.inherits(ol.source.Source, ol.Object); - -/** - * Turns various ways of defining an attribution to an array of `ol.Attributions`. - * - * @param {ol.AttributionLike|undefined} - * attributionLike The attributions as string, array of strings, - * `ol.Attribution`, array of `ol.Attribution` or undefined. - * @return {Array.<ol.Attribution>} The array of `ol.Attribution` or null if - * `undefined` was given. - */ -ol.source.Source.toAttributionsArray_ = function(attributionLike) { - if (typeof attributionLike === 'string') { - return [new ol.Attribution({html: attributionLike})]; - } else if (attributionLike instanceof ol.Attribution) { - return [attributionLike]; - } else if (Array.isArray(attributionLike)) { - var len = attributionLike.length; - var attributions = new Array(len); - for (var i = 0; i < len; i++) { - var item = attributionLike[i]; - if (typeof item === 'string') { - attributions[i] = new ol.Attribution({html: item}); - } else { - attributions[i] = item; - } - } - return attributions; - } else { - return null; - } -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {Object.<string, boolean>} skippedFeatureUids Skipped feature uids. - * @param {function((ol.Feature|ol.render.Feature)): T} callback Feature - * callback. - * @return {T|undefined} Callback result. - * @template T - */ -ol.source.Source.prototype.forEachFeatureAtCoordinate = ol.nullFunction; - - -/** - * Get the attributions of the source. - * @return {Array.<ol.Attribution>} Attributions. - * @api - */ -ol.source.Source.prototype.getAttributions = function() { - return this.attributions_; -}; - - -/** - * Get the logo of the source. - * @return {string|olx.LogoOptions|undefined} Logo. - * @api - */ -ol.source.Source.prototype.getLogo = function() { - return this.logo_; -}; - - -/** - * Get the projection of the source. - * @return {ol.proj.Projection} Projection. - * @api - */ -ol.source.Source.prototype.getProjection = function() { - return this.projection_; -}; - - -/** - * @abstract - * @return {Array.<number>|undefined} Resolutions. - */ -ol.source.Source.prototype.getResolutions = function() {}; - - -/** - * Get the state of the source, see {@link ol.source.State} for possible states. - * @return {ol.source.State} State. - * @api - */ -ol.source.Source.prototype.getState = function() { - return this.state_; -}; - - -/** - * @return {boolean|undefined} Wrap X. - */ -ol.source.Source.prototype.getWrapX = function() { - return this.wrapX_; -}; - - -/** - * Refreshes the source and finally dispatches a 'change' event. - * @api - */ -ol.source.Source.prototype.refresh = function() { - this.changed(); -}; - - -/** - * Set the attributions of the source. - * @param {ol.AttributionLike|undefined} attributions Attributions. - * Can be passed as `string`, `Array<string>`, `{@link ol.Attribution}`, - * `Array<{@link ol.Attribution}>` or `undefined`. - * @api - */ -ol.source.Source.prototype.setAttributions = function(attributions) { - this.attributions_ = ol.source.Source.toAttributionsArray_(attributions); - this.changed(); -}; - - -/** - * Set the logo of the source. - * @param {string|olx.LogoOptions|undefined} logo Logo. - */ -ol.source.Source.prototype.setLogo = function(logo) { - this.logo_ = logo; -}; - - -/** - * Set the state of the source. - * @param {ol.source.State} state State. - * @protected - */ -ol.source.Source.prototype.setState = function(state) { - this.state_ = state; - this.changed(); -}; - -goog.provide('ol.source.VectorEventType'); - -/** - * @enum {string} - */ -ol.source.VectorEventType = { - /** - * Triggered when a feature is added to the source. - * @event ol.source.Vector.Event#addfeature - * @api - */ - ADDFEATURE: 'addfeature', - - /** - * Triggered when a feature is updated. - * @event ol.source.Vector.Event#changefeature - * @api - */ - CHANGEFEATURE: 'changefeature', - - /** - * Triggered when the clear method is called on the source. - * @event ol.source.Vector.Event#clear - * @api - */ - CLEAR: 'clear', - - /** - * Triggered when a feature is removed from the source. - * See {@link ol.source.Vector#clear source.clear()} for exceptions. - * @event ol.source.Vector.Event#removefeature - * @api - */ - REMOVEFEATURE: 'removefeature' -}; - -// FIXME bulk feature upload - suppress events -// FIXME make change-detection more refined (notably, geometry hint) - -goog.provide('ol.source.Vector'); - -goog.require('ol'); -goog.require('ol.Collection'); -goog.require('ol.CollectionEventType'); -goog.require('ol.ObjectEventType'); -goog.require('ol.array'); -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.featureloader'); -goog.require('ol.functions'); -goog.require('ol.loadingstrategy'); -goog.require('ol.obj'); -goog.require('ol.source.Source'); -goog.require('ol.source.State'); -goog.require('ol.source.VectorEventType'); -goog.require('ol.structs.RBush'); - - -/** - * @classdesc - * Provides a source of features for vector layers. Vector features provided - * by this source are suitable for editing. See {@link ol.source.VectorTile} for - * vector data that is optimized for rendering. - * - * @constructor - * @extends {ol.source.Source} - * @fires ol.source.Vector.Event - * @param {olx.source.VectorOptions=} opt_options Vector source options. - * @api - */ -ol.source.Vector = function(opt_options) { - - var options = opt_options || {}; - - ol.source.Source.call(this, { - attributions: options.attributions, - logo: options.logo, - projection: undefined, - state: ol.source.State.READY, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - - /** - * @private - * @type {ol.FeatureLoader} - */ - this.loader_ = ol.nullFunction; - - /** - * @private - * @type {ol.format.Feature|undefined} - */ - this.format_ = options.format; - - /** - * @private - * @type {boolean} - */ - this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; - - /** - * @private - * @type {string|ol.FeatureUrlFunction|undefined} - */ - this.url_ = options.url; - - if (options.loader !== undefined) { - this.loader_ = options.loader; - } else if (this.url_ !== undefined) { - ol.asserts.assert(this.format_, 7); // `format` must be set when `url` is set - // create a XHR feature loader for "url" and "format" - this.loader_ = ol.featureloader.xhr(this.url_, /** @type {ol.format.Feature} */ (this.format_)); - } - - /** - * @private - * @type {ol.LoadingStrategy} - */ - this.strategy_ = options.strategy !== undefined ? options.strategy : - ol.loadingstrategy.all; - - var useSpatialIndex = - options.useSpatialIndex !== undefined ? options.useSpatialIndex : true; - - /** - * @private - * @type {ol.structs.RBush.<ol.Feature>} - */ - this.featuresRtree_ = useSpatialIndex ? new ol.structs.RBush() : null; - - /** - * @private - * @type {ol.structs.RBush.<{extent: ol.Extent}>} - */ - this.loadedExtentsRtree_ = new ol.structs.RBush(); - - /** - * @private - * @type {Object.<string, ol.Feature>} - */ - this.nullGeometryFeatures_ = {}; - - /** - * A lookup of features by id (the return from feature.getId()). - * @private - * @type {Object.<string, ol.Feature>} - */ - this.idIndex_ = {}; - - /** - * A lookup of features without id (keyed by ol.getUid(feature)). - * @private - * @type {Object.<string, ol.Feature>} - */ - this.undefIdIndex_ = {}; - - /** - * @private - * @type {Object.<string, Array.<ol.EventsKey>>} - */ - this.featureChangeKeys_ = {}; - - /** - * @private - * @type {ol.Collection.<ol.Feature>} - */ - this.featuresCollection_ = null; - - var collection, features; - if (options.features instanceof ol.Collection) { - collection = options.features; - features = collection.getArray(); - } else if (Array.isArray(options.features)) { - features = options.features; - } - if (!useSpatialIndex && collection === undefined) { - collection = new ol.Collection(features); - } - if (features !== undefined) { - this.addFeaturesInternal(features); - } - if (collection !== undefined) { - this.bindFeaturesCollection_(collection); - } - -}; -ol.inherits(ol.source.Vector, ol.source.Source); - - -/** - * Add a single feature to the source. If you want to add a batch of features - * at once, call {@link ol.source.Vector#addFeatures source.addFeatures()} - * instead. A feature will not be added to the source if feature with - * the same id is already there. The reason for this behavior is to avoid - * feature duplication when using bbox or tile loading strategies. - * @param {ol.Feature} feature Feature to add. - * @api - */ -ol.source.Vector.prototype.addFeature = function(feature) { - this.addFeatureInternal(feature); - this.changed(); -}; - - -/** - * Add a feature without firing a `change` event. - * @param {ol.Feature} feature Feature. - * @protected - */ -ol.source.Vector.prototype.addFeatureInternal = function(feature) { - var featureKey = ol.getUid(feature).toString(); - - if (!this.addToIndex_(featureKey, feature)) { - return; - } - - this.setupChangeEvents_(featureKey, feature); - - var geometry = feature.getGeometry(); - if (geometry) { - var extent = geometry.getExtent(); - if (this.featuresRtree_) { - this.featuresRtree_.insert(extent, feature); - } - } else { - this.nullGeometryFeatures_[featureKey] = feature; - } - - this.dispatchEvent( - new ol.source.Vector.Event(ol.source.VectorEventType.ADDFEATURE, feature)); -}; - - -/** - * @param {string} featureKey Unique identifier for the feature. - * @param {ol.Feature} feature The feature. - * @private - */ -ol.source.Vector.prototype.setupChangeEvents_ = function(featureKey, feature) { - this.featureChangeKeys_[featureKey] = [ - ol.events.listen(feature, ol.events.EventType.CHANGE, - this.handleFeatureChange_, this), - ol.events.listen(feature, ol.ObjectEventType.PROPERTYCHANGE, - this.handleFeatureChange_, this) - ]; -}; - - -/** - * @param {string} featureKey Unique identifier for the feature. - * @param {ol.Feature} feature The feature. - * @return {boolean} The feature is "valid", in the sense that it is also a - * candidate for insertion into the Rtree. - * @private - */ -ol.source.Vector.prototype.addToIndex_ = function(featureKey, feature) { - var valid = true; - var id = feature.getId(); - if (id !== undefined) { - if (!(id.toString() in this.idIndex_)) { - this.idIndex_[id.toString()] = feature; - } else { - valid = false; - } - } else { - ol.asserts.assert(!(featureKey in this.undefIdIndex_), - 30); // The passed `feature` was already added to the source - this.undefIdIndex_[featureKey] = feature; - } - return valid; -}; - - -/** - * Add a batch of features to the source. - * @param {Array.<ol.Feature>} features Features to add. - * @api - */ -ol.source.Vector.prototype.addFeatures = function(features) { - this.addFeaturesInternal(features); - this.changed(); -}; - - -/** - * Add features without firing a `change` event. - * @param {Array.<ol.Feature>} features Features. - * @protected - */ -ol.source.Vector.prototype.addFeaturesInternal = function(features) { - var featureKey, i, length, feature; - - var extents = []; - var newFeatures = []; - var geometryFeatures = []; - - for (i = 0, length = features.length; i < length; i++) { - feature = features[i]; - featureKey = ol.getUid(feature).toString(); - if (this.addToIndex_(featureKey, feature)) { - newFeatures.push(feature); - } - } - - for (i = 0, length = newFeatures.length; i < length; i++) { - feature = newFeatures[i]; - featureKey = ol.getUid(feature).toString(); - this.setupChangeEvents_(featureKey, feature); - - var geometry = feature.getGeometry(); - if (geometry) { - var extent = geometry.getExtent(); - extents.push(extent); - geometryFeatures.push(feature); - } else { - this.nullGeometryFeatures_[featureKey] = feature; - } - } - if (this.featuresRtree_) { - this.featuresRtree_.load(extents, geometryFeatures); - } - - for (i = 0, length = newFeatures.length; i < length; i++) { - this.dispatchEvent(new ol.source.Vector.Event( - ol.source.VectorEventType.ADDFEATURE, newFeatures[i])); - } -}; - - -/** - * @param {!ol.Collection.<ol.Feature>} collection Collection. - * @private - */ -ol.source.Vector.prototype.bindFeaturesCollection_ = function(collection) { - var modifyingCollection = false; - ol.events.listen(this, ol.source.VectorEventType.ADDFEATURE, - function(evt) { - if (!modifyingCollection) { - modifyingCollection = true; - collection.push(evt.feature); - modifyingCollection = false; - } - }); - ol.events.listen(this, ol.source.VectorEventType.REMOVEFEATURE, - function(evt) { - if (!modifyingCollection) { - modifyingCollection = true; - collection.remove(evt.feature); - modifyingCollection = false; - } - }); - ol.events.listen(collection, ol.CollectionEventType.ADD, - function(evt) { - if (!modifyingCollection) { - modifyingCollection = true; - this.addFeature(/** @type {ol.Feature} */ (evt.element)); - modifyingCollection = false; - } - }, this); - ol.events.listen(collection, ol.CollectionEventType.REMOVE, - function(evt) { - if (!modifyingCollection) { - modifyingCollection = true; - this.removeFeature(/** @type {ol.Feature} */ (evt.element)); - modifyingCollection = false; - } - }, this); - this.featuresCollection_ = collection; -}; - - -/** - * Remove all features from the source. - * @param {boolean=} opt_fast Skip dispatching of {@link removefeature} events. - * @api - */ -ol.source.Vector.prototype.clear = function(opt_fast) { - if (opt_fast) { - for (var featureId in this.featureChangeKeys_) { - var keys = this.featureChangeKeys_[featureId]; - keys.forEach(ol.events.unlistenByKey); - } - if (!this.featuresCollection_) { - this.featureChangeKeys_ = {}; - this.idIndex_ = {}; - this.undefIdIndex_ = {}; - } - } else { - if (this.featuresRtree_) { - this.featuresRtree_.forEach(this.removeFeatureInternal, this); - for (var id in this.nullGeometryFeatures_) { - this.removeFeatureInternal(this.nullGeometryFeatures_[id]); - } - } - } - if (this.featuresCollection_) { - this.featuresCollection_.clear(); - } - - if (this.featuresRtree_) { - this.featuresRtree_.clear(); - } - this.loadedExtentsRtree_.clear(); - this.nullGeometryFeatures_ = {}; - - var clearEvent = new ol.source.Vector.Event(ol.source.VectorEventType.CLEAR); - this.dispatchEvent(clearEvent); - this.changed(); -}; - - -/** - * Iterate through all features on the source, calling the provided callback - * with each one. If the callback returns any "truthy" value, iteration will - * stop and the function will return the same value. - * - * @param {function(this: T, ol.Feature): S} callback Called with each feature - * on the source. Return a truthy value to stop iteration. - * @param {T=} opt_this The object to use as `this` in the callback. - * @return {S|undefined} The return value from the last call to the callback. - * @template T,S - * @api - */ -ol.source.Vector.prototype.forEachFeature = function(callback, opt_this) { - if (this.featuresRtree_) { - return this.featuresRtree_.forEach(callback, opt_this); - } else if (this.featuresCollection_) { - return this.featuresCollection_.forEach(callback, opt_this); - } -}; - - -/** - * Iterate through all features whose geometries contain the provided - * coordinate, calling the callback with each feature. If the callback returns - * a "truthy" value, iteration will stop and the function will return the same - * value. - * - * @param {ol.Coordinate} coordinate Coordinate. - * @param {function(this: T, ol.Feature): S} callback Called with each feature - * whose goemetry contains the provided coordinate. - * @param {T=} opt_this The object to use as `this` in the callback. - * @return {S|undefined} The return value from the last call to the callback. - * @template T,S - */ -ol.source.Vector.prototype.forEachFeatureAtCoordinateDirect = function(coordinate, callback, opt_this) { - var extent = [coordinate[0], coordinate[1], coordinate[0], coordinate[1]]; - return this.forEachFeatureInExtent(extent, function(feature) { - var geometry = feature.getGeometry(); - if (geometry.intersectsCoordinate(coordinate)) { - return callback.call(opt_this, feature); - } else { - return undefined; - } - }); -}; - - -/** - * Iterate through all features whose bounding box intersects the provided - * extent (note that the feature's geometry may not intersect the extent), - * calling the callback with each feature. If the callback returns a "truthy" - * value, iteration will stop and the function will return the same value. - * - * If you are interested in features whose geometry intersects an extent, call - * the {@link ol.source.Vector#forEachFeatureIntersectingExtent - * source.forEachFeatureIntersectingExtent()} method instead. - * - * When `useSpatialIndex` is set to false, this method will loop through all - * features, equivalent to {@link ol.source.Vector#forEachFeature}. - * - * @param {ol.Extent} extent Extent. - * @param {function(this: T, ol.Feature): S} callback Called with each feature - * whose bounding box intersects the provided extent. - * @param {T=} opt_this The object to use as `this` in the callback. - * @return {S|undefined} The return value from the last call to the callback. - * @template T,S - * @api - */ -ol.source.Vector.prototype.forEachFeatureInExtent = function(extent, callback, opt_this) { - if (this.featuresRtree_) { - return this.featuresRtree_.forEachInExtent(extent, callback, opt_this); - } else if (this.featuresCollection_) { - return this.featuresCollection_.forEach(callback, opt_this); - } -}; - - -/** - * Iterate through all features whose geometry intersects the provided extent, - * calling the callback with each feature. If the callback returns a "truthy" - * value, iteration will stop and the function will return the same value. - * - * If you only want to test for bounding box intersection, call the - * {@link ol.source.Vector#forEachFeatureInExtent - * source.forEachFeatureInExtent()} method instead. - * - * @param {ol.Extent} extent Extent. - * @param {function(this: T, ol.Feature): S} callback Called with each feature - * whose geometry intersects the provided extent. - * @param {T=} opt_this The object to use as `this` in the callback. - * @return {S|undefined} The return value from the last call to the callback. - * @template T,S - * @api - */ -ol.source.Vector.prototype.forEachFeatureIntersectingExtent = function(extent, callback, opt_this) { - return this.forEachFeatureInExtent(extent, - /** - * @param {ol.Feature} feature Feature. - * @return {S|undefined} The return value from the last call to the callback. - * @template S - */ - function(feature) { - var geometry = feature.getGeometry(); - if (geometry.intersectsExtent(extent)) { - var result = callback.call(opt_this, feature); - if (result) { - return result; - } - } - }); -}; - - -/** - * Get the features collection associated with this source. Will be `null` - * unless the source was configured with `useSpatialIndex` set to `false`, or - * with an {@link ol.Collection} as `features`. - * @return {ol.Collection.<ol.Feature>} The collection of features. - * @api - */ -ol.source.Vector.prototype.getFeaturesCollection = function() { - return this.featuresCollection_; -}; - - -/** - * Get all features on the source in random order. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.source.Vector.prototype.getFeatures = function() { - var features; - if (this.featuresCollection_) { - features = this.featuresCollection_.getArray(); - } else if (this.featuresRtree_) { - features = this.featuresRtree_.getAll(); - if (!ol.obj.isEmpty(this.nullGeometryFeatures_)) { - ol.array.extend( - features, ol.obj.getValues(this.nullGeometryFeatures_)); - } - } - return /** @type {Array.<ol.Feature>} */ (features); -}; - - -/** - * Get all features whose geometry intersects the provided coordinate. - * @param {ol.Coordinate} coordinate Coordinate. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.source.Vector.prototype.getFeaturesAtCoordinate = function(coordinate) { - var features = []; - this.forEachFeatureAtCoordinateDirect(coordinate, function(feature) { - features.push(feature); - }); - return features; -}; - - -/** - * Get all features in the provided extent. Note that this returns an array of - * all features intersecting the given extent in random order (so it may include - * features whose geometries do not intersect the extent). - * - * This method is not available when the source is configured with - * `useSpatialIndex` set to `false`. - * @param {ol.Extent} extent Extent. - * @return {Array.<ol.Feature>} Features. - * @api - */ -ol.source.Vector.prototype.getFeaturesInExtent = function(extent) { - return this.featuresRtree_.getInExtent(extent); -}; - - -/** - * Get the closest feature to the provided coordinate. - * - * This method is not available when the source is configured with - * `useSpatialIndex` set to `false`. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {function(ol.Feature):boolean=} opt_filter Feature filter function. - * The filter function will receive one argument, the {@link ol.Feature feature} - * and it should return a boolean value. By default, no filtering is made. - * @return {ol.Feature} Closest feature. - * @api - */ -ol.source.Vector.prototype.getClosestFeatureToCoordinate = function(coordinate, opt_filter) { - // Find the closest feature using branch and bound. We start searching an - // infinite extent, and find the distance from the first feature found. This - // becomes the closest feature. We then compute a smaller extent which any - // closer feature must intersect. We continue searching with this smaller - // extent, trying to find a closer feature. Every time we find a closer - // feature, we update the extent being searched so that any even closer - // feature must intersect it. We continue until we run out of features. - var x = coordinate[0]; - var y = coordinate[1]; - var closestFeature = null; - var closestPoint = [NaN, NaN]; - var minSquaredDistance = Infinity; - var extent = [-Infinity, -Infinity, Infinity, Infinity]; - var filter = opt_filter ? opt_filter : ol.functions.TRUE; - this.featuresRtree_.forEachInExtent(extent, - /** - * @param {ol.Feature} feature Feature. - */ - function(feature) { - if (filter(feature)) { - var geometry = feature.getGeometry(); - var previousMinSquaredDistance = minSquaredDistance; - minSquaredDistance = geometry.closestPointXY( - x, y, closestPoint, minSquaredDistance); - if (minSquaredDistance < previousMinSquaredDistance) { - closestFeature = feature; - // This is sneaky. Reduce the extent that it is currently being - // searched while the R-Tree traversal using this same extent object - // is still in progress. This is safe because the new extent is - // strictly contained by the old extent. - var minDistance = Math.sqrt(minSquaredDistance); - extent[0] = x - minDistance; - extent[1] = y - minDistance; - extent[2] = x + minDistance; - extent[3] = y + minDistance; - } - } - }); - return closestFeature; -}; - - -/** - * Get the extent of the features currently in the source. - * - * This method is not available when the source is configured with - * `useSpatialIndex` set to `false`. - * @param {ol.Extent=} opt_extent Destination extent. If provided, no new extent - * will be created. Instead, that extent's coordinates will be overwritten. - * @return {!ol.Extent} Extent. - * @api - */ -ol.source.Vector.prototype.getExtent = function(opt_extent) { - return this.featuresRtree_.getExtent(opt_extent); -}; - - -/** - * Get a feature by its identifier (the value returned by feature.getId()). - * Note that the index treats string and numeric identifiers as the same. So - * `source.getFeatureById(2)` will return a feature with id `'2'` or `2`. - * - * @param {string|number} id Feature identifier. - * @return {ol.Feature} The feature (or `null` if not found). - * @api - */ -ol.source.Vector.prototype.getFeatureById = function(id) { - var feature = this.idIndex_[id.toString()]; - return feature !== undefined ? feature : null; -}; - - -/** - * Get the format associated with this source. - * - * @return {ol.format.Feature|undefined} The feature format. - * @api - */ -ol.source.Vector.prototype.getFormat = function() { - return this.format_; -}; - - -/** - * @return {boolean} The source can have overlapping geometries. - */ -ol.source.Vector.prototype.getOverlaps = function() { - return this.overlaps_; -}; - - -/** - * @override - */ -ol.source.Vector.prototype.getResolutions = function() {}; - - -/** - * Get the url associated with this source. - * - * @return {string|ol.FeatureUrlFunction|undefined} The url. - * @api - */ -ol.source.Vector.prototype.getUrl = function() { - return this.url_; -}; - - -/** - * @param {ol.events.Event} event Event. - * @private - */ -ol.source.Vector.prototype.handleFeatureChange_ = function(event) { - var feature = /** @type {ol.Feature} */ (event.target); - var featureKey = ol.getUid(feature).toString(); - var geometry = feature.getGeometry(); - if (!geometry) { - if (!(featureKey in this.nullGeometryFeatures_)) { - if (this.featuresRtree_) { - this.featuresRtree_.remove(feature); - } - this.nullGeometryFeatures_[featureKey] = feature; - } - } else { - var extent = geometry.getExtent(); - if (featureKey in this.nullGeometryFeatures_) { - delete this.nullGeometryFeatures_[featureKey]; - if (this.featuresRtree_) { - this.featuresRtree_.insert(extent, feature); - } - } else { - if (this.featuresRtree_) { - this.featuresRtree_.update(extent, feature); - } - } - } - var id = feature.getId(); - if (id !== undefined) { - var sid = id.toString(); - if (featureKey in this.undefIdIndex_) { - delete this.undefIdIndex_[featureKey]; - this.idIndex_[sid] = feature; - } else { - if (this.idIndex_[sid] !== feature) { - this.removeFromIdIndex_(feature); - this.idIndex_[sid] = feature; - } - } - } else { - if (!(featureKey in this.undefIdIndex_)) { - this.removeFromIdIndex_(feature); - this.undefIdIndex_[featureKey] = feature; - } - } - this.changed(); - this.dispatchEvent(new ol.source.Vector.Event( - ol.source.VectorEventType.CHANGEFEATURE, feature)); -}; - - -/** - * @return {boolean} Is empty. - */ -ol.source.Vector.prototype.isEmpty = function() { - return this.featuresRtree_.isEmpty() && - ol.obj.isEmpty(this.nullGeometryFeatures_); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {ol.proj.Projection} projection Projection. - */ -ol.source.Vector.prototype.loadFeatures = function( - extent, resolution, projection) { - var loadedExtentsRtree = this.loadedExtentsRtree_; - var extentsToLoad = this.strategy_(extent, resolution); - var i, ii; - for (i = 0, ii = extentsToLoad.length; i < ii; ++i) { - var extentToLoad = extentsToLoad[i]; - var alreadyLoaded = loadedExtentsRtree.forEachInExtent(extentToLoad, - /** - * @param {{extent: ol.Extent}} object Object. - * @return {boolean} Contains. - */ - function(object) { - return ol.extent.containsExtent(object.extent, extentToLoad); - }); - if (!alreadyLoaded) { - this.loader_.call(this, extentToLoad, resolution, projection); - loadedExtentsRtree.insert(extentToLoad, {extent: extentToLoad.slice()}); - } - } -}; - - -/** - * Remove a single feature from the source. If you want to remove all features - * at once, use the {@link ol.source.Vector#clear source.clear()} method - * instead. - * @param {ol.Feature} feature Feature to remove. - * @api - */ -ol.source.Vector.prototype.removeFeature = function(feature) { - var featureKey = ol.getUid(feature).toString(); - if (featureKey in this.nullGeometryFeatures_) { - delete this.nullGeometryFeatures_[featureKey]; - } else { - if (this.featuresRtree_) { - this.featuresRtree_.remove(feature); - } - } - this.removeFeatureInternal(feature); - this.changed(); -}; - - -/** - * Remove feature without firing a `change` event. - * @param {ol.Feature} feature Feature. - * @protected - */ -ol.source.Vector.prototype.removeFeatureInternal = function(feature) { - var featureKey = ol.getUid(feature).toString(); - this.featureChangeKeys_[featureKey].forEach(ol.events.unlistenByKey); - delete this.featureChangeKeys_[featureKey]; - var id = feature.getId(); - if (id !== undefined) { - delete this.idIndex_[id.toString()]; - } else { - delete this.undefIdIndex_[featureKey]; - } - this.dispatchEvent(new ol.source.Vector.Event( - ol.source.VectorEventType.REMOVEFEATURE, feature)); -}; - - -/** - * Remove a feature from the id index. Called internally when the feature id - * may have changed. - * @param {ol.Feature} feature The feature. - * @return {boolean} Removed the feature from the index. - * @private - */ -ol.source.Vector.prototype.removeFromIdIndex_ = function(feature) { - var removed = false; - for (var id in this.idIndex_) { - if (this.idIndex_[id] === feature) { - delete this.idIndex_[id]; - removed = true; - break; - } - } - return removed; -}; - - -/** - * @classdesc - * Events emitted by {@link ol.source.Vector} instances are instances of this - * type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.source.Vector.Event} - * @param {string} type Type. - * @param {ol.Feature=} opt_feature Feature. - */ -ol.source.Vector.Event = function(type, opt_feature) { - - ol.events.Event.call(this, type); - - /** - * The feature being added or removed. - * @type {ol.Feature|undefined} - * @api - */ - this.feature = opt_feature; - -}; -ol.inherits(ol.source.Vector.Event, ol.events.Event); - -goog.provide('ol.interaction.Draw'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.Object'); -goog.require('ol.coordinate'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.condition'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.geom.Circle'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.interaction.DrawEventType'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.interaction.Property'); -goog.require('ol.layer.Vector'); -goog.require('ol.source.Vector'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * Interaction for drawing feature geometries. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @fires ol.interaction.Draw.Event - * @param {olx.interaction.DrawOptions} options Options. - * @api - */ -ol.interaction.Draw = function(options) { - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.Draw.handleDownEvent_, - handleEvent: ol.interaction.Draw.handleEvent, - handleUpEvent: ol.interaction.Draw.handleUpEvent_ - }); - - /** - * @type {boolean} - * @private - */ - this.shouldHandle_ = false; - - /** - * @type {ol.Pixel} - * @private - */ - this.downPx_ = null; - - /** - * @type {boolean} - * @private - */ - this.freehand_ = false; - - /** - * Target source for drawn features. - * @type {ol.source.Vector} - * @private - */ - this.source_ = options.source ? options.source : null; - - /** - * Target collection for drawn features. - * @type {ol.Collection.<ol.Feature>} - * @private - */ - this.features_ = options.features ? options.features : null; - - /** - * Pixel distance for snapping. - * @type {number} - * @private - */ - this.snapTolerance_ = options.snapTolerance ? options.snapTolerance : 12; - - /** - * Geometry type. - * @type {ol.geom.GeometryType} - * @private - */ - this.type_ = options.type; - - /** - * Drawing mode (derived from geometry type. - * @type {ol.interaction.Draw.Mode_} - * @private - */ - this.mode_ = ol.interaction.Draw.getMode_(this.type_); - - /** - * The number of points that must be drawn before a polygon ring or line - * string can be finished. The default is 3 for polygon rings and 2 for - * line strings. - * @type {number} - * @private - */ - this.minPoints_ = options.minPoints ? - options.minPoints : - (this.mode_ === ol.interaction.Draw.Mode_.POLYGON ? 3 : 2); - - /** - * The number of points that can be drawn before a polygon ring or line string - * is finished. The default is no restriction. - * @type {number} - * @private - */ - this.maxPoints_ = options.maxPoints ? options.maxPoints : Infinity; - - /** - * A function to decide if a potential finish coordinate is permissible - * @private - * @type {ol.EventsConditionType} - */ - this.finishCondition_ = options.finishCondition ? options.finishCondition : ol.functions.TRUE; - - var geometryFunction = options.geometryFunction; - if (!geometryFunction) { - if (this.type_ === ol.geom.GeometryType.CIRCLE) { - /** - * @param {!Array.<ol.Coordinate>} coordinates - * The coordinates. - * @param {ol.geom.SimpleGeometry=} opt_geometry Optional geometry. - * @return {ol.geom.SimpleGeometry} A geometry. - */ - geometryFunction = function(coordinates, opt_geometry) { - var circle = opt_geometry ? /** @type {ol.geom.Circle} */ (opt_geometry) : - new ol.geom.Circle([NaN, NaN]); - var squaredLength = ol.coordinate.squaredDistance( - coordinates[0], coordinates[1]); - circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength)); - return circle; - }; - } else { - var Constructor; - var mode = this.mode_; - if (mode === ol.interaction.Draw.Mode_.POINT) { - Constructor = ol.geom.Point; - } else if (mode === ol.interaction.Draw.Mode_.LINE_STRING) { - Constructor = ol.geom.LineString; - } else if (mode === ol.interaction.Draw.Mode_.POLYGON) { - Constructor = ol.geom.Polygon; - } - /** - * @param {!Array.<ol.Coordinate>} coordinates - * The coordinates. - * @param {ol.geom.SimpleGeometry=} opt_geometry Optional geometry. - * @return {ol.geom.SimpleGeometry} A geometry. - */ - geometryFunction = function(coordinates, opt_geometry) { - var geometry = opt_geometry; - if (geometry) { - if (mode === ol.interaction.Draw.Mode_.POLYGON) { - geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])]); - } else { - geometry.setCoordinates(coordinates); - } - } else { - geometry = new Constructor(coordinates); - } - return geometry; - }; - } - } - - /** - * @type {ol.DrawGeometryFunctionType} - * @private - */ - this.geometryFunction_ = geometryFunction; - - /** - * Finish coordinate for the feature (first point for polygons, last point for - * linestrings). - * @type {ol.Coordinate} - * @private - */ - this.finishCoordinate_ = null; - - /** - * Sketch feature. - * @type {ol.Feature} - * @private - */ - this.sketchFeature_ = null; - - /** - * Sketch point. - * @type {ol.Feature} - * @private - */ - this.sketchPoint_ = null; - - /** - * Sketch coordinates. Used when drawing a line or polygon. - * @type {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} - * @private - */ - this.sketchCoords_ = null; - - /** - * Sketch line. Used when drawing polygon. - * @type {ol.Feature} - * @private - */ - this.sketchLine_ = null; - - /** - * Sketch line coordinates. Used when drawing a polygon or circle. - * @type {Array.<ol.Coordinate>} - * @private - */ - this.sketchLineCoords_ = null; - - /** - * Squared tolerance for handling up events. If the squared distance - * between a down and up event is greater than this tolerance, up events - * will not be handled. - * @type {number} - * @private - */ - this.squaredClickTolerance_ = options.clickTolerance ? - options.clickTolerance * options.clickTolerance : 36; - - /** - * Draw overlay where our sketch features are drawn. - * @type {ol.layer.Vector} - * @private - */ - this.overlay_ = new ol.layer.Vector({ - source: new ol.source.Vector({ - useSpatialIndex: false, - wrapX: options.wrapX ? options.wrapX : false - }), - style: options.style ? options.style : - ol.interaction.Draw.getDefaultStyleFunction() - }); - - /** - * Name of the geometry attribute for newly created features. - * @type {string|undefined} - * @private - */ - this.geometryName_ = options.geometryName; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.noModifierKeys; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.freehandCondition_; - if (options.freehand) { - this.freehandCondition_ = ol.events.condition.always; - } else { - this.freehandCondition_ = options.freehandCondition ? - options.freehandCondition : ol.events.condition.shiftKeyOnly; - } - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.interaction.Property.ACTIVE), - this.updateState_, this); - -}; -ol.inherits(ol.interaction.Draw, ol.interaction.Pointer); - - -/** - * @return {ol.StyleFunction} Styles. - */ -ol.interaction.Draw.getDefaultStyleFunction = function() { - var styles = ol.style.Style.createDefaultEditing(); - return function(feature, resolution) { - return styles[feature.getGeometry().getType()]; - }; -}; - - -/** - * @inheritDoc - */ -ol.interaction.Draw.prototype.setMap = function(map) { - ol.interaction.Pointer.prototype.setMap.call(this, map); - this.updateState_(); -}; - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} and may actually - * draw or finish the drawing. - * @param {ol.MapBrowserEvent} event Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.Draw} - * @api - */ -ol.interaction.Draw.handleEvent = function(event) { - this.freehand_ = this.mode_ !== ol.interaction.Draw.Mode_.POINT && this.freehandCondition_(event); - var pass = !this.freehand_; - if (this.freehand_ && - event.type === ol.MapBrowserEventType.POINTERDRAG && this.sketchFeature_ !== null) { - this.addToDrawing_(event); - pass = false; - } else if (event.type === - ol.MapBrowserEventType.POINTERMOVE) { - pass = this.handlePointerMove_(event); - } else if (event.type === ol.MapBrowserEventType.DBLCLICK) { - pass = false; - } - return ol.interaction.Pointer.handleEvent.call(this, event) && pass; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} event Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.Draw} - * @private - */ -ol.interaction.Draw.handleDownEvent_ = function(event) { - this.shouldHandle_ = !this.freehand_; - - if (this.freehand_) { - this.downPx_ = event.pixel; - if (!this.finishCoordinate_) { - this.startDrawing_(event); - } - return true; - } else if (this.condition_(event)) { - this.downPx_ = event.pixel; - return true; - } else { - return false; - } -}; - - -/** - * @param {ol.MapBrowserPointerEvent} event Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.Draw} - * @private - */ -ol.interaction.Draw.handleUpEvent_ = function(event) { - var pass = true; - - this.handlePointerMove_(event); - - var circleMode = this.mode_ === ol.interaction.Draw.Mode_.CIRCLE; - - if (this.shouldHandle_) { - if (!this.finishCoordinate_) { - this.startDrawing_(event); - if (this.mode_ === ol.interaction.Draw.Mode_.POINT) { - this.finishDrawing(); - } - } else if (this.freehand_ || circleMode) { - this.finishDrawing(); - } else if (this.atFinish_(event)) { - if (this.finishCondition_(event)) { - this.finishDrawing(); - } - } else { - this.addToDrawing_(event); - } - pass = false; - } else if (this.freehand_) { - this.finishCoordinate_ = null; - this.abortDrawing_(); - } - return pass; -}; - - -/** - * Handle move events. - * @param {ol.MapBrowserEvent} event A move event. - * @return {boolean} Pass the event to other interactions. - * @private - */ -ol.interaction.Draw.prototype.handlePointerMove_ = function(event) { - if (this.downPx_ && - ((!this.freehand_ && this.shouldHandle_) || - (this.freehand_ && !this.shouldHandle_))) { - var downPx = this.downPx_; - var clickPx = event.pixel; - var dx = downPx[0] - clickPx[0]; - var dy = downPx[1] - clickPx[1]; - var squaredDistance = dx * dx + dy * dy; - this.shouldHandle_ = this.freehand_ ? - squaredDistance > this.squaredClickTolerance_ : - squaredDistance <= this.squaredClickTolerance_; - } - - if (this.finishCoordinate_) { - this.modifyDrawing_(event); - } else { - this.createOrUpdateSketchPoint_(event); - } - return true; -}; - - -/** - * Determine if an event is within the snapping tolerance of the start coord. - * @param {ol.MapBrowserEvent} event Event. - * @return {boolean} The event is within the snapping tolerance of the start. - * @private - */ -ol.interaction.Draw.prototype.atFinish_ = function(event) { - var at = false; - if (this.sketchFeature_) { - var potentiallyDone = false; - var potentiallyFinishCoordinates = [this.finishCoordinate_]; - if (this.mode_ === ol.interaction.Draw.Mode_.LINE_STRING) { - potentiallyDone = this.sketchCoords_.length > this.minPoints_; - } else if (this.mode_ === ol.interaction.Draw.Mode_.POLYGON) { - potentiallyDone = this.sketchCoords_[0].length > - this.minPoints_; - potentiallyFinishCoordinates = [this.sketchCoords_[0][0], - this.sketchCoords_[0][this.sketchCoords_[0].length - 2]]; - } - if (potentiallyDone) { - var map = event.map; - for (var i = 0, ii = potentiallyFinishCoordinates.length; i < ii; i++) { - var finishCoordinate = potentiallyFinishCoordinates[i]; - var finishPixel = map.getPixelFromCoordinate(finishCoordinate); - var pixel = event.pixel; - var dx = pixel[0] - finishPixel[0]; - var dy = pixel[1] - finishPixel[1]; - var snapTolerance = this.freehand_ ? 1 : this.snapTolerance_; - at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance; - if (at) { - this.finishCoordinate_ = finishCoordinate; - break; - } - } - } - } - return at; -}; - - -/** - * @param {ol.MapBrowserEvent} event Event. - * @private - */ -ol.interaction.Draw.prototype.createOrUpdateSketchPoint_ = function(event) { - var coordinates = event.coordinate.slice(); - if (!this.sketchPoint_) { - this.sketchPoint_ = new ol.Feature(new ol.geom.Point(coordinates)); - this.updateSketchFeatures_(); - } else { - var sketchPointGeom = /** @type {ol.geom.Point} */ (this.sketchPoint_.getGeometry()); - sketchPointGeom.setCoordinates(coordinates); - } -}; - - -/** - * Start the drawing. - * @param {ol.MapBrowserEvent} event Event. - * @private - */ -ol.interaction.Draw.prototype.startDrawing_ = function(event) { - var start = event.coordinate; - this.finishCoordinate_ = start; - if (this.mode_ === ol.interaction.Draw.Mode_.POINT) { - this.sketchCoords_ = start.slice(); - } else if (this.mode_ === ol.interaction.Draw.Mode_.POLYGON) { - this.sketchCoords_ = [[start.slice(), start.slice()]]; - this.sketchLineCoords_ = this.sketchCoords_[0]; - } else { - this.sketchCoords_ = [start.slice(), start.slice()]; - if (this.mode_ === ol.interaction.Draw.Mode_.CIRCLE) { - this.sketchLineCoords_ = this.sketchCoords_; - } - } - if (this.sketchLineCoords_) { - this.sketchLine_ = new ol.Feature( - new ol.geom.LineString(this.sketchLineCoords_)); - } - var geometry = this.geometryFunction_(this.sketchCoords_); - this.sketchFeature_ = new ol.Feature(); - if (this.geometryName_) { - this.sketchFeature_.setGeometryName(this.geometryName_); - } - this.sketchFeature_.setGeometry(geometry); - this.updateSketchFeatures_(); - this.dispatchEvent(new ol.interaction.Draw.Event( - ol.interaction.DrawEventType.DRAWSTART, this.sketchFeature_)); -}; - - -/** - * Modify the drawing. - * @param {ol.MapBrowserEvent} event Event. - * @private - */ -ol.interaction.Draw.prototype.modifyDrawing_ = function(event) { - var coordinate = event.coordinate; - var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.sketchFeature_.getGeometry()); - var coordinates, last; - if (this.mode_ === ol.interaction.Draw.Mode_.POINT) { - last = this.sketchCoords_; - } else if (this.mode_ === ol.interaction.Draw.Mode_.POLYGON) { - coordinates = this.sketchCoords_[0]; - last = coordinates[coordinates.length - 1]; - if (this.atFinish_(event)) { - // snap to finish - coordinate = this.finishCoordinate_.slice(); - } - } else { - coordinates = this.sketchCoords_; - last = coordinates[coordinates.length - 1]; - } - last[0] = coordinate[0]; - last[1] = coordinate[1]; - this.geometryFunction_(/** @type {!Array.<ol.Coordinate>} */ (this.sketchCoords_), geometry); - if (this.sketchPoint_) { - var sketchPointGeom = /** @type {ol.geom.Point} */ (this.sketchPoint_.getGeometry()); - sketchPointGeom.setCoordinates(coordinate); - } - var sketchLineGeom; - if (geometry instanceof ol.geom.Polygon && - this.mode_ !== ol.interaction.Draw.Mode_.POLYGON) { - if (!this.sketchLine_) { - this.sketchLine_ = new ol.Feature(new ol.geom.LineString(null)); - } - var ring = geometry.getLinearRing(0); - sketchLineGeom = /** @type {ol.geom.LineString} */ (this.sketchLine_.getGeometry()); - sketchLineGeom.setFlatCoordinates( - ring.getLayout(), ring.getFlatCoordinates()); - } else if (this.sketchLineCoords_) { - sketchLineGeom = /** @type {ol.geom.LineString} */ (this.sketchLine_.getGeometry()); - sketchLineGeom.setCoordinates(this.sketchLineCoords_); - } - this.updateSketchFeatures_(); -}; - - -/** - * Add a new coordinate to the drawing. - * @param {ol.MapBrowserEvent} event Event. - * @private - */ -ol.interaction.Draw.prototype.addToDrawing_ = function(event) { - var coordinate = event.coordinate; - var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.sketchFeature_.getGeometry()); - var done; - var coordinates; - if (this.mode_ === ol.interaction.Draw.Mode_.LINE_STRING) { - this.finishCoordinate_ = coordinate.slice(); - coordinates = this.sketchCoords_; - if (coordinates.length >= this.maxPoints_) { - if (this.freehand_) { - coordinates.pop(); - } else { - done = true; - } - } - coordinates.push(coordinate.slice()); - this.geometryFunction_(coordinates, geometry); - } else if (this.mode_ === ol.interaction.Draw.Mode_.POLYGON) { - coordinates = this.sketchCoords_[0]; - if (coordinates.length >= this.maxPoints_) { - if (this.freehand_) { - coordinates.pop(); - } else { - done = true; - } - } - coordinates.push(coordinate.slice()); - if (done) { - this.finishCoordinate_ = coordinates[0]; - } - this.geometryFunction_(this.sketchCoords_, geometry); - } - this.updateSketchFeatures_(); - if (done) { - this.finishDrawing(); - } -}; - - -/** - * Remove last point of the feature currently being drawn. - * @api - */ -ol.interaction.Draw.prototype.removeLastPoint = function() { - if (!this.sketchFeature_) { - return; - } - var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.sketchFeature_.getGeometry()); - var coordinates, sketchLineGeom; - if (this.mode_ === ol.interaction.Draw.Mode_.LINE_STRING) { - coordinates = this.sketchCoords_; - coordinates.splice(-2, 1); - this.geometryFunction_(coordinates, geometry); - if (coordinates.length >= 2) { - this.finishCoordinate_ = coordinates[coordinates.length - 2].slice(); - } - } else if (this.mode_ === ol.interaction.Draw.Mode_.POLYGON) { - coordinates = this.sketchCoords_[0]; - coordinates.splice(-2, 1); - sketchLineGeom = /** @type {ol.geom.LineString} */ (this.sketchLine_.getGeometry()); - sketchLineGeom.setCoordinates(coordinates); - this.geometryFunction_(this.sketchCoords_, geometry); - } - - if (coordinates.length === 0) { - this.finishCoordinate_ = null; - } - - this.updateSketchFeatures_(); -}; - - -/** - * Stop drawing and add the sketch feature to the target layer. - * The {@link ol.interaction.DrawEventType.DRAWEND} event is dispatched before - * inserting the feature. - * @api - */ -ol.interaction.Draw.prototype.finishDrawing = function() { - var sketchFeature = this.abortDrawing_(); - var coordinates = this.sketchCoords_; - var geometry = /** @type {ol.geom.SimpleGeometry} */ (sketchFeature.getGeometry()); - if (this.mode_ === ol.interaction.Draw.Mode_.LINE_STRING) { - // remove the redundant last point - coordinates.pop(); - this.geometryFunction_(coordinates, geometry); - } else if (this.mode_ === ol.interaction.Draw.Mode_.POLYGON) { - // remove the redundant last point in ring - coordinates[0].pop(); - this.geometryFunction_(coordinates, geometry); - coordinates = geometry.getCoordinates(); - } - - // cast multi-part geometries - if (this.type_ === ol.geom.GeometryType.MULTI_POINT) { - sketchFeature.setGeometry(new ol.geom.MultiPoint([coordinates])); - } else if (this.type_ === ol.geom.GeometryType.MULTI_LINE_STRING) { - sketchFeature.setGeometry(new ol.geom.MultiLineString([coordinates])); - } else if (this.type_ === ol.geom.GeometryType.MULTI_POLYGON) { - sketchFeature.setGeometry(new ol.geom.MultiPolygon([coordinates])); - } - - // First dispatch event to allow full set up of feature - this.dispatchEvent(new ol.interaction.Draw.Event( - ol.interaction.DrawEventType.DRAWEND, sketchFeature)); - - // Then insert feature - if (this.features_) { - this.features_.push(sketchFeature); - } - if (this.source_) { - this.source_.addFeature(sketchFeature); - } -}; - - -/** - * Stop drawing without adding the sketch feature to the target layer. - * @return {ol.Feature} The sketch feature (or null if none). - * @private - */ -ol.interaction.Draw.prototype.abortDrawing_ = function() { - this.finishCoordinate_ = null; - var sketchFeature = this.sketchFeature_; - if (sketchFeature) { - this.sketchFeature_ = null; - this.sketchPoint_ = null; - this.sketchLine_ = null; - this.overlay_.getSource().clear(true); - } - return sketchFeature; -}; - - -/** - * Extend an existing geometry by adding additional points. This only works - * on features with `LineString` geometries, where the interaction will - * extend lines by adding points to the end of the coordinates array. - * @param {!ol.Feature} feature Feature to be extended. - * @api - */ -ol.interaction.Draw.prototype.extend = function(feature) { - var geometry = feature.getGeometry(); - var lineString = /** @type {ol.geom.LineString} */ (geometry); - this.sketchFeature_ = feature; - this.sketchCoords_ = lineString.getCoordinates(); - var last = this.sketchCoords_[this.sketchCoords_.length - 1]; - this.finishCoordinate_ = last.slice(); - this.sketchCoords_.push(last.slice()); - this.updateSketchFeatures_(); - this.dispatchEvent(new ol.interaction.Draw.Event( - ol.interaction.DrawEventType.DRAWSTART, this.sketchFeature_)); -}; - - -/** - * @inheritDoc - */ -ol.interaction.Draw.prototype.shouldStopEvent = ol.functions.FALSE; - - -/** - * Redraw the sketch features. - * @private - */ -ol.interaction.Draw.prototype.updateSketchFeatures_ = function() { - var sketchFeatures = []; - if (this.sketchFeature_) { - sketchFeatures.push(this.sketchFeature_); - } - if (this.sketchLine_) { - sketchFeatures.push(this.sketchLine_); - } - if (this.sketchPoint_) { - sketchFeatures.push(this.sketchPoint_); - } - var overlaySource = this.overlay_.getSource(); - overlaySource.clear(true); - overlaySource.addFeatures(sketchFeatures); -}; - - -/** - * @private - */ -ol.interaction.Draw.prototype.updateState_ = function() { - var map = this.getMap(); - var active = this.getActive(); - if (!map || !active) { - this.abortDrawing_(); - } - this.overlay_.setMap(active ? map : null); -}; - - -/** - * Create a `geometryFunction` for `type: 'Circle'` that will create a regular - * polygon with a user specified number of sides and start angle instead of an - * `ol.geom.Circle` geometry. - * @param {number=} opt_sides Number of sides of the regular polygon. Default is - * 32. - * @param {number=} opt_angle Angle of the first point in radians. 0 means East. - * Default is the angle defined by the heading from the center of the - * regular polygon to the current pointer position. - * @return {ol.DrawGeometryFunctionType} Function that draws a - * polygon. - * @api - */ -ol.interaction.Draw.createRegularPolygon = function(opt_sides, opt_angle) { - return ( - /** - * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates - * @param {ol.geom.SimpleGeometry=} opt_geometry - * @return {ol.geom.SimpleGeometry} - */ - function(coordinates, opt_geometry) { - var center = coordinates[0]; - var end = coordinates[1]; - var radius = Math.sqrt( - ol.coordinate.squaredDistance(center, end)); - var geometry = opt_geometry ? /** @type {ol.geom.Polygon} */ (opt_geometry) : - ol.geom.Polygon.fromCircle(new ol.geom.Circle(center), opt_sides); - var angle = opt_angle ? opt_angle : - Math.atan((end[1] - center[1]) / (end[0] - center[0])); - ol.geom.Polygon.makeRegular(geometry, center, radius, angle); - return geometry; - } - ); -}; - - -/** - * Create a `geometryFunction` that will create a box-shaped polygon (aligned - * with the coordinate system axes). Use this with the draw interaction and - * `type: 'Circle'` to return a box instead of a circle geometry. - * @return {ol.DrawGeometryFunctionType} Function that draws a box-shaped polygon. - * @api - */ -ol.interaction.Draw.createBox = function() { - return ( - /** - * @param {Array.<ol.Coordinate>} coordinates - * @param {ol.geom.SimpleGeometry=} opt_geometry - * @return {ol.geom.SimpleGeometry} - */ - function(coordinates, opt_geometry) { - var extent = ol.extent.boundingExtent(coordinates); - var geometry = opt_geometry || new ol.geom.Polygon(null); - geometry.setCoordinates([[ - ol.extent.getBottomLeft(extent), - ol.extent.getBottomRight(extent), - ol.extent.getTopRight(extent), - ol.extent.getTopLeft(extent), - ol.extent.getBottomLeft(extent) - ]]); - return geometry; - } - ); -}; - - -/** - * Get the drawing mode. The mode for mult-part geometries is the same as for - * their single-part cousins. - * @param {ol.geom.GeometryType} type Geometry type. - * @return {ol.interaction.Draw.Mode_} Drawing mode. - * @private - */ -ol.interaction.Draw.getMode_ = function(type) { - var mode; - if (type === ol.geom.GeometryType.POINT || - type === ol.geom.GeometryType.MULTI_POINT) { - mode = ol.interaction.Draw.Mode_.POINT; - } else if (type === ol.geom.GeometryType.LINE_STRING || - type === ol.geom.GeometryType.MULTI_LINE_STRING) { - mode = ol.interaction.Draw.Mode_.LINE_STRING; - } else if (type === ol.geom.GeometryType.POLYGON || - type === ol.geom.GeometryType.MULTI_POLYGON) { - mode = ol.interaction.Draw.Mode_.POLYGON; - } else if (type === ol.geom.GeometryType.CIRCLE) { - mode = ol.interaction.Draw.Mode_.CIRCLE; - } - return /** @type {!ol.interaction.Draw.Mode_} */ (mode); -}; - - -/** - * Draw mode. This collapses multi-part geometry types with their single-part - * cousins. - * @enum {string} - * @private - */ -ol.interaction.Draw.Mode_ = { - POINT: 'Point', - LINE_STRING: 'LineString', - POLYGON: 'Polygon', - CIRCLE: 'Circle' -}; - -/** - * @classdesc - * Events emitted by {@link ol.interaction.Draw} instances are instances of - * this type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.DrawEvent} - * @param {ol.interaction.DrawEventType} type Type. - * @param {ol.Feature} feature The feature drawn. - */ -ol.interaction.Draw.Event = function(type, feature) { - - ol.events.Event.call(this, type); - - /** - * The feature being drawn. - * @type {ol.Feature} - * @api - */ - this.feature = feature; - -}; -ol.inherits(ol.interaction.Draw.Event, ol.events.Event); - -goog.provide('ol.interaction.Extent'); - -goog.require('ol'); -goog.require('ol.Feature'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.MapBrowserPointerEvent'); -goog.require('ol.coordinate'); -goog.require('ol.events.Event'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.layer.Vector'); -goog.require('ol.source.Vector'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * Allows the user to draw a vector box by clicking and dragging on the map. - * Once drawn, the vector box can be modified by dragging its vertices or edges. - * This interaction is only supported for mouse devices. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @fires ol.interaction.Extent.Event - * @param {olx.interaction.ExtentOptions=} opt_options Options. - * @api - */ -ol.interaction.Extent = function(opt_options) { - - /** - * Extent of the drawn box - * @type {ol.Extent} - * @private - */ - this.extent_ = null; - - /** - * Handler for pointer move events - * @type {function (ol.Coordinate): ol.Extent|null} - * @private - */ - this.pointerHandler_ = null; - - /** - * Pixel threshold to snap to extent - * @type {number} - * @private - */ - this.pixelTolerance_ = 10; - - /** - * Is the pointer snapped to an extent vertex - * @type {boolean} - * @private - */ - this.snappedToVertex_ = false; - - /** - * Feature for displaying the visible extent - * @type {ol.Feature} - * @private - */ - this.extentFeature_ = null; - - /** - * Feature for displaying the visible pointer - * @type {ol.Feature} - * @private - */ - this.vertexFeature_ = null; - - if (!opt_options) { - opt_options = {}; - } - - if (opt_options.extent) { - this.setExtent(opt_options.extent); - } - - /* Inherit ol.interaction.Pointer */ - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.Extent.handleDownEvent_, - handleDragEvent: ol.interaction.Extent.handleDragEvent_, - handleEvent: ol.interaction.Extent.handleEvent_, - handleUpEvent: ol.interaction.Extent.handleUpEvent_ - }); - - /** - * Layer for the extentFeature - * @type {ol.layer.Vector} - * @private - */ - this.extentOverlay_ = new ol.layer.Vector({ - source: new ol.source.Vector({ - useSpatialIndex: false, - wrapX: !!opt_options.wrapX - }), - style: opt_options.boxStyle ? opt_options.boxStyle : ol.interaction.Extent.getDefaultExtentStyleFunction_(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); - - /** - * Layer for the vertexFeature - * @type {ol.layer.Vector} - * @private - */ - this.vertexOverlay_ = new ol.layer.Vector({ - source: new ol.source.Vector({ - useSpatialIndex: false, - wrapX: !!opt_options.wrapX - }), - style: opt_options.pointerStyle ? opt_options.pointerStyle : ol.interaction.Extent.getDefaultPointerStyleFunction_(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); -}; - -ol.inherits(ol.interaction.Extent, ol.interaction.Pointer); - -/** - * @param {ol.MapBrowserEvent} mapBrowserEvent Event. - * @return {boolean} Propagate event? - * @this {ol.interaction.Extent} - * @private - */ -ol.interaction.Extent.handleEvent_ = function(mapBrowserEvent) { - if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) { - return true; - } - //display pointer (if not dragging) - if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERMOVE && !this.handlingDownUpSequence) { - this.handlePointerMove_(mapBrowserEvent); - } - //call pointer to determine up/down/drag - ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent); - //return false to stop propagation - return false; -}; - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Event handled? - * @this {ol.interaction.Extent} - * @private - */ -ol.interaction.Extent.handleDownEvent_ = function(mapBrowserEvent) { - var pixel = mapBrowserEvent.pixel; - var map = mapBrowserEvent.map; - - var extent = this.getExtent(); - var vertex = this.snapToVertex_(pixel, map); - - //find the extent corner opposite the passed corner - var getOpposingPoint = function(point) { - var x_ = null; - var y_ = null; - if (point[0] == extent[0]) { - x_ = extent[2]; - } else if (point[0] == extent[2]) { - x_ = extent[0]; - } - if (point[1] == extent[1]) { - y_ = extent[3]; - } else if (point[1] == extent[3]) { - y_ = extent[1]; - } - if (x_ !== null && y_ !== null) { - return [x_, y_]; - } - return null; - }; - if (vertex && extent) { - var x = (vertex[0] == extent[0] || vertex[0] == extent[2]) ? vertex[0] : null; - var y = (vertex[1] == extent[1] || vertex[1] == extent[3]) ? vertex[1] : null; - - //snap to point - if (x !== null && y !== null) { - this.pointerHandler_ = ol.interaction.Extent.getPointHandler_(getOpposingPoint(vertex)); - //snap to edge - } else if (x !== null) { - this.pointerHandler_ = ol.interaction.Extent.getEdgeHandler_( - getOpposingPoint([x, extent[1]]), - getOpposingPoint([x, extent[3]]) - ); - } else if (y !== null) { - this.pointerHandler_ = ol.interaction.Extent.getEdgeHandler_( - getOpposingPoint([extent[0], y]), - getOpposingPoint([extent[2], y]) - ); - } - //no snap - new bbox - } else { - vertex = map.getCoordinateFromPixel(pixel); - this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]); - this.pointerHandler_ = ol.interaction.Extent.getPointHandler_(vertex); - } - return true; //event handled; start downup sequence -}; - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Event handled? - * @this {ol.interaction.Extent} - * @private - */ -ol.interaction.Extent.handleDragEvent_ = function(mapBrowserEvent) { - if (this.pointerHandler_) { - var pixelCoordinate = mapBrowserEvent.coordinate; - this.setExtent(this.pointerHandler_(pixelCoordinate)); - this.createOrUpdatePointerFeature_(pixelCoordinate); - } - return true; -}; - -/** - * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.Extent} - * @private - */ -ol.interaction.Extent.handleUpEvent_ = function(mapBrowserEvent) { - this.pointerHandler_ = null; - //If bbox is zero area, set to null; - var extent = this.getExtent(); - if (!extent || ol.extent.getArea(extent) === 0) { - this.setExtent(null); - } - return false; //Stop handling downup sequence -}; - -/** - * Returns the default style for the drawn bbox - * - * @return {ol.StyleFunction} Default Extent style - * @private - */ -ol.interaction.Extent.getDefaultExtentStyleFunction_ = function() { - var style = ol.style.Style.createDefaultEditing(); - return function(feature, resolution) { - return style[ol.geom.GeometryType.POLYGON]; - }; -}; - -/** - * Returns the default style for the pointer - * - * @return {ol.StyleFunction} Default pointer style - * @private - */ -ol.interaction.Extent.getDefaultPointerStyleFunction_ = function() { - var style = ol.style.Style.createDefaultEditing(); - return function(feature, resolution) { - return style[ol.geom.GeometryType.POINT]; - }; -}; - -/** - * @param {ol.Coordinate} fixedPoint corner that will be unchanged in the new extent - * @returns {function (ol.Coordinate): ol.Extent} event handler - * @private - */ -ol.interaction.Extent.getPointHandler_ = function(fixedPoint) { - return function(point) { - return ol.extent.boundingExtent([fixedPoint, point]); - }; -}; - -/** - * @param {ol.Coordinate} fixedP1 first corner that will be unchanged in the new extent - * @param {ol.Coordinate} fixedP2 second corner that will be unchanged in the new extent - * @returns {function (ol.Coordinate): ol.Extent|null} event handler - * @private - */ -ol.interaction.Extent.getEdgeHandler_ = function(fixedP1, fixedP2) { - if (fixedP1[0] == fixedP2[0]) { - return function(point) { - return ol.extent.boundingExtent([fixedP1, [point[0], fixedP2[1]]]); - }; - } else if (fixedP1[1] == fixedP2[1]) { - return function(point) { - return ol.extent.boundingExtent([fixedP1, [fixedP2[0], point[1]]]); - }; - } else { - return null; - } -}; - -/** - * @param {ol.Extent} extent extent - * @returns {Array<Array<ol.Coordinate>>} extent line segments - * @private - */ -ol.interaction.Extent.getSegments_ = function(extent) { - return [ - [[extent[0], extent[1]], [extent[0], extent[3]]], - [[extent[0], extent[3]], [extent[2], extent[3]]], - [[extent[2], extent[3]], [extent[2], extent[1]]], - [[extent[2], extent[1]], [extent[0], extent[1]]] - ]; -}; - -/** - * @param {ol.Pixel} pixel cursor location - * @param {ol.Map} map map - * @returns {ol.Coordinate|null} snapped vertex on extent - * @private - */ -ol.interaction.Extent.prototype.snapToVertex_ = function(pixel, map) { - var pixelCoordinate = map.getCoordinateFromPixel(pixel); - var sortByDistance = function(a, b) { - return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a) - - ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b); - }; - var extent = this.getExtent(); - if (extent) { - //convert extents to line segments and find the segment closest to pixelCoordinate - var segments = ol.interaction.Extent.getSegments_(extent); - segments.sort(sortByDistance); - var closestSegment = segments[0]; - - var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, - closestSegment)); - var vertexPixel = map.getPixelFromCoordinate(vertex); - - //if the distance is within tolerance, snap to the segment - if (ol.coordinate.distance(pixel, vertexPixel) <= this.pixelTolerance_) { - //test if we should further snap to a vertex - var pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - var pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); - var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); - var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - this.snappedToVertex_ = dist <= this.pixelTolerance_; - if (this.snappedToVertex_) { - vertex = squaredDist1 > squaredDist2 ? - closestSegment[1] : closestSegment[0]; - } - return vertex; - } - } - return null; -}; - -/** - * @param {ol.MapBrowserEvent} mapBrowserEvent pointer move event - * @private - */ -ol.interaction.Extent.prototype.handlePointerMove_ = function(mapBrowserEvent) { - var pixel = mapBrowserEvent.pixel; - var map = mapBrowserEvent.map; - - var vertex = this.snapToVertex_(pixel, map); - if (!vertex) { - vertex = map.getCoordinateFromPixel(pixel); - } - this.createOrUpdatePointerFeature_(vertex); -}; - -/** - * @param {ol.Extent} extent extent - * @returns {ol.Feature} extent as featrue - * @private - */ -ol.interaction.Extent.prototype.createOrUpdateExtentFeature_ = function(extent) { - var extentFeature = this.extentFeature_; - - if (!extentFeature) { - if (!extent) { - extentFeature = new ol.Feature({}); - } else { - extentFeature = new ol.Feature(ol.geom.Polygon.fromExtent(extent)); - } - this.extentFeature_ = extentFeature; - this.extentOverlay_.getSource().addFeature(extentFeature); - } else { - if (!extent) { - extentFeature.setGeometry(undefined); - } else { - extentFeature.setGeometry(ol.geom.Polygon.fromExtent(extent)); - } - } - return extentFeature; -}; - - -/** - * @param {ol.Coordinate} vertex location of feature - * @returns {ol.Feature} vertex as feature - * @private - */ -ol.interaction.Extent.prototype.createOrUpdatePointerFeature_ = function(vertex) { - var vertexFeature = this.vertexFeature_; - if (!vertexFeature) { - vertexFeature = new ol.Feature(new ol.geom.Point(vertex)); - this.vertexFeature_ = vertexFeature; - this.vertexOverlay_.getSource().addFeature(vertexFeature); - } else { - var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); - geometry.setCoordinates(vertex); - } - return vertexFeature; -}; - - -/** - * @inheritDoc - */ -ol.interaction.Extent.prototype.setMap = function(map) { - this.extentOverlay_.setMap(map); - this.vertexOverlay_.setMap(map); - ol.interaction.Pointer.prototype.setMap.call(this, map); -}; - -/** - * Returns the current drawn extent in the view projection - * - * @return {ol.Extent} Drawn extent in the view projection. - * @api - */ -ol.interaction.Extent.prototype.getExtent = function() { - return this.extent_; -}; - -/** - * Manually sets the drawn extent, using the view projection. - * - * @param {ol.Extent} extent Extent - * @api - */ -ol.interaction.Extent.prototype.setExtent = function(extent) { - //Null extent means no bbox - this.extent_ = extent ? extent : null; - this.createOrUpdateExtentFeature_(extent); - this.dispatchEvent(new ol.interaction.Extent.Event(this.extent_)); -}; - - -/** - * @classdesc - * Events emitted by {@link ol.interaction.Extent} instances are instances of - * this type. - * - * @constructor - * @param {ol.Extent} extent the new extent - * @extends {ol.events.Event} - */ -ol.interaction.Extent.Event = function(extent) { - ol.events.Event.call(this, ol.interaction.Extent.EventType_.EXTENTCHANGED); - - /** - * The current extent. - * @type {ol.Extent} - * @api - */ - this.extent_ = extent; -}; -ol.inherits(ol.interaction.Extent.Event, ol.events.Event); - - -/** - * @enum {string} - * @private - */ -ol.interaction.Extent.EventType_ = { - /** - * Triggered after the extent is changed - * @event ol.interaction.Extent.Event - * @api - */ - EXTENTCHANGED: 'extentchanged' -}; - -goog.provide('ol.interaction.ModifyEventType'); - - -/** - * @enum {string} - */ -ol.interaction.ModifyEventType = { - /** - * Triggered upon feature modification start - * @event ol.interaction.Modify.Event#modifystart - * @api - */ - MODIFYSTART: 'modifystart', - /** - * Triggered upon feature modification end - * @event ol.interaction.Modify.Event#modifyend - * @api - */ - MODIFYEND: 'modifyend' -}; - -goog.provide('ol.interaction.Modify'); - -goog.require('ol'); -goog.require('ol.CollectionEventType'); -goog.require('ol.Feature'); -goog.require('ol.MapBrowserEventType'); -goog.require('ol.MapBrowserPointerEvent'); -goog.require('ol.ViewHint'); -goog.require('ol.array'); -goog.require('ol.coordinate'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.EventType'); -goog.require('ol.events.condition'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Point'); -goog.require('ol.interaction.ModifyEventType'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.layer.Vector'); -goog.require('ol.source.Vector'); -goog.require('ol.structs.RBush'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * Interaction for modifying feature geometries. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.ModifyOptions} options Options. - * @fires ol.interaction.Modify.Event - * @api - */ -ol.interaction.Modify = function(options) { - - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.Modify.handleDownEvent_, - handleDragEvent: ol.interaction.Modify.handleDragEvent_, - handleEvent: ol.interaction.Modify.handleEvent, - handleUpEvent: ol.interaction.Modify.handleUpEvent_ - }); - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.primaryAction; - - - /** - * @private - * @param {ol.MapBrowserEvent} mapBrowserEvent Browser event. - * @return {boolean} Combined condition result. - */ - this.defaultDeleteCondition_ = function(mapBrowserEvent) { - return ol.events.condition.noModifierKeys(mapBrowserEvent) && - ol.events.condition.singleClick(mapBrowserEvent); - }; - - /** - * @type {ol.EventsConditionType} - * @private - */ - this.deleteCondition_ = options.deleteCondition ? - options.deleteCondition : this.defaultDeleteCondition_; - - /** - * @type {ol.EventsConditionType} - * @private - */ - this.insertVertexCondition_ = options.insertVertexCondition ? - options.insertVertexCondition : ol.events.condition.always; - - /** - * Editing vertex. - * @type {ol.Feature} - * @private - */ - this.vertexFeature_ = null; - - /** - * Segments intersecting {@link this.vertexFeature_} by segment uid. - * @type {Object.<string, boolean>} - * @private - */ - this.vertexSegments_ = null; - - /** - * @type {ol.Pixel} - * @private - */ - this.lastPixel_ = [0, 0]; - - /** - * Tracks if the next `singleclick` event should be ignored to prevent - * accidental deletion right after vertex creation. - * @type {boolean} - * @private - */ - this.ignoreNextSingleClick_ = false; - - /** - * @type {boolean} - * @private - */ - this.modified_ = false; - - /** - * Segment RTree for each layer - * @type {ol.structs.RBush.<ol.ModifySegmentDataType>} - * @private - */ - this.rBush_ = new ol.structs.RBush(); - - /** - * @type {number} - * @private - */ - this.pixelTolerance_ = options.pixelTolerance !== undefined ? - options.pixelTolerance : 10; - - /** - * @type {boolean} - * @private - */ - this.snappedToVertex_ = false; - - /** - * Indicate whether the interaction is currently changing a feature's - * coordinates. - * @type {boolean} - * @private - */ - this.changingFeature_ = false; - - /** - * @type {Array} - * @private - */ - this.dragSegments_ = []; - - /** - * Draw overlay where sketch features are drawn. - * @type {ol.layer.Vector} - * @private - */ - this.overlay_ = new ol.layer.Vector({ - source: new ol.source.Vector({ - useSpatialIndex: false, - wrapX: !!options.wrapX - }), - style: options.style ? options.style : - ol.interaction.Modify.getDefaultStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); - - /** - * @const - * @private - * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)>} - */ - this.SEGMENT_WRITERS_ = { - 'Point': this.writePointGeometry_, - 'LineString': this.writeLineStringGeometry_, - 'LinearRing': this.writeLineStringGeometry_, - 'Polygon': this.writePolygonGeometry_, - 'MultiPoint': this.writeMultiPointGeometry_, - 'MultiLineString': this.writeMultiLineStringGeometry_, - 'MultiPolygon': this.writeMultiPolygonGeometry_, - 'Circle': this.writeCircleGeometry_, - 'GeometryCollection': this.writeGeometryCollectionGeometry_ - }; - - /** - * @type {ol.Collection.<ol.Feature>} - * @private - */ - this.features_ = options.features; - - this.features_.forEach(this.addFeature_, this); - ol.events.listen(this.features_, ol.CollectionEventType.ADD, - this.handleFeatureAdd_, this); - ol.events.listen(this.features_, ol.CollectionEventType.REMOVE, - this.handleFeatureRemove_, this); - - /** - * @type {ol.MapBrowserPointerEvent} - * @private - */ - this.lastPointerEvent_ = null; - -}; -ol.inherits(ol.interaction.Modify, ol.interaction.Pointer); - - -/** - * @define {number} The segment index assigned to a circle's center when - * breaking up a cicrle into ModifySegmentDataType segments. - */ -ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX = 0; - -/** - * @define {number} The segment index assigned to a circle's circumference when - * breaking up a circle into ModifySegmentDataType segments. - */ -ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX = 1; - - -/** - * @param {ol.Feature} feature Feature. - * @private - */ -ol.interaction.Modify.prototype.addFeature_ = function(feature) { - var geometry = feature.getGeometry(); - if (geometry && geometry.getType() in this.SEGMENT_WRITERS_) { - this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); - } - var map = this.getMap(); - if (map && map.isRendered() && this.getActive()) { - this.handlePointerAtPixel_(this.lastPixel_, map); - } - ol.events.listen(feature, ol.events.EventType.CHANGE, - this.handleFeatureChange_, this); -}; - - -/** - * @param {ol.MapBrowserPointerEvent} evt Map browser event - * @private - */ -ol.interaction.Modify.prototype.willModifyFeatures_ = function(evt) { - if (!this.modified_) { - this.modified_ = true; - this.dispatchEvent(new ol.interaction.Modify.Event( - ol.interaction.ModifyEventType.MODIFYSTART, this.features_, evt)); - } -}; - - -/** - * @param {ol.Feature} feature Feature. - * @private - */ -ol.interaction.Modify.prototype.removeFeature_ = function(feature) { - this.removeFeatureSegmentData_(feature); - // Remove the vertex feature if the collection of canditate features - // is empty. - if (this.vertexFeature_ && this.features_.getLength() === 0) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } - ol.events.unlisten(feature, ol.events.EventType.CHANGE, - this.handleFeatureChange_, this); -}; - - -/** - * @param {ol.Feature} feature Feature. - * @private - */ -ol.interaction.Modify.prototype.removeFeatureSegmentData_ = function(feature) { - var rBush = this.rBush_; - var /** @type {Array.<ol.ModifySegmentDataType>} */ nodesToRemove = []; - rBush.forEach( - /** - * @param {ol.ModifySegmentDataType} node RTree node. - */ - function(node) { - if (feature === node.feature) { - nodesToRemove.push(node); - } - }); - for (var i = nodesToRemove.length - 1; i >= 0; --i) { - rBush.remove(nodesToRemove[i]); - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.Modify.prototype.setActive = function(active) { - if (this.vertexFeature_ && !active) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } - ol.interaction.Pointer.prototype.setActive.call(this, active); -}; - - -/** - * @inheritDoc - */ -ol.interaction.Modify.prototype.setMap = function(map) { - this.overlay_.setMap(map); - ol.interaction.Pointer.prototype.setMap.call(this, map); -}; - - -/** - * @param {ol.Collection.Event} evt Event. - * @private - */ -ol.interaction.Modify.prototype.handleFeatureAdd_ = function(evt) { - this.addFeature_(/** @type {ol.Feature} */ (evt.element)); -}; - - -/** - * @param {ol.events.Event} evt Event. - * @private - */ -ol.interaction.Modify.prototype.handleFeatureChange_ = function(evt) { - if (!this.changingFeature_) { - var feature = /** @type {ol.Feature} */ (evt.target); - this.removeFeature_(feature); - this.addFeature_(feature); - } -}; - - -/** - * @param {ol.Collection.Event} evt Event. - * @private - */ -ol.interaction.Modify.prototype.handleFeatureRemove_ = function(evt) { - var feature = /** @type {ol.Feature} */ (evt.element); - this.removeFeature_(feature); -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Point} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writePointGeometry_ = function(feature, geometry) { - var coordinates = geometry.getCoordinates(); - var segmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiPoint} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writeMultiPointGeometry_ = function(feature, geometry) { - var points = geometry.getCoordinates(); - var coordinates, i, ii, segmentData; - for (i = 0, ii = points.length; i < ii; ++i) { - coordinates = points[i]; - segmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - depth: [i], - index: i, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.LineString} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writeLineStringGeometry_ = function(feature, geometry) { - var coordinates = geometry.getCoordinates(); - var i, ii, segment, segmentData; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - index: i, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiLineString} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { - var lines = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, segment, segmentData; - for (j = 0, jj = lines.length; j < jj; ++j) { - coordinates = lines[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - depth: [j], - index: i, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Polygon} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writePolygonGeometry_ = function(feature, geometry) { - var rings = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, segment, segmentData; - for (j = 0, jj = rings.length; j < jj; ++j) { - coordinates = rings[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - depth: [j], - index: i, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiPolygon} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { - var polygons = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; - for (k = 0, kk = polygons.length; k < kk; ++k) { - rings = polygons[k]; - for (j = 0, jj = rings.length; j < jj; ++j) { - coordinates = rings[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - depth: [j, k], - index: i, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } - } -}; - - -/** - * We convert a circle into two segments. The segment at index - * {@link ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX} is the - * circle's center (a point). The segment at index - * {@link ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX} is - * the circumference, and is not a line segment. - * - * @param {ol.Feature} feature Feature. - * @param {ol.geom.Circle} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writeCircleGeometry_ = function(feature, geometry) { - var coordinates = geometry.getCenter(); - var centerSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - index: ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX, - segment: [coordinates, coordinates] - }); - var circumferenceSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ - feature: feature, - geometry: geometry, - index: ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX, - segment: [coordinates, coordinates] - }); - var featureSegments = [centerSegmentData, circumferenceSegmentData]; - centerSegmentData.featureSegments = circumferenceSegmentData.featureSegments = featureSegments; - this.rBush_.insert(ol.extent.createOrUpdateFromCoordinate(coordinates), centerSegmentData); - this.rBush_.insert(geometry.getExtent(), circumferenceSegmentData); -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.GeometryCollection} geometry Geometry. - * @private - */ -ol.interaction.Modify.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { - var i, geometries = geometry.getGeometriesArray(); - for (i = 0; i < geometries.length; ++i) { - this.SEGMENT_WRITERS_[geometries[i].getType()].call( - this, feature, geometries[i]); - } -}; - - -/** - * @param {ol.Coordinate} coordinates Coordinates. - * @return {ol.Feature} Vertex feature. - * @private - */ -ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ = function(coordinates) { - var vertexFeature = this.vertexFeature_; - if (!vertexFeature) { - vertexFeature = new ol.Feature(new ol.geom.Point(coordinates)); - this.vertexFeature_ = vertexFeature; - this.overlay_.getSource().addFeature(vertexFeature); - } else { - var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); - geometry.setCoordinates(coordinates); - } - return vertexFeature; -}; - - -/** - * @param {ol.ModifySegmentDataType} a The first segment data. - * @param {ol.ModifySegmentDataType} b The second segment data. - * @return {number} The difference in indexes. - * @private - */ -ol.interaction.Modify.compareIndexes_ = function(a, b) { - return a.index - b.index; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} evt Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.Modify} - * @private - */ -ol.interaction.Modify.handleDownEvent_ = function(evt) { - if (!this.condition_(evt)) { - return false; - } - this.handlePointerAtPixel_(evt.pixel, evt.map); - var pixelCoordinate = evt.map.getCoordinateFromPixel(evt.pixel); - this.dragSegments_.length = 0; - this.modified_ = false; - var vertexFeature = this.vertexFeature_; - if (vertexFeature) { - var insertVertices = []; - var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); - var vertex = geometry.getCoordinates(); - var vertexExtent = ol.extent.boundingExtent([vertex]); - var segmentDataMatches = this.rBush_.getInExtent(vertexExtent); - var componentSegments = {}; - segmentDataMatches.sort(ol.interaction.Modify.compareIndexes_); - for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) { - var segmentDataMatch = segmentDataMatches[i]; - var segment = segmentDataMatch.segment; - var uid = ol.getUid(segmentDataMatch.feature); - var depth = segmentDataMatch.depth; - if (depth) { - uid += '-' + depth.join('-'); // separate feature components - } - if (!componentSegments[uid]) { - componentSegments[uid] = new Array(2); - } - if (segmentDataMatch.geometry.getType() === ol.geom.GeometryType.CIRCLE && - segmentDataMatch.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { - - var closestVertex = ol.interaction.Modify.closestOnSegmentData_(pixelCoordinate, segmentDataMatch); - if (ol.coordinate.equals(closestVertex, vertex) && !componentSegments[uid][0]) { - this.dragSegments_.push([segmentDataMatch, 0]); - componentSegments[uid][0] = segmentDataMatch; - } - } else if (ol.coordinate.equals(segment[0], vertex) && - !componentSegments[uid][0]) { - this.dragSegments_.push([segmentDataMatch, 0]); - componentSegments[uid][0] = segmentDataMatch; - } else if (ol.coordinate.equals(segment[1], vertex) && - !componentSegments[uid][1]) { - - // prevent dragging closed linestrings by the connecting node - if ((segmentDataMatch.geometry.getType() === - ol.geom.GeometryType.LINE_STRING || - segmentDataMatch.geometry.getType() === - ol.geom.GeometryType.MULTI_LINE_STRING) && - componentSegments[uid][0] && - componentSegments[uid][0].index === 0) { - continue; - } - - this.dragSegments_.push([segmentDataMatch, 1]); - componentSegments[uid][1] = segmentDataMatch; - } else if (this.insertVertexCondition_(evt) && ol.getUid(segment) in this.vertexSegments_ && - (!componentSegments[uid][0] && !componentSegments[uid][1])) { - insertVertices.push([segmentDataMatch, vertex]); - } - } - if (insertVertices.length) { - this.willModifyFeatures_(evt); - } - for (var j = insertVertices.length - 1; j >= 0; --j) { - this.insertVertex_.apply(this, insertVertices[j]); - } - } - return !!this.vertexFeature_; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} evt Event. - * @this {ol.interaction.Modify} - * @private - */ -ol.interaction.Modify.handleDragEvent_ = function(evt) { - this.ignoreNextSingleClick_ = false; - this.willModifyFeatures_(evt); - - var vertex = evt.coordinate; - for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) { - var dragSegment = this.dragSegments_[i]; - var segmentData = dragSegment[0]; - var depth = segmentData.depth; - var geometry = segmentData.geometry; - var coordinates; - var segment = segmentData.segment; - var index = dragSegment[1]; - - while (vertex.length < geometry.getStride()) { - vertex.push(segment[index][vertex.length]); - } - - switch (geometry.getType()) { - case ol.geom.GeometryType.POINT: - coordinates = vertex; - segment[0] = segment[1] = vertex; - break; - case ol.geom.GeometryType.MULTI_POINT: - coordinates = geometry.getCoordinates(); - coordinates[segmentData.index] = vertex; - segment[0] = segment[1] = vertex; - break; - case ol.geom.GeometryType.LINE_STRING: - coordinates = geometry.getCoordinates(); - coordinates[segmentData.index + index] = vertex; - segment[index] = vertex; - break; - case ol.geom.GeometryType.MULTI_LINE_STRING: - coordinates = geometry.getCoordinates(); - coordinates[depth[0]][segmentData.index + index] = vertex; - segment[index] = vertex; - break; - case ol.geom.GeometryType.POLYGON: - coordinates = geometry.getCoordinates(); - coordinates[depth[0]][segmentData.index + index] = vertex; - segment[index] = vertex; - break; - case ol.geom.GeometryType.MULTI_POLYGON: - coordinates = geometry.getCoordinates(); - coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex; - segment[index] = vertex; - break; - case ol.geom.GeometryType.CIRCLE: - segment[0] = segment[1] = vertex; - if (segmentData.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX) { - this.changingFeature_ = true; - geometry.setCenter(vertex); - this.changingFeature_ = false; - } else { // We're dragging the circle's circumference: - this.changingFeature_ = true; - geometry.setRadius(ol.coordinate.distance(geometry.getCenter(), vertex)); - this.changingFeature_ = false; - } - break; - default: - // pass - } - - if (coordinates) { - this.setGeometryCoordinates_(geometry, coordinates); - } - } - this.createOrUpdateVertexFeature_(vertex); -}; - - -/** - * @param {ol.MapBrowserPointerEvent} evt Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.Modify} - * @private - */ -ol.interaction.Modify.handleUpEvent_ = function(evt) { - var segmentData; - var geometry; - for (var i = this.dragSegments_.length - 1; i >= 0; --i) { - segmentData = this.dragSegments_[i][0]; - geometry = segmentData.geometry; - if (geometry.getType() === ol.geom.GeometryType.CIRCLE) { - // Update a circle object in the R* bush: - var coordinates = geometry.getCenter(); - var centerSegmentData = segmentData.featureSegments[0]; - var circumferenceSegmentData = segmentData.featureSegments[1]; - centerSegmentData.segment[0] = centerSegmentData.segment[1] = coordinates; - circumferenceSegmentData.segment[0] = circumferenceSegmentData.segment[1] = coordinates; - this.rBush_.update(ol.extent.createOrUpdateFromCoordinate(coordinates), centerSegmentData); - this.rBush_.update(geometry.getExtent(), circumferenceSegmentData); - } else { - this.rBush_.update(ol.extent.boundingExtent(segmentData.segment), - segmentData); - } - } - if (this.modified_) { - this.dispatchEvent(new ol.interaction.Modify.Event( - ol.interaction.ModifyEventType.MODIFYEND, this.features_, evt)); - this.modified_ = false; - } - return false; -}; - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} and may modify the - * geometry. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.Modify} - * @api - */ -ol.interaction.Modify.handleEvent = function(mapBrowserEvent) { - if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) { - return true; - } - this.lastPointerEvent_ = mapBrowserEvent; - - var handled; - if (!mapBrowserEvent.map.getView().getHints()[ol.ViewHint.INTERACTING] && - mapBrowserEvent.type == ol.MapBrowserEventType.POINTERMOVE && - !this.handlingDownUpSequence) { - this.handlePointerMove_(mapBrowserEvent); - } - if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) { - if (mapBrowserEvent.type != ol.MapBrowserEventType.SINGLECLICK || - !this.ignoreNextSingleClick_) { - handled = this.removePoint(); - } else { - handled = true; - } - } - - if (mapBrowserEvent.type == ol.MapBrowserEventType.SINGLECLICK) { - this.ignoreNextSingleClick_ = false; - } - - return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && - !handled; -}; - - -/** - * @param {ol.MapBrowserEvent} evt Event. - * @private - */ -ol.interaction.Modify.prototype.handlePointerMove_ = function(evt) { - this.lastPixel_ = evt.pixel; - this.handlePointerAtPixel_(evt.pixel, evt.map); -}; - - -/** - * @param {ol.Pixel} pixel Pixel - * @param {ol.Map} map Map. - * @private - */ -ol.interaction.Modify.prototype.handlePointerAtPixel_ = function(pixel, map) { - var pixelCoordinate = map.getCoordinateFromPixel(pixel); - var sortByDistance = function(a, b) { - return ol.interaction.Modify.pointDistanceToSegmentDataSquared_(pixelCoordinate, a) - - ol.interaction.Modify.pointDistanceToSegmentDataSquared_(pixelCoordinate, b); - }; - - var box = ol.extent.buffer( - ol.extent.createOrUpdateFromCoordinate(pixelCoordinate), - map.getView().getResolution() * this.pixelTolerance_); - - var rBush = this.rBush_; - var nodes = rBush.getInExtent(box); - if (nodes.length > 0) { - nodes.sort(sortByDistance); - var node = nodes[0]; - var closestSegment = node.segment; - var vertex = ol.interaction.Modify.closestOnSegmentData_(pixelCoordinate, node); - var vertexPixel = map.getPixelFromCoordinate(vertex); - var dist = ol.coordinate.distance(pixel, vertexPixel); - if (dist <= this.pixelTolerance_) { - var vertexSegments = {}; - - if (node.geometry.getType() === ol.geom.GeometryType.CIRCLE && - node.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { - - this.snappedToVertex_ = true; - this.createOrUpdateVertexFeature_(vertex); - } else { - var pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - var pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); - var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); - dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - this.snappedToVertex_ = dist <= this.pixelTolerance_; - if (this.snappedToVertex_) { - vertex = squaredDist1 > squaredDist2 ? - closestSegment[1] : closestSegment[0]; - } - this.createOrUpdateVertexFeature_(vertex); - var segment; - for (var i = 1, ii = nodes.length; i < ii; ++i) { - segment = nodes[i].segment; - if ((ol.coordinate.equals(closestSegment[0], segment[0]) && - ol.coordinate.equals(closestSegment[1], segment[1]) || - (ol.coordinate.equals(closestSegment[0], segment[1]) && - ol.coordinate.equals(closestSegment[1], segment[0])))) { - vertexSegments[ol.getUid(segment)] = true; - } else { - break; - } - } - } - - vertexSegments[ol.getUid(closestSegment)] = true; - this.vertexSegments_ = vertexSegments; - return; - } - } - if (this.vertexFeature_) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } -}; - - -/** - * Returns the distance from a point to a line segment. - * - * @param {ol.Coordinate} pointCoordinates The coordinates of the point from - * which to calculate the distance. - * @param {ol.ModifySegmentDataType} segmentData The object describing the line - * segment we are calculating the distance to. - * @return {number} The square of the distance between a point and a line segment. - */ -ol.interaction.Modify.pointDistanceToSegmentDataSquared_ = function(pointCoordinates, segmentData) { - var geometry = segmentData.geometry; - - if (geometry.getType() === ol.geom.GeometryType.CIRCLE) { - var circleGeometry = /** @type {ol.geom.Circle} */ (geometry); - - if (segmentData.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { - var distanceToCenterSquared = - ol.coordinate.squaredDistance(circleGeometry.getCenter(), pointCoordinates); - var distanceToCircumference = - Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius(); - return distanceToCircumference * distanceToCircumference; - } - } - return ol.coordinate.squaredDistanceToSegment(pointCoordinates, segmentData.segment); -}; - -/** - * Returns the point closest to a given line segment. - * - * @param {ol.Coordinate} pointCoordinates The point to which a closest point - * should be found. - * @param {ol.ModifySegmentDataType} segmentData The object describing the line - * segment which should contain the closest point. - * @return {ol.Coordinate} The point closest to the specified line segment. - */ -ol.interaction.Modify.closestOnSegmentData_ = function(pointCoordinates, segmentData) { - var geometry = segmentData.geometry; - - if (geometry.getType() === ol.geom.GeometryType.CIRCLE && - segmentData.index === ol.interaction.Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) { - return geometry.getClosestPoint(pointCoordinates); - } - return ol.coordinate.closestOnSegment(pointCoordinates, segmentData.segment); -}; - - -/** - * @param {ol.ModifySegmentDataType} segmentData Segment data. - * @param {ol.Coordinate} vertex Vertex. - * @private - */ -ol.interaction.Modify.prototype.insertVertex_ = function(segmentData, vertex) { - var segment = segmentData.segment; - var feature = segmentData.feature; - var geometry = segmentData.geometry; - var depth = segmentData.depth; - var index = /** @type {number} */ (segmentData.index); - var coordinates; - - while (vertex.length < geometry.getStride()) { - vertex.push(0); - } - - switch (geometry.getType()) { - case ol.geom.GeometryType.MULTI_LINE_STRING: - coordinates = geometry.getCoordinates(); - coordinates[depth[0]].splice(index + 1, 0, vertex); - break; - case ol.geom.GeometryType.POLYGON: - coordinates = geometry.getCoordinates(); - coordinates[depth[0]].splice(index + 1, 0, vertex); - break; - case ol.geom.GeometryType.MULTI_POLYGON: - coordinates = geometry.getCoordinates(); - coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex); - break; - case ol.geom.GeometryType.LINE_STRING: - coordinates = geometry.getCoordinates(); - coordinates.splice(index + 1, 0, vertex); - break; - default: - return; - } - - this.setGeometryCoordinates_(geometry, coordinates); - var rTree = this.rBush_; - rTree.remove(segmentData); - this.updateSegmentIndices_(geometry, index, depth, 1); - var newSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ - segment: [segment[0], vertex], - feature: feature, - geometry: geometry, - depth: depth, - index: index - }); - rTree.insert(ol.extent.boundingExtent(newSegmentData.segment), - newSegmentData); - this.dragSegments_.push([newSegmentData, 1]); - - var newSegmentData2 = /** @type {ol.ModifySegmentDataType} */ ({ - segment: [vertex, segment[1]], - feature: feature, - geometry: geometry, - depth: depth, - index: index + 1 - }); - rTree.insert(ol.extent.boundingExtent(newSegmentData2.segment), - newSegmentData2); - this.dragSegments_.push([newSegmentData2, 0]); - this.ignoreNextSingleClick_ = true; -}; - -/** - * Removes the vertex currently being pointed. - * @return {boolean} True when a vertex was removed. - * @api - */ -ol.interaction.Modify.prototype.removePoint = function() { - if (this.lastPointerEvent_ && this.lastPointerEvent_.type != ol.MapBrowserEventType.POINTERDRAG) { - var evt = this.lastPointerEvent_; - this.willModifyFeatures_(evt); - this.removeVertex_(); - this.dispatchEvent(new ol.interaction.Modify.Event( - ol.interaction.ModifyEventType.MODIFYEND, this.features_, evt)); - this.modified_ = false; - return true; - } - return false; -}; - -/** - * Removes a vertex from all matching features. - * @return {boolean} True when a vertex was removed. - * @private - */ -ol.interaction.Modify.prototype.removeVertex_ = function() { - var dragSegments = this.dragSegments_; - var segmentsByFeature = {}; - var deleted = false; - var component, coordinates, dragSegment, geometry, i, index, left; - var newIndex, right, segmentData, uid; - for (i = dragSegments.length - 1; i >= 0; --i) { - dragSegment = dragSegments[i]; - segmentData = dragSegment[0]; - uid = ol.getUid(segmentData.feature); - if (segmentData.depth) { - // separate feature components - uid += '-' + segmentData.depth.join('-'); - } - if (!(uid in segmentsByFeature)) { - segmentsByFeature[uid] = {}; - } - if (dragSegment[1] === 0) { - segmentsByFeature[uid].right = segmentData; - segmentsByFeature[uid].index = segmentData.index; - } else if (dragSegment[1] == 1) { - segmentsByFeature[uid].left = segmentData; - segmentsByFeature[uid].index = segmentData.index + 1; - } - - } - for (uid in segmentsByFeature) { - right = segmentsByFeature[uid].right; - left = segmentsByFeature[uid].left; - index = segmentsByFeature[uid].index; - newIndex = index - 1; - if (left !== undefined) { - segmentData = left; - } else { - segmentData = right; - } - if (newIndex < 0) { - newIndex = 0; - } - geometry = segmentData.geometry; - coordinates = geometry.getCoordinates(); - component = coordinates; - deleted = false; - switch (geometry.getType()) { - case ol.geom.GeometryType.MULTI_LINE_STRING: - if (coordinates[segmentData.depth[0]].length > 2) { - coordinates[segmentData.depth[0]].splice(index, 1); - deleted = true; - } - break; - case ol.geom.GeometryType.LINE_STRING: - if (coordinates.length > 2) { - coordinates.splice(index, 1); - deleted = true; - } - break; - case ol.geom.GeometryType.MULTI_POLYGON: - component = component[segmentData.depth[1]]; - /* falls through */ - case ol.geom.GeometryType.POLYGON: - component = component[segmentData.depth[0]]; - if (component.length > 4) { - if (index == component.length - 1) { - index = 0; - } - component.splice(index, 1); - deleted = true; - if (index === 0) { - // close the ring again - component.pop(); - component.push(component[0]); - newIndex = component.length - 1; - } - } - break; - default: - // pass - } - - if (deleted) { - this.setGeometryCoordinates_(geometry, coordinates); - var segments = []; - if (left !== undefined) { - this.rBush_.remove(left); - segments.push(left.segment[0]); - } - if (right !== undefined) { - this.rBush_.remove(right); - segments.push(right.segment[1]); - } - if (left !== undefined && right !== undefined) { - var newSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ - depth: segmentData.depth, - feature: segmentData.feature, - geometry: segmentData.geometry, - index: newIndex, - segment: segments - }); - this.rBush_.insert(ol.extent.boundingExtent(newSegmentData.segment), - newSegmentData); - } - this.updateSegmentIndices_(geometry, index, segmentData.depth, -1); - if (this.vertexFeature_) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } - dragSegments.length = 0; - } - - } - return deleted; -}; - - -/** - * @param {ol.geom.SimpleGeometry} geometry Geometry. - * @param {Array} coordinates Coordinates. - * @private - */ -ol.interaction.Modify.prototype.setGeometryCoordinates_ = function(geometry, coordinates) { - this.changingFeature_ = true; - geometry.setCoordinates(coordinates); - this.changingFeature_ = false; -}; - - -/** - * @param {ol.geom.SimpleGeometry} geometry Geometry. - * @param {number} index Index. - * @param {Array.<number>|undefined} depth Depth. - * @param {number} delta Delta (1 or -1). - * @private - */ -ol.interaction.Modify.prototype.updateSegmentIndices_ = function( - geometry, index, depth, delta) { - this.rBush_.forEachInExtent(geometry.getExtent(), function(segmentDataMatch) { - if (segmentDataMatch.geometry === geometry && - (depth === undefined || segmentDataMatch.depth === undefined || - ol.array.equals(segmentDataMatch.depth, depth)) && - segmentDataMatch.index > index) { - segmentDataMatch.index += delta; - } - }); -}; - - -/** - * @return {ol.StyleFunction} Styles. - */ -ol.interaction.Modify.getDefaultStyleFunction = function() { - var style = ol.style.Style.createDefaultEditing(); - return function(feature, resolution) { - return style[ol.geom.GeometryType.POINT]; - }; -}; - - -/** - * @classdesc - * Events emitted by {@link ol.interaction.Modify} instances are instances of - * this type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.ModifyEvent} - * @param {ol.interaction.ModifyEventType} type Type. - * @param {ol.Collection.<ol.Feature>} features The features modified. - * @param {ol.MapBrowserPointerEvent} mapBrowserPointerEvent Associated - * {@link ol.MapBrowserPointerEvent}. - */ -ol.interaction.Modify.Event = function(type, features, mapBrowserPointerEvent) { - - ol.events.Event.call(this, type); - - /** - * The features being modified. - * @type {ol.Collection.<ol.Feature>} - * @api - */ - this.features = features; - - /** - * Associated {@link ol.MapBrowserEvent}. - * @type {ol.MapBrowserEvent} - * @api - */ - this.mapBrowserEvent = mapBrowserPointerEvent; -}; -ol.inherits(ol.interaction.Modify.Event, ol.events.Event); - -goog.provide('ol.interaction.Select'); - -goog.require('ol'); -goog.require('ol.CollectionEventType'); -goog.require('ol.array'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.condition'); -goog.require('ol.functions'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.layer.Vector'); -goog.require('ol.obj'); -goog.require('ol.source.Vector'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * Interaction for selecting vector features. By default, selected features are - * styled differently, so this interaction can be used for visual highlighting, - * as well as selecting features for other actions, such as modification or - * output. There are three ways of controlling which features are selected: - * using the browser event as defined by the `condition` and optionally the - * `toggle`, `add`/`remove`, and `multi` options; a `layers` filter; and a - * further feature filter using the `filter` option. - * - * Selected features are added to an internal unmanaged layer. - * - * @constructor - * @extends {ol.interaction.Interaction} - * @param {olx.interaction.SelectOptions=} opt_options Options. - * @fires ol.interaction.Select.Event - * @api - */ -ol.interaction.Select = function(opt_options) { - - ol.interaction.Interaction.call(this, { - handleEvent: ol.interaction.Select.handleEvent - }); - - var options = opt_options ? opt_options : {}; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.condition_ = options.condition ? - options.condition : ol.events.condition.singleClick; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.addCondition_ = options.addCondition ? - options.addCondition : ol.events.condition.never; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.removeCondition_ = options.removeCondition ? - options.removeCondition : ol.events.condition.never; - - /** - * @private - * @type {ol.EventsConditionType} - */ - this.toggleCondition_ = options.toggleCondition ? - options.toggleCondition : ol.events.condition.shiftKeyOnly; - - /** - * @private - * @type {boolean} - */ - this.multi_ = options.multi ? options.multi : false; - - /** - * @private - * @type {ol.SelectFilterFunction} - */ - this.filter_ = options.filter ? options.filter : - ol.functions.TRUE; - - /** - * @private - * @type {number} - */ - this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; - - var featureOverlay = new ol.layer.Vector({ - source: new ol.source.Vector({ - useSpatialIndex: false, - features: options.features, - wrapX: options.wrapX - }), - style: options.style ? options.style : - ol.interaction.Select.getDefaultStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); - - /** - * @private - * @type {ol.layer.Vector} - */ - this.featureOverlay_ = featureOverlay; - - /** @type {function(ol.layer.Layer): boolean} */ - var layerFilter; - if (options.layers) { - if (typeof options.layers === 'function') { - layerFilter = options.layers; - } else { - var layers = options.layers; - layerFilter = function(layer) { - return ol.array.includes(layers, layer); - }; - } - } else { - layerFilter = ol.functions.TRUE; - } - - /** - * @private - * @type {function(ol.layer.Layer): boolean} - */ - this.layerFilter_ = layerFilter; - - /** - * An association between selected feature (key) - * and layer (value) - * @private - * @type {Object.<number, ol.layer.Layer>} - */ - this.featureLayerAssociation_ = {}; - - var features = this.featureOverlay_.getSource().getFeaturesCollection(); - ol.events.listen(features, ol.CollectionEventType.ADD, - this.addFeature_, this); - ol.events.listen(features, ol.CollectionEventType.REMOVE, - this.removeFeature_, this); - -}; -ol.inherits(ol.interaction.Select, ol.interaction.Interaction); - - -/** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {ol.layer.Layer} layer Layer. - * @private - */ -ol.interaction.Select.prototype.addFeatureLayerAssociation_ = function(feature, layer) { - var key = ol.getUid(feature); - this.featureLayerAssociation_[key] = layer; -}; - - -/** - * Get the selected features. - * @return {ol.Collection.<ol.Feature>} Features collection. - * @api - */ -ol.interaction.Select.prototype.getFeatures = function() { - return this.featureOverlay_.getSource().getFeaturesCollection(); -}; - - -/** - * Returns the Hit-detection tolerance. - * @returns {number} Hit tolerance in pixels. - * @api - */ -ol.interaction.Select.prototype.getHitTolerance = function() { - return this.hitTolerance_; -}; - - -/** - * Returns the associated {@link ol.layer.Vector vectorlayer} of - * the (last) selected feature. Note that this will not work with any - * programmatic method like pushing features to - * {@link ol.interaction.Select#getFeatures collection}. - * @param {ol.Feature|ol.render.Feature} feature Feature - * @return {ol.layer.Vector} Layer. - * @api - */ -ol.interaction.Select.prototype.getLayer = function(feature) { - var key = ol.getUid(feature); - return /** @type {ol.layer.Vector} */ (this.featureLayerAssociation_[key]); -}; - - -/** - * Handles the {@link ol.MapBrowserEvent map browser event} and may change the - * selected state of features. - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.Select} - * @api - */ -ol.interaction.Select.handleEvent = function(mapBrowserEvent) { - if (!this.condition_(mapBrowserEvent)) { - return true; - } - var add = this.addCondition_(mapBrowserEvent); - var remove = this.removeCondition_(mapBrowserEvent); - var toggle = this.toggleCondition_(mapBrowserEvent); - var set = !add && !remove && !toggle; - var map = mapBrowserEvent.map; - var features = this.featureOverlay_.getSource().getFeaturesCollection(); - var deselected = []; - var selected = []; - if (set) { - // Replace the currently selected feature(s) with the feature(s) at the - // pixel, or clear the selected feature(s) if there is no feature at - // the pixel. - ol.obj.clear(this.featureLayerAssociation_); - map.forEachFeatureAtPixel(mapBrowserEvent.pixel, - (/** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {ol.layer.Layer} layer Layer. - * @return {boolean|undefined} Continue to iterate over the features. - */ - function(feature, layer) { - if (this.filter_(feature, layer)) { - selected.push(feature); - this.addFeatureLayerAssociation_(feature, layer); - return !this.multi_; - } - }).bind(this), { - layerFilter: this.layerFilter_, - hitTolerance: this.hitTolerance_ - }); - var i; - for (i = features.getLength() - 1; i >= 0; --i) { - var feature = features.item(i); - var index = selected.indexOf(feature); - if (index > -1) { - // feature is already selected - selected.splice(index, 1); - } else { - features.remove(feature); - deselected.push(feature); - } - } - if (selected.length !== 0) { - features.extend(selected); - } - } else { - // Modify the currently selected feature(s). - map.forEachFeatureAtPixel(mapBrowserEvent.pixel, - (/** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {ol.layer.Layer} layer Layer. - * @return {boolean|undefined} Continue to iterate over the features. - */ - function(feature, layer) { - if (this.filter_(feature, layer)) { - if ((add || toggle) && - !ol.array.includes(features.getArray(), feature)) { - selected.push(feature); - this.addFeatureLayerAssociation_(feature, layer); - } else if ((remove || toggle) && - ol.array.includes(features.getArray(), feature)) { - deselected.push(feature); - this.removeFeatureLayerAssociation_(feature); - } - return !this.multi_; - } - }).bind(this), { - layerFilter: this.layerFilter_, - hitTolerance: this.hitTolerance_ - }); - var j; - for (j = deselected.length - 1; j >= 0; --j) { - features.remove(deselected[j]); - } - features.extend(selected); - } - if (selected.length > 0 || deselected.length > 0) { - this.dispatchEvent( - new ol.interaction.Select.Event(ol.interaction.Select.EventType_.SELECT, - selected, deselected, mapBrowserEvent)); - } - return ol.events.condition.pointerMove(mapBrowserEvent); -}; - - -/** - * Hit-detection tolerance. Pixels inside the radius around the given position - * will be checked for features. This only works for the canvas renderer and - * not for WebGL. - * @param {number} hitTolerance Hit tolerance in pixels. - * @api - */ -ol.interaction.Select.prototype.setHitTolerance = function(hitTolerance) { - this.hitTolerance_ = hitTolerance; -}; - - -/** - * Remove the interaction from its current map, if any, and attach it to a new - * map, if any. Pass `null` to just remove the interaction from the current map. - * @param {ol.Map} map Map. - * @override - * @api - */ -ol.interaction.Select.prototype.setMap = function(map) { - var currentMap = this.getMap(); - var selectedFeatures = - this.featureOverlay_.getSource().getFeaturesCollection(); - if (currentMap) { - selectedFeatures.forEach(currentMap.unskipFeature, currentMap); - } - ol.interaction.Interaction.prototype.setMap.call(this, map); - this.featureOverlay_.setMap(map); - if (map) { - selectedFeatures.forEach(map.skipFeature, map); - } -}; - - -/** - * @return {ol.StyleFunction} Styles. - */ -ol.interaction.Select.getDefaultStyleFunction = function() { - var styles = ol.style.Style.createDefaultEditing(); - ol.array.extend(styles[ol.geom.GeometryType.POLYGON], - styles[ol.geom.GeometryType.LINE_STRING]); - ol.array.extend(styles[ol.geom.GeometryType.GEOMETRY_COLLECTION], - styles[ol.geom.GeometryType.LINE_STRING]); - - return function(feature, resolution) { - if (!feature.getGeometry()) { - return null; - } - return styles[feature.getGeometry().getType()]; - }; -}; - - -/** - * @param {ol.Collection.Event} evt Event. - * @private - */ -ol.interaction.Select.prototype.addFeature_ = function(evt) { - var map = this.getMap(); - if (map) { - map.skipFeature(/** @type {ol.Feature} */ (evt.element)); - } -}; - - -/** - * @param {ol.Collection.Event} evt Event. - * @private - */ -ol.interaction.Select.prototype.removeFeature_ = function(evt) { - var map = this.getMap(); - if (map) { - map.unskipFeature(/** @type {ol.Feature} */ (evt.element)); - } -}; - - -/** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @private - */ -ol.interaction.Select.prototype.removeFeatureLayerAssociation_ = function(feature) { - var key = ol.getUid(feature); - delete this.featureLayerAssociation_[key]; -}; - - -/** - * @classdesc - * Events emitted by {@link ol.interaction.Select} instances are instances of - * this type. - * - * @param {ol.interaction.Select.EventType_} type The event type. - * @param {Array.<ol.Feature>} selected Selected features. - * @param {Array.<ol.Feature>} deselected Deselected features. - * @param {ol.MapBrowserEvent} mapBrowserEvent Associated - * {@link ol.MapBrowserEvent}. - * @implements {oli.SelectEvent} - * @extends {ol.events.Event} - * @constructor - */ -ol.interaction.Select.Event = function(type, selected, deselected, mapBrowserEvent) { - ol.events.Event.call(this, type); - - /** - * Selected features array. - * @type {Array.<ol.Feature>} - * @api - */ - this.selected = selected; - - /** - * Deselected features array. - * @type {Array.<ol.Feature>} - * @api - */ - this.deselected = deselected; - - /** - * Associated {@link ol.MapBrowserEvent}. - * @type {ol.MapBrowserEvent} - * @api - */ - this.mapBrowserEvent = mapBrowserEvent; -}; -ol.inherits(ol.interaction.Select.Event, ol.events.Event); - - -/** - * @enum {string} - * @private - */ -ol.interaction.Select.EventType_ = { - /** - * Triggered when feature(s) has been (de)selected. - * @event ol.interaction.Select.Event#select - * @api - */ - SELECT: 'select' -}; - -goog.provide('ol.interaction.Snap'); - -goog.require('ol'); -goog.require('ol.Collection'); -goog.require('ol.CollectionEventType'); -goog.require('ol.coordinate'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Polygon'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.obj'); -goog.require('ol.source.Vector'); -goog.require('ol.source.VectorEventType'); -goog.require('ol.structs.RBush'); - - -/** - * @classdesc - * Handles snapping of vector features while modifying or drawing them. The - * features can come from a {@link ol.source.Vector} or {@link ol.Collection} - * Any interaction object that allows the user to interact - * with the features using the mouse can benefit from the snapping, as long - * as it is added before. - * - * The snap interaction modifies map browser event `coordinate` and `pixel` - * properties to force the snap to occur to any interaction that them. - * - * Example: - * - * var snap = new ol.interaction.Snap({ - * source: source - * }); - * - * @constructor - * @extends {ol.interaction.Pointer} - * @param {olx.interaction.SnapOptions=} opt_options Options. - * @api - */ -ol.interaction.Snap = function(opt_options) { - - ol.interaction.Pointer.call(this, { - handleEvent: ol.interaction.Snap.handleEvent_, - handleDownEvent: ol.functions.TRUE, - handleUpEvent: ol.interaction.Snap.handleUpEvent_ - }); - - var options = opt_options ? opt_options : {}; - - /** - * @type {ol.source.Vector} - * @private - */ - this.source_ = options.source ? options.source : null; - - /** - * @private - * @type {boolean} - */ - this.vertex_ = options.vertex !== undefined ? options.vertex : true; - - /** - * @private - * @type {boolean} - */ - this.edge_ = options.edge !== undefined ? options.edge : true; - - /** - * @type {ol.Collection.<ol.Feature>} - * @private - */ - this.features_ = options.features ? options.features : null; - - /** - * @type {Array.<ol.EventsKey>} - * @private - */ - this.featuresListenerKeys_ = []; - - /** - * @type {Object.<number, ol.EventsKey>} - * @private - */ - this.featureChangeListenerKeys_ = {}; - - /** - * Extents are preserved so indexed segment can be quickly removed - * when its feature geometry changes - * @type {Object.<number, ol.Extent>} - * @private - */ - this.indexedFeaturesExtents_ = {}; - - /** - * If a feature geometry changes while a pointer drag|move event occurs, the - * feature doesn't get updated right away. It will be at the next 'pointerup' - * event fired. - * @type {Object.<number, ol.Feature>} - * @private - */ - this.pendingFeatures_ = {}; - - /** - * Used for distance sorting in sortByDistance_ - * @type {ol.Coordinate} - * @private - */ - this.pixelCoordinate_ = null; - - /** - * @type {number} - * @private - */ - this.pixelTolerance_ = options.pixelTolerance !== undefined ? - options.pixelTolerance : 10; - - /** - * @type {function(ol.SnapSegmentDataType, ol.SnapSegmentDataType): number} - * @private - */ - this.sortByDistance_ = ol.interaction.Snap.sortByDistance.bind(this); - - - /** - * Segment RTree for each layer - * @type {ol.structs.RBush.<ol.SnapSegmentDataType>} - * @private - */ - this.rBush_ = new ol.structs.RBush(); - - - /** - * @const - * @private - * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)>} - */ - this.SEGMENT_WRITERS_ = { - 'Point': this.writePointGeometry_, - 'LineString': this.writeLineStringGeometry_, - 'LinearRing': this.writeLineStringGeometry_, - 'Polygon': this.writePolygonGeometry_, - 'MultiPoint': this.writeMultiPointGeometry_, - 'MultiLineString': this.writeMultiLineStringGeometry_, - 'MultiPolygon': this.writeMultiPolygonGeometry_, - 'GeometryCollection': this.writeGeometryCollectionGeometry_, - 'Circle': this.writeCircleGeometry_ - }; -}; -ol.inherits(ol.interaction.Snap, ol.interaction.Pointer); - - -/** - * Add a feature to the collection of features that we may snap to. - * @param {ol.Feature} feature Feature. - * @param {boolean=} opt_listen Whether to listen to the feature change or not - * Defaults to `true`. - * @api - */ -ol.interaction.Snap.prototype.addFeature = function(feature, opt_listen) { - var listen = opt_listen !== undefined ? opt_listen : true; - var feature_uid = ol.getUid(feature); - var geometry = feature.getGeometry(); - if (geometry) { - var segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()]; - if (segmentWriter) { - this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent( - ol.extent.createEmpty()); - segmentWriter.call(this, feature, geometry); - } - } - - if (listen) { - this.featureChangeListenerKeys_[feature_uid] = ol.events.listen( - feature, - ol.events.EventType.CHANGE, - this.handleFeatureChange_, this); - } -}; - - -/** - * @param {ol.Feature} feature Feature. - * @private - */ -ol.interaction.Snap.prototype.forEachFeatureAdd_ = function(feature) { - this.addFeature(feature); -}; - - -/** - * @param {ol.Feature} feature Feature. - * @private - */ -ol.interaction.Snap.prototype.forEachFeatureRemove_ = function(feature) { - this.removeFeature(feature); -}; - - -/** - * @return {ol.Collection.<ol.Feature>|Array.<ol.Feature>} Features. - * @private - */ -ol.interaction.Snap.prototype.getFeatures_ = function() { - var features; - if (this.features_) { - features = this.features_; - } else if (this.source_) { - features = this.source_.getFeatures(); - } - return /** @type {!Array.<ol.Feature>|!ol.Collection.<ol.Feature>} */ (features); -}; - - -/** - * @param {ol.source.Vector.Event|ol.Collection.Event} evt Event. - * @private - */ -ol.interaction.Snap.prototype.handleFeatureAdd_ = function(evt) { - var feature; - if (evt instanceof ol.source.Vector.Event) { - feature = evt.feature; - } else if (evt instanceof ol.Collection.Event) { - feature = evt.element; - } - this.addFeature(/** @type {ol.Feature} */ (feature)); -}; - - -/** - * @param {ol.source.Vector.Event|ol.Collection.Event} evt Event. - * @private - */ -ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { - var feature; - if (evt instanceof ol.source.Vector.Event) { - feature = evt.feature; - } else if (evt instanceof ol.Collection.Event) { - feature = evt.element; - } - this.removeFeature(/** @type {ol.Feature} */ (feature)); -}; - - -/** - * @param {ol.events.Event} evt Event. - * @private - */ -ol.interaction.Snap.prototype.handleFeatureChange_ = function(evt) { - var feature = /** @type {ol.Feature} */ (evt.target); - if (this.handlingDownUpSequence) { - var uid = ol.getUid(feature); - if (!(uid in this.pendingFeatures_)) { - this.pendingFeatures_[uid] = feature; - } - } else { - this.updateFeature_(feature); - } -}; - - -/** - * Remove a feature from the collection of features that we may snap to. - * @param {ol.Feature} feature Feature - * @param {boolean=} opt_unlisten Whether to unlisten to the feature change - * or not. Defaults to `true`. - * @api - */ -ol.interaction.Snap.prototype.removeFeature = function(feature, opt_unlisten) { - var unlisten = opt_unlisten !== undefined ? opt_unlisten : true; - var feature_uid = ol.getUid(feature); - var extent = this.indexedFeaturesExtents_[feature_uid]; - if (extent) { - var rBush = this.rBush_; - var i, nodesToRemove = []; - rBush.forEachInExtent(extent, function(node) { - if (feature === node.feature) { - nodesToRemove.push(node); - } - }); - for (i = nodesToRemove.length - 1; i >= 0; --i) { - rBush.remove(nodesToRemove[i]); - } - } - - if (unlisten) { - ol.events.unlistenByKey(this.featureChangeListenerKeys_[feature_uid]); - delete this.featureChangeListenerKeys_[feature_uid]; - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.Snap.prototype.setMap = function(map) { - var currentMap = this.getMap(); - var keys = this.featuresListenerKeys_; - var features = this.getFeatures_(); - - if (currentMap) { - keys.forEach(ol.events.unlistenByKey); - keys.length = 0; - features.forEach(this.forEachFeatureRemove_, this); - } - ol.interaction.Pointer.prototype.setMap.call(this, map); - - if (map) { - if (this.features_) { - keys.push( - ol.events.listen(this.features_, ol.CollectionEventType.ADD, - this.handleFeatureAdd_, this), - ol.events.listen(this.features_, ol.CollectionEventType.REMOVE, - this.handleFeatureRemove_, this) - ); - } else if (this.source_) { - keys.push( - ol.events.listen(this.source_, ol.source.VectorEventType.ADDFEATURE, - this.handleFeatureAdd_, this), - ol.events.listen(this.source_, ol.source.VectorEventType.REMOVEFEATURE, - this.handleFeatureRemove_, this) - ); - } - features.forEach(this.forEachFeatureAdd_, this); - } -}; - - -/** - * @inheritDoc - */ -ol.interaction.Snap.prototype.shouldStopEvent = ol.functions.FALSE; - - -/** - * @param {ol.Pixel} pixel Pixel - * @param {ol.Coordinate} pixelCoordinate Coordinate - * @param {ol.Map} map Map. - * @return {ol.SnapResultType} Snap result - */ -ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { - - var lowerLeft = map.getCoordinateFromPixel( - [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]); - var upperRight = map.getCoordinateFromPixel( - [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]); - var box = ol.extent.boundingExtent([lowerLeft, upperRight]); - - var segments = this.rBush_.getInExtent(box); - - // If snapping on vertices only, don't consider circles - if (this.vertex_ && !this.edge_) { - segments = segments.filter(function(segment) { - return segment.feature.getGeometry().getType() !== - ol.geom.GeometryType.CIRCLE; - }); - } - - var snappedToVertex = false; - var snapped = false; - var vertex = null; - var vertexPixel = null; - var dist, pixel1, pixel2, squaredDist1, squaredDist2; - if (segments.length > 0) { - this.pixelCoordinate_ = pixelCoordinate; - segments.sort(this.sortByDistance_); - var closestSegment = segments[0].segment; - var isCircle = segments[0].feature.getGeometry().getType() === - ol.geom.GeometryType.CIRCLE; - if (this.vertex_ && !this.edge_) { - pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - squaredDist1 = ol.coordinate.squaredDistance(pixel, pixel1); - squaredDist2 = ol.coordinate.squaredDistance(pixel, pixel2); - dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - snappedToVertex = dist <= this.pixelTolerance_; - if (snappedToVertex) { - snapped = true; - vertex = squaredDist1 > squaredDist2 ? - closestSegment[1] : closestSegment[0]; - vertexPixel = map.getPixelFromCoordinate(vertex); - } - } else if (this.edge_) { - if (isCircle) { - vertex = ol.coordinate.closestOnCircle(pixelCoordinate, - /** @type {ol.geom.Circle} */ (segments[0].feature.getGeometry())); - } else { - vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, - closestSegment)); - } - vertexPixel = map.getPixelFromCoordinate(vertex); - if (ol.coordinate.distance(pixel, vertexPixel) <= this.pixelTolerance_) { - snapped = true; - if (this.vertex_ && !isCircle) { - pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); - squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); - dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - snappedToVertex = dist <= this.pixelTolerance_; - if (snappedToVertex) { - vertex = squaredDist1 > squaredDist2 ? - closestSegment[1] : closestSegment[0]; - vertexPixel = map.getPixelFromCoordinate(vertex); - } - } - } - } - if (snapped) { - vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])]; - } - } - return /** @type {ol.SnapResultType} */ ({ - snapped: snapped, - vertex: vertex, - vertexPixel: vertexPixel - }); -}; - - -/** - * @param {ol.Feature} feature Feature - * @private - */ -ol.interaction.Snap.prototype.updateFeature_ = function(feature) { - this.removeFeature(feature, false); - this.addFeature(feature, false); -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Circle} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeCircleGeometry_ = function(feature, geometry) { - var polygon = ol.geom.Polygon.fromCircle(geometry); - var coordinates = polygon.getCoordinates()[0]; - var i, ii, segment, segmentData; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.GeometryCollection} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { - var i, geometries = geometry.getGeometriesArray(); - for (i = 0; i < geometries.length; ++i) { - var segmentWriter = this.SEGMENT_WRITERS_[geometries[i].getType()]; - if (segmentWriter) { - segmentWriter.call(this, feature, geometries[i]); - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.LineString} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeLineStringGeometry_ = function(feature, geometry) { - var coordinates = geometry.getCoordinates(); - var i, ii, segment, segmentData; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiLineString} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { - var lines = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, segment, segmentData; - for (j = 0, jj = lines.length; j < jj; ++j) { - coordinates = lines[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiPoint} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeMultiPointGeometry_ = function(feature, geometry) { - var points = geometry.getCoordinates(); - var coordinates, i, ii, segmentData; - for (i = 0, ii = points.length; i < ii; ++i) { - coordinates = points[i]; - segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiPolygon} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { - var polygons = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; - for (k = 0, kk = polygons.length; k < kk; ++k) { - rings = polygons[k]; - for (j = 0, jj = rings.length; j < jj; ++j) { - coordinates = rings[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Point} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writePointGeometry_ = function(feature, geometry) { - var coordinates = geometry.getCoordinates(); - var segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Polygon} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writePolygonGeometry_ = function(feature, geometry) { - var rings = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, segment, segmentData; - for (j = 0, jj = rings.length; j < jj; ++j) { - coordinates = rings[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.SnapSegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } -}; - - -/** - * Handle all pointer events events. - * @param {ol.MapBrowserEvent} evt A move event. - * @return {boolean} Pass the event to other interactions. - * @this {ol.interaction.Snap} - * @private - */ -ol.interaction.Snap.handleEvent_ = function(evt) { - var result = this.snapTo(evt.pixel, evt.coordinate, evt.map); - if (result.snapped) { - evt.coordinate = result.vertex.slice(0, 2); - evt.pixel = result.vertexPixel; - } - return ol.interaction.Pointer.handleEvent.call(this, evt); -}; - - -/** - * @param {ol.MapBrowserPointerEvent} evt Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.Snap} - * @private - */ -ol.interaction.Snap.handleUpEvent_ = function(evt) { - var featuresToUpdate = ol.obj.getValues(this.pendingFeatures_); - if (featuresToUpdate.length) { - featuresToUpdate.forEach(this.updateFeature_, this); - this.pendingFeatures_ = {}; - } - return false; -}; - - -/** - * Sort segments by distance, helper function - * @param {ol.SnapSegmentDataType} a The first segment data. - * @param {ol.SnapSegmentDataType} b The second segment data. - * @return {number} The difference in distance. - * @this {ol.interaction.Snap} - */ -ol.interaction.Snap.sortByDistance = function(a, b) { - return ol.coordinate.squaredDistanceToSegment( - this.pixelCoordinate_, a.segment) - - ol.coordinate.squaredDistanceToSegment( - this.pixelCoordinate_, b.segment); -}; - -goog.provide('ol.interaction.TranslateEventType'); - - -/** - * @enum {string} - */ -ol.interaction.TranslateEventType = { - /** - * Triggered upon feature translation start. - * @event ol.interaction.Translate.Event#translatestart - * @api - */ - TRANSLATESTART: 'translatestart', - /** - * Triggered upon feature translation. - * @event ol.interaction.Translate.Event#translating - * @api - */ - TRANSLATING: 'translating', - /** - * Triggered upon feature translation end. - * @event ol.interaction.Translate.Event#translateend - * @api - */ - TRANSLATEEND: 'translateend' -}; - -goog.provide('ol.interaction.Translate'); - -goog.require('ol'); -goog.require('ol.Collection'); -goog.require('ol.Object'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.functions'); -goog.require('ol.array'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.interaction.Property'); -goog.require('ol.interaction.TranslateEventType'); - - -/** - * @classdesc - * Interaction for translating (moving) features. - * - * @constructor - * @extends {ol.interaction.Pointer} - * @fires ol.interaction.Translate.Event - * @param {olx.interaction.TranslateOptions=} opt_options Options. - * @api - */ -ol.interaction.Translate = function(opt_options) { - ol.interaction.Pointer.call(this, { - handleDownEvent: ol.interaction.Translate.handleDownEvent_, - handleDragEvent: ol.interaction.Translate.handleDragEvent_, - handleMoveEvent: ol.interaction.Translate.handleMoveEvent_, - handleUpEvent: ol.interaction.Translate.handleUpEvent_ - }); - - var options = opt_options ? opt_options : {}; - - /** - * The last position we translated to. - * @type {ol.Coordinate} - * @private - */ - this.lastCoordinate_ = null; - - - /** - * @type {ol.Collection.<ol.Feature>} - * @private - */ - this.features_ = options.features !== undefined ? options.features : null; - - /** @type {function(ol.layer.Layer): boolean} */ - var layerFilter; - if (options.layers) { - if (typeof options.layers === 'function') { - layerFilter = options.layers; - } else { - var layers = options.layers; - layerFilter = function(layer) { - return ol.array.includes(layers, layer); - }; - } - } else { - layerFilter = ol.functions.TRUE; - } - - /** - * @private - * @type {function(ol.layer.Layer): boolean} - */ - this.layerFilter_ = layerFilter; - - /** - * @private - * @type {number} - */ - this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; - - /** - * @type {ol.Feature} - * @private - */ - this.lastFeature_ = null; - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.interaction.Property.ACTIVE), - this.handleActiveChanged_, this); - -}; -ol.inherits(ol.interaction.Translate, ol.interaction.Pointer); - - -/** - * @param {ol.MapBrowserPointerEvent} event Event. - * @return {boolean} Start drag sequence? - * @this {ol.interaction.Translate} - * @private - */ -ol.interaction.Translate.handleDownEvent_ = function(event) { - this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map); - if (!this.lastCoordinate_ && this.lastFeature_) { - this.lastCoordinate_ = event.coordinate; - ol.interaction.Translate.handleMoveEvent_.call(this, event); - - var features = this.features_ || new ol.Collection([this.lastFeature_]); - - this.dispatchEvent( - new ol.interaction.Translate.Event( - ol.interaction.TranslateEventType.TRANSLATESTART, features, - event.coordinate)); - return true; - } - return false; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} event Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.Translate} - * @private - */ -ol.interaction.Translate.handleUpEvent_ = function(event) { - if (this.lastCoordinate_) { - this.lastCoordinate_ = null; - ol.interaction.Translate.handleMoveEvent_.call(this, event); - - var features = this.features_ || new ol.Collection([this.lastFeature_]); - - this.dispatchEvent( - new ol.interaction.Translate.Event( - ol.interaction.TranslateEventType.TRANSLATEEND, features, - event.coordinate)); - return true; - } - return false; -}; - - -/** - * @param {ol.MapBrowserPointerEvent} event Event. - * @this {ol.interaction.Translate} - * @private - */ -ol.interaction.Translate.handleDragEvent_ = function(event) { - if (this.lastCoordinate_) { - var newCoordinate = event.coordinate; - var deltaX = newCoordinate[0] - this.lastCoordinate_[0]; - var deltaY = newCoordinate[1] - this.lastCoordinate_[1]; - - var features = this.features_ || new ol.Collection([this.lastFeature_]); - - features.forEach(function(feature) { - var geom = feature.getGeometry(); - geom.translate(deltaX, deltaY); - feature.setGeometry(geom); - }); - - this.lastCoordinate_ = newCoordinate; - this.dispatchEvent( - new ol.interaction.Translate.Event( - ol.interaction.TranslateEventType.TRANSLATING, features, - newCoordinate)); - } -}; - - -/** - * @param {ol.MapBrowserEvent} event Event. - * @this {ol.interaction.Translate} - * @private - */ -ol.interaction.Translate.handleMoveEvent_ = function(event) { - var elem = event.map.getViewport(); - - // Change the cursor to grab/grabbing if hovering any of the features managed - // by the interaction - if (this.featuresAtPixel_(event.pixel, event.map)) { - elem.classList.remove(this.lastCoordinate_ ? 'ol-grab' : 'ol-grabbing'); - elem.classList.add(this.lastCoordinate_ ? 'ol-grabbing' : 'ol-grab'); - } else { - elem.classList.remove('ol-grab', 'ol-grabbing'); - } -}; - - -/** - * Tests to see if the given coordinates intersects any of our selected - * features. - * @param {ol.Pixel} pixel Pixel coordinate to test for intersection. - * @param {ol.Map} map Map to test the intersection on. - * @return {ol.Feature} Returns the feature found at the specified pixel - * coordinates. - * @private - */ -ol.interaction.Translate.prototype.featuresAtPixel_ = function(pixel, map) { - return map.forEachFeatureAtPixel(pixel, - function(feature) { - if (!this.features_ || - ol.array.includes(this.features_.getArray(), feature)) { - return feature; - } - }.bind(this), { - layerFilter: this.layerFilter_, - hitTolerance: this.hitTolerance_ - }); -}; - - -/** - * Returns the Hit-detection tolerance. - * @returns {number} Hit tolerance in pixels. - * @api - */ -ol.interaction.Translate.prototype.getHitTolerance = function() { - return this.hitTolerance_; -}; - - -/** - * Hit-detection tolerance. Pixels inside the radius around the given position - * will be checked for features. This only works for the canvas renderer and - * not for WebGL. - * @param {number} hitTolerance Hit tolerance in pixels. - * @api - */ -ol.interaction.Translate.prototype.setHitTolerance = function(hitTolerance) { - this.hitTolerance_ = hitTolerance; -}; - - -/** - * @inheritDoc - */ -ol.interaction.Translate.prototype.setMap = function(map) { - var oldMap = this.getMap(); - ol.interaction.Pointer.prototype.setMap.call(this, map); - this.updateState_(oldMap); -}; - - -/** - * @private - */ -ol.interaction.Translate.prototype.handleActiveChanged_ = function() { - this.updateState_(null); -}; - - -/** - * @param {ol.Map} oldMap Old map. - * @private - */ -ol.interaction.Translate.prototype.updateState_ = function(oldMap) { - var map = this.getMap(); - var active = this.getActive(); - if ((!map || !active)) { - if (!map) { - map = oldMap; - } - - var elem = map.getViewport(); - elem.classList.remove('ol-grab', 'ol-grabbing'); - } -}; - - -/** - * @classdesc - * Events emitted by {@link ol.interaction.Translate} instances are instances of - * this type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.interaction.TranslateEvent} - * @param {ol.interaction.TranslateEventType} type Type. - * @param {ol.Collection.<ol.Feature>} features The features translated. - * @param {ol.Coordinate} coordinate The event coordinate. - */ -ol.interaction.Translate.Event = function(type, features, coordinate) { - - ol.events.Event.call(this, type); - - /** - * The features being translated. - * @type {ol.Collection.<ol.Feature>} - * @api - */ - this.features = features; - - /** - * The coordinate of the drag event. - * @const - * @type {ol.Coordinate} - * @api - */ - this.coordinate = coordinate; -}; -ol.inherits(ol.interaction.Translate.Event, ol.events.Event); - -goog.provide('ol.layer.Heatmap'); - -goog.require('ol.events'); -goog.require('ol'); -goog.require('ol.Object'); -goog.require('ol.dom'); -goog.require('ol.layer.Vector'); -goog.require('ol.math'); -goog.require('ol.obj'); -goog.require('ol.render.EventType'); -goog.require('ol.style.Icon'); -goog.require('ol.style.Style'); - - -/** - * @classdesc - * Layer for rendering vector data as a heatmap. - * Note that any property set in the options is set as a {@link ol.Object} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {ol.layer.Vector} - * @fires ol.render.Event - * @param {olx.layer.HeatmapOptions=} opt_options Options. - * @api - */ -ol.layer.Heatmap = function(opt_options) { - var options = opt_options ? opt_options : {}; - - var baseOptions = ol.obj.assign({}, options); - - delete baseOptions.gradient; - delete baseOptions.radius; - delete baseOptions.blur; - delete baseOptions.shadow; - delete baseOptions.weight; - ol.layer.Vector.call(this, /** @type {olx.layer.VectorOptions} */ (baseOptions)); - - /** - * @private - * @type {Uint8ClampedArray} - */ - this.gradient_ = null; - - /** - * @private - * @type {number} - */ - this.shadow_ = options.shadow !== undefined ? options.shadow : 250; - - /** - * @private - * @type {string|undefined} - */ - this.circleImage_ = undefined; - - /** - * @private - * @type {Array.<Array.<ol.style.Style>>} - */ - this.styleCache_ = null; - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.layer.Heatmap.Property_.GRADIENT), - this.handleGradientChanged_, this); - - this.setGradient(options.gradient ? - options.gradient : ol.layer.Heatmap.DEFAULT_GRADIENT); - - this.setBlur(options.blur !== undefined ? options.blur : 15); - - this.setRadius(options.radius !== undefined ? options.radius : 8); - - ol.events.listen(this, - ol.Object.getChangeEventType(ol.layer.Heatmap.Property_.BLUR), - this.handleStyleChanged_, this); - ol.events.listen(this, - ol.Object.getChangeEventType(ol.layer.Heatmap.Property_.RADIUS), - this.handleStyleChanged_, this); - - this.handleStyleChanged_(); - - var weight = options.weight ? options.weight : 'weight'; - var weightFunction; - if (typeof weight === 'string') { - weightFunction = function(feature) { - return feature.get(weight); - }; - } else { - weightFunction = weight; - } - - this.setStyle(function(feature, resolution) { - var weight = weightFunction(feature); - var opacity = weight !== undefined ? ol.math.clamp(weight, 0, 1) : 1; - // cast to 8 bits - var index = (255 * opacity) | 0; - var style = this.styleCache_[index]; - if (!style) { - style = [ - new ol.style.Style({ - image: new ol.style.Icon({ - opacity: opacity, - src: this.circleImage_ - }) - }) - ]; - this.styleCache_[index] = style; - } - return style; - }.bind(this)); - - // For performance reasons, don't sort the features before rendering. - // The render order is not relevant for a heatmap representation. - this.setRenderOrder(null); - - ol.events.listen(this, ol.render.EventType.RENDER, this.handleRender_, this); -}; -ol.inherits(ol.layer.Heatmap, ol.layer.Vector); - - -/** - * @const - * @type {Array.<string>} - */ -ol.layer.Heatmap.DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00']; - - -/** - * @param {Array.<string>} colors A list of colored. - * @return {Uint8ClampedArray} An array. - * @private - */ -ol.layer.Heatmap.createGradient_ = function(colors) { - var width = 1; - var height = 256; - var context = ol.dom.createCanvasContext2D(width, height); - - var gradient = context.createLinearGradient(0, 0, width, height); - var step = 1 / (colors.length - 1); - for (var i = 0, ii = colors.length; i < ii; ++i) { - gradient.addColorStop(i * step, colors[i]); - } - - context.fillStyle = gradient; - context.fillRect(0, 0, width, height); - - return context.getImageData(0, 0, width, height).data; -}; - - -/** - * @return {string} Data URL for a circle. - * @private - */ -ol.layer.Heatmap.prototype.createCircle_ = function() { - var radius = this.getRadius(); - var blur = this.getBlur(); - var halfSize = radius + blur + 1; - var size = 2 * halfSize; - var context = ol.dom.createCanvasContext2D(size, size); - context.shadowOffsetX = context.shadowOffsetY = this.shadow_; - context.shadowBlur = blur; - context.shadowColor = '#000'; - context.beginPath(); - var center = halfSize - this.shadow_; - context.arc(center, center, radius, 0, Math.PI * 2, true); - context.fill(); - return context.canvas.toDataURL(); -}; - - -/** - * Return the blur size in pixels. - * @return {number} Blur size in pixels. - * @api - * @observable - */ -ol.layer.Heatmap.prototype.getBlur = function() { - return /** @type {number} */ (this.get(ol.layer.Heatmap.Property_.BLUR)); -}; - - -/** - * Return the gradient colors as array of strings. - * @return {Array.<string>} Colors. - * @api - * @observable - */ -ol.layer.Heatmap.prototype.getGradient = function() { - return /** @type {Array.<string>} */ ( - this.get(ol.layer.Heatmap.Property_.GRADIENT)); -}; - - -/** - * Return the size of the radius in pixels. - * @return {number} Radius size in pixel. - * @api - * @observable - */ -ol.layer.Heatmap.prototype.getRadius = function() { - return /** @type {number} */ (this.get(ol.layer.Heatmap.Property_.RADIUS)); -}; - - -/** - * @private - */ -ol.layer.Heatmap.prototype.handleGradientChanged_ = function() { - this.gradient_ = ol.layer.Heatmap.createGradient_(this.getGradient()); -}; - - -/** - * @private - */ -ol.layer.Heatmap.prototype.handleStyleChanged_ = function() { - this.circleImage_ = this.createCircle_(); - this.styleCache_ = new Array(256); - this.changed(); -}; - - -/** - * @param {ol.render.Event} event Post compose event - * @private - */ -ol.layer.Heatmap.prototype.handleRender_ = function(event) { - var context = event.context; - var canvas = context.canvas; - var image = context.getImageData(0, 0, canvas.width, canvas.height); - var view8 = image.data; - var i, ii, alpha; - for (i = 0, ii = view8.length; i < ii; i += 4) { - alpha = view8[i + 3] * 4; - if (alpha) { - view8[i] = this.gradient_[alpha]; - view8[i + 1] = this.gradient_[alpha + 1]; - view8[i + 2] = this.gradient_[alpha + 2]; - } - } - context.putImageData(image, 0, 0); -}; - - -/** - * Set the blur size in pixels. - * @param {number} blur Blur size in pixels. - * @api - * @observable - */ -ol.layer.Heatmap.prototype.setBlur = function(blur) { - this.set(ol.layer.Heatmap.Property_.BLUR, blur); -}; - - -/** - * Set the gradient colors as array of strings. - * @param {Array.<string>} colors Gradient. - * @api - * @observable - */ -ol.layer.Heatmap.prototype.setGradient = function(colors) { - this.set(ol.layer.Heatmap.Property_.GRADIENT, colors); -}; - - -/** - * Set the size of the radius in pixels. - * @param {number} radius Radius size in pixel. - * @api - * @observable - */ -ol.layer.Heatmap.prototype.setRadius = function(radius) { - this.set(ol.layer.Heatmap.Property_.RADIUS, radius); -}; - - -/** - * @enum {string} - * @private - */ -ol.layer.Heatmap.Property_ = { - BLUR: 'blur', - GRADIENT: 'gradient', - RADIUS: 'radius' -}; - -goog.provide('ol.renderer.canvas.IntermediateCanvas'); - -goog.require('ol'); -goog.require('ol.coordinate'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.renderer.canvas.Layer'); -goog.require('ol.transform'); - - -/** - * @constructor - * @abstract - * @extends {ol.renderer.canvas.Layer} - * @param {ol.layer.Layer} layer Layer. - */ -ol.renderer.canvas.IntermediateCanvas = function(layer) { - - ol.renderer.canvas.Layer.call(this, layer); - - /** - * @protected - * @type {ol.Transform} - */ - this.coordinateToCanvasPixelTransform = ol.transform.create(); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.hitCanvasContext_ = null; - -}; -ol.inherits(ol.renderer.canvas.IntermediateCanvas, ol.renderer.canvas.Layer); - - -/** - * @inheritDoc - */ -ol.renderer.canvas.IntermediateCanvas.prototype.composeFrame = function(frameState, layerState, context) { - - this.preCompose(context, frameState); - - var image = this.getImage(); - if (image) { - - // clipped rendering if layer extent is set - var extent = layerState.extent; - var clipped = extent !== undefined && - !ol.extent.containsExtent(extent, frameState.extent) && - ol.extent.intersects(extent, frameState.extent); - if (clipped) { - this.clip(context, frameState, /** @type {ol.Extent} */ (extent)); - } - - var imageTransform = this.getImageTransform(); - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - var alpha = context.globalAlpha; - context.globalAlpha = layerState.opacity; - - // for performance reasons, context.setTransform is only used - // when the view is rotated. see http://jsperf.com/canvas-transform - var dx = imageTransform[4]; - var dy = imageTransform[5]; - var dw = image.width * imageTransform[0]; - var dh = image.height * imageTransform[3]; - context.drawImage(image, 0, 0, +image.width, +image.height, - Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); - context.globalAlpha = alpha; - - if (clipped) { - context.restore(); - } - } - - this.postCompose(context, frameState, layerState); -}; - - -/** - * @abstract - * @return {HTMLCanvasElement|HTMLVideoElement|Image} Canvas. - */ -ol.renderer.canvas.IntermediateCanvas.prototype.getImage = function() {}; - - -/** - * @abstract - * @return {!ol.Transform} Image transform. - */ -ol.renderer.canvas.IntermediateCanvas.prototype.getImageTransform = function() {}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.IntermediateCanvas.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - var layer = this.getLayer(); - var source = layer.getSource(); - var resolution = frameState.viewState.resolution; - var rotation = frameState.viewState.rotation; - var skippedFeatureUids = frameState.skippedFeatureUids; - return source.forEachFeatureAtCoordinate( - coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - return callback.call(thisArg, feature, layer); - }); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.IntermediateCanvas.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, callback, thisArg) { - if (!this.getImage()) { - return undefined; - } - - if (this.getLayer().getSource().forEachFeatureAtCoordinate !== ol.nullFunction) { - // for ImageVector sources use the original hit-detection logic, - // so that for example also transparent polygons are detected - return ol.renderer.canvas.Layer.prototype.forEachLayerAtCoordinate.apply(this, arguments); - } else { - var pixel = ol.transform.apply(this.coordinateToCanvasPixelTransform, coordinate.slice()); - ol.coordinate.scale(pixel, frameState.viewState.resolution / this.renderedResolution); - - if (!this.hitCanvasContext_) { - this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1); - } - - this.hitCanvasContext_.clearRect(0, 0, 1, 1); - this.hitCanvasContext_.drawImage(this.getImage(), pixel[0], pixel[1], 1, 1, 0, 0, 1, 1); - - var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; - if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); - } else { - return undefined; - } - } -}; - -goog.provide('ol.renderer.canvas.ImageLayer'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.extent'); -goog.require('ol.renderer.canvas.IntermediateCanvas'); -goog.require('ol.transform'); - - -/** - * @constructor - * @extends {ol.renderer.canvas.IntermediateCanvas} - * @param {ol.layer.Image} imageLayer Single image layer. - */ -ol.renderer.canvas.ImageLayer = function(imageLayer) { - - ol.renderer.canvas.IntermediateCanvas.call(this, imageLayer); - - /** - * @private - * @type {?ol.ImageBase} - */ - this.image_ = null; - - /** - * @private - * @type {ol.Transform} - */ - this.imageTransform_ = ol.transform.create(); - -}; -ol.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.IntermediateCanvas); - - -/** - * @inheritDoc - */ -ol.renderer.canvas.ImageLayer.prototype.getImage = function() { - return !this.image_ ? null : this.image_.getImage(); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.ImageLayer.prototype.getImageTransform = function() { - return this.imageTransform_; -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, layerState) { - - var pixelRatio = frameState.pixelRatio; - var size = frameState.size; - var viewState = frameState.viewState; - var viewCenter = viewState.center; - var viewResolution = viewState.resolution; - - var image; - var imageLayer = /** @type {ol.layer.Image} */ (this.getLayer()); - var imageSource = imageLayer.getSource(); - - var hints = frameState.viewHints; - - var renderedExtent = frameState.extent; - if (layerState.extent !== undefined) { - renderedExtent = ol.extent.getIntersection( - renderedExtent, layerState.extent); - } - - if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && - !ol.extent.isEmpty(renderedExtent)) { - var projection = viewState.projection; - if (!ol.ENABLE_RASTER_REPROJECTION) { - var sourceProjection = imageSource.getProjection(); - if (sourceProjection) { - projection = sourceProjection; - } - } - image = imageSource.getImage( - renderedExtent, viewResolution, pixelRatio, projection); - if (image) { - var loaded = this.loadImage(image); - if (loaded) { - this.image_ = image; - } - } - } - - if (this.image_) { - image = this.image_; - var imageExtent = image.getExtent(); - var imageResolution = image.getResolution(); - var imagePixelRatio = image.getPixelRatio(); - var scale = pixelRatio * imageResolution / - (viewResolution * imagePixelRatio); - var transform = ol.transform.compose(this.imageTransform_, - pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, - scale, scale, - 0, - imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, - imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); - ol.transform.compose(this.coordinateToCanvasPixelTransform, - pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], - pixelRatio / viewResolution, -pixelRatio / viewResolution, - 0, - -viewCenter[0], -viewCenter[1]); - - this.updateAttributions(frameState.attributions, image.getAttributions()); - this.updateLogos(frameState, imageSource); - this.renderedResolution = viewResolution * pixelRatio / imagePixelRatio; - } - - return !!this.image_; -}; - -goog.provide('ol.reproj'); - -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.math'); -goog.require('ol.proj'); - - -/** - * Calculates ideal resolution to use from the source in order to achieve - * pixel mapping as close as possible to 1:1 during reprojection. - * The resolution is calculated regardless of what resolutions - * are actually available in the dataset (TileGrid, Image, ...). - * - * @param {ol.proj.Projection} sourceProj Source projection. - * @param {ol.proj.Projection} targetProj Target projection. - * @param {ol.Coordinate} targetCenter Target center. - * @param {number} targetResolution Target resolution. - * @return {number} The best resolution to use. Can be +-Infinity, NaN or 0. - */ -ol.reproj.calculateSourceResolution = function(sourceProj, targetProj, - targetCenter, targetResolution) { - - var sourceCenter = ol.proj.transform(targetCenter, targetProj, sourceProj); - - // calculate the ideal resolution of the source data - var sourceResolution = - ol.proj.getPointResolution(targetProj, targetResolution, targetCenter); - - var targetMetersPerUnit = targetProj.getMetersPerUnit(); - if (targetMetersPerUnit !== undefined) { - sourceResolution *= targetMetersPerUnit; - } - var sourceMetersPerUnit = sourceProj.getMetersPerUnit(); - if (sourceMetersPerUnit !== undefined) { - sourceResolution /= sourceMetersPerUnit; - } - - // Based on the projection properties, the point resolution at the specified - // coordinates may be slightly different. We need to reverse-compensate this - // in order to achieve optimal results. - - var compensationFactor = - ol.proj.getPointResolution(sourceProj, sourceResolution, sourceCenter) / - sourceResolution; - - if (isFinite(compensationFactor) && compensationFactor > 0) { - sourceResolution /= compensationFactor; - } - - return sourceResolution; -}; - - -/** - * Enlarge the clipping triangle point by 1 pixel to ensure the edges overlap - * in order to mask gaps caused by antialiasing. - * - * @param {number} centroidX Centroid of the triangle (x coordinate in pixels). - * @param {number} centroidY Centroid of the triangle (y coordinate in pixels). - * @param {number} x X coordinate of the point (in pixels). - * @param {number} y Y coordinate of the point (in pixels). - * @return {ol.Coordinate} New point 1 px farther from the centroid. - * @private - */ -ol.reproj.enlargeClipPoint_ = function(centroidX, centroidY, x, y) { - var dX = x - centroidX, dY = y - centroidY; - var distance = Math.sqrt(dX * dX + dY * dY); - return [Math.round(x + dX / distance), Math.round(y + dY / distance)]; -}; - - -/** - * Renders the source data into new canvas based on the triangulation. - * - * @param {number} width Width of the canvas. - * @param {number} height Height of the canvas. - * @param {number} pixelRatio Pixel ratio. - * @param {number} sourceResolution Source resolution. - * @param {ol.Extent} sourceExtent Extent of the data source. - * @param {number} targetResolution Target resolution. - * @param {ol.Extent} targetExtent Target extent. - * @param {ol.reproj.Triangulation} triangulation Calculated triangulation. - * @param {Array.<{extent: ol.Extent, - * image: (HTMLCanvasElement|Image|HTMLVideoElement)}>} sources - * Array of sources. - * @param {number} gutter Gutter of the sources. - * @param {boolean=} opt_renderEdges Render reprojection edges. - * @return {HTMLCanvasElement} Canvas with reprojected data. - */ -ol.reproj.render = function(width, height, pixelRatio, - sourceResolution, sourceExtent, targetResolution, targetExtent, - triangulation, sources, gutter, opt_renderEdges) { - - var context = ol.dom.createCanvasContext2D(Math.round(pixelRatio * width), - Math.round(pixelRatio * height)); - - if (sources.length === 0) { - return context.canvas; - } - - context.scale(pixelRatio, pixelRatio); - - var sourceDataExtent = ol.extent.createEmpty(); - sources.forEach(function(src, i, arr) { - ol.extent.extend(sourceDataExtent, src.extent); - }); - - var canvasWidthInUnits = ol.extent.getWidth(sourceDataExtent); - var canvasHeightInUnits = ol.extent.getHeight(sourceDataExtent); - var stitchContext = ol.dom.createCanvasContext2D( - Math.round(pixelRatio * canvasWidthInUnits / sourceResolution), - Math.round(pixelRatio * canvasHeightInUnits / sourceResolution)); - - var stitchScale = pixelRatio / sourceResolution; - - sources.forEach(function(src, i, arr) { - var xPos = src.extent[0] - sourceDataExtent[0]; - var yPos = -(src.extent[3] - sourceDataExtent[3]); - var srcWidth = ol.extent.getWidth(src.extent); - var srcHeight = ol.extent.getHeight(src.extent); - - stitchContext.drawImage( - src.image, - gutter, gutter, - src.image.width - 2 * gutter, src.image.height - 2 * gutter, - xPos * stitchScale, yPos * stitchScale, - srcWidth * stitchScale, srcHeight * stitchScale); - }); - - var targetTopLeft = ol.extent.getTopLeft(targetExtent); - - triangulation.getTriangles().forEach(function(triangle, i, arr) { - /* Calculate affine transform (src -> dst) - * Resulting matrix can be used to transform coordinate - * from `sourceProjection` to destination pixels. - * - * To optimize number of context calls and increase numerical stability, - * we also do the following operations: - * trans(-topLeftExtentCorner), scale(1 / targetResolution), scale(1, -1) - * here before solving the linear system so [ui, vi] are pixel coordinates. - * - * Src points: xi, yi - * Dst points: ui, vi - * Affine coefficients: aij - * - * | x0 y0 1 0 0 0 | |a00| |u0| - * | x1 y1 1 0 0 0 | |a01| |u1| - * | x2 y2 1 0 0 0 | x |a02| = |u2| - * | 0 0 0 x0 y0 1 | |a10| |v0| - * | 0 0 0 x1 y1 1 | |a11| |v1| - * | 0 0 0 x2 y2 1 | |a12| |v2| - */ - var source = triangle.source, target = triangle.target; - var x0 = source[0][0], y0 = source[0][1], - x1 = source[1][0], y1 = source[1][1], - x2 = source[2][0], y2 = source[2][1]; - var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution, - v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution; - var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution, - v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution; - var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution, - v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution; - - // Shift all the source points to improve numerical stability - // of all the subsequent calculations. The [x0, y0] is used here. - // This is also used to simplify the linear system. - var sourceNumericalShiftX = x0, sourceNumericalShiftY = y0; - x0 = 0; - y0 = 0; - x1 -= sourceNumericalShiftX; - y1 -= sourceNumericalShiftY; - x2 -= sourceNumericalShiftX; - y2 -= sourceNumericalShiftY; - - var augmentedMatrix = [ - [x1, y1, 0, 0, u1 - u0], - [x2, y2, 0, 0, u2 - u0], - [0, 0, x1, y1, v1 - v0], - [0, 0, x2, y2, v2 - v0] - ]; - var affineCoefs = ol.math.solveLinearSystem(augmentedMatrix); - if (!affineCoefs) { - return; - } - - context.save(); - context.beginPath(); - var centroidX = (u0 + u1 + u2) / 3, centroidY = (v0 + v1 + v2) / 3; - var p0 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u0, v0); - var p1 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u1, v1); - var p2 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u2, v2); - - context.moveTo(p1[0], p1[1]); - context.lineTo(p0[0], p0[1]); - context.lineTo(p2[0], p2[1]); - context.clip(); - - context.transform( - affineCoefs[0], affineCoefs[2], affineCoefs[1], affineCoefs[3], u0, v0); - - context.translate(sourceDataExtent[0] - sourceNumericalShiftX, - sourceDataExtent[3] - sourceNumericalShiftY); - - context.scale(sourceResolution / pixelRatio, - -sourceResolution / pixelRatio); - - context.drawImage(stitchContext.canvas, 0, 0); - context.restore(); - }); - - if (opt_renderEdges) { - context.save(); - - context.strokeStyle = 'black'; - context.lineWidth = 1; - - triangulation.getTriangles().forEach(function(triangle, i, arr) { - var target = triangle.target; - var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution, - v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution; - var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution, - v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution; - var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution, - v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution; - - context.beginPath(); - context.moveTo(u1, v1); - context.lineTo(u0, v0); - context.lineTo(u2, v2); - context.closePath(); - context.stroke(); - }); - - context.restore(); - } - return context.canvas; -}; - -goog.provide('ol.reproj.Triangulation'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.math'); -goog.require('ol.proj'); - - -/** - * @classdesc - * Class containing triangulation of the given target extent. - * Used for determining source data and the reprojection itself. - * - * @param {ol.proj.Projection} sourceProj Source projection. - * @param {ol.proj.Projection} targetProj Target projection. - * @param {ol.Extent} targetExtent Target extent to triangulate. - * @param {ol.Extent} maxSourceExtent Maximal source extent that can be used. - * @param {number} errorThreshold Acceptable error (in source units). - * @constructor - */ -ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent, - maxSourceExtent, errorThreshold) { - - /** - * @type {ol.proj.Projection} - * @private - */ - this.sourceProj_ = sourceProj; - - /** - * @type {ol.proj.Projection} - * @private - */ - this.targetProj_ = targetProj; - - /** @type {!Object.<string, ol.Coordinate>} */ - var transformInvCache = {}; - var transformInv = ol.proj.getTransform(this.targetProj_, this.sourceProj_); - - /** - * @param {ol.Coordinate} c A coordinate. - * @return {ol.Coordinate} Transformed coordinate. - * @private - */ - this.transformInv_ = function(c) { - var key = c[0] + '/' + c[1]; - if (!transformInvCache[key]) { - transformInvCache[key] = transformInv(c); - } - return transformInvCache[key]; - }; - - /** - * @type {ol.Extent} - * @private - */ - this.maxSourceExtent_ = maxSourceExtent; - - /** - * @type {number} - * @private - */ - this.errorThresholdSquared_ = errorThreshold * errorThreshold; - - /** - * @type {Array.<ol.ReprojTriangle>} - * @private - */ - this.triangles_ = []; - - /** - * Indicates that the triangulation crosses edge of the source projection. - * @type {boolean} - * @private - */ - this.wrapsXInSource_ = false; - - /** - * @type {boolean} - * @private - */ - this.canWrapXInSource_ = this.sourceProj_.canWrapX() && - !!maxSourceExtent && - !!this.sourceProj_.getExtent() && - (ol.extent.getWidth(maxSourceExtent) == - ol.extent.getWidth(this.sourceProj_.getExtent())); - - /** - * @type {?number} - * @private - */ - this.sourceWorldWidth_ = this.sourceProj_.getExtent() ? - ol.extent.getWidth(this.sourceProj_.getExtent()) : null; - - /** - * @type {?number} - * @private - */ - this.targetWorldWidth_ = this.targetProj_.getExtent() ? - ol.extent.getWidth(this.targetProj_.getExtent()) : null; - - var destinationTopLeft = ol.extent.getTopLeft(targetExtent); - var destinationTopRight = ol.extent.getTopRight(targetExtent); - var destinationBottomRight = ol.extent.getBottomRight(targetExtent); - var destinationBottomLeft = ol.extent.getBottomLeft(targetExtent); - var sourceTopLeft = this.transformInv_(destinationTopLeft); - var sourceTopRight = this.transformInv_(destinationTopRight); - var sourceBottomRight = this.transformInv_(destinationBottomRight); - var sourceBottomLeft = this.transformInv_(destinationBottomLeft); - - this.addQuad_( - destinationTopLeft, destinationTopRight, - destinationBottomRight, destinationBottomLeft, - sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft, - ol.RASTER_REPROJECTION_MAX_SUBDIVISION); - - if (this.wrapsXInSource_) { - var leftBound = Infinity; - this.triangles_.forEach(function(triangle, i, arr) { - leftBound = Math.min(leftBound, - triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]); - }); - - // Shift triangles to be as close to `leftBound` as possible - // (if the distance is more than `worldWidth / 2` it can be closer. - this.triangles_.forEach(function(triangle) { - if (Math.max(triangle.source[0][0], triangle.source[1][0], - triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) { - var newTriangle = [[triangle.source[0][0], triangle.source[0][1]], - [triangle.source[1][0], triangle.source[1][1]], - [triangle.source[2][0], triangle.source[2][1]]]; - if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) { - newTriangle[0][0] -= this.sourceWorldWidth_; - } - if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) { - newTriangle[1][0] -= this.sourceWorldWidth_; - } - if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) { - newTriangle[2][0] -= this.sourceWorldWidth_; - } - - // Rarely (if the extent contains both the dateline and prime meridian) - // the shift can in turn break some triangles. - // Detect this here and don't shift in such cases. - var minX = Math.min( - newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); - var maxX = Math.max( - newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); - if ((maxX - minX) < this.sourceWorldWidth_ / 2) { - triangle.source = newTriangle; - } - } - }, this); - } - - transformInvCache = {}; -}; - - -/** - * Adds triangle to the triangulation. - * @param {ol.Coordinate} a The target a coordinate. - * @param {ol.Coordinate} b The target b coordinate. - * @param {ol.Coordinate} c The target c coordinate. - * @param {ol.Coordinate} aSrc The source a coordinate. - * @param {ol.Coordinate} bSrc The source b coordinate. - * @param {ol.Coordinate} cSrc The source c coordinate. - * @private - */ -ol.reproj.Triangulation.prototype.addTriangle_ = function(a, b, c, - aSrc, bSrc, cSrc) { - this.triangles_.push({ - source: [aSrc, bSrc, cSrc], - target: [a, b, c] - }); -}; - - -/** - * Adds quad (points in clock-wise order) to the triangulation - * (and reprojects the vertices) if valid. - * Performs quad subdivision if needed to increase precision. - * - * @param {ol.Coordinate} a The target a coordinate. - * @param {ol.Coordinate} b The target b coordinate. - * @param {ol.Coordinate} c The target c coordinate. - * @param {ol.Coordinate} d The target d coordinate. - * @param {ol.Coordinate} aSrc The source a coordinate. - * @param {ol.Coordinate} bSrc The source b coordinate. - * @param {ol.Coordinate} cSrc The source c coordinate. - * @param {ol.Coordinate} dSrc The source d coordinate. - * @param {number} maxSubdivision Maximal allowed subdivision of the quad. - * @private - */ -ol.reproj.Triangulation.prototype.addQuad_ = function(a, b, c, d, - aSrc, bSrc, cSrc, dSrc, maxSubdivision) { - - var sourceQuadExtent = ol.extent.boundingExtent([aSrc, bSrc, cSrc, dSrc]); - var sourceCoverageX = this.sourceWorldWidth_ ? - ol.extent.getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null; - var sourceWorldWidth = /** @type {number} */ (this.sourceWorldWidth_); - - // when the quad is wrapped in the source projection - // it covers most of the projection extent, but not fully - var wrapsX = this.sourceProj_.canWrapX() && - sourceCoverageX > 0.5 && sourceCoverageX < 1; - - var needsSubdivision = false; - - if (maxSubdivision > 0) { - if (this.targetProj_.isGlobal() && this.targetWorldWidth_) { - var targetQuadExtent = ol.extent.boundingExtent([a, b, c, d]); - var targetCoverageX = - ol.extent.getWidth(targetQuadExtent) / this.targetWorldWidth_; - needsSubdivision |= - targetCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH; - } - if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) { - needsSubdivision |= - sourceCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH; - } - } - - if (!needsSubdivision && this.maxSourceExtent_) { - if (!ol.extent.intersects(sourceQuadExtent, this.maxSourceExtent_)) { - // whole quad outside source projection extent -> ignore - return; - } - } - - if (!needsSubdivision) { - if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) || - !isFinite(bSrc[0]) || !isFinite(bSrc[1]) || - !isFinite(cSrc[0]) || !isFinite(cSrc[1]) || - !isFinite(dSrc[0]) || !isFinite(dSrc[1])) { - if (maxSubdivision > 0) { - needsSubdivision = true; - } else { - return; - } - } - } - - if (maxSubdivision > 0) { - if (!needsSubdivision) { - var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2]; - var centerSrc = this.transformInv_(center); - - var dx; - if (wrapsX) { - var centerSrcEstimX = - (ol.math.modulo(aSrc[0], sourceWorldWidth) + - ol.math.modulo(cSrc[0], sourceWorldWidth)) / 2; - dx = centerSrcEstimX - - ol.math.modulo(centerSrc[0], sourceWorldWidth); - } else { - dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0]; - } - var dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1]; - var centerSrcErrorSquared = dx * dx + dy * dy; - needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_; - } - if (needsSubdivision) { - if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) { - // split horizontally (top & bottom) - var bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2]; - var bcSrc = this.transformInv_(bc); - var da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2]; - var daSrc = this.transformInv_(da); - - this.addQuad_( - a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1); - this.addQuad_( - da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1); - } else { - // split vertically (left & right) - var ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]; - var abSrc = this.transformInv_(ab); - var cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2]; - var cdSrc = this.transformInv_(cd); - - this.addQuad_( - a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1); - this.addQuad_( - ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1); - } - return; - } - } - - if (wrapsX) { - if (!this.canWrapXInSource_) { - return; - } - this.wrapsXInSource_ = true; - } - - this.addTriangle_(a, c, d, aSrc, cSrc, dSrc); - this.addTriangle_(a, b, c, aSrc, bSrc, cSrc); -}; - - -/** - * Calculates extent of the 'source' coordinates from all the triangles. - * - * @return {ol.Extent} Calculated extent. - */ -ol.reproj.Triangulation.prototype.calculateSourceExtent = function() { - var extent = ol.extent.createEmpty(); - - this.triangles_.forEach(function(triangle, i, arr) { - var src = triangle.source; - ol.extent.extendCoordinate(extent, src[0]); - ol.extent.extendCoordinate(extent, src[1]); - ol.extent.extendCoordinate(extent, src[2]); - }); - - return extent; -}; - - -/** - * @return {Array.<ol.ReprojTriangle>} Array of the calculated triangles. - */ -ol.reproj.Triangulation.prototype.getTriangles = function() { - return this.triangles_; -}; - -goog.provide('ol.reproj.Image'); - -goog.require('ol'); -goog.require('ol.ImageBase'); -goog.require('ol.ImageState'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.reproj'); -goog.require('ol.reproj.Triangulation'); - - -/** - * @classdesc - * Class encapsulating single reprojected image. - * See {@link ol.source.Image}. - * - * @constructor - * @extends {ol.ImageBase} - * @param {ol.proj.Projection} sourceProj Source projection (of the data). - * @param {ol.proj.Projection} targetProj Target projection. - * @param {ol.Extent} targetExtent Target extent. - * @param {number} targetResolution Target resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.ReprojImageFunctionType} getImageFunction - * Function returning source images (extent, resolution, pixelRatio). - */ -ol.reproj.Image = function(sourceProj, targetProj, - targetExtent, targetResolution, pixelRatio, getImageFunction) { - - /** - * @private - * @type {ol.proj.Projection} - */ - this.targetProj_ = targetProj; - - /** - * @private - * @type {ol.Extent} - */ - this.maxSourceExtent_ = sourceProj.getExtent(); - var maxTargetExtent = targetProj.getExtent(); - - var limitedTargetExtent = maxTargetExtent ? - ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent; - - var targetCenter = ol.extent.getCenter(limitedTargetExtent); - var sourceResolution = ol.reproj.calculateSourceResolution( - sourceProj, targetProj, targetCenter, targetResolution); - - var errorThresholdInPixels = ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD; - - /** - * @private - * @type {!ol.reproj.Triangulation} - */ - this.triangulation_ = new ol.reproj.Triangulation( - sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_, - sourceResolution * errorThresholdInPixels); - - /** - * @private - * @type {number} - */ - this.targetResolution_ = targetResolution; - - /** - * @private - * @type {ol.Extent} - */ - this.targetExtent_ = targetExtent; - - var sourceExtent = this.triangulation_.calculateSourceExtent(); - - /** - * @private - * @type {ol.ImageBase} - */ - this.sourceImage_ = - getImageFunction(sourceExtent, sourceResolution, pixelRatio); - - /** - * @private - * @type {number} - */ - this.sourcePixelRatio_ = - this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; - - /** - * @private - * @type {?ol.EventsKey} - */ - this.sourceListenerKey_ = null; - - - var state = ol.ImageState.LOADED; - var attributions = []; - - if (this.sourceImage_) { - state = ol.ImageState.IDLE; - attributions = this.sourceImage_.getAttributions(); - } - - ol.ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, - state, attributions); -}; -ol.inherits(ol.reproj.Image, ol.ImageBase); - - -/** - * @inheritDoc - */ -ol.reproj.Image.prototype.disposeInternal = function() { - if (this.state == ol.ImageState.LOADING) { - this.unlistenSource_(); - } - ol.ImageBase.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -ol.reproj.Image.prototype.getImage = function(opt_context) { - return this.canvas_; -}; - - -/** - * @return {ol.proj.Projection} Projection. - */ -ol.reproj.Image.prototype.getProjection = function() { - return this.targetProj_; -}; - - -/** - * @private - */ -ol.reproj.Image.prototype.reproject_ = function() { - var sourceState = this.sourceImage_.getState(); - if (sourceState == ol.ImageState.LOADED) { - var width = ol.extent.getWidth(this.targetExtent_) / this.targetResolution_; - var height = - ol.extent.getHeight(this.targetExtent_) / this.targetResolution_; - - this.canvas_ = ol.reproj.render(width, height, this.sourcePixelRatio_, - this.sourceImage_.getResolution(), this.maxSourceExtent_, - this.targetResolution_, this.targetExtent_, this.triangulation_, [{ - extent: this.sourceImage_.getExtent(), - image: this.sourceImage_.getImage() - }], 0); - } - this.state = sourceState; - this.changed(); -}; - - -/** - * @inheritDoc - */ -ol.reproj.Image.prototype.load = function() { - if (this.state == ol.ImageState.IDLE) { - this.state = ol.ImageState.LOADING; - this.changed(); - - var sourceState = this.sourceImage_.getState(); - if (sourceState == ol.ImageState.LOADED || - sourceState == ol.ImageState.ERROR) { - this.reproject_(); - } else { - this.sourceListenerKey_ = ol.events.listen(this.sourceImage_, - ol.events.EventType.CHANGE, function(e) { - var sourceState = this.sourceImage_.getState(); - if (sourceState == ol.ImageState.LOADED || - sourceState == ol.ImageState.ERROR) { - this.unlistenSource_(); - this.reproject_(); - } - }, this); - this.sourceImage_.load(); - } - } -}; - - -/** - * @private - */ -ol.reproj.Image.prototype.unlistenSource_ = function() { - ol.events.unlistenByKey(/** @type {!ol.EventsKey} */ (this.sourceListenerKey_)); - this.sourceListenerKey_ = null; -}; - -goog.provide('ol.source.Image'); - -goog.require('ol'); -goog.require('ol.ImageState'); -goog.require('ol.array'); -goog.require('ol.events.Event'); -goog.require('ol.extent'); -goog.require('ol.proj'); -goog.require('ol.reproj.Image'); -goog.require('ol.source.Source'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for sources providing a single image. - * - * @constructor - * @abstract - * @extends {ol.source.Source} - * @param {ol.SourceImageOptions} options Single image source options. - * @api - */ -ol.source.Image = function(options) { - ol.source.Source.call(this, { - attributions: options.attributions, - extent: options.extent, - logo: options.logo, - projection: options.projection, - state: options.state - }); - - /** - * @private - * @type {Array.<number>} - */ - this.resolutions_ = options.resolutions !== undefined ? - options.resolutions : null; - - - /** - * @private - * @type {ol.reproj.Image} - */ - this.reprojectedImage_ = null; - - - /** - * @private - * @type {number} - */ - this.reprojectedRevision_ = 0; -}; -ol.inherits(ol.source.Image, ol.source.Source); - - -/** - * @return {Array.<number>} Resolutions. - * @override - */ -ol.source.Image.prototype.getResolutions = function() { - return this.resolutions_; -}; - - -/** - * @protected - * @param {number} resolution Resolution. - * @return {number} Resolution. - */ -ol.source.Image.prototype.findNearestResolution = function(resolution) { - if (this.resolutions_) { - var idx = ol.array.linearFindNearest(this.resolutions_, resolution, 0); - resolution = this.resolutions_[idx]; - } - return resolution; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {ol.ImageBase} Single image. - */ -ol.source.Image.prototype.getImage = function(extent, resolution, pixelRatio, projection) { - var sourceProjection = this.getProjection(); - if (!ol.ENABLE_RASTER_REPROJECTION || - !sourceProjection || - !projection || - ol.proj.equivalent(sourceProjection, projection)) { - if (sourceProjection) { - projection = sourceProjection; - } - return this.getImageInternal(extent, resolution, pixelRatio, projection); - } else { - if (this.reprojectedImage_) { - if (this.reprojectedRevision_ == this.getRevision() && - ol.proj.equivalent( - this.reprojectedImage_.getProjection(), projection) && - this.reprojectedImage_.getResolution() == resolution && - this.reprojectedImage_.getPixelRatio() == pixelRatio && - ol.extent.equals(this.reprojectedImage_.getExtent(), extent)) { - return this.reprojectedImage_; - } - this.reprojectedImage_.dispose(); - this.reprojectedImage_ = null; - } - - this.reprojectedImage_ = new ol.reproj.Image( - sourceProjection, projection, extent, resolution, pixelRatio, - function(extent, resolution, pixelRatio) { - return this.getImageInternal(extent, resolution, - pixelRatio, sourceProjection); - }.bind(this)); - this.reprojectedRevision_ = this.getRevision(); - - return this.reprojectedImage_; - } -}; - - -/** - * @abstract - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {ol.ImageBase} Single image. - * @protected - */ -ol.source.Image.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {}; - - -/** - * Handle image change events. - * @param {ol.events.Event} event Event. - * @protected - */ -ol.source.Image.prototype.handleImageChange = function(event) { - var image = /** @type {ol.Image} */ (event.target); - switch (image.getState()) { - case ol.ImageState.LOADING: - this.dispatchEvent( - new ol.source.Image.Event(ol.source.Image.EventType_.IMAGELOADSTART, - image)); - break; - case ol.ImageState.LOADED: - this.dispatchEvent( - new ol.source.Image.Event(ol.source.Image.EventType_.IMAGELOADEND, - image)); - break; - case ol.ImageState.ERROR: - this.dispatchEvent( - new ol.source.Image.Event(ol.source.Image.EventType_.IMAGELOADERROR, - image)); - break; - default: - // pass - } -}; - - -/** - * Default image load function for image sources that use ol.Image image - * instances. - * @param {ol.Image} image Image. - * @param {string} src Source. - */ -ol.source.Image.defaultImageLoadFunction = function(image, src) { - image.getImage().src = src; -}; - - -/** - * @classdesc - * Events emitted by {@link ol.source.Image} instances are instances of this - * type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.source.ImageEvent} - * @param {string} type Type. - * @param {ol.Image} image The image. - */ -ol.source.Image.Event = function(type, image) { - - ol.events.Event.call(this, type); - - /** - * The image related to the event. - * @type {ol.Image} - * @api - */ - this.image = image; - -}; -ol.inherits(ol.source.Image.Event, ol.events.Event); - - -/** - * @enum {string} - * @private - */ -ol.source.Image.EventType_ = { - - /** - * Triggered when an image starts loading. - * @event ol.source.Image.Event#imageloadstart - * @api - */ - IMAGELOADSTART: 'imageloadstart', - - /** - * Triggered when an image finishes loading. - * @event ol.source.Image.Event#imageloadend - * @api - */ - IMAGELOADEND: 'imageloadend', - - /** - * Triggered if image loading results in an error. - * @event ol.source.Image.Event#imageloaderror - * @api - */ - IMAGELOADERROR: 'imageloaderror' - -}; - -goog.provide('ol.source.ImageCanvas'); - -goog.require('ol'); -goog.require('ol.ImageCanvas'); -goog.require('ol.extent'); -goog.require('ol.source.Image'); - - -/** - * @classdesc - * Base class for image sources where a canvas element is the image. - * - * @constructor - * @extends {ol.source.Image} - * @param {olx.source.ImageCanvasOptions} options Constructor options. - * @api - */ -ol.source.ImageCanvas = function(options) { - - ol.source.Image.call(this, { - attributions: options.attributions, - logo: options.logo, - projection: options.projection, - resolutions: options.resolutions, - state: options.state - }); - - /** - * @private - * @type {ol.CanvasFunctionType} - */ - this.canvasFunction_ = options.canvasFunction; - - /** - * @private - * @type {ol.ImageCanvas} - */ - this.canvas_ = null; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? - options.ratio : 1.5; - -}; -ol.inherits(ol.source.ImageCanvas, ol.source.Image); - - -/** - * @inheritDoc - */ -ol.source.ImageCanvas.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - resolution = this.findNearestResolution(resolution); - - var canvas = this.canvas_; - if (canvas && - this.renderedRevision_ == this.getRevision() && - canvas.getResolution() == resolution && - canvas.getPixelRatio() == pixelRatio && - ol.extent.containsExtent(canvas.getExtent(), extent)) { - return canvas; - } - - extent = extent.slice(); - ol.extent.scaleFromCenter(extent, this.ratio_); - var width = ol.extent.getWidth(extent) / resolution; - var height = ol.extent.getHeight(extent) / resolution; - var size = [width * pixelRatio, height * pixelRatio]; - - var canvasElement = this.canvasFunction_( - extent, resolution, pixelRatio, size, projection); - if (canvasElement) { - canvas = new ol.ImageCanvas(extent, resolution, pixelRatio, - this.getAttributions(), canvasElement); - } - this.canvas_ = canvas; - this.renderedRevision_ = this.getRevision(); - - return canvas; -}; - -goog.provide('ol.source.ImageVector'); - -goog.require('ol'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.render.canvas.ReplayGroup'); -goog.require('ol.renderer.vector'); -goog.require('ol.source.ImageCanvas'); -goog.require('ol.style.Style'); -goog.require('ol.transform'); - - -/** - * @classdesc - * An image source whose images are canvas elements into which vector features - * read from a vector source (`ol.source.Vector`) are drawn. An - * `ol.source.ImageVector` object is to be used as the `source` of an image - * layer (`ol.layer.Image`). Image layers are rotated, scaled, and translated, - * as opposed to being re-rendered, during animations and interactions. So, like - * any other image layer, an image layer configured with an - * `ol.source.ImageVector` will exhibit this behaviour. This is in contrast to a - * vector layer, where vector features are re-drawn during animations and - * interactions. - * - * @constructor - * @extends {ol.source.ImageCanvas} - * @param {olx.source.ImageVectorOptions} options Options. - * @api - */ -ol.source.ImageVector = function(options) { - - /** - * @private - * @type {ol.source.Vector} - */ - this.source_ = options.source; - - /** - * @private - * @type {ol.Transform} - */ - this.transform_ = ol.transform.create(); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.canvasContext_ = ol.dom.createCanvasContext2D(); - - /** - * @private - * @type {ol.Size} - */ - this.canvasSize_ = [0, 0]; - - /** - * @private - * @type {number} - */ - this.renderBuffer_ = options.renderBuffer == undefined ? 100 : options.renderBuffer; - - /** - * @private - * @type {ol.render.canvas.ReplayGroup} - */ - this.replayGroup_ = null; - - ol.source.ImageCanvas.call(this, { - attributions: options.attributions, - canvasFunction: this.canvasFunctionInternal_.bind(this), - logo: options.logo, - projection: options.projection, - ratio: options.ratio, - resolutions: options.resolutions, - state: this.source_.getState() - }); - - /** - * User provided style. - * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} - * @private - */ - this.style_ = null; - - /** - * Style function for use within the library. - * @type {ol.StyleFunction|undefined} - * @private - */ - this.styleFunction_ = undefined; - - this.setStyle(options.style); - - ol.events.listen(this.source_, ol.events.EventType.CHANGE, - this.handleSourceChange_, this); - -}; -ol.inherits(ol.source.ImageVector, ol.source.ImageCanvas); - - -/** - * @param {ol.Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Size} size Size. - * @param {ol.proj.Projection} projection Projection; - * @return {HTMLCanvasElement} Canvas element. - * @private - */ -ol.source.ImageVector.prototype.canvasFunctionInternal_ = function(extent, resolution, pixelRatio, size, projection) { - - var replayGroup = new ol.render.canvas.ReplayGroup( - ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, - resolution, this.source_.getOverlaps(), this.renderBuffer_); - - this.source_.loadFeatures(extent, resolution, projection); - - var loading = false; - this.source_.forEachFeatureInExtent(extent, - /** - * @param {ol.Feature} feature Feature. - */ - function(feature) { - loading = loading || - this.renderFeature_(feature, resolution, pixelRatio, replayGroup); - }, this); - replayGroup.finish(); - - if (loading) { - return null; - } - - if (this.canvasSize_[0] != size[0] || this.canvasSize_[1] != size[1]) { - this.canvasContext_.canvas.width = size[0]; - this.canvasContext_.canvas.height = size[1]; - this.canvasSize_[0] = size[0]; - this.canvasSize_[1] = size[1]; - } else { - this.canvasContext_.clearRect(0, 0, size[0], size[1]); - } - - var transform = this.getTransform_(ol.extent.getCenter(extent), - resolution, pixelRatio, size); - replayGroup.replay(this.canvasContext_, pixelRatio, transform, 0, {}); - - this.replayGroup_ = replayGroup; - - return this.canvasContext_.canvas; -}; - - -/** - * @inheritDoc - */ -ol.source.ImageVector.prototype.forEachFeatureAtCoordinate = function( - coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, callback) { - if (!this.replayGroup_) { - return undefined; - } else { - /** @type {Object.<string, boolean>} */ - var features = {}; - return this.replayGroup_.forEachFeatureAtCoordinate( - coordinate, resolution, 0, hitTolerance, skippedFeatureUids, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - var key = ol.getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback(feature); - } - }); - } -}; - - -/** - * Get a reference to the wrapped source. - * @return {ol.source.Vector} Source. - * @api - */ -ol.source.ImageVector.prototype.getSource = function() { - return this.source_; -}; - - -/** - * Get the style for features. This returns whatever was passed to the `style` - * option at construction or to the `setStyle` method. - * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} - * Layer style. - * @api - */ -ol.source.ImageVector.prototype.getStyle = function() { - return this.style_; -}; - - -/** - * Get the style function. - * @return {ol.StyleFunction|undefined} Layer style function. - * @api - */ -ol.source.ImageVector.prototype.getStyleFunction = function() { - return this.styleFunction_; -}; - - -/** - * @param {ol.Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Size} size Size. - * @return {!ol.Transform} Transform. - * @private - */ -ol.source.ImageVector.prototype.getTransform_ = function(center, resolution, pixelRatio, size) { - var dx1 = size[0] / 2; - var dy1 = size[1] / 2; - var sx = pixelRatio / resolution; - var sy = -sx; - var dx2 = -center[0]; - var dy2 = -center[1]; - - return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, 0, dx2, dy2); -}; - - -/** - * Handle changes in image style state. - * @param {ol.events.Event} event Image style change event. - * @private - */ -ol.source.ImageVector.prototype.handleImageChange_ = function(event) { - this.changed(); -}; - - -/** - * @private - */ -ol.source.ImageVector.prototype.handleSourceChange_ = function() { - // setState will trigger a CHANGE event, so we always rely - // change events by calling setState. - this.setState(this.source_.getState()); -}; - - -/** - * @param {ol.Feature} feature Feature. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - * @private - */ -ol.source.ImageVector.prototype.renderFeature_ = function(feature, resolution, pixelRatio, replayGroup) { - var styles; - var styleFunction = feature.getStyleFunction(); - if (styleFunction) { - styles = styleFunction.call(feature, resolution); - } else if (this.styleFunction_) { - styles = this.styleFunction_(feature, resolution); - } - if (!styles) { - return false; - } - var i, ii, loading = false; - if (!Array.isArray(styles)) { - styles = [styles]; - } - for (i = 0, ii = styles.length; i < ii; ++i) { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles[i], - ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), - this.handleImageChange_, this) || loading; - } - return loading; -}; - - -/** - * Set the style for features. This can be a single style object, an array - * of styles, or a function that takes a feature and resolution and returns - * an array of styles. If it is `undefined` the default style is used. If - * it is `null` the layer has no style (a `null` style), so only features - * that have their own styles will be rendered in the layer. See - * {@link ol.style} for information on the default style. - * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|undefined} - * style Layer style. - * @api - */ -ol.source.ImageVector.prototype.setStyle = function(style) { - this.style_ = style !== undefined ? style : ol.style.Style.defaultFunction; - this.styleFunction_ = !style ? - undefined : ol.style.Style.createFunction(this.style_); - this.changed(); -}; - -goog.provide('ol.renderer.webgl.ImageLayer'); - -goog.require('ol'); -goog.require('ol.ViewHint'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.functions'); -goog.require('ol.renderer.webgl.Layer'); -goog.require('ol.source.ImageVector'); -goog.require('ol.transform'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Context'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.renderer.webgl.Layer} - * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. - * @param {ol.layer.Image} imageLayer Tile layer. - */ - ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) { - - ol.renderer.webgl.Layer.call(this, mapRenderer, imageLayer); - - /** - * The last rendered image. - * @private - * @type {?ol.ImageBase} - */ - this.image_ = null; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.hitCanvasContext_ = null; - - /** - * @private - * @type {?ol.Transform} - */ - this.hitTransformationMatrix_ = null; - - }; - ol.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer); - - - /** - * @param {ol.ImageBase} image Image. - * @private - * @return {WebGLTexture} Texture. - */ - ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) { - - // We meet the conditions to work with non-power of two textures. - // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support - // http://learningwebgl.com/blog/?p=2101 - - var imageElement = image.getImage(); - var gl = this.mapRenderer.getGL(); - - return ol.webgl.Context.createTexture( - gl, imageElement, ol.webgl.CLAMP_TO_EDGE, ol.webgl.CLAMP_TO_EDGE); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - var layer = this.getLayer(); - var source = layer.getSource(); - var resolution = frameState.viewState.resolution; - var rotation = frameState.viewState.rotation; - var skippedFeatureUids = frameState.skippedFeatureUids; - return source.forEachFeatureAtCoordinate( - coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, - - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - return callback.call(thisArg, feature, layer); - }); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.ImageLayer.prototype.prepareFrame = function(frameState, layerState, context) { - - var gl = this.mapRenderer.getGL(); - - var pixelRatio = frameState.pixelRatio; - var viewState = frameState.viewState; - var viewCenter = viewState.center; - var viewResolution = viewState.resolution; - var viewRotation = viewState.rotation; - - var image = this.image_; - var texture = this.texture; - var imageLayer = /** @type {ol.layer.Image} */ (this.getLayer()); - var imageSource = imageLayer.getSource(); - - var hints = frameState.viewHints; - - var renderedExtent = frameState.extent; - if (layerState.extent !== undefined) { - renderedExtent = ol.extent.getIntersection( - renderedExtent, layerState.extent); - } - if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && - !ol.extent.isEmpty(renderedExtent)) { - var projection = viewState.projection; - if (!ol.ENABLE_RASTER_REPROJECTION) { - var sourceProjection = imageSource.getProjection(); - if (sourceProjection) { - projection = sourceProjection; - } - } - var image_ = imageSource.getImage(renderedExtent, viewResolution, - pixelRatio, projection); - if (image_) { - var loaded = this.loadImage(image_); - if (loaded) { - image = image_; - texture = this.createTexture_(image_); - if (this.texture) { - /** - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLTexture} texture Texture. - */ - var postRenderFunction = function(gl, texture) { - if (!gl.isContextLost()) { - gl.deleteTexture(texture); - } - }.bind(null, gl, this.texture); - frameState.postRenderFunctions.push( - /** @type {ol.PostRenderFunction} */ (postRenderFunction) - ); - } - } - } - } - - if (image) { - var canvas = this.mapRenderer.getContext().getCanvas(); - - this.updateProjectionMatrix_(canvas.width, canvas.height, - pixelRatio, viewCenter, viewResolution, viewRotation, - image.getExtent()); - this.hitTransformationMatrix_ = null; - - // Translate and scale to flip the Y coord. - var texCoordMatrix = this.texCoordMatrix; - ol.transform.reset(texCoordMatrix); - ol.transform.scale(texCoordMatrix, 1, -1); - ol.transform.translate(texCoordMatrix, 0, -1); - - this.image_ = image; - this.texture = texture; - - this.updateAttributions(frameState.attributions, image.getAttributions()); - this.updateLogos(frameState, imageSource); - } - - return !!image; - }; - - - /** - * @param {number} canvasWidth Canvas width. - * @param {number} canvasHeight Canvas height. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.Coordinate} viewCenter View center. - * @param {number} viewResolution View resolution. - * @param {number} viewRotation View rotation. - * @param {ol.Extent} imageExtent Image extent. - * @private - */ - ol.renderer.webgl.ImageLayer.prototype.updateProjectionMatrix_ = function(canvasWidth, canvasHeight, pixelRatio, - viewCenter, viewResolution, viewRotation, imageExtent) { - - var canvasExtentWidth = canvasWidth * viewResolution; - var canvasExtentHeight = canvasHeight * viewResolution; - - var projectionMatrix = this.projectionMatrix; - ol.transform.reset(projectionMatrix); - ol.transform.scale(projectionMatrix, - pixelRatio * 2 / canvasExtentWidth, - pixelRatio * 2 / canvasExtentHeight); - ol.transform.rotate(projectionMatrix, -viewRotation); - ol.transform.translate(projectionMatrix, - imageExtent[0] - viewCenter[0], - imageExtent[1] - viewCenter[1]); - ol.transform.scale(projectionMatrix, - (imageExtent[2] - imageExtent[0]) / 2, - (imageExtent[3] - imageExtent[1]) / 2); - ol.transform.translate(projectionMatrix, 1, 1); - - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.ImageLayer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) { - var hasFeature = this.forEachFeatureAtCoordinate( - coordinate, frameState, 0, ol.functions.TRUE, this); - return hasFeature !== undefined; - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - if (!this.image_ || !this.image_.getImage()) { - return undefined; - } - - if (this.getLayer().getSource() instanceof ol.source.ImageVector) { - // for ImageVector sources use the original hit-detection logic, - // so that for example also transparent polygons are detected - var coordinate = ol.transform.apply( - frameState.pixelToCoordinateTransform, pixel.slice()); - var hasFeature = this.forEachFeatureAtCoordinate( - coordinate, frameState, 0, ol.functions.TRUE, this); - - if (hasFeature) { - return callback.call(thisArg, this.getLayer(), null); - } else { - return undefined; - } - } else { - var imageSize = - [this.image_.getImage().width, this.image_.getImage().height]; - - if (!this.hitTransformationMatrix_) { - this.hitTransformationMatrix_ = this.getHitTransformationMatrix_( - frameState.size, imageSize); - } - - var pixelOnFrameBuffer = ol.transform.apply( - this.hitTransformationMatrix_, pixel.slice()); - - if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] || - pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) { - // outside the image, no need to check - return undefined; - } - - if (!this.hitCanvasContext_) { - this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1); - } - - this.hitCanvasContext_.clearRect(0, 0, 1, 1); - this.hitCanvasContext_.drawImage(this.image_.getImage(), - pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1); - - var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; - if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); - } else { - return undefined; - } - } - }; - - - /** - * The transformation matrix to get the pixel on the image for a - * pixel on the map. - * @param {ol.Size} mapSize The map size. - * @param {ol.Size} imageSize The image size. - * @return {ol.Transform} The transformation matrix. - * @private - */ - ol.renderer.webgl.ImageLayer.prototype.getHitTransformationMatrix_ = function(mapSize, imageSize) { - // the first matrix takes a map pixel, flips the y-axis and scales to - // a range between -1 ... 1 - var mapCoordTransform = ol.transform.create(); - ol.transform.translate(mapCoordTransform, -1, -1); - ol.transform.scale(mapCoordTransform, 2 / mapSize[0], 2 / mapSize[1]); - ol.transform.translate(mapCoordTransform, 0, mapSize[1]); - ol.transform.scale(mapCoordTransform, 1, -1); - - // the second matrix is the inverse of the projection matrix used in the - // shader for drawing - var projectionMatrixInv = ol.transform.invert(this.projectionMatrix.slice()); - - // the third matrix scales to the image dimensions and flips the y-axis again - var transform = ol.transform.create(); - ol.transform.translate(transform, 0, imageSize[1]); - ol.transform.scale(transform, 1, -1); - ol.transform.scale(transform, imageSize[0] / 2, imageSize[1] / 2); - ol.transform.translate(transform, 1, 1); - - ol.transform.multiply(transform, projectionMatrixInv); - ol.transform.multiply(transform, mapCoordTransform); - - return transform; - }; - -} - -goog.provide('ol.layer.Image'); - -goog.require('ol'); -goog.require('ol.layer.Layer'); -goog.require('ol.renderer.Type'); -goog.require('ol.renderer.canvas.ImageLayer'); -goog.require('ol.renderer.webgl.ImageLayer'); - - -/** - * @classdesc - * Server-rendered images that are available for arbitrary extents and - * resolutions. - * Note that any property set in the options is set as a {@link ol.Object} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {ol.layer.Layer} - * @fires ol.render.Event - * @param {olx.layer.ImageOptions=} opt_options Layer options. - * @api - */ -ol.layer.Image = function(opt_options) { - var options = opt_options ? opt_options : {}; - ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (options)); -}; -ol.inherits(ol.layer.Image, ol.layer.Layer); - - -/** - * @inheritDoc - */ -ol.layer.Image.prototype.createRenderer = function(mapRenderer) { - var renderer = null; - var type = mapRenderer.getType(); - if (ol.ENABLE_CANVAS && type === ol.renderer.Type.CANVAS) { - renderer = new ol.renderer.canvas.ImageLayer(this); - } else if (ol.ENABLE_WEBGL && type === ol.renderer.Type.WEBGL) { - renderer = new ol.renderer.webgl.ImageLayer(/** @type {ol.renderer.webgl.Map} */ (mapRenderer), this); - } - return renderer; -}; - - -/** - * Return the associated {@link ol.source.Image source} of the image layer. - * @function - * @return {ol.source.Image} Source. - * @api - */ -ol.layer.Image.prototype.getSource; - -goog.provide('ol.layer.TileProperty'); - -/** - * @enum {string} - */ -ol.layer.TileProperty = { - PRELOAD: 'preload', - USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError' -}; - -// FIXME find correct globalCompositeOperation - -goog.provide('ol.renderer.canvas.TileLayer'); - -goog.require('ol'); -goog.require('ol.TileRange'); -goog.require('ol.TileState'); -goog.require('ol.ViewHint'); -goog.require('ol.array'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.renderer.canvas.IntermediateCanvas'); -goog.require('ol.transform'); - - -/** - * @constructor - * @extends {ol.renderer.canvas.IntermediateCanvas} - * @param {ol.layer.Tile|ol.layer.VectorTile} tileLayer Tile layer. - */ -ol.renderer.canvas.TileLayer = function(tileLayer) { - - ol.renderer.canvas.IntermediateCanvas.call(this, tileLayer); - - /** - * @protected - * @type {CanvasRenderingContext2D} - */ - this.context = this.context === null ? null : ol.dom.createCanvasContext2D(); - - /** - * @private - * @type {number} - */ - this.oversampling_; - - /** - * @private - * @type {ol.Extent} - */ - this.renderedExtent_ = null; - - /** - * @protected - * @type {number} - */ - this.renderedRevision; - - /** - * @protected - * @type {!Array.<ol.Tile>} - */ - this.renderedTiles = []; - - /** - * @protected - * @type {ol.Extent} - */ - this.tmpExtent = ol.extent.createEmpty(); - - /** - * @private - * @type {ol.TileRange} - */ - this.tmpTileRange_ = new ol.TileRange(0, 0, 0, 0); - - /** - * @private - * @type {ol.Transform} - */ - this.imageTransform_ = ol.transform.create(); - - /** - * @protected - * @type {number} - */ - this.zDirection = 0; - -}; -ol.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.IntermediateCanvas); - - -/** - * @private - * @param {ol.Tile} tile Tile. - * @return {boolean} Tile is drawable. - */ -ol.renderer.canvas.TileLayer.prototype.isDrawableTile_ = function(tile) { - var tileState = tile.getState(); - var useInterimTilesOnError = this.getLayer().getUseInterimTilesOnError(); - return tileState == ol.TileState.LOADED || - tileState == ol.TileState.EMPTY || - tileState == ol.TileState.ERROR && !useInterimTilesOnError; -}; - -/** - * @inheritDoc - */ -ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layerState) { - - var pixelRatio = frameState.pixelRatio; - var size = frameState.size; - var viewState = frameState.viewState; - var projection = viewState.projection; - var viewResolution = viewState.resolution; - var viewCenter = viewState.center; - - var tileLayer = this.getLayer(); - var tileSource = /** @type {ol.source.Tile} */ (tileLayer.getSource()); - var sourceRevision = tileSource.getRevision(); - var tileGrid = tileSource.getTileGridForProjection(projection); - var z = tileGrid.getZForResolution(viewResolution, this.zDirection); - var tileResolution = tileGrid.getResolution(z); - var oversampling = Math.round(viewResolution / tileResolution) || 1; - var extent = frameState.extent; - - if (layerState.extent !== undefined) { - extent = ol.extent.getIntersection(extent, layerState.extent); - } - if (ol.extent.isEmpty(extent)) { - // Return false to prevent the rendering of the layer. - return false; - } - - var tileRange = tileGrid.getTileRangeForExtentAndResolution( - extent, tileResolution); - var imageExtent = tileGrid.getTileRangeExtent(z, tileRange); - - var tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio); - - /** - * @type {Object.<number, Object.<string, ol.Tile>>} - */ - var tilesToDrawByZ = {}; - tilesToDrawByZ[z] = {}; - - var findLoadedTiles = this.createLoadedTileFinder( - tileSource, projection, tilesToDrawByZ); - - var tmpExtent = this.tmpExtent; - var tmpTileRange = this.tmpTileRange_; - var newTiles = false; - var tile, x, y; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tile = tileSource.getTile(z, x, y, pixelRatio, projection); - // When useInterimTilesOnError is false, we consider the error tile as loaded. - if (tile.getState() == ol.TileState.ERROR && !this.getLayer().getUseInterimTilesOnError()) { - tile.setState(ol.TileState.LOADED); - } - if (!this.isDrawableTile_(tile)) { - tile = tile.getInterimTile(); - } - if (this.isDrawableTile_(tile)) { - if (tile.getState() == ol.TileState.LOADED) { - tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; - if (!newTiles && this.renderedTiles.indexOf(tile) == -1) { - newTiles = true; - } - } - continue; - } - - var fullyLoaded = tileGrid.forEachTileCoordParentTileRange( - tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); - if (!fullyLoaded) { - var childTileRange = tileGrid.getTileCoordChildTileRange( - tile.tileCoord, tmpTileRange, tmpExtent); - if (childTileRange) { - findLoadedTiles(z + 1, childTileRange); - } - } - - } - } - - var renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; - var hints = frameState.viewHints; - var animatingOrInteracting = hints[ol.ViewHint.ANIMATING] || hints[ol.ViewHint.INTERACTING]; - if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && ( - newTiles || - !(this.renderedExtent_ && ol.extent.containsExtent(this.renderedExtent_, extent)) || - this.renderedRevision != sourceRevision || - oversampling != this.oversampling_ || - !animatingOrInteracting && renderedResolution != this.renderedResolution - )) { - - var context = this.context; - if (context) { - var tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection); - var width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling); - var height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling); - var canvas = context.canvas; - if (canvas.width != width || canvas.height != height) { - this.oversampling_ = oversampling; - canvas.width = width; - canvas.height = height; - } else { - context.clearRect(0, 0, width, height); - oversampling = this.oversampling_; - } - } - - this.renderedTiles.length = 0; - /** @type {Array.<number>} */ - var zs = Object.keys(tilesToDrawByZ).map(Number); - zs.sort(ol.array.numberSafeCompareFunction); - var currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii; - var tileExtent, tileGutter, tilesToDraw, w, h; - for (i = 0, ii = zs.length; i < ii; ++i) { - currentZ = zs[i]; - currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); - currentResolution = tileGrid.getResolution(currentZ); - currentScale = currentResolution / tileResolution; - tileGutter = tilePixelRatio * tileSource.getGutter(projection); - tilesToDraw = tilesToDrawByZ[currentZ]; - for (var tileCoordKey in tilesToDraw) { - tile = tilesToDraw[tileCoordKey]; - tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent); - x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio / oversampling; - y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling; - w = currentTilePixelSize[0] * currentScale / oversampling; - h = currentTilePixelSize[1] * currentScale / oversampling; - this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter); - this.renderedTiles.push(tile); - } - } - - this.renderedRevision = sourceRevision; - this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; - this.renderedExtent_ = imageExtent; - } - - var scale = this.renderedResolution / viewResolution; - var transform = ol.transform.compose(this.imageTransform_, - pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, - scale, scale, - 0, - (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio, - (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio); - ol.transform.compose(this.coordinateToCanvasPixelTransform, - pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], - pixelRatio / viewResolution, -pixelRatio / viewResolution, - 0, - -viewCenter[0], -viewCenter[1]); - - - this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, - projection, extent, z, tileLayer.getPreload()); - this.scheduleExpireCache(frameState, tileSource); - this.updateLogos(frameState, tileSource); - - return this.renderedTiles.length > 0; -}; - - -/** - * @param {ol.Tile} tile Tile. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @param {number} x Left of the tile. - * @param {number} y Top of the tile. - * @param {number} w Width of the tile. - * @param {number} h Height of the tile. - * @param {number} gutter Tile gutter. - */ -ol.renderer.canvas.TileLayer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter) { - if (!this.getLayer().getSource().getOpaque(frameState.viewState.projection)) { - this.context.clearRect(x, y, w, h); - } - var image = tile.getImage(); - if (image) { - this.context.drawImage(image, gutter, gutter, - image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h); - } -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.TileLayer.prototype.getImage = function() { - var context = this.context; - return context ? context.canvas : null; -}; - - -/** - * @function - * @return {ol.layer.Tile|ol.layer.VectorTile} - */ -ol.renderer.canvas.TileLayer.prototype.getLayer; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.TileLayer.prototype.getImageTransform = function() { - return this.imageTransform_; -}; - -// This file is automatically generated, do not edit -/* eslint openlayers-internal/no-missing-requires: 0 */ -goog.provide('ol.renderer.webgl.tilelayershader'); - -goog.require('ol'); -goog.require('ol.webgl.Fragment'); -goog.require('ol.webgl.Vertex'); - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.webgl.Fragment} - * @struct - */ - ol.renderer.webgl.tilelayershader.Fragment = function() { - ol.webgl.Fragment.call(this, ol.renderer.webgl.tilelayershader.Fragment.SOURCE); - }; - ol.inherits(ol.renderer.webgl.tilelayershader.Fragment, ol.webgl.Fragment); - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.tilelayershader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform sampler2D u_texture;\n\nvoid main(void) {\n gl_FragColor = texture2D(u_texture, v_texCoord);\n}\n'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.tilelayershader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.tilelayershader.Fragment.SOURCE = ol.DEBUG_WEBGL ? - ol.renderer.webgl.tilelayershader.Fragment.DEBUG_SOURCE : - ol.renderer.webgl.tilelayershader.Fragment.OPTIMIZED_SOURCE; - - - ol.renderer.webgl.tilelayershader.fragment = new ol.renderer.webgl.tilelayershader.Fragment(); - - - /** - * @constructor - * @extends {ol.webgl.Vertex} - * @struct - */ - ol.renderer.webgl.tilelayershader.Vertex = function() { - ol.webgl.Vertex.call(this, ol.renderer.webgl.tilelayershader.Vertex.SOURCE); - }; - ol.inherits(ol.renderer.webgl.tilelayershader.Vertex, ol.webgl.Vertex); - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.tilelayershader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nuniform vec4 u_tileOffset;\n\nvoid main(void) {\n gl_Position = vec4(a_position * u_tileOffset.xy + u_tileOffset.zw, 0., 1.);\n v_texCoord = a_texCoord;\n}\n\n\n'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.tilelayershader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}'; - - - /** - * @const - * @type {string} - */ - ol.renderer.webgl.tilelayershader.Vertex.SOURCE = ol.DEBUG_WEBGL ? - ol.renderer.webgl.tilelayershader.Vertex.DEBUG_SOURCE : - ol.renderer.webgl.tilelayershader.Vertex.OPTIMIZED_SOURCE; - - - ol.renderer.webgl.tilelayershader.vertex = new ol.renderer.webgl.tilelayershader.Vertex(); - - - /** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ - ol.renderer.webgl.tilelayershader.Locations = function(gl, program) { - - /** - * @type {WebGLUniformLocation} - */ - this.u_texture = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_texture' : 'e'); - - /** - * @type {WebGLUniformLocation} - */ - this.u_tileOffset = gl.getUniformLocation( - program, ol.DEBUG_WEBGL ? 'u_tileOffset' : 'd'); - - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_position' : 'b'); - - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, ol.DEBUG_WEBGL ? 'a_texCoord' : 'c'); - }; - -} - -// FIXME large resolutions lead to too large framebuffers :-( -// FIXME animated shaders! check in redraw - -goog.provide('ol.renderer.webgl.TileLayer'); - -goog.require('ol'); -goog.require('ol.TileState'); -goog.require('ol.TileRange'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.math'); -goog.require('ol.renderer.webgl.Layer'); -goog.require('ol.renderer.webgl.tilelayershader'); -goog.require('ol.size'); -goog.require('ol.transform'); -goog.require('ol.webgl'); -goog.require('ol.webgl.Buffer'); - - -if (ol.ENABLE_WEBGL) { - - /** - * @constructor - * @extends {ol.renderer.webgl.Layer} - * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. - * @param {ol.layer.Tile} tileLayer Tile layer. - */ - ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) { - - ol.renderer.webgl.Layer.call(this, mapRenderer, tileLayer); - - /** - * @private - * @type {ol.webgl.Fragment} - */ - this.fragmentShader_ = ol.renderer.webgl.tilelayershader.fragment; - - /** - * @private - * @type {ol.webgl.Vertex} - */ - this.vertexShader_ = ol.renderer.webgl.tilelayershader.vertex; - - /** - * @private - * @type {ol.renderer.webgl.tilelayershader.Locations} - */ - this.locations_ = null; - - /** - * @private - * @type {ol.webgl.Buffer} - */ - this.renderArrayBuffer_ = new ol.webgl.Buffer([ - 0, 0, 0, 1, - 1, 0, 1, 1, - 0, 1, 0, 0, - 1, 1, 1, 0 - ]); - - /** - * @private - * @type {ol.TileRange} - */ - this.renderedTileRange_ = null; - - /** - * @private - * @type {ol.Extent} - */ - this.renderedFramebufferExtent_ = null; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = -1; - - /** - * @private - * @type {ol.Size} - */ - this.tmpSize_ = [0, 0]; - - }; - ol.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer); - - - /** - * @inheritDoc - */ - ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() { - var context = this.mapRenderer.getContext(); - context.deleteBuffer(this.renderArrayBuffer_); - ol.renderer.webgl.Layer.prototype.disposeInternal.call(this); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder = function(source, projection, tiles) { - var mapRenderer = this.mapRenderer; - - return ( - /** - * @param {number} zoom Zoom level. - * @param {ol.TileRange} tileRange Tile range. - * @return {boolean} The tile range is fully loaded. - */ - function(zoom, tileRange) { - function callback(tile) { - var loaded = mapRenderer.isTileTextureLoaded(tile); - if (loaded) { - if (!tiles[zoom]) { - tiles[zoom] = {}; - } - tiles[zoom][tile.tileCoord.toString()] = tile; - } - return loaded; - } - return source.forEachLoadedTile(projection, zoom, tileRange, callback); - }); - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() { - ol.renderer.webgl.Layer.prototype.handleWebGLContextLost.call(this); - this.locations_ = null; - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.TileLayer.prototype.prepareFrame = function(frameState, layerState, context) { - - var mapRenderer = this.mapRenderer; - var gl = context.getGL(); - - var viewState = frameState.viewState; - var projection = viewState.projection; - - var tileLayer = /** @type {ol.layer.Tile} */ (this.getLayer()); - var tileSource = tileLayer.getSource(); - var tileGrid = tileSource.getTileGridForProjection(projection); - var z = tileGrid.getZForResolution(viewState.resolution); - var tileResolution = tileGrid.getResolution(z); - - var tilePixelSize = - tileSource.getTilePixelSize(z, frameState.pixelRatio, projection); - var pixelRatio = tilePixelSize[0] / - ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize_)[0]; - var tilePixelResolution = tileResolution / pixelRatio; - var tileGutter = tileSource.getTilePixelRatio(pixelRatio) * tileSource.getGutter(projection); - - var center = viewState.center; - var extent = frameState.extent; - var tileRange = tileGrid.getTileRangeForExtentAndResolution( - extent, tileResolution); - - var framebufferExtent; - if (this.renderedTileRange_ && - this.renderedTileRange_.equals(tileRange) && - this.renderedRevision_ == tileSource.getRevision()) { - framebufferExtent = this.renderedFramebufferExtent_; - } else { - - var tileRangeSize = tileRange.getSize(); - - var maxDimension = Math.max( - tileRangeSize[0] * tilePixelSize[0], - tileRangeSize[1] * tilePixelSize[1]); - var framebufferDimension = ol.math.roundUpToPowerOfTwo(maxDimension); - var framebufferExtentDimension = tilePixelResolution * framebufferDimension; - var origin = tileGrid.getOrigin(z); - var minX = origin[0] + - tileRange.minX * tilePixelSize[0] * tilePixelResolution; - var minY = origin[1] + - tileRange.minY * tilePixelSize[1] * tilePixelResolution; - framebufferExtent = [ - minX, minY, - minX + framebufferExtentDimension, minY + framebufferExtentDimension - ]; - - this.bindFramebuffer(frameState, framebufferDimension); - gl.viewport(0, 0, framebufferDimension, framebufferDimension); - - gl.clearColor(0, 0, 0, 0); - gl.clear(ol.webgl.COLOR_BUFFER_BIT); - gl.disable(ol.webgl.BLEND); - - var program = context.getProgram(this.fragmentShader_, this.vertexShader_); - context.useProgram(program); - if (!this.locations_) { - // eslint-disable-next-line openlayers-internal/no-missing-requires - this.locations_ = new ol.renderer.webgl.tilelayershader.Locations(gl, program); - } - - context.bindBuffer(ol.webgl.ARRAY_BUFFER, this.renderArrayBuffer_); - gl.enableVertexAttribArray(this.locations_.a_position); - gl.vertexAttribPointer( - this.locations_.a_position, 2, ol.webgl.FLOAT, false, 16, 0); - gl.enableVertexAttribArray(this.locations_.a_texCoord); - gl.vertexAttribPointer( - this.locations_.a_texCoord, 2, ol.webgl.FLOAT, false, 16, 8); - gl.uniform1i(this.locations_.u_texture, 0); - - /** - * @type {Object.<number, Object.<string, ol.Tile>>} - */ - var tilesToDrawByZ = {}; - tilesToDrawByZ[z] = {}; - - var findLoadedTiles = this.createLoadedTileFinder( - tileSource, projection, tilesToDrawByZ); - - var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); - var allTilesLoaded = true; - var tmpExtent = ol.extent.createEmpty(); - var tmpTileRange = new ol.TileRange(0, 0, 0, 0); - var childTileRange, drawable, fullyLoaded, tile, tileState; - var x, y, tileExtent; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - - tile = tileSource.getTile(z, x, y, pixelRatio, projection); - if (layerState.extent !== undefined) { - // ignore tiles outside layer extent - tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); - if (!ol.extent.intersects(tileExtent, layerState.extent)) { - continue; - } - } - tileState = tile.getState(); - drawable = tileState == ol.TileState.LOADED || - tileState == ol.TileState.EMPTY || - tileState == ol.TileState.ERROR && !useInterimTilesOnError; - if (!drawable) { - tile = tile.getInterimTile(); - } - tileState = tile.getState(); - if (tileState == ol.TileState.LOADED) { - if (mapRenderer.isTileTextureLoaded(tile)) { - tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; - continue; - } - } else if (tileState == ol.TileState.EMPTY || - (tileState == ol.TileState.ERROR && - !useInterimTilesOnError)) { - continue; - } - - allTilesLoaded = false; - fullyLoaded = tileGrid.forEachTileCoordParentTileRange( - tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); - if (!fullyLoaded) { - childTileRange = tileGrid.getTileCoordChildTileRange( - tile.tileCoord, tmpTileRange, tmpExtent); - if (childTileRange) { - findLoadedTiles(z + 1, childTileRange); - } - } - - } - - } - - /** @type {Array.<number>} */ - var zs = Object.keys(tilesToDrawByZ).map(Number); - zs.sort(ol.array.numberSafeCompareFunction); - var u_tileOffset = new Float32Array(4); - var i, ii, tileKey, tilesToDraw; - for (i = 0, ii = zs.length; i < ii; ++i) { - tilesToDraw = tilesToDrawByZ[zs[i]]; - for (tileKey in tilesToDraw) { - tile = tilesToDraw[tileKey]; - tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); - u_tileOffset[0] = 2 * (tileExtent[2] - tileExtent[0]) / - framebufferExtentDimension; - u_tileOffset[1] = 2 * (tileExtent[3] - tileExtent[1]) / - framebufferExtentDimension; - u_tileOffset[2] = 2 * (tileExtent[0] - framebufferExtent[0]) / - framebufferExtentDimension - 1; - u_tileOffset[3] = 2 * (tileExtent[1] - framebufferExtent[1]) / - framebufferExtentDimension - 1; - gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset); - mapRenderer.bindTileTexture(tile, tilePixelSize, - tileGutter * pixelRatio, ol.webgl.LINEAR, ol.webgl.LINEAR); - gl.drawArrays(ol.webgl.TRIANGLE_STRIP, 0, 4); - } - } - - if (allTilesLoaded) { - this.renderedTileRange_ = tileRange; - this.renderedFramebufferExtent_ = framebufferExtent; - this.renderedRevision_ = tileSource.getRevision(); - } else { - this.renderedTileRange_ = null; - this.renderedFramebufferExtent_ = null; - this.renderedRevision_ = -1; - frameState.animate = true; - } - - } - - this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - var tileTextureQueue = mapRenderer.getTileTextureQueue(); - this.manageTilePyramid( - frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, - tileLayer.getPreload(), - /** - * @param {ol.Tile} tile Tile. - */ - function(tile) { - if (tile.getState() == ol.TileState.LOADED && - !mapRenderer.isTileTextureLoaded(tile) && - !tileTextureQueue.isKeyQueued(tile.getKey())) { - tileTextureQueue.enqueue([ - tile, - tileGrid.getTileCoordCenter(tile.tileCoord), - tileGrid.getResolution(tile.tileCoord[0]), - tilePixelSize, tileGutter * pixelRatio - ]); - } - }, this); - this.scheduleExpireCache(frameState, tileSource); - this.updateLogos(frameState, tileSource); - - var texCoordMatrix = this.texCoordMatrix; - ol.transform.reset(texCoordMatrix); - ol.transform.translate(texCoordMatrix, - (Math.round(center[0] / tileResolution) * tileResolution - framebufferExtent[0]) / - (framebufferExtent[2] - framebufferExtent[0]), - (Math.round(center[1] / tileResolution) * tileResolution - framebufferExtent[1]) / - (framebufferExtent[3] - framebufferExtent[1])); - if (viewState.rotation !== 0) { - ol.transform.rotate(texCoordMatrix, viewState.rotation); - } - ol.transform.scale(texCoordMatrix, - frameState.size[0] * viewState.resolution / - (framebufferExtent[2] - framebufferExtent[0]), - frameState.size[1] * viewState.resolution / - (framebufferExtent[3] - framebufferExtent[1])); - ol.transform.translate(texCoordMatrix, -0.5, -0.5); - - return true; - }; - - - /** - * @inheritDoc - */ - ol.renderer.webgl.TileLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - if (!this.framebuffer) { - return undefined; - } - - var pixelOnMapScaled = [ - pixel[0] / frameState.size[0], - (frameState.size[1] - pixel[1]) / frameState.size[1]]; - - var pixelOnFrameBufferScaled = ol.transform.apply( - this.texCoordMatrix, pixelOnMapScaled.slice()); - var pixelOnFrameBuffer = [ - pixelOnFrameBufferScaled[0] * this.framebufferDimension, - pixelOnFrameBufferScaled[1] * this.framebufferDimension]; - - var gl = this.mapRenderer.getContext().getGL(); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); - var imageData = new Uint8Array(4); - gl.readPixels(pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, - gl.RGBA, gl.UNSIGNED_BYTE, imageData); - - if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); - } else { - return undefined; - } - }; - -} - -goog.provide('ol.layer.Tile'); - -goog.require('ol'); -goog.require('ol.layer.Layer'); -goog.require('ol.layer.TileProperty'); -goog.require('ol.obj'); -goog.require('ol.renderer.Type'); -goog.require('ol.renderer.canvas.TileLayer'); -goog.require('ol.renderer.webgl.TileLayer'); - - -/** - * @classdesc - * For layer sources that provide pre-rendered, tiled images in grids that are - * organized by zoom levels for specific resolutions. - * Note that any property set in the options is set as a {@link ol.Object} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {ol.layer.Layer} - * @fires ol.render.Event - * @param {olx.layer.TileOptions=} opt_options Tile layer options. - * @api - */ -ol.layer.Tile = function(opt_options) { - var options = opt_options ? opt_options : {}; - - var baseOptions = ol.obj.assign({}, options); - - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (baseOptions)); - - this.setPreload(options.preload !== undefined ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? - options.useInterimTilesOnError : true); -}; -ol.inherits(ol.layer.Tile, ol.layer.Layer); - - -/** - * @inheritDoc - */ -ol.layer.Tile.prototype.createRenderer = function(mapRenderer) { - var renderer = null; - var type = mapRenderer.getType(); - if (ol.ENABLE_CANVAS && type === ol.renderer.Type.CANVAS) { - renderer = new ol.renderer.canvas.TileLayer(this); - } else if (ol.ENABLE_WEBGL && type === ol.renderer.Type.WEBGL) { - renderer = new ol.renderer.webgl.TileLayer(/** @type {ol.renderer.webgl.Map} */ (mapRenderer), this); - } - return renderer; -}; - - -/** - * Return the level as number to which we will preload tiles up to. - * @return {number} The level to preload tiles up to. - * @observable - * @api - */ -ol.layer.Tile.prototype.getPreload = function() { - return /** @type {number} */ (this.get(ol.layer.TileProperty.PRELOAD)); -}; - - -/** - * Return the associated {@link ol.source.Tile tilesource} of the layer. - * @function - * @return {ol.source.Tile} Source. - * @api - */ -ol.layer.Tile.prototype.getSource; - - -/** - * Set the level as number to which we will preload tiles up to. - * @param {number} preload The level to preload tiles up to. - * @observable - * @api - */ -ol.layer.Tile.prototype.setPreload = function(preload) { - this.set(ol.layer.TileProperty.PRELOAD, preload); -}; - - -/** - * Whether we use interim tiles on error. - * @return {boolean} Use interim tiles on error. - * @observable - * @api - */ -ol.layer.Tile.prototype.getUseInterimTilesOnError = function() { - return /** @type {boolean} */ ( - this.get(ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR)); -}; - - -/** - * Set whether we use interim tiles on error. - * @param {boolean} useInterimTilesOnError Use interim tiles on error. - * @observable - * @api - */ -ol.layer.Tile.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) { - this.set( - ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); -}; - -goog.provide('ol.layer.VectorTileRenderType'); - -/** - * @enum {string} - * Render mode for vector tiles: - * * `'image'`: Vector tiles are rendered as images. Great performance, but - * point symbols and texts are always rotated with the view and pixels are - * scaled during zoom animations. - * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels - * are scaled during zoom animations. Point symbols and texts are accurately - * rendered as vectors and can stay upright on rotated views. - * * `'vector'`: Vector tiles are rendered as vectors. Most accurate rendering - * even during animations, but slower performance than the other options. - * @api - */ -ol.layer.VectorTileRenderType = { - IMAGE: 'image', - HYBRID: 'hybrid', - VECTOR: 'vector' -}; - -goog.provide('ol.renderer.canvas.VectorTileLayer'); - -goog.require('ol'); -goog.require('ol.TileState'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.proj'); -goog.require('ol.proj.Units'); -goog.require('ol.layer.VectorTileRenderType'); -goog.require('ol.render.ReplayType'); -goog.require('ol.render.canvas'); -goog.require('ol.render.canvas.ReplayGroup'); -goog.require('ol.render.replay'); -goog.require('ol.renderer.canvas.TileLayer'); -goog.require('ol.renderer.vector'); -goog.require('ol.transform'); - - -/** - * @constructor - * @extends {ol.renderer.canvas.TileLayer} - * @param {ol.layer.VectorTile} layer VectorTile layer. - */ -ol.renderer.canvas.VectorTileLayer = function(layer) { - - /** - * @type {CanvasRenderingContext2D} - */ - this.context = null; - - ol.renderer.canvas.TileLayer.call(this, layer); - - /** - * @private - * @type {boolean} - */ - this.dirty_ = false; - - /** - * @private - * @type {number} - */ - this.renderedLayerRevision_; - - /** - * @private - * @type {ol.Transform} - */ - this.tmpTransform_ = ol.transform.create(); - - // Use lower resolution for pure vector rendering. Closest resolution otherwise. - this.zDirection = - layer.getRenderMode() == ol.layer.VectorTileRenderType.VECTOR ? 1 : 0; - -}; -ol.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.TileLayer); - - -/** - * @const - * @type {!Object.<string, Array.<ol.render.ReplayType>>} - */ -ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS = { - 'image': ol.render.replay.ORDER, - 'hybrid': [ol.render.ReplayType.POLYGON, ol.render.ReplayType.LINE_STRING] -}; - - -/** - * @const - * @type {!Object.<string, Array.<ol.render.ReplayType>>} - */ -ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS = { - 'hybrid': [ol.render.ReplayType.IMAGE, ol.render.ReplayType.TEXT], - 'vector': ol.render.replay.ORDER -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) { - var layer = this.getLayer(); - var layerRevision = layer.getRevision(); - if (this.renderedLayerRevision_ != layerRevision) { - this.renderedTiles.length = 0; - var renderMode = layer.getRenderMode(); - if (!this.context && renderMode != ol.layer.VectorTileRenderType.VECTOR) { - this.context = ol.dom.createCanvasContext2D(); - } - if (this.context && renderMode == ol.layer.VectorTileRenderType.VECTOR) { - this.context = null; - } - } - this.renderedLayerRevision_ = layerRevision; - return ol.renderer.canvas.TileLayer.prototype.prepareFrame.apply(this, arguments); -}; - - -/** - * @param {ol.VectorImageTile} tile Tile. - * @param {olx.FrameState} frameState Frame state. - * @private - */ -ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function( - tile, frameState) { - var layer = this.getLayer(); - var pixelRatio = frameState.pixelRatio; - var projection = frameState.viewState.projection; - var revision = layer.getRevision(); - var renderOrder = /** @type {ol.RenderOrderFunction} */ - (layer.getRenderOrder()) || null; - - var replayState = tile.getReplayState(); - if (!replayState.dirty && replayState.renderedRevision == revision && - replayState.renderedRenderOrder == renderOrder) { - return; - } - - for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - var sourceTile = tile.getTile(tile.tileKeys[t]); - sourceTile.replayGroup = null; - replayState.dirty = false; - - var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); - var sourceTileGrid = source.getTileGrid(); - var sourceTileCoord = sourceTile.tileCoord; - var tileProjection = sourceTile.getProjection(); - var tileGrid = source.getTileGridForProjection(projection); - var resolution = tileGrid.getResolution(tile.tileCoord[0]); - var sourceTileResolution = sourceTileGrid.getResolution(sourceTile.tileCoord[0]); - var tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); - var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); - var sharedExtent = ol.extent.getIntersection(tileExtent, sourceTileExtent); - var extent, reproject, tileResolution; - if (tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS) { - var tilePixelRatio = tileResolution = source.getTilePixelRatio(); - var transform = ol.transform.compose(this.tmpTransform_, - 0, 0, - 1 / sourceTileResolution * tilePixelRatio, -1 / sourceTileResolution * tilePixelRatio, - 0, - -sourceTileExtent[0], -sourceTileExtent[3]); - extent = (ol.transform.apply(transform, [sharedExtent[0], sharedExtent[3]]) - .concat(ol.transform.apply(transform, [sharedExtent[2], sharedExtent[1]]))); - } else { - tileResolution = resolution; - extent = sharedExtent; - if (!ol.proj.equivalent(projection, tileProjection)) { - reproject = true; - sourceTile.setProjection(projection); - } - } - replayState.dirty = false; - var replayGroup = new ol.render.canvas.ReplayGroup(0, extent, - tileResolution, source.getOverlaps(), layer.getRenderBuffer()); - var squaredTolerance = ol.renderer.vector.getSquaredTolerance( - tileResolution, pixelRatio); - - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @this {ol.renderer.canvas.VectorTileLayer} - */ - var renderFeature = function(feature) { - var styles; - var styleFunction = feature.getStyleFunction(); - if (styleFunction) { - styles = styleFunction.call(/** @type {ol.Feature} */ (feature), resolution); - } else { - styleFunction = layer.getStyleFunction(); - if (styleFunction) { - styles = styleFunction(feature, resolution); - } - } - if (styles) { - if (!Array.isArray(styles)) { - styles = [styles]; - } - var dirty = this.renderFeature(feature, squaredTolerance, styles, - replayGroup); - this.dirty_ = this.dirty_ || dirty; - replayState.dirty = replayState.dirty || dirty; - } - }; - - var features = sourceTile.getFeatures(); - if (renderOrder && renderOrder !== replayState.renderedRenderOrder) { - features.sort(renderOrder); - } - var feature; - for (var i = 0, ii = features.length; i < ii; ++i) { - feature = features[i]; - if (reproject) { - feature.getGeometry().transform(tileProjection, projection); - } - renderFeature.call(this, feature); - } - replayGroup.finish(); - sourceTile.setReplayGroup(tile.tileCoord.toString(), replayGroup); - } - replayState.renderedRevision = revision; - replayState.renderedRenderOrder = renderOrder; -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorTileLayer.prototype.drawTileImage = function( - tile, frameState, layerState, x, y, w, h, gutter) { - var vectorImageTile = /** @type {ol.VectorImageTile} */ (tile); - this.createReplayGroup_(vectorImageTile, frameState); - if (this.context) { - this.renderTileImage_(vectorImageTile, frameState, layerState); - ol.renderer.canvas.TileLayer.prototype.drawTileImage.apply(this, arguments); - } -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - var resolution = frameState.viewState.resolution; - var rotation = frameState.viewState.rotation; - hitTolerance = hitTolerance == undefined ? 0 : hitTolerance; - var layer = this.getLayer(); - /** @type {Object.<string, boolean>} */ - var features = {}; - - /** @type {Array.<ol.VectorImageTile>} */ - var renderedTiles = this.renderedTiles; - - var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); - var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); - var sourceTileGrid = source.getTileGrid(); - var bufferedExtent, found, tileSpaceCoordinate; - var i, ii, origin, replayGroup; - var tile, tileCoord, tileExtent, tilePixelRatio, tileResolution; - for (i = 0, ii = renderedTiles.length; i < ii; ++i) { - tile = renderedTiles[i]; - tileCoord = tile.tileCoord; - tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); - bufferedExtent = ol.extent.buffer(tileExtent, hitTolerance * resolution, bufferedExtent); - if (!ol.extent.containsCoordinate(bufferedExtent, coordinate)) { - continue; - } - for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - var sourceTile = tile.getTile(tile.tileKeys[t]); - if (sourceTile.getProjection().getUnits() === ol.proj.Units.TILE_PIXELS) { - var sourceTileCoord = sourceTile.tileCoord; - var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord, this.tmpExtent); - origin = ol.extent.getTopLeft(sourceTileExtent); - tilePixelRatio = source.getTilePixelRatio(); - tileResolution = sourceTileGrid.getResolution(sourceTileCoord[0]) / tilePixelRatio; - tileSpaceCoordinate = [ - (coordinate[0] - origin[0]) / tileResolution, - (origin[1] - coordinate[1]) / tileResolution - ]; - resolution = tilePixelRatio; - } else { - tileSpaceCoordinate = coordinate; - } - replayGroup = sourceTile.getReplayGroup(tile.tileCoord); - found = found || replayGroup.forEachFeatureAtCoordinate( - tileSpaceCoordinate, resolution, rotation, hitTolerance, {}, - /** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - var key = ol.getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, layer); - } - }); - } - } - return found; -}; - - -/** - * @param {ol.VectorTile} tile Tile. - * @param {olx.FrameState} frameState Frame state. - * @return {ol.Transform} transform Transform. - * @private - */ -ol.renderer.canvas.VectorTileLayer.prototype.getReplayTransform_ = function(tile, frameState) { - if (tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) { - var layer = this.getLayer(); - var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); - var tileGrid = source.getTileGrid(); - var tileCoord = tile.tileCoord; - var tileResolution = - tileGrid.getResolution(tileCoord[0]) / source.getTilePixelRatio(); - var viewState = frameState.viewState; - var pixelRatio = frameState.pixelRatio; - var renderResolution = viewState.resolution / pixelRatio; - var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); - var center = viewState.center; - var origin = ol.extent.getTopLeft(tileExtent); - var size = frameState.size; - var offsetX = Math.round(pixelRatio * size[0] / 2); - var offsetY = Math.round(pixelRatio * size[1] / 2); - return ol.transform.compose(this.tmpTransform_, - offsetX, offsetY, - tileResolution / renderResolution, tileResolution / renderResolution, - viewState.rotation, - (origin[0] - center[0]) / tileResolution, - (center[1] - origin[1]) / tileResolution); - } else { - return this.getTransform(frameState, 0); - } -}; - - -/** - * Handle changes in image style state. - * @param {ol.events.Event} event Image style change event. - * @private - */ -ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ = function(event) { - this.renderIfReadyAndVisible(); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, frameState, layerState) { - var layer = this.getLayer(); - var source = layer.getSource(); - var renderMode = layer.getRenderMode(); - var replays = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode]; - if (replays) { - var pixelRatio = frameState.pixelRatio; - var rotation = frameState.viewState.rotation; - var size = frameState.size; - var offsetX = Math.round(pixelRatio * size[0] / 2); - var offsetY = Math.round(pixelRatio * size[1] / 2); - var tiles = this.renderedTiles; - var tilePixelRatio = layer.getSource().getTilePixelRatio(); - var sourceTileGrid = source.getTileGrid(); - var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); - var clips = []; - var zs = []; - for (var i = tiles.length - 1; i >= 0; --i) { - var tile = /** @type {ol.VectorImageTile} */ (tiles[i]); - if (tile.getState() == ol.TileState.ABORT) { - continue; - } - var tileCoord = tile.tileCoord; - var worldOffset = tileGrid.getTileCoordExtent(tileCoord)[0] - - tileGrid.getTileCoordExtent(tile.wrappedTileCoord)[0]; - for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - var sourceTile = tile.getTile(tile.tileKeys[t]); - var currentZ = sourceTile.tileCoord[0]; - var sourceResolution = sourceTileGrid.getResolution(currentZ); - var transform = this.getReplayTransform_(sourceTile, frameState); - ol.transform.translate(transform, worldOffset * tilePixelRatio / sourceResolution, 0); - var replayGroup = sourceTile.getReplayGroup(tileCoord.toString()); - var currentClip = replayGroup.getClipCoords(transform); - context.save(); - context.globalAlpha = layerState.opacity; - ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY); - // Create a clip mask for regions in this low resolution tile that are - // already filled by a higher resolution tile - for (var j = 0, jj = clips.length; j < jj; ++j) { - var clip = clips[j]; - if (currentZ < zs[j]) { - context.beginPath(); - // counter-clockwise (outer ring) for current tile - context.moveTo(currentClip[0], currentClip[1]); - context.lineTo(currentClip[2], currentClip[3]); - context.lineTo(currentClip[4], currentClip[5]); - context.lineTo(currentClip[6], currentClip[7]); - // clockwise (inner ring) for higher resolution tile - context.moveTo(clip[6], clip[7]); - context.lineTo(clip[4], clip[5]); - context.lineTo(clip[2], clip[3]); - context.lineTo(clip[0], clip[1]); - context.clip(); - } - } - replayGroup.replay(context, pixelRatio, transform, rotation, {}, replays); - context.restore(); - clips.push(currentClip); - zs.push(currentZ); - } - } - } - ol.renderer.canvas.TileLayer.prototype.postCompose.apply(this, arguments); -}; - - -/** - * @param {ol.Feature|ol.render.Feature} feature Feature. - * @param {number} squaredTolerance Squared tolerance. - * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of - * styles. - * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - */ -ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, squaredTolerance, styles, replayGroup) { - if (!styles) { - return false; - } - var loading = false; - if (Array.isArray(styles)) { - for (var i = 0, ii = styles.length; i < ii; ++i) { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles[i], squaredTolerance, - this.handleStyleImageChange_, this) || loading; - } - } else { - loading = ol.renderer.vector.renderFeature( - replayGroup, feature, styles, squaredTolerance, - this.handleStyleImageChange_, this) || loading; - } - return loading; -}; - - -/** - * @param {ol.VectorImageTile} tile Tile. - * @param {olx.FrameState} frameState Frame state. - * @param {ol.LayerState} layerState Layer state. - * @private - */ -ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function( - tile, frameState, layerState) { - var layer = this.getLayer(); - var replayState = tile.getReplayState(); - var revision = layer.getRevision(); - var replays = ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS[layer.getRenderMode()]; - if (replays && replayState.renderedTileRevision !== revision) { - replayState.renderedTileRevision = revision; - var tileCoord = tile.wrappedTileCoord; - var z = tileCoord[0]; - var pixelRatio = frameState.pixelRatio; - var source = layer.getSource(); - var sourceTileGrid = source.getTileGrid(); - var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); - var resolution = tileGrid.getResolution(z); - var tilePixelRatio = source.getTilePixelRatio(); - var context = tile.getContext(); - var size = source.getTilePixelSize(z, pixelRatio, frameState.viewState.projection); - context.canvas.width = size[0]; - context.canvas.height = size[1]; - var tileExtent = tileGrid.getTileCoordExtent(tileCoord); - for (var i = 0, ii = tile.tileKeys.length; i < ii; ++i) { - var sourceTile = tile.getTile(tile.tileKeys[i]); - var sourceTileCoord = sourceTile.tileCoord; - var pixelScale = pixelRatio / resolution; - var transform = ol.transform.reset(this.tmpTransform_); - if (sourceTile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS) { - var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord, this.tmpExtent); - var sourceResolution = sourceTileGrid.getResolution(sourceTileCoord[0]); - var renderPixelRatio = pixelRatio / tilePixelRatio * sourceResolution / resolution; - ol.transform.scale(transform, renderPixelRatio, renderPixelRatio); - var offsetX = (sourceTileExtent[0] - tileExtent[0]) / sourceResolution * tilePixelRatio; - var offsetY = (tileExtent[3] - sourceTileExtent[3]) / sourceResolution * tilePixelRatio; - ol.transform.translate(transform, Math.round(offsetX), Math.round(offsetY)); - } else { - ol.transform.scale(transform, pixelScale, -pixelScale); - ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]); - } - var replayGroup = sourceTile.getReplayGroup(tile.tileCoord.toString()); - replayGroup.replay(context, pixelRatio, transform, 0, {}, replays); - } - } -}; - -goog.provide('ol.layer.VectorTile'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.layer.TileProperty'); -goog.require('ol.layer.Vector'); -goog.require('ol.layer.VectorTileRenderType'); -goog.require('ol.obj'); -goog.require('ol.renderer.Type'); -goog.require('ol.renderer.canvas.VectorTileLayer'); - - -/** - * @classdesc - * Layer for vector tile data that is rendered client-side. - * Note that any property set in the options is set as a {@link ol.Object} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {ol.layer.Vector} - * @param {olx.layer.VectorTileOptions=} opt_options Options. - * @api - */ -ol.layer.VectorTile = function(opt_options) { - var options = opt_options ? opt_options : {}; - - var baseOptions = ol.obj.assign({}, options); - - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - ol.layer.Vector.call(this, /** @type {olx.layer.VectorOptions} */ (baseOptions)); - - this.setPreload(options.preload ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError ? - options.useInterimTilesOnError : true); - - ol.asserts.assert(options.renderMode == undefined || - options.renderMode == ol.layer.VectorTileRenderType.IMAGE || - options.renderMode == ol.layer.VectorTileRenderType.HYBRID || - options.renderMode == ol.layer.VectorTileRenderType.VECTOR, - 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` - - /** - * @private - * @type {ol.layer.VectorTileRenderType|string} - */ - this.renderMode_ = options.renderMode || ol.layer.VectorTileRenderType.HYBRID; - -}; -ol.inherits(ol.layer.VectorTile, ol.layer.Vector); - - -/** - * @inheritDoc - */ -ol.layer.VectorTile.prototype.createRenderer = function(mapRenderer) { - var renderer = null; - var type = mapRenderer.getType(); - if (ol.ENABLE_CANVAS && type === ol.renderer.Type.CANVAS) { - renderer = new ol.renderer.canvas.VectorTileLayer(this); - } - return renderer; -}; - - -/** - * Return the level as number to which we will preload tiles up to. - * @return {number} The level to preload tiles up to. - * @observable - * @api - */ -ol.layer.VectorTile.prototype.getPreload = function() { - return /** @type {number} */ (this.get(ol.layer.TileProperty.PRELOAD)); -}; - - -/** - * @return {ol.layer.VectorTileRenderType|string} The render mode. - */ -ol.layer.VectorTile.prototype.getRenderMode = function() { - return this.renderMode_; -}; - - -/** - * Whether we use interim tiles on error. - * @return {boolean} Use interim tiles on error. - * @observable - * @api - */ -ol.layer.VectorTile.prototype.getUseInterimTilesOnError = function() { - return /** @type {boolean} */ ( - this.get(ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR)); -}; - - -/** - * Set the level as number to which we will preload tiles up to. - * @param {number} preload The level to preload tiles up to. - * @observable - * @api - */ -ol.layer.VectorTile.prototype.setPreload = function(preload) { - this.set(ol.layer.TileProperty.PRELOAD, preload); -}; - - -/** - * Set whether we use interim tiles on error. - * @param {boolean} useInterimTilesOnError Use interim tiles on error. - * @observable - * @api - */ -ol.layer.VectorTile.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) { - this.set( - ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); -}; - -goog.provide('ol.net'); - -goog.require('ol'); - - -/** - * Simple JSONP helper. Supports error callbacks and a custom callback param. - * The error callback will be called when no JSONP is executed after 10 seconds. - * - * @param {string} url Request url. A 'callback' query parameter will be - * appended. - * @param {Function} callback Callback on success. - * @param {function()=} opt_errback Callback on error. - * @param {string=} opt_callbackParam Custom query parameter for the JSONP - * callback. Default is 'callback'. - */ -ol.net.jsonp = function(url, callback, opt_errback, opt_callbackParam) { - var script = document.createElement('script'); - var key = 'olc_' + ol.getUid(callback); - function cleanup() { - delete window[key]; - script.parentNode.removeChild(script); - } - script.async = true; - script.src = url + (url.indexOf('?') == -1 ? '?' : '&') + - (opt_callbackParam || 'callback') + '=' + key; - var timer = setTimeout(function() { - cleanup(); - if (opt_errback) { - opt_errback(); - } - }, 10000); - window[key] = function(data) { - clearTimeout(timer); - cleanup(); - callback(data); - }; - document.getElementsByTagName('head')[0].appendChild(script); -}; - -goog.provide('ol.proj.common'); - -goog.require('ol.proj'); - - -/** - * Deprecated. Transforms between EPSG:4326 and EPSG:3857 are now included by - * default. There is no need to call this function in application code and it - * will be removed in a future major release. - * @deprecated This function is no longer necessary. - * @api - */ -ol.proj.common.add = ol.proj.addCommon; - -goog.provide('ol.render'); - -goog.require('ol.has'); -goog.require('ol.transform'); -goog.require('ol.render.canvas.Immediate'); - - -/** - * Binds a Canvas Immediate API to a canvas context, to allow drawing geometries - * to the context's canvas. - * - * The units for geometry coordinates are css pixels relative to the top left - * corner of the canvas element. - * ```js - * var canvas = document.createElement('canvas'); - * var render = ol.render.toContext(canvas.getContext('2d'), - * { size: [100, 100] }); - * render.setFillStrokeStyle(new ol.style.Fill({ color: blue })); - * render.drawPolygon( - * new ol.geom.Polygon([[[0, 0], [100, 100], [100, 0], [0, 0]]])); - * ``` - * - * @param {CanvasRenderingContext2D} context Canvas context. - * @param {olx.render.ToContextOptions=} opt_options Options. - * @return {ol.render.canvas.Immediate} Canvas Immediate. - * @api - */ -ol.render.toContext = function(context, opt_options) { - var canvas = context.canvas; - var options = opt_options ? opt_options : {}; - var pixelRatio = options.pixelRatio || ol.has.DEVICE_PIXEL_RATIO; - var size = options.size; - if (size) { - canvas.width = size[0] * pixelRatio; - canvas.height = size[1] * pixelRatio; - canvas.style.width = size[0] + 'px'; - canvas.style.height = size[1] + 'px'; - } - var extent = [0, 0, canvas.width, canvas.height]; - var transform = ol.transform.scale(ol.transform.create(), pixelRatio, pixelRatio); - return new ol.render.canvas.Immediate(context, pixelRatio, extent, transform, - 0); -}; - -goog.provide('ol.reproj.Tile'); - -goog.require('ol'); -goog.require('ol.Tile'); -goog.require('ol.TileState'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.math'); -goog.require('ol.reproj'); -goog.require('ol.reproj.Triangulation'); - - -/** - * @classdesc - * Class encapsulating single reprojected tile. - * See {@link ol.source.TileImage}. - * - * @constructor - * @extends {ol.Tile} - * @param {ol.proj.Projection} sourceProj Source projection. - * @param {ol.tilegrid.TileGrid} sourceTileGrid Source tile grid. - * @param {ol.proj.Projection} targetProj Target projection. - * @param {ol.tilegrid.TileGrid} targetTileGrid Target tile grid. - * @param {ol.TileCoord} tileCoord Coordinate of the tile. - * @param {ol.TileCoord} wrappedTileCoord Coordinate of the tile wrapped in X. - * @param {number} pixelRatio Pixel ratio. - * @param {number} gutter Gutter of the source tiles. - * @param {ol.ReprojTileFunctionType} getTileFunction - * Function returning source tiles (z, x, y, pixelRatio). - * @param {number=} opt_errorThreshold Acceptable reprojection error (in px). - * @param {boolean=} opt_renderEdges Render reprojection edges. - */ -ol.reproj.Tile = function(sourceProj, sourceTileGrid, - targetProj, targetTileGrid, tileCoord, wrappedTileCoord, - pixelRatio, gutter, getTileFunction, - opt_errorThreshold, - opt_renderEdges) { - ol.Tile.call(this, tileCoord, ol.TileState.IDLE); - - /** - * @private - * @type {boolean} - */ - this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false; - - /** - * @private - * @type {number} - */ - this.pixelRatio_ = pixelRatio; - - /** - * @private - * @type {number} - */ - this.gutter_ = gutter; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; - - /** - * @private - * @type {ol.tilegrid.TileGrid} - */ - this.sourceTileGrid_ = sourceTileGrid; - - /** - * @private - * @type {ol.tilegrid.TileGrid} - */ - this.targetTileGrid_ = targetTileGrid; - - /** - * @private - * @type {ol.TileCoord} - */ - this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord; - - /** - * @private - * @type {!Array.<ol.Tile>} - */ - this.sourceTiles_ = []; - - /** - * @private - * @type {Array.<ol.EventsKey>} - */ - this.sourcesListenerKeys_ = null; - - /** - * @private - * @type {number} - */ - this.sourceZ_ = 0; - - var targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_); - var maxTargetExtent = this.targetTileGrid_.getExtent(); - var maxSourceExtent = this.sourceTileGrid_.getExtent(); - - var limitedTargetExtent = maxTargetExtent ? - ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent; - - if (ol.extent.getArea(limitedTargetExtent) === 0) { - // Tile is completely outside range -> EMPTY - // TODO: is it actually correct that the source even creates the tile ? - this.state = ol.TileState.EMPTY; - return; - } - - var sourceProjExtent = sourceProj.getExtent(); - if (sourceProjExtent) { - if (!maxSourceExtent) { - maxSourceExtent = sourceProjExtent; - } else { - maxSourceExtent = ol.extent.getIntersection( - maxSourceExtent, sourceProjExtent); - } - } - - var targetResolution = targetTileGrid.getResolution( - this.wrappedTileCoord_[0]); - - var targetCenter = ol.extent.getCenter(limitedTargetExtent); - var sourceResolution = ol.reproj.calculateSourceResolution( - sourceProj, targetProj, targetCenter, targetResolution); - - if (!isFinite(sourceResolution) || sourceResolution <= 0) { - // invalid sourceResolution -> EMPTY - // probably edges of the projections when no extent is defined - this.state = ol.TileState.EMPTY; - return; - } - - var errorThresholdInPixels = opt_errorThreshold !== undefined ? - opt_errorThreshold : ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD; - - /** - * @private - * @type {!ol.reproj.Triangulation} - */ - this.triangulation_ = new ol.reproj.Triangulation( - sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, - sourceResolution * errorThresholdInPixels); - - if (this.triangulation_.getTriangles().length === 0) { - // no valid triangles -> EMPTY - this.state = ol.TileState.EMPTY; - return; - } - - this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution); - var sourceExtent = this.triangulation_.calculateSourceExtent(); - - if (maxSourceExtent) { - if (sourceProj.canWrapX()) { - sourceExtent[1] = ol.math.clamp( - sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]); - sourceExtent[3] = ol.math.clamp( - sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]); - } else { - sourceExtent = ol.extent.getIntersection(sourceExtent, maxSourceExtent); - } - } - - if (!ol.extent.getArea(sourceExtent)) { - this.state = ol.TileState.EMPTY; - } else { - var sourceRange = sourceTileGrid.getTileRangeForExtentAndZ( - sourceExtent, this.sourceZ_); - - for (var srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) { - for (var srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) { - var tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio); - if (tile) { - this.sourceTiles_.push(tile); - } - } - } - - if (this.sourceTiles_.length === 0) { - this.state = ol.TileState.EMPTY; - } - } -}; -ol.inherits(ol.reproj.Tile, ol.Tile); - - -/** - * @inheritDoc - */ -ol.reproj.Tile.prototype.disposeInternal = function() { - if (this.state == ol.TileState.LOADING) { - this.unlistenSources_(); - } - ol.Tile.prototype.disposeInternal.call(this); -}; - - -/** - * Get the HTML Canvas element for this tile. - * @return {HTMLCanvasElement} Canvas. - */ -ol.reproj.Tile.prototype.getImage = function() { - return this.canvas_; -}; - - -/** - * @private - */ -ol.reproj.Tile.prototype.reproject_ = function() { - var sources = []; - this.sourceTiles_.forEach(function(tile, i, arr) { - if (tile && tile.getState() == ol.TileState.LOADED) { - sources.push({ - extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord), - image: tile.getImage() - }); - } - }, this); - this.sourceTiles_.length = 0; - - if (sources.length === 0) { - this.state = ol.TileState.ERROR; - } else { - var z = this.wrappedTileCoord_[0]; - var size = this.targetTileGrid_.getTileSize(z); - var width = typeof size === 'number' ? size : size[0]; - var height = typeof size === 'number' ? size : size[1]; - var targetResolution = this.targetTileGrid_.getResolution(z); - var sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_); - - var targetExtent = this.targetTileGrid_.getTileCoordExtent( - this.wrappedTileCoord_); - this.canvas_ = ol.reproj.render(width, height, this.pixelRatio_, - sourceResolution, this.sourceTileGrid_.getExtent(), - targetResolution, targetExtent, this.triangulation_, sources, - this.gutter_, this.renderEdges_); - - this.state = ol.TileState.LOADED; - } - this.changed(); -}; - - -/** - * @inheritDoc - */ -ol.reproj.Tile.prototype.load = function() { - if (this.state == ol.TileState.IDLE) { - this.state = ol.TileState.LOADING; - this.changed(); - - var leftToLoad = 0; - - this.sourcesListenerKeys_ = []; - this.sourceTiles_.forEach(function(tile, i, arr) { - var state = tile.getState(); - if (state == ol.TileState.IDLE || state == ol.TileState.LOADING) { - leftToLoad++; - - var sourceListenKey; - sourceListenKey = ol.events.listen(tile, ol.events.EventType.CHANGE, - function(e) { - var state = tile.getState(); - if (state == ol.TileState.LOADED || - state == ol.TileState.ERROR || - state == ol.TileState.EMPTY) { - ol.events.unlistenByKey(sourceListenKey); - leftToLoad--; - if (leftToLoad === 0) { - this.unlistenSources_(); - this.reproject_(); - } - } - }, this); - this.sourcesListenerKeys_.push(sourceListenKey); - } - }, this); - - this.sourceTiles_.forEach(function(tile, i, arr) { - var state = tile.getState(); - if (state == ol.TileState.IDLE) { - tile.load(); - } - }); - - if (leftToLoad === 0) { - setTimeout(this.reproject_.bind(this), 0); - } - } -}; - - -/** - * @private - */ -ol.reproj.Tile.prototype.unlistenSources_ = function() { - this.sourcesListenerKeys_.forEach(ol.events.unlistenByKey); - this.sourcesListenerKeys_ = null; -}; - -goog.provide('ol.TileUrlFunction'); - -goog.require('ol.asserts'); -goog.require('ol.math'); -goog.require('ol.tilecoord'); - - -/** - * @param {string} template Template. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @return {ol.TileUrlFunctionType} Tile URL function. - */ -ol.TileUrlFunction.createFromTemplate = function(template, tileGrid) { - var zRegEx = /\{z\}/g; - var xRegEx = /\{x\}/g; - var yRegEx = /\{y\}/g; - var dashYRegEx = /\{-y\}/g; - return ( - /** - * @param {ol.TileCoord} tileCoord Tile Coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - return template.replace(zRegEx, tileCoord[0].toString()) - .replace(xRegEx, tileCoord[1].toString()) - .replace(yRegEx, function() { - var y = -tileCoord[2] - 1; - return y.toString(); - }) - .replace(dashYRegEx, function() { - var z = tileCoord[0]; - var range = tileGrid.getFullTileRange(z); - ol.asserts.assert(range, 55); // The {-y} placeholder requires a tile grid with extent - var y = range.getHeight() + tileCoord[2]; - return y.toString(); - }); - } - }); -}; - - -/** - * @param {Array.<string>} templates Templates. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @return {ol.TileUrlFunctionType} Tile URL function. - */ -ol.TileUrlFunction.createFromTemplates = function(templates, tileGrid) { - var len = templates.length; - var tileUrlFunctions = new Array(len); - for (var i = 0; i < len; ++i) { - tileUrlFunctions[i] = ol.TileUrlFunction.createFromTemplate( - templates[i], tileGrid); - } - return ol.TileUrlFunction.createFromTileUrlFunctions(tileUrlFunctions); -}; - - -/** - * @param {Array.<ol.TileUrlFunctionType>} tileUrlFunctions Tile URL Functions. - * @return {ol.TileUrlFunctionType} Tile URL function. - */ -ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) { - if (tileUrlFunctions.length === 1) { - return tileUrlFunctions[0]; - } - return ( - /** - * @param {ol.TileCoord} tileCoord Tile Coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - var h = ol.tilecoord.hash(tileCoord); - var index = ol.math.modulo(h, tileUrlFunctions.length); - return tileUrlFunctions[index](tileCoord, pixelRatio, projection); - } - }); -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ -ol.TileUrlFunction.nullTileUrlFunction = function(tileCoord, pixelRatio, projection) { - return undefined; -}; - - -/** - * @param {string} url URL. - * @return {Array.<string>} Array of urls. - */ -ol.TileUrlFunction.expandUrl = function(url) { - var urls = []; - var match = /\{([a-z])-([a-z])\}/.exec(url); - if (match) { - // char range - var startCharCode = match[1].charCodeAt(0); - var stopCharCode = match[2].charCodeAt(0); - var charCode; - for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) { - urls.push(url.replace(match[0], String.fromCharCode(charCode))); - } - return urls; - } - match = match = /\{(\d+)-(\d+)\}/.exec(url); - if (match) { - // number range - var stop = parseInt(match[2], 10); - for (var i = parseInt(match[1], 10); i <= stop; i++) { - urls.push(url.replace(match[0], i.toString())); - } - return urls; - } - urls.push(url); - return urls; -}; - -goog.provide('ol.TileCache'); - -goog.require('ol'); -goog.require('ol.structs.LRUCache'); - - -/** - * @constructor - * @extends {ol.structs.LRUCache.<ol.Tile>} - * @param {number=} opt_highWaterMark High water mark. - * @struct - */ -ol.TileCache = function(opt_highWaterMark) { - - ol.structs.LRUCache.call(this); - - /** - * @type {number} - */ - this.highWaterMark = opt_highWaterMark !== undefined ? opt_highWaterMark : 2048; - -}; -ol.inherits(ol.TileCache, ol.structs.LRUCache); - - -/** - * @return {boolean} Can expire cache. - */ -ol.TileCache.prototype.canExpireCache = function() { - return this.getCount() > this.highWaterMark; -}; - - -/** - * @param {Object.<string, ol.TileRange>} usedTiles Used tiles. - */ -ol.TileCache.prototype.expireCache = function(usedTiles) { - var tile, zKey; - while (this.canExpireCache()) { - tile = this.peekLast(); - zKey = tile.tileCoord[0].toString(); - if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) { - break; - } else { - this.pop().dispose(); - } - } -}; - -goog.provide('ol.source.Tile'); - -goog.require('ol'); -goog.require('ol.TileCache'); -goog.require('ol.TileState'); -goog.require('ol.events.Event'); -goog.require('ol.proj'); -goog.require('ol.size'); -goog.require('ol.source.Source'); -goog.require('ol.tilecoord'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for sources providing images divided into a tile grid. - * - * @constructor - * @abstract - * @extends {ol.source.Source} - * @param {ol.SourceTileOptions} options Tile source options. - * @api - */ -ol.source.Tile = function(options) { - - ol.source.Source.call(this, { - attributions: options.attributions, - extent: options.extent, - logo: options.logo, - projection: options.projection, - state: options.state, - wrapX: options.wrapX - }); - - /** - * @private - * @type {boolean} - */ - this.opaque_ = options.opaque !== undefined ? options.opaque : false; - - /** - * @private - * @type {number} - */ - this.tilePixelRatio_ = options.tilePixelRatio !== undefined ? - options.tilePixelRatio : 1; - - /** - * @protected - * @type {ol.tilegrid.TileGrid} - */ - this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null; - - /** - * @protected - * @type {ol.TileCache} - */ - this.tileCache = new ol.TileCache(options.cacheSize); - - /** - * @protected - * @type {ol.Size} - */ - this.tmpSize = [0, 0]; - - /** - * @private - * @type {string} - */ - this.key_ = ''; - -}; -ol.inherits(ol.source.Tile, ol.source.Source); - - -/** - * @return {boolean} Can expire cache. - */ -ol.source.Tile.prototype.canExpireCache = function() { - return this.tileCache.canExpireCache(); -}; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @param {Object.<string, ol.TileRange>} usedTiles Used tiles. - */ -ol.source.Tile.prototype.expireCache = function(projection, usedTiles) { - var tileCache = this.getTileCacheForProjection(projection); - if (tileCache) { - tileCache.expireCache(usedTiles); - } -}; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @param {number} z Zoom level. - * @param {ol.TileRange} tileRange Tile range. - * @param {function(ol.Tile):(boolean|undefined)} callback Called with each - * loaded tile. If the callback returns `false`, the tile will not be - * considered loaded. - * @return {boolean} The tile range is fully covered with loaded tiles. - */ -ol.source.Tile.prototype.forEachLoadedTile = function(projection, z, tileRange, callback) { - var tileCache = this.getTileCacheForProjection(projection); - if (!tileCache) { - return false; - } - - var covered = true; - var tile, tileCoordKey, loaded; - for (var x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (var y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoordKey = this.getKeyZXY(z, x, y); - loaded = false; - if (tileCache.containsKey(tileCoordKey)) { - tile = /** @type {!ol.Tile} */ (tileCache.get(tileCoordKey)); - loaded = tile.getState() === ol.TileState.LOADED; - if (loaded) { - loaded = (callback(tile) !== false); - } - } - if (!loaded) { - covered = false; - } - } - } - return covered; -}; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @return {number} Gutter. - */ -ol.source.Tile.prototype.getGutter = function(projection) { - return 0; -}; - - -/** - * Return the key to be used for all tiles in the source. - * @return {string} The key for all tiles. - * @protected - */ -ol.source.Tile.prototype.getKey = function() { - return this.key_; -}; - - -/** - * Set the value to be used as the key for all tiles in the source. - * @param {string} key The key for tiles. - * @protected - */ -ol.source.Tile.prototype.setKey = function(key) { - if (this.key_ !== key) { - this.key_ = key; - this.changed(); - } -}; - - -/** - * @param {number} z Z. - * @param {number} x X. - * @param {number} y Y. - * @return {string} Key. - * @protected - */ -ol.source.Tile.prototype.getKeyZXY = ol.tilecoord.getKeyZXY; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @return {boolean} Opaque. - */ -ol.source.Tile.prototype.getOpaque = function(projection) { - return this.opaque_; -}; - - -/** - * @inheritDoc - */ -ol.source.Tile.prototype.getResolutions = function() { - return this.tileGrid.getResolutions(); -}; - - -/** - * @abstract - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {!ol.Tile} Tile. - */ -ol.source.Tile.prototype.getTile = function(z, x, y, pixelRatio, projection) {}; - - -/** - * Return the tile grid of the tile source. - * @return {ol.tilegrid.TileGrid} Tile grid. - * @api - */ -ol.source.Tile.prototype.getTileGrid = function() { - return this.tileGrid; -}; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @return {!ol.tilegrid.TileGrid} Tile grid. - */ -ol.source.Tile.prototype.getTileGridForProjection = function(projection) { - if (!this.tileGrid) { - return ol.tilegrid.getForProjection(projection); - } else { - return this.tileGrid; - } -}; - - -/** - * @param {ol.proj.Projection} projection Projection. - * @return {ol.TileCache} Tile cache. - * @protected - */ -ol.source.Tile.prototype.getTileCacheForProjection = function(projection) { - var thisProj = this.getProjection(); - if (thisProj && !ol.proj.equivalent(thisProj, projection)) { - return null; - } else { - return this.tileCache; - } -}; - - -/** - * Get the tile pixel ratio for this source. Subclasses may override this - * method, which is meant to return a supported pixel ratio that matches the - * provided `opt_pixelRatio` as close as possible. When no `opt_pixelRatio` is - * provided, it is meant to return `this.tilePixelRatio_`. - * @param {number=} opt_pixelRatio Pixel ratio. - * @return {number} Tile pixel ratio. - */ -ol.source.Tile.prototype.getTilePixelRatio = function(opt_pixelRatio) { - return this.tilePixelRatio_; -}; - - -/** - * @param {number} z Z. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {ol.Size} Tile size. - */ -ol.source.Tile.prototype.getTilePixelSize = function(z, pixelRatio, projection) { - var tileGrid = this.getTileGridForProjection(projection); - var tilePixelRatio = this.getTilePixelRatio(pixelRatio); - var tileSize = ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize); - if (tilePixelRatio == 1) { - return tileSize; - } else { - return ol.size.scale(tileSize, tilePixelRatio, this.tmpSize); - } -}; - - -/** - * Returns a tile coordinate wrapped around the x-axis. When the tile coordinate - * is outside the resolution and extent range of the tile grid, `null` will be - * returned. - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.proj.Projection=} opt_projection Projection. - * @return {ol.TileCoord} Tile coordinate to be passed to the tileUrlFunction or - * null if no tile URL should be created for the passed `tileCoord`. - */ -ol.source.Tile.prototype.getTileCoordForTileUrlFunction = function(tileCoord, opt_projection) { - var projection = opt_projection !== undefined ? - opt_projection : this.getProjection(); - var tileGrid = this.getTileGridForProjection(projection); - if (this.getWrapX() && projection.isGlobal()) { - tileCoord = ol.tilegrid.wrapX(tileGrid, tileCoord, projection); - } - return ol.tilecoord.withinExtentAndZ(tileCoord, tileGrid) ? tileCoord : null; -}; - - -/** - * @inheritDoc - */ -ol.source.Tile.prototype.refresh = function() { - this.tileCache.clear(); - this.changed(); -}; - - -/** - * Marks a tile coord as being used, without triggering a load. - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {ol.proj.Projection} projection Projection. - */ -ol.source.Tile.prototype.useTile = ol.nullFunction; - - -/** - * @classdesc - * Events emitted by {@link ol.source.Tile} instances are instances of this - * type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.source.Tile.Event} - * @param {string} type Type. - * @param {ol.Tile} tile The tile. - */ -ol.source.Tile.Event = function(type, tile) { - - ol.events.Event.call(this, type); - - /** - * The tile related to the event. - * @type {ol.Tile} - * @api - */ - this.tile = tile; - -}; -ol.inherits(ol.source.Tile.Event, ol.events.Event); - -goog.provide('ol.source.TileEventType'); - -/** - * @enum {string} - */ -ol.source.TileEventType = { - - /** - * Triggered when a tile starts loading. - * @event ol.source.Tile.Event#tileloadstart - * @api - */ - TILELOADSTART: 'tileloadstart', - - /** - * Triggered when a tile finishes loading. - * @event ol.source.Tile.Event#tileloadend - * @api - */ - TILELOADEND: 'tileloadend', - - /** - * Triggered if tile loading results in an error. - * @event ol.source.Tile.Event#tileloaderror - * @api - */ - TILELOADERROR: 'tileloaderror' - -}; - -goog.provide('ol.source.UrlTile'); - -goog.require('ol'); -goog.require('ol.TileState'); -goog.require('ol.TileUrlFunction'); -goog.require('ol.source.Tile'); -goog.require('ol.source.TileEventType'); - - -/** - * @classdesc - * Base class for sources providing tiles divided into a tile grid over http. - * - * @constructor - * @abstract - * @fires ol.source.Tile.Event - * @extends {ol.source.Tile} - * @param {ol.SourceUrlTileOptions} options Image tile options. - */ -ol.source.UrlTile = function(options) { - - ol.source.Tile.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - extent: options.extent, - logo: options.logo, - opaque: options.opaque, - projection: options.projection, - state: options.state, - tileGrid: options.tileGrid, - tilePixelRatio: options.tilePixelRatio, - wrapX: options.wrapX - }); - - /** - * @protected - * @type {ol.TileLoadFunctionType} - */ - this.tileLoadFunction = options.tileLoadFunction; - - /** - * @protected - * @type {ol.TileUrlFunctionType} - */ - this.tileUrlFunction = this.fixedTileUrlFunction ? - this.fixedTileUrlFunction.bind(this) : - ol.TileUrlFunction.nullTileUrlFunction; - - /** - * @protected - * @type {!Array.<string>|null} - */ - this.urls = null; - - if (options.urls) { - this.setUrls(options.urls); - } else if (options.url) { - this.setUrl(options.url); - } - if (options.tileUrlFunction) { - this.setTileUrlFunction(options.tileUrlFunction); - } - -}; -ol.inherits(ol.source.UrlTile, ol.source.Tile); - - -/** - * @type {ol.TileUrlFunctionType|undefined} - * @protected - */ -ol.source.UrlTile.prototype.fixedTileUrlFunction; - -/** - * Return the tile load function of the source. - * @return {ol.TileLoadFunctionType} TileLoadFunction - * @api - */ -ol.source.UrlTile.prototype.getTileLoadFunction = function() { - return this.tileLoadFunction; -}; - - -/** - * Return the tile URL function of the source. - * @return {ol.TileUrlFunctionType} TileUrlFunction - * @api - */ -ol.source.UrlTile.prototype.getTileUrlFunction = function() { - return this.tileUrlFunction; -}; - - -/** - * Return the URLs used for this source. - * When a tileUrlFunction is used instead of url or urls, - * null will be returned. - * @return {!Array.<string>|null} URLs. - * @api - */ -ol.source.UrlTile.prototype.getUrls = function() { - return this.urls; -}; - - -/** - * Handle tile change events. - * @param {ol.events.Event} event Event. - * @protected - */ -ol.source.UrlTile.prototype.handleTileChange = function(event) { - var tile = /** @type {ol.Tile} */ (event.target); - switch (tile.getState()) { - case ol.TileState.LOADING: - this.dispatchEvent( - new ol.source.Tile.Event(ol.source.TileEventType.TILELOADSTART, tile)); - break; - case ol.TileState.LOADED: - this.dispatchEvent( - new ol.source.Tile.Event(ol.source.TileEventType.TILELOADEND, tile)); - break; - case ol.TileState.ERROR: - this.dispatchEvent( - new ol.source.Tile.Event(ol.source.TileEventType.TILELOADERROR, tile)); - break; - default: - // pass - } -}; - - -/** - * Set the tile load function of the source. - * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. - * @api - */ -ol.source.UrlTile.prototype.setTileLoadFunction = function(tileLoadFunction) { - this.tileCache.clear(); - this.tileLoadFunction = tileLoadFunction; - this.changed(); -}; - - -/** - * Set the tile URL function of the source. - * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function. - * @param {string=} opt_key Optional new tile key for the source. - * @api - */ -ol.source.UrlTile.prototype.setTileUrlFunction = function(tileUrlFunction, opt_key) { - this.tileUrlFunction = tileUrlFunction; - if (typeof opt_key !== 'undefined') { - this.setKey(opt_key); - } else { - this.changed(); - } -}; - - -/** - * Set the URL to use for requests. - * @param {string} url URL. - * @api - */ -ol.source.UrlTile.prototype.setUrl = function(url) { - var urls = this.urls = ol.TileUrlFunction.expandUrl(url); - this.setTileUrlFunction(this.fixedTileUrlFunction ? - this.fixedTileUrlFunction.bind(this) : - ol.TileUrlFunction.createFromTemplates(urls, this.tileGrid), url); -}; - - -/** - * Set the URLs to use for requests. - * @param {Array.<string>} urls URLs. - * @api - */ -ol.source.UrlTile.prototype.setUrls = function(urls) { - this.urls = urls; - var key = urls.join('\n'); - this.setTileUrlFunction(this.fixedTileUrlFunction ? - this.fixedTileUrlFunction.bind(this) : - ol.TileUrlFunction.createFromTemplates(urls, this.tileGrid), key); -}; - - -/** - * @inheritDoc - */ -ol.source.UrlTile.prototype.useTile = function(z, x, y) { - var tileCoordKey = this.getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - this.tileCache.get(tileCoordKey); - } -}; - -goog.provide('ol.source.TileImage'); - -goog.require('ol'); -goog.require('ol.ImageTile'); -goog.require('ol.TileCache'); -goog.require('ol.TileState'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.proj'); -goog.require('ol.reproj.Tile'); -goog.require('ol.source.UrlTile'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * Base class for sources providing images divided into a tile grid. - * - * @constructor - * @fires ol.source.Tile.Event - * @extends {ol.source.UrlTile} - * @param {olx.source.TileImageOptions} options Image tile options. - * @api - */ -ol.source.TileImage = function(options) { - - ol.source.UrlTile.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - extent: options.extent, - logo: options.logo, - opaque: options.opaque, - projection: options.projection, - state: options.state, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction ? - options.tileLoadFunction : ol.source.TileImage.defaultTileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: options.tileUrlFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX - }); - - /** - * @protected - * @type {?string} - */ - this.crossOrigin = - options.crossOrigin !== undefined ? options.crossOrigin : null; - - /** - * @protected - * @type {function(new: ol.ImageTile, ol.TileCoord, ol.TileState, string, - * ?string, ol.TileLoadFunctionType)} - */ - this.tileClass = options.tileClass !== undefined ? - options.tileClass : ol.ImageTile; - - /** - * @protected - * @type {Object.<string, ol.TileCache>} - */ - this.tileCacheForProjection = {}; - - /** - * @protected - * @type {Object.<string, ol.tilegrid.TileGrid>} - */ - this.tileGridForProjection = {}; - - /** - * @private - * @type {number|undefined} - */ - this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold; - - /** - * @private - * @type {boolean} - */ - this.renderReprojectionEdges_ = false; -}; -ol.inherits(ol.source.TileImage, ol.source.UrlTile); - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.canExpireCache = function() { - if (!ol.ENABLE_RASTER_REPROJECTION) { - return ol.source.UrlTile.prototype.canExpireCache.call(this); - } - if (this.tileCache.canExpireCache()) { - return true; - } else { - for (var key in this.tileCacheForProjection) { - if (this.tileCacheForProjection[key].canExpireCache()) { - return true; - } - } - } - return false; -}; - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.expireCache = function(projection, usedTiles) { - if (!ol.ENABLE_RASTER_REPROJECTION) { - ol.source.UrlTile.prototype.expireCache.call(this, projection, usedTiles); - return; - } - var usedTileCache = this.getTileCacheForProjection(projection); - - this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {}); - for (var id in this.tileCacheForProjection) { - var tileCache = this.tileCacheForProjection[id]; - tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {}); - } -}; - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.getGutter = function(projection) { - if (ol.ENABLE_RASTER_REPROJECTION && - this.getProjection() && projection && - !ol.proj.equivalent(this.getProjection(), projection)) { - return 0; - } else { - return this.getGutterInternal(); - } -}; - - -/** - * @protected - * @return {number} Gutter. - */ -ol.source.TileImage.prototype.getGutterInternal = function() { - return 0; -}; - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.getOpaque = function(projection) { - if (ol.ENABLE_RASTER_REPROJECTION && - this.getProjection() && projection && - !ol.proj.equivalent(this.getProjection(), projection)) { - return false; - } else { - return ol.source.UrlTile.prototype.getOpaque.call(this, projection); - } -}; - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.getTileGridForProjection = function(projection) { - if (!ol.ENABLE_RASTER_REPROJECTION) { - return ol.source.UrlTile.prototype.getTileGridForProjection.call(this, projection); - } - var thisProj = this.getProjection(); - if (this.tileGrid && - (!thisProj || ol.proj.equivalent(thisProj, projection))) { - return this.tileGrid; - } else { - var projKey = ol.getUid(projection).toString(); - if (!(projKey in this.tileGridForProjection)) { - this.tileGridForProjection[projKey] = - ol.tilegrid.getForProjection(projection); - } - return /** @type {!ol.tilegrid.TileGrid} */ (this.tileGridForProjection[projKey]); - } -}; - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.getTileCacheForProjection = function(projection) { - if (!ol.ENABLE_RASTER_REPROJECTION) { - return ol.source.UrlTile.prototype.getTileCacheForProjection.call(this, projection); - } - var thisProj = this.getProjection(); - if (!thisProj || ol.proj.equivalent(thisProj, projection)) { - return this.tileCache; - } else { - var projKey = ol.getUid(projection).toString(); - if (!(projKey in this.tileCacheForProjection)) { - this.tileCacheForProjection[projKey] = new ol.TileCache(this.tileCache.highWaterMark); - } - return this.tileCacheForProjection[projKey]; - } -}; - - -/** - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {string} key The key set on the tile. - * @return {!ol.Tile} Tile. - * @private - */ -ol.source.TileImage.prototype.createTile_ = function(z, x, y, pixelRatio, projection, key) { - var tileCoord = [z, x, y]; - var urlTileCoord = this.getTileCoordForTileUrlFunction( - tileCoord, projection); - var tileUrl = urlTileCoord ? - this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; - var tile = new this.tileClass( - tileCoord, - tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, - tileUrl !== undefined ? tileUrl : '', - this.crossOrigin, - this.tileLoadFunction); - tile.key = key; - ol.events.listen(tile, ol.events.EventType.CHANGE, - this.handleTileChange, this); - return tile; -}; - - -/** - * @inheritDoc - */ -ol.source.TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection) { - if (!ol.ENABLE_RASTER_REPROJECTION || - !this.getProjection() || - !projection || - ol.proj.equivalent(this.getProjection(), projection)) { - return this.getTileInternal(z, x, y, pixelRatio, /** @type {!ol.proj.Projection} */ (projection)); - } else { - var cache = this.getTileCacheForProjection(projection); - var tileCoord = [z, x, y]; - var tile; - var tileCoordKey = this.getKeyZXY.apply(this, tileCoord); - if (cache.containsKey(tileCoordKey)) { - tile = /** @type {!ol.Tile} */ (cache.get(tileCoordKey)); - } - var key = this.getKey(); - if (tile && tile.key == key) { - return tile; - } else { - var sourceProjection = /** @type {!ol.proj.Projection} */ (this.getProjection()); - var sourceTileGrid = this.getTileGridForProjection(sourceProjection); - var targetTileGrid = this.getTileGridForProjection(projection); - var wrappedTileCoord = - this.getTileCoordForTileUrlFunction(tileCoord, projection); - var newTile = new ol.reproj.Tile( - sourceProjection, sourceTileGrid, - projection, targetTileGrid, - tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio), - this.getGutterInternal(), - function(z, x, y, pixelRatio) { - return this.getTileInternal(z, x, y, pixelRatio, sourceProjection); - }.bind(this), this.reprojectionErrorThreshold_, - this.renderReprojectionEdges_); - newTile.key = key; - - if (tile) { - newTile.interimTile = tile; - cache.replace(tileCoordKey, newTile); - } else { - cache.set(tileCoordKey, newTile); - } - return newTile; - } - } -}; - - -/** - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {!ol.proj.Projection} projection Projection. - * @return {!ol.Tile} Tile. - * @protected - */ -ol.source.TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, projection) { - var tile = null; - var tileCoordKey = this.getKeyZXY(z, x, y); - var key = this.getKey(); - if (!this.tileCache.containsKey(tileCoordKey)) { - tile = this.createTile_(z, x, y, pixelRatio, projection, key); - this.tileCache.set(tileCoordKey, tile); - } else { - tile = this.tileCache.get(tileCoordKey); - if (tile.key != key) { - // The source's params changed. If the tile has an interim tile and if we - // can use it then we use it. Otherwise we create a new tile. In both - // cases we attempt to assign an interim tile to the new tile. - var interimTile = tile; - tile = this.createTile_(z, x, y, pixelRatio, projection, key); - - //make the new tile the head of the list, - if (interimTile.getState() == ol.TileState.IDLE) { - //the old tile hasn't begun loading yet, and is now outdated, so we can simply discard it - tile.interimTile = interimTile.interimTile; - } else { - tile.interimTile = interimTile; - } - tile.refreshInterimChain(); - this.tileCache.replace(tileCoordKey, tile); - } - } - return tile; -}; - - -/** - * Sets whether to render reprojection edges or not (usually for debugging). - * @param {boolean} render Render the edges. - * @api - */ -ol.source.TileImage.prototype.setRenderReprojectionEdges = function(render) { - if (!ol.ENABLE_RASTER_REPROJECTION || - this.renderReprojectionEdges_ == render) { - return; - } - this.renderReprojectionEdges_ = render; - for (var id in this.tileCacheForProjection) { - this.tileCacheForProjection[id].clear(); - } - this.changed(); -}; - - -/** - * Sets the tile grid to use when reprojecting the tiles to the given - * projection instead of the default tile grid for the projection. - * - * This can be useful when the default tile grid cannot be created - * (e.g. projection has no extent defined) or - * for optimization reasons (custom tile size, resolutions, ...). - * - * @param {ol.ProjectionLike} projection Projection. - * @param {ol.tilegrid.TileGrid} tilegrid Tile grid to use for the projection. - * @api - */ -ol.source.TileImage.prototype.setTileGridForProjection = function(projection, tilegrid) { - if (ol.ENABLE_RASTER_REPROJECTION) { - var proj = ol.proj.get(projection); - if (proj) { - var projKey = ol.getUid(proj).toString(); - if (!(projKey in this.tileGridForProjection)) { - this.tileGridForProjection[projKey] = tilegrid; - } - } - } -}; - - -/** - * @param {ol.ImageTile} imageTile Image tile. - * @param {string} src Source. - */ -ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) { - imageTile.getImage().src = src; -}; - -goog.provide('ol.source.BingMaps'); - -goog.require('ol'); -goog.require('ol.Attribution'); -goog.require('ol.TileUrlFunction'); -goog.require('ol.extent'); -goog.require('ol.net'); -goog.require('ol.proj'); -goog.require('ol.source.State'); -goog.require('ol.source.TileImage'); -goog.require('ol.tilecoord'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * Layer source for Bing Maps tile data. - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.BingMapsOptions} options Bing Maps options. - * @api - */ -ol.source.BingMaps = function(options) { - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : false; - - ol.source.TileImage.call(this, { - cacheSize: options.cacheSize, - crossOrigin: 'anonymous', - opaque: true, - projection: ol.proj.get('EPSG:3857'), - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - state: ol.source.State.LOADING, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: this.hidpi_ ? 2 : 1, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - - /** - * @private - * @type {string} - */ - this.culture_ = options.culture !== undefined ? options.culture : 'en-us'; - - /** - * @private - * @type {number} - */ - this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1; - - /** - * @private - * @type {string} - */ - this.apiKey_ = options.key; - - /** - * @private - * @type {string} - */ - this.imagerySet_ = options.imagerySet; - - var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' + - this.imagerySet_ + - '?uriScheme=https&include=ImageryProviders&key=' + this.apiKey_; - - ol.net.jsonp(url, this.handleImageryMetadataResponse.bind(this), undefined, - 'jsonp'); - -}; -ol.inherits(ol.source.BingMaps, ol.source.TileImage); - - -/** - * The attribution containing a link to the Microsoft® Bingâ„¢ Maps Platform APIs’ - * Terms Of Use. - * @const - * @type {ol.Attribution} - * @api - */ -ol.source.BingMaps.TOS_ATTRIBUTION = new ol.Attribution({ - html: '<a class="ol-attribution-bing-tos" ' + - 'href="http://www.microsoft.com/maps/product/terms.html">' + - 'Terms of Use</a>' -}); - - -/** - * Get the api key used for this source. - * - * @return {string} The api key. - * @api - */ -ol.source.BingMaps.prototype.getApiKey = function() { - return this.apiKey_; -}; - - -/** - * Get the imagery set associated with this source. - * - * @return {string} The imagery set. - * @api - */ -ol.source.BingMaps.prototype.getImagerySet = function() { - return this.imagerySet_; -}; - - -/** - * @param {BingMapsImageryMetadataResponse} response Response. - */ -ol.source.BingMaps.prototype.handleImageryMetadataResponse = function(response) { - if (response.statusCode != 200 || - response.statusDescription != 'OK' || - response.authenticationResultCode != 'ValidCredentials' || - response.resourceSets.length != 1 || - response.resourceSets[0].resources.length != 1) { - this.setState(ol.source.State.ERROR); - return; - } - - var brandLogoUri = response.brandLogoUri; - if (brandLogoUri.indexOf('https') == -1) { - brandLogoUri = brandLogoUri.replace('http', 'https'); - } - //var copyright = response.copyright; // FIXME do we need to display this? - var resource = response.resourceSets[0].resources[0]; - var maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_; - - var sourceProjection = this.getProjection(); - var extent = ol.tilegrid.extentFromProjection(sourceProjection); - var tileSize = resource.imageWidth == resource.imageHeight ? - resource.imageWidth : [resource.imageWidth, resource.imageHeight]; - var tileGrid = ol.tilegrid.createXYZ({ - extent: extent, - minZoom: resource.zoomMin, - maxZoom: maxZoom, - tileSize: tileSize / this.getTilePixelRatio() - }); - this.tileGrid = tileGrid; - - var culture = this.culture_; - var hidpi = this.hidpi_; - this.tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions( - resource.imageUrlSubdomains.map(function(subdomain) { - var quadKeyTileCoord = [0, 0, 0]; - var imageUrl = resource.imageUrl - .replace('{subdomain}', subdomain) - .replace('{culture}', culture); - return ( - /** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - ol.tilecoord.createOrUpdate(tileCoord[0], tileCoord[1], - -tileCoord[2] - 1, quadKeyTileCoord); - var url = imageUrl; - if (hidpi) { - url += '&dpi=d1&device=mobile'; - } - return url.replace('{quadkey}', ol.tilecoord.quadKey( - quadKeyTileCoord)); - } - }); - })); - - if (resource.imageryProviders) { - var transform = ol.proj.getTransformFromProjections( - ol.proj.get('EPSG:4326'), this.getProjection()); - - var attributions = resource.imageryProviders.map(function(imageryProvider) { - var html = imageryProvider.attribution; - /** @type {Object.<string, Array.<ol.TileRange>>} */ - var tileRanges = {}; - imageryProvider.coverageAreas.forEach(function(coverageArea) { - var minZ = coverageArea.zoomMin; - var maxZ = Math.min(coverageArea.zoomMax, maxZoom); - var bbox = coverageArea.bbox; - var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; - var extent = ol.extent.applyTransform(epsg4326Extent, transform); - var tileRange, z, zKey; - for (z = minZ; z <= maxZ; ++z) { - zKey = z.toString(); - tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - if (zKey in tileRanges) { - tileRanges[zKey].push(tileRange); - } else { - tileRanges[zKey] = [tileRange]; - } - } - }); - return new ol.Attribution({html: html, tileRanges: tileRanges}); - }); - attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION); - this.setAttributions(attributions); - } - - this.setLogo(brandLogoUri); - - this.setState(ol.source.State.READY); -}; - -goog.provide('ol.source.XYZ'); - -goog.require('ol'); -goog.require('ol.source.TileImage'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * Layer source for tile data with URLs in a set XYZ format that are - * defined in a URL template. By default, this follows the widely-used - * Google grid where `x` 0 and `y` 0 are in the top left. Grids like - * TMS where `x` 0 and `y` 0 are in the bottom left can be used by - * using the `{-y}` placeholder in the URL template, so long as the - * source does not have a custom tile grid. In this case, - * {@link ol.source.TileImage} can be used with a `tileUrlFunction` - * such as: - * - * tileUrlFunction: function(coordinate) { - * return 'http://mapserver.com/' + coordinate[0] + '/' + - * coordinate[1] + '/' + coordinate[2] + '.png'; - * } - * - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.XYZOptions=} opt_options XYZ options. - * @api - */ -ol.source.XYZ = function(opt_options) { - var options = opt_options || {}; - var projection = options.projection !== undefined ? - options.projection : 'EPSG:3857'; - - var tileGrid = options.tileGrid !== undefined ? options.tileGrid : - ol.tilegrid.createXYZ({ - extent: ol.tilegrid.extentFromProjection(projection), - maxZoom: options.maxZoom, - minZoom: options.minZoom, - tileSize: options.tileSize - }); - - ol.source.TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - logo: options.logo, - opaque: options.opaque, - projection: projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileGrid: tileGrid, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: options.tileUrlFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - -}; -ol.inherits(ol.source.XYZ, ol.source.TileImage); - -goog.provide('ol.source.CartoDB'); - -goog.require('ol'); -goog.require('ol.obj'); -goog.require('ol.source.State'); -goog.require('ol.source.XYZ'); - - -/** - * @classdesc - * Layer source for the CartoDB tiles. - * - * @constructor - * @extends {ol.source.XYZ} - * @param {olx.source.CartoDBOptions} options CartoDB options. - * @api - */ -ol.source.CartoDB = function(options) { - - /** - * @type {string} - * @private - */ - this.account_ = options.account; - - /** - * @type {string} - * @private - */ - this.mapId_ = options.map || ''; - - /** - * @type {!Object} - * @private - */ - this.config_ = options.config || {}; - - /** - * @type {!Object.<string, CartoDBLayerInfo>} - * @private - */ - this.templateCache_ = {}; - - ol.source.XYZ.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - logo: options.logo, - maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18, - minZoom: options.minZoom, - projection: options.projection, - state: ol.source.State.LOADING, - wrapX: options.wrapX - }); - this.initializeMap_(); -}; -ol.inherits(ol.source.CartoDB, ol.source.XYZ); - - -/** - * Returns the current config. - * @return {!Object} The current configuration. - * @api - */ -ol.source.CartoDB.prototype.getConfig = function() { - return this.config_; -}; - - -/** - * Updates the carto db config. - * @param {Object} config a key-value lookup. Values will replace current values - * in the config. - * @api - */ -ol.source.CartoDB.prototype.updateConfig = function(config) { - ol.obj.assign(this.config_, config); - this.initializeMap_(); -}; - - -/** - * Sets the CartoDB config - * @param {Object} config In the case of anonymous maps, a CartoDB configuration - * object. - * If using named maps, a key-value lookup with the template parameters. - * @api - */ -ol.source.CartoDB.prototype.setConfig = function(config) { - this.config_ = config || {}; - this.initializeMap_(); -}; - - -/** - * Issue a request to initialize the CartoDB map. - * @private - */ -ol.source.CartoDB.prototype.initializeMap_ = function() { - var paramHash = JSON.stringify(this.config_); - if (this.templateCache_[paramHash]) { - this.applyTemplate_(this.templateCache_[paramHash]); - return; - } - var mapUrl = 'https://' + this.account_ + '.cartodb.com/api/v1/map'; - - if (this.mapId_) { - mapUrl += '/named/' + this.mapId_; - } - - var client = new XMLHttpRequest(); - client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash)); - client.addEventListener('error', this.handleInitError_.bind(this)); - client.open('POST', mapUrl); - client.setRequestHeader('Content-type', 'application/json'); - client.send(JSON.stringify(this.config_)); -}; - - -/** - * Handle map initialization response. - * @param {string} paramHash a hash representing the parameter set that was used - * for the request - * @param {Event} event Event. - * @private - */ -ol.source.CartoDB.prototype.handleInitResponse_ = function(paramHash, event) { - var client = /** @type {XMLHttpRequest} */ (event.target); - // status will be 0 for file:// urls - if (!client.status || client.status >= 200 && client.status < 300) { - var response; - try { - response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText)); - } catch (err) { - this.setState(ol.source.State.ERROR); - return; - } - this.applyTemplate_(response); - this.templateCache_[paramHash] = response; - this.setState(ol.source.State.READY); - } else { - this.setState(ol.source.State.ERROR); - } -}; - - -/** - * @private - * @param {Event} event Event. - */ -ol.source.CartoDB.prototype.handleInitError_ = function(event) { - this.setState(ol.source.State.ERROR); -}; - - -/** - * Apply the new tile urls returned by carto db - * @param {CartoDBLayerInfo} data Result of carto db call. - * @private - */ -ol.source.CartoDB.prototype.applyTemplate_ = function(data) { - var tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ + - '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png'; - this.setUrl(tilesUrl); -}; - -// FIXME keep cluster cache by resolution ? -// FIXME distance not respected because of the centroid - -goog.provide('ol.source.Cluster'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.Feature'); -goog.require('ol.coordinate'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.geom.Point'); -goog.require('ol.source.Vector'); - - -/** - * @classdesc - * Layer source to cluster vector data. Works out of the box with point - * geometries. For other geometry types, or if not all geometries should be - * considered for clustering, a custom `geometryFunction` can be defined. - * - * @constructor - * @param {olx.source.ClusterOptions} options Constructor options. - * @extends {ol.source.Vector} - * @api - */ -ol.source.Cluster = function(options) { - ol.source.Vector.call(this, { - attributions: options.attributions, - extent: options.extent, - logo: options.logo, - projection: options.projection, - wrapX: options.wrapX - }); - - /** - * @type {number|undefined} - * @protected - */ - this.resolution = undefined; - - /** - * @type {number} - * @protected - */ - this.distance = options.distance !== undefined ? options.distance : 20; - - /** - * @type {Array.<ol.Feature>} - * @protected - */ - this.features = []; - - /** - * @param {ol.Feature} feature Feature. - * @return {ol.geom.Point} Cluster calculation point. - * @protected - */ - this.geometryFunction = options.geometryFunction || function(feature) { - var geometry = /** @type {ol.geom.Point} */ (feature.getGeometry()); - ol.asserts.assert(geometry instanceof ol.geom.Point, - 10); // The default `geometryFunction` can only handle `ol.geom.Point` geometries - return geometry; - }; - - /** - * @type {ol.source.Vector} - * @protected - */ - this.source = options.source; - - this.source.on(ol.events.EventType.CHANGE, - ol.source.Cluster.prototype.refresh, this); -}; -ol.inherits(ol.source.Cluster, ol.source.Vector); - - -/** - * Get the distance in pixels between clusters. - * @return {number} Distance. - * @api - */ -ol.source.Cluster.prototype.getDistance = function() { - return this.distance; -}; - - -/** - * Get a reference to the wrapped source. - * @return {ol.source.Vector} Source. - * @api - */ -ol.source.Cluster.prototype.getSource = function() { - return this.source; -}; - - -/** - * @inheritDoc - */ -ol.source.Cluster.prototype.loadFeatures = function(extent, resolution, - projection) { - this.source.loadFeatures(extent, resolution, projection); - if (resolution !== this.resolution) { - this.clear(); - this.resolution = resolution; - this.cluster(); - this.addFeatures(this.features); - } -}; - - -/** - * Set the distance in pixels between clusters. - * @param {number} distance The distance in pixels. - * @api - */ -ol.source.Cluster.prototype.setDistance = function(distance) { - this.distance = distance; - this.refresh(); -}; - - -/** - * handle the source changing - * @override - */ -ol.source.Cluster.prototype.refresh = function() { - this.clear(); - this.cluster(); - this.addFeatures(this.features); - ol.source.Vector.prototype.refresh.call(this); -}; - - -/** - * @protected - */ -ol.source.Cluster.prototype.cluster = function() { - if (this.resolution === undefined) { - return; - } - this.features.length = 0; - var extent = ol.extent.createEmpty(); - var mapDistance = this.distance * this.resolution; - var features = this.source.getFeatures(); - - /** - * @type {!Object.<string, boolean>} - */ - var clustered = {}; - - for (var i = 0, ii = features.length; i < ii; i++) { - var feature = features[i]; - if (!(ol.getUid(feature).toString() in clustered)) { - var geometry = this.geometryFunction(feature); - if (geometry) { - var coordinates = geometry.getCoordinates(); - ol.extent.createOrUpdateFromCoordinate(coordinates, extent); - ol.extent.buffer(extent, mapDistance, extent); - - var neighbors = this.source.getFeaturesInExtent(extent); - neighbors = neighbors.filter(function(neighbor) { - var uid = ol.getUid(neighbor).toString(); - if (!(uid in clustered)) { - clustered[uid] = true; - return true; - } else { - return false; - } - }); - this.features.push(this.createCluster(neighbors)); - } - } - } -}; - - -/** - * @param {Array.<ol.Feature>} features Features - * @return {ol.Feature} The cluster feature. - * @protected - */ -ol.source.Cluster.prototype.createCluster = function(features) { - var centroid = [0, 0]; - for (var i = features.length - 1; i >= 0; --i) { - var geometry = this.geometryFunction(features[i]); - if (geometry) { - ol.coordinate.add(centroid, geometry.getCoordinates()); - } else { - features.splice(i, 1); - } - } - ol.coordinate.scale(centroid, 1 / features.length); - - var cluster = new ol.Feature(new ol.geom.Point(centroid)); - cluster.set('features', features); - return cluster; -}; - -goog.provide('ol.uri'); - - -/** - * Appends query parameters to a URI. - * - * @param {string} uri The original URI, which may already have query data. - * @param {!Object} params An object where keys are URI-encoded parameter keys, - * and the values are arbitrary types or arrays. - * @return {string} The new URI. - */ -ol.uri.appendParams = function(uri, params) { - var keyParams = []; - // Skip any null or undefined parameter values - Object.keys(params).forEach(function(k) { - if (params[k] !== null && params[k] !== undefined) { - keyParams.push(k + '=' + encodeURIComponent(params[k])); - } - }); - var qs = keyParams.join('&'); - // remove any trailing ? or & - uri = uri.replace(/[?&]$/, ''); - // append ? or & depending on whether uri has existing parameters - uri = uri.indexOf('?') === -1 ? uri + '?' : uri + '&'; - return uri + qs; -}; - -goog.provide('ol.source.ImageArcGISRest'); - -goog.require('ol'); -goog.require('ol.Image'); -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.source.Image'); -goog.require('ol.uri'); - - -/** - * @classdesc - * Source for data from ArcGIS Rest services providing single, untiled images. - * Useful when underlying map service has labels. - * - * If underlying map service is not using labels, - * take advantage of ol image caching and use - * {@link ol.source.TileArcGISRest} data source. - * - * @constructor - * @fires ol.source.Image.Event - * @extends {ol.source.Image} - * @param {olx.source.ImageArcGISRestOptions=} opt_options Image ArcGIS Rest Options. - * @api - */ -ol.source.ImageArcGISRest = function(opt_options) { - - var options = opt_options || {}; - - ol.source.Image.call(this, { - attributions: options.attributions, - logo: options.logo, - projection: options.projection, - resolutions: options.resolutions - }); - - /** - * @private - * @type {?string} - */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {string|undefined} - */ - this.url_ = options.url; - - /** - * @private - * @type {ol.ImageLoadFunctionType} - */ - this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? - options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; - - - /** - * @private - * @type {!Object} - */ - this.params_ = options.params || {}; - - /** - * @private - * @type {ol.Image} - */ - this.image_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.imageSize_ = [0, 0]; - - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; - -}; -ol.inherits(ol.source.ImageArcGISRest, ol.source.Image); - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ol.source.ImageArcGISRest.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @inheritDoc - */ -ol.source.ImageArcGISRest.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - - if (this.url_ === undefined) { - return null; - } - - resolution = this.findNearestResolution(resolution); - pixelRatio = this.hidpi_ ? pixelRatio : 1; - - var image = this.image_; - if (image && - this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && - image.getPixelRatio() == pixelRatio && - ol.extent.containsExtent(image.getExtent(), extent)) { - return image; - } - - var params = { - 'F': 'image', - 'FORMAT': 'PNG32', - 'TRANSPARENT': true - }; - ol.obj.assign(params, this.params_); - - extent = extent.slice(); - var centerX = (extent[0] + extent[2]) / 2; - var centerY = (extent[1] + extent[3]) / 2; - if (this.ratio_ != 1) { - var halfWidth = this.ratio_ * ol.extent.getWidth(extent) / 2; - var halfHeight = this.ratio_ * ol.extent.getHeight(extent) / 2; - extent[0] = centerX - halfWidth; - extent[1] = centerY - halfHeight; - extent[2] = centerX + halfWidth; - extent[3] = centerY + halfHeight; - } - - var imageResolution = resolution / pixelRatio; - - // Compute an integer width and height. - var width = Math.ceil(ol.extent.getWidth(extent) / imageResolution); - var height = Math.ceil(ol.extent.getHeight(extent) / imageResolution); - - // Modify the extent to match the integer width and height. - extent[0] = centerX - imageResolution * width / 2; - extent[2] = centerX + imageResolution * width / 2; - extent[1] = centerY - imageResolution * height / 2; - extent[3] = centerY + imageResolution * height / 2; - - this.imageSize_[0] = width; - this.imageSize_[1] = height; - - var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio, - projection, params); - - this.image_ = new ol.Image(extent, resolution, pixelRatio, - this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); - - this.renderedRevision_ = this.getRevision(); - - ol.events.listen(this.image_, ol.events.EventType.CHANGE, - this.handleImageChange, this); - - return this.image_; - -}; - - -/** - * Return the image load function of the source. - * @return {ol.ImageLoadFunctionType} The image load function. - * @api - */ -ol.source.ImageArcGISRest.prototype.getImageLoadFunction = function() { - return this.imageLoadFunction_; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {Object} params Params. - * @return {string} Request URL. - * @private - */ -ol.source.ImageArcGISRest.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) { - // ArcGIS Server only wants the numeric portion of the projection ID. - var srid = projection.getCode().split(':').pop(); - - params['SIZE'] = size[0] + ',' + size[1]; - params['BBOX'] = extent.join(','); - params['BBOXSR'] = srid; - params['IMAGESR'] = srid; - params['DPI'] = Math.round(90 * pixelRatio); - - var url = this.url_; - - var modifiedUrl = url - .replace(/MapServer\/?$/, 'MapServer/export') - .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); - if (modifiedUrl == url) { - ol.asserts.assert(false, 50); // `options.featureTypes` should be an Array - } - return ol.uri.appendParams(modifiedUrl, params); -}; - - -/** - * Return the URL used for this ArcGIS source. - * @return {string|undefined} URL. - * @api - */ -ol.source.ImageArcGISRest.prototype.getUrl = function() { - return this.url_; -}; - - -/** - * Set the image load function of the source. - * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. - * @api - */ -ol.source.ImageArcGISRest.prototype.setImageLoadFunction = function(imageLoadFunction) { - this.image_ = null; - this.imageLoadFunction_ = imageLoadFunction; - this.changed(); -}; - - -/** - * Set the URL to use for requests. - * @param {string|undefined} url URL. - * @api - */ -ol.source.ImageArcGISRest.prototype.setUrl = function(url) { - if (url != this.url_) { - this.url_ = url; - this.image_ = null; - this.changed(); - } -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ol.source.ImageArcGISRest.prototype.updateParams = function(params) { - ol.obj.assign(this.params_, params); - this.image_ = null; - this.changed(); -}; - -goog.provide('ol.source.ImageMapGuide'); - -goog.require('ol'); -goog.require('ol.Image'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.source.Image'); -goog.require('ol.uri'); - - -/** - * @classdesc - * Source for images from Mapguide servers - * - * @constructor - * @fires ol.source.Image.Event - * @extends {ol.source.Image} - * @param {olx.source.ImageMapGuideOptions} options Options. - * @api - */ -ol.source.ImageMapGuide = function(options) { - - ol.source.Image.call(this, { - projection: options.projection, - resolutions: options.resolutions - }); - - /** - * @private - * @type {?string} - */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; - - /** - * @private - * @type {number} - */ - this.displayDpi_ = options.displayDpi !== undefined ? - options.displayDpi : 96; - - /** - * @private - * @type {!Object} - */ - this.params_ = options.params || {}; - - /** - * @private - * @type {string|undefined} - */ - this.url_ = options.url; - - /** - * @private - * @type {ol.ImageLoadFunctionType} - */ - this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? - options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {number} - */ - this.metersPerUnit_ = options.metersPerUnit !== undefined ? - options.metersPerUnit : 1; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? options.ratio : 1; - - /** - * @private - * @type {boolean} - */ - this.useOverlay_ = options.useOverlay !== undefined ? - options.useOverlay : false; - - /** - * @private - * @type {ol.Image} - */ - this.image_ = null; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; - -}; -ol.inherits(ol.source.ImageMapGuide, ol.source.Image); - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ol.source.ImageMapGuide.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @inheritDoc - */ -ol.source.ImageMapGuide.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - resolution = this.findNearestResolution(resolution); - pixelRatio = this.hidpi_ ? pixelRatio : 1; - - var image = this.image_; - if (image && - this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && - image.getPixelRatio() == pixelRatio && - ol.extent.containsExtent(image.getExtent(), extent)) { - return image; - } - - if (this.ratio_ != 1) { - extent = extent.slice(); - ol.extent.scaleFromCenter(extent, this.ratio_); - } - var width = ol.extent.getWidth(extent) / resolution; - var height = ol.extent.getHeight(extent) / resolution; - var size = [width * pixelRatio, height * pixelRatio]; - - if (this.url_ !== undefined) { - var imageUrl = this.getUrl(this.url_, this.params_, extent, size, - projection); - image = new ol.Image(extent, resolution, pixelRatio, - this.getAttributions(), imageUrl, this.crossOrigin_, - this.imageLoadFunction_); - ol.events.listen(image, ol.events.EventType.CHANGE, - this.handleImageChange, this); - } else { - image = null; - } - this.image_ = image; - this.renderedRevision_ = this.getRevision(); - - return image; -}; - - -/** - * Return the image load function of the source. - * @return {ol.ImageLoadFunctionType} The image load function. - * @api - */ -ol.source.ImageMapGuide.prototype.getImageLoadFunction = function() { - return this.imageLoadFunction_; -}; - - -/** - * @param {ol.Extent} extent The map extents. - * @param {ol.Size} size The viewport size. - * @param {number} metersPerUnit The meters-per-unit value. - * @param {number} dpi The display resolution. - * @return {number} The computed map scale. - */ -ol.source.ImageMapGuide.getScale = function(extent, size, metersPerUnit, dpi) { - var mcsW = ol.extent.getWidth(extent); - var mcsH = ol.extent.getHeight(extent); - var devW = size[0]; - var devH = size[1]; - var mpp = 0.0254 / dpi; - if (devH * mcsW > devW * mcsH) { - return mcsW * metersPerUnit / (devW * mpp); // width limited - } else { - return mcsH * metersPerUnit / (devH * mpp); // height limited - } -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ol.source.ImageMapGuide.prototype.updateParams = function(params) { - ol.obj.assign(this.params_, params); - this.changed(); -}; - - -/** - * @param {string} baseUrl The mapagent url. - * @param {Object.<string, string|number>} params Request parameters. - * @param {ol.Extent} extent Extent. - * @param {ol.Size} size Size. - * @param {ol.proj.Projection} projection Projection. - * @return {string} The mapagent map image request URL. - */ -ol.source.ImageMapGuide.prototype.getUrl = function(baseUrl, params, extent, size, projection) { - var scale = ol.source.ImageMapGuide.getScale(extent, size, - this.metersPerUnit_, this.displayDpi_); - var center = ol.extent.getCenter(extent); - var baseParams = { - 'OPERATION': this.useOverlay_ ? 'GETDYNAMICMAPOVERLAYIMAGE' : 'GETMAPIMAGE', - 'VERSION': '2.0.0', - 'LOCALE': 'en', - 'CLIENTAGENT': 'ol.source.ImageMapGuide source', - 'CLIP': '1', - 'SETDISPLAYDPI': this.displayDpi_, - 'SETDISPLAYWIDTH': Math.round(size[0]), - 'SETDISPLAYHEIGHT': Math.round(size[1]), - 'SETVIEWSCALE': scale, - 'SETVIEWCENTERX': center[0], - 'SETVIEWCENTERY': center[1] - }; - ol.obj.assign(baseParams, params); - return ol.uri.appendParams(baseUrl, baseParams); -}; - - -/** - * Set the image load function of the MapGuide source. - * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. - * @api - */ -ol.source.ImageMapGuide.prototype.setImageLoadFunction = function( - imageLoadFunction) { - this.image_ = null; - this.imageLoadFunction_ = imageLoadFunction; - this.changed(); -}; - -goog.provide('ol.source.ImageStatic'); - -goog.require('ol'); -goog.require('ol.Image'); -goog.require('ol.ImageState'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.proj'); -goog.require('ol.source.Image'); - - -/** - * @classdesc - * A layer source for displaying a single, static image. - * - * @constructor - * @extends {ol.source.Image} - * @param {olx.source.ImageStaticOptions} options Options. - * @api - */ -ol.source.ImageStatic = function(options) { - var imageExtent = options.imageExtent; - - var crossOrigin = options.crossOrigin !== undefined ? - options.crossOrigin : null; - - var /** @type {ol.ImageLoadFunctionType} */ imageLoadFunction = - options.imageLoadFunction !== undefined ? - options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; - - ol.source.Image.call(this, { - attributions: options.attributions, - logo: options.logo, - projection: ol.proj.get(options.projection) - }); - - /** - * @private - * @type {ol.Image} - */ - this.image_ = new ol.Image(imageExtent, undefined, 1, this.getAttributions(), - options.url, crossOrigin, imageLoadFunction); - - /** - * @private - * @type {ol.Size} - */ - this.imageSize_ = options.imageSize ? options.imageSize : null; - - ol.events.listen(this.image_, ol.events.EventType.CHANGE, - this.handleImageChange, this); - -}; -ol.inherits(ol.source.ImageStatic, ol.source.Image); - - -/** - * @inheritDoc - */ -ol.source.ImageStatic.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - if (ol.extent.intersects(extent, this.image_.getExtent())) { - return this.image_; - } - return null; -}; - - -/** - * @inheritDoc - */ -ol.source.ImageStatic.prototype.handleImageChange = function(evt) { - if (this.image_.getState() == ol.ImageState.LOADED) { - var imageExtent = this.image_.getExtent(); - var image = this.image_.getImage(); - var imageWidth, imageHeight; - if (this.imageSize_) { - imageWidth = this.imageSize_[0]; - imageHeight = this.imageSize_[1]; - } else { - imageWidth = image.width; - imageHeight = image.height; - } - var resolution = ol.extent.getHeight(imageExtent) / imageHeight; - var targetWidth = Math.ceil(ol.extent.getWidth(imageExtent) / resolution); - if (targetWidth != imageWidth) { - var context = ol.dom.createCanvasContext2D(targetWidth, imageHeight); - var canvas = context.canvas; - context.drawImage(image, 0, 0, imageWidth, imageHeight, - 0, 0, canvas.width, canvas.height); - this.image_.setImage(canvas); - } - } - ol.source.Image.prototype.handleImageChange.call(this, evt); -}; - -goog.provide('ol.source.WMSServerType'); - - -/** - * Available server types: `'carmentaserver'`, `'geoserver'`, `'mapserver'`, - * `'qgis'`. These are servers that have vendor parameters beyond the WMS - * specification that OpenLayers can make use of. - * @enum {string} - */ -ol.source.WMSServerType = { - CARMENTA_SERVER: 'carmentaserver', - GEOSERVER: 'geoserver', - MAPSERVER: 'mapserver', - QGIS: 'qgis' -}; - -// FIXME cannot be shared between maps with different projections - -goog.provide('ol.source.ImageWMS'); - -goog.require('ol'); -goog.require('ol.Image'); -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.source.Image'); -goog.require('ol.source.WMSServerType'); -goog.require('ol.string'); -goog.require('ol.uri'); - - -/** - * @classdesc - * Source for WMS servers providing single, untiled images. - * - * @constructor - * @fires ol.source.Image.Event - * @extends {ol.source.Image} - * @param {olx.source.ImageWMSOptions=} opt_options Options. - * @api - */ -ol.source.ImageWMS = function(opt_options) { - - var options = opt_options || {}; - - ol.source.Image.call(this, { - attributions: options.attributions, - logo: options.logo, - projection: options.projection, - resolutions: options.resolutions - }); - - /** - * @private - * @type {?string} - */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; - - /** - * @private - * @type {string|undefined} - */ - this.url_ = options.url; - - /** - * @private - * @type {ol.ImageLoadFunctionType} - */ - this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? - options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; - - /** - * @private - * @type {!Object} - */ - this.params_ = options.params || {}; - - /** - * @private - * @type {boolean} - */ - this.v13_ = true; - this.updateV13_(); - - /** - * @private - * @type {ol.source.WMSServerType|undefined} - */ - this.serverType_ = - /** @type {ol.source.WMSServerType|undefined} */ (options.serverType); - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {ol.Image} - */ - this.image_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.imageSize_ = [0, 0]; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; - -}; -ol.inherits(ol.source.ImageWMS, ol.source.Image); - - -/** - * @const - * @type {ol.Size} - * @private - */ -ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_ = [101, 101]; - - -/** - * Return the GetFeatureInfo URL for the passed coordinate, resolution, and - * projection. Return `undefined` if the GetFeatureInfo URL cannot be - * constructed. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {ol.ProjectionLike} projection Projection. - * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should - * be provided. If `QUERY_LAYERS` is not provided then the layers specified - * in the `LAYERS` parameter will be used. `VERSION` should not be - * specified here. - * @return {string|undefined} GetFeatureInfo URL. - * @api - */ -ol.source.ImageWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { - if (this.url_ === undefined) { - return undefined; - } - - var extent = ol.extent.getForViewAndSize( - coordinate, resolution, 0, - ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_); - - var baseParams = { - 'SERVICE': 'WMS', - 'VERSION': ol.DEFAULT_WMS_VERSION, - 'REQUEST': 'GetFeatureInfo', - 'FORMAT': 'image/png', - 'TRANSPARENT': true, - 'QUERY_LAYERS': this.params_['LAYERS'] - }; - ol.obj.assign(baseParams, this.params_, params); - - var x = Math.floor((coordinate[0] - extent[0]) / resolution); - var y = Math.floor((extent[3] - coordinate[1]) / resolution); - baseParams[this.v13_ ? 'I' : 'X'] = x; - baseParams[this.v13_ ? 'J' : 'Y'] = y; - - return this.getRequestUrl_( - extent, ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_, - 1, ol.proj.get(projection), baseParams); -}; - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ol.source.ImageWMS.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @inheritDoc - */ -ol.source.ImageWMS.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - - if (this.url_ === undefined) { - return null; - } - - resolution = this.findNearestResolution(resolution); - - if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { - pixelRatio = 1; - } - - var imageResolution = resolution / pixelRatio; - - var center = ol.extent.getCenter(extent); - var viewWidth = Math.ceil(ol.extent.getWidth(extent) / imageResolution); - var viewHeight = Math.ceil(ol.extent.getHeight(extent) / imageResolution); - var viewExtent = ol.extent.getForViewAndSize(center, imageResolution, 0, - [viewWidth, viewHeight]); - var requestWidth = Math.ceil(this.ratio_ * ol.extent.getWidth(extent) / imageResolution); - var requestHeight = Math.ceil(this.ratio_ * ol.extent.getHeight(extent) / imageResolution); - var requestExtent = ol.extent.getForViewAndSize(center, imageResolution, 0, - [requestWidth, requestHeight]); - - var image = this.image_; - if (image && - this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && - image.getPixelRatio() == pixelRatio && - ol.extent.containsExtent(image.getExtent(), viewExtent)) { - return image; - } - - var params = { - 'SERVICE': 'WMS', - 'VERSION': ol.DEFAULT_WMS_VERSION, - 'REQUEST': 'GetMap', - 'FORMAT': 'image/png', - 'TRANSPARENT': true - }; - ol.obj.assign(params, this.params_); - - this.imageSize_[0] = Math.round(ol.extent.getWidth(requestExtent) / imageResolution); - this.imageSize_[1] = Math.round(ol.extent.getHeight(requestExtent) / imageResolution); - - var url = this.getRequestUrl_(requestExtent, this.imageSize_, pixelRatio, - projection, params); - - this.image_ = new ol.Image(requestExtent, resolution, pixelRatio, - this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); - - this.renderedRevision_ = this.getRevision(); - - ol.events.listen(this.image_, ol.events.EventType.CHANGE, - this.handleImageChange, this); - - return this.image_; - -}; - - -/** - * Return the image load function of the source. - * @return {ol.ImageLoadFunctionType} The image load function. - * @api - */ -ol.source.ImageWMS.prototype.getImageLoadFunction = function() { - return this.imageLoadFunction_; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {ol.Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {Object} params Params. - * @return {string} Request URL. - * @private - */ -ol.source.ImageWMS.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) { - - ol.asserts.assert(this.url_ !== undefined, 9); // `url` must be configured or set using `#setUrl()` - - params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); - - if (!('STYLES' in this.params_)) { - params['STYLES'] = ''; - } - - if (pixelRatio != 1) { - switch (this.serverType_) { - case ol.source.WMSServerType.GEOSERVER: - var dpi = (90 * pixelRatio + 0.5) | 0; - if ('FORMAT_OPTIONS' in params) { - params['FORMAT_OPTIONS'] += ';dpi:' + dpi; - } else { - params['FORMAT_OPTIONS'] = 'dpi:' + dpi; - } - break; - case ol.source.WMSServerType.MAPSERVER: - params['MAP_RESOLUTION'] = 90 * pixelRatio; - break; - case ol.source.WMSServerType.CARMENTA_SERVER: - case ol.source.WMSServerType.QGIS: - params['DPI'] = 90 * pixelRatio; - break; - default: - ol.asserts.assert(false, 8); // Unknown `serverType` configured - break; - } - } - - params['WIDTH'] = size[0]; - params['HEIGHT'] = size[1]; - - var axisOrientation = projection.getAxisOrientation(); - var bbox; - if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { - bbox = [extent[1], extent[0], extent[3], extent[2]]; - } else { - bbox = extent; - } - params['BBOX'] = bbox.join(','); - - return ol.uri.appendParams(/** @type {string} */ (this.url_), params); -}; - - -/** - * Return the URL used for this WMS source. - * @return {string|undefined} URL. - * @api - */ -ol.source.ImageWMS.prototype.getUrl = function() { - return this.url_; -}; - - -/** - * Set the image load function of the source. - * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. - * @api - */ -ol.source.ImageWMS.prototype.setImageLoadFunction = function( - imageLoadFunction) { - this.image_ = null; - this.imageLoadFunction_ = imageLoadFunction; - this.changed(); -}; - - -/** - * Set the URL to use for requests. - * @param {string|undefined} url URL. - * @api - */ -ol.source.ImageWMS.prototype.setUrl = function(url) { - if (url != this.url_) { - this.url_ = url; - this.image_ = null; - this.changed(); - } -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ol.source.ImageWMS.prototype.updateParams = function(params) { - ol.obj.assign(this.params_, params); - this.updateV13_(); - this.image_ = null; - this.changed(); -}; - - -/** - * @private - */ -ol.source.ImageWMS.prototype.updateV13_ = function() { - var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION; - this.v13_ = ol.string.compareVersions(version, '1.3') >= 0; -}; - -goog.provide('ol.source.OSM'); - -goog.require('ol'); -goog.require('ol.Attribution'); -goog.require('ol.source.XYZ'); - - -/** - * @classdesc - * Layer source for the OpenStreetMap tile server. - * - * @constructor - * @extends {ol.source.XYZ} - * @param {olx.source.OSMOptions=} opt_options Open Street Map options. - * @api - */ -ol.source.OSM = function(opt_options) { - - var options = opt_options || {}; - - var attributions; - if (options.attributions !== undefined) { - attributions = options.attributions; - } else { - attributions = [ol.source.OSM.ATTRIBUTION]; - } - - var crossOrigin = options.crossOrigin !== undefined ? - options.crossOrigin : 'anonymous'; - - var url = options.url !== undefined ? - options.url : 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'; - - ol.source.XYZ.call(this, { - attributions: attributions, - cacheSize: options.cacheSize, - crossOrigin: crossOrigin, - opaque: options.opaque !== undefined ? options.opaque : true, - maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileLoadFunction: options.tileLoadFunction, - url: url, - wrapX: options.wrapX - }); - -}; -ol.inherits(ol.source.OSM, ol.source.XYZ); - - -/** - * The attribution containing a link to the OpenStreetMap Copyright and License - * page. - * @const - * @type {ol.Attribution} - * @api - */ -ol.source.OSM.ATTRIBUTION = new ol.Attribution({ - html: '© ' + - '<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> ' + - 'contributors.' -}); - - -/** - * @fileoverview - * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, unusedLocalVariables, uselessCode, visibility} - */ -goog.provide('ol.ext.pixelworks.Processor'); - -/** @typedef {function(*)} */ -ol.ext.pixelworks.Processor = function() {}; - -(function() {(function (exports) { -'use strict'; - -var hasImageData = true; -try { - new ImageData(10, 10); -} catch (_) { - hasImageData = false; -} -var context = document.createElement('canvas').getContext('2d'); -function newImageData$1(data, width, height) { - if (hasImageData) { - return new ImageData(data, width, height); - } else { - var imageData = context.createImageData(width, height); - imageData.data.set(data); - return imageData; - } -} -var newImageData_1 = newImageData$1; -var util = { - newImageData: newImageData_1 -}; - -var newImageData = util.newImageData; -function createMinion(operation) { - var workerHasImageData = true; - try { - new ImageData(10, 10); - } catch (_) { - workerHasImageData = false; - } - function newWorkerImageData(data, width, height) { - if (workerHasImageData) { - return new ImageData(data, width, height); - } else { - return {data: data, width: width, height: height}; - } - } - return function(data) { - var buffers = data['buffers']; - var meta = data['meta']; - var imageOps = data['imageOps']; - var width = data['width']; - var height = data['height']; - var numBuffers = buffers.length; - var numBytes = buffers[0].byteLength; - var output, b; - if (imageOps) { - var images = new Array(numBuffers); - for (b = 0; b < numBuffers; ++b) { - images[b] = newWorkerImageData( - new Uint8ClampedArray(buffers[b]), width, height); - } - output = operation(images, meta).data; - } else { - output = new Uint8ClampedArray(numBytes); - var arrays = new Array(numBuffers); - var pixels = new Array(numBuffers); - for (b = 0; b < numBuffers; ++b) { - arrays[b] = new Uint8ClampedArray(buffers[b]); - pixels[b] = [0, 0, 0, 0]; - } - for (var i = 0; i < numBytes; i += 4) { - for (var j = 0; j < numBuffers; ++j) { - var array = arrays[j]; - pixels[j][0] = array[i]; - pixels[j][1] = array[i + 1]; - pixels[j][2] = array[i + 2]; - pixels[j][3] = array[i + 3]; - } - var pixel = operation(pixels, meta); - output[i] = pixel[0]; - output[i + 1] = pixel[1]; - output[i + 2] = pixel[2]; - output[i + 3] = pixel[3]; - } - } - return output.buffer; - }; -} -function createWorker(config, onMessage) { - var lib = Object.keys(config.lib || {}).map(function(name) { - return 'var ' + name + ' = ' + config.lib[name].toString() + ';'; - }); - var lines = lib.concat([ - 'var __minion__ = (' + createMinion.toString() + ')(', config.operation.toString(), ');', - 'self.addEventListener("message", function(event) {', - ' var buffer = __minion__(event.data);', - ' self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);', - '});' - ]); - var blob = new Blob(lines, {type: 'text/javascript'}); - var source = URL.createObjectURL(blob); - var worker = new Worker(source); - worker.addEventListener('message', onMessage); - return worker; -} -function createFauxWorker(config, onMessage) { - var minion = createMinion(config.operation); - return { - postMessage: function(data) { - setTimeout(function() { - onMessage({'data': {'buffer': minion(data), 'meta': data['meta']}}); - }, 0); - } - }; -} -function Processor(config) { - this._imageOps = !!config.imageOps; - var threads; - if (config.threads === 0) { - threads = 0; - } else if (this._imageOps) { - threads = 1; - } else { - threads = config.threads || 1; - } - var workers = []; - if (threads) { - for (var i = 0; i < threads; ++i) { - workers[i] = createWorker(config, this._onWorkerMessage.bind(this, i)); - } - } else { - workers[0] = createFauxWorker(config, this._onWorkerMessage.bind(this, 0)); - } - this._workers = workers; - this._queue = []; - this._maxQueueLength = config.queue || Infinity; - this._running = 0; - this._dataLookup = {}; - this._job = null; -} -Processor.prototype.process = function(inputs, meta, callback) { - this._enqueue({ - inputs: inputs, - meta: meta, - callback: callback - }); - this._dispatch(); -}; -Processor.prototype.destroy = function() { - for (var key in this) { - this[key] = null; - } - this._destroyed = true; -}; -Processor.prototype._enqueue = function(job) { - this._queue.push(job); - while (this._queue.length > this._maxQueueLength) { - this._queue.shift().callback(null, null); - } -}; -Processor.prototype._dispatch = function() { - if (this._running === 0 && this._queue.length > 0) { - var job = this._job = this._queue.shift(); - var width = job.inputs[0].width; - var height = job.inputs[0].height; - var buffers = job.inputs.map(function(input) { - return input.data.buffer; - }); - var threads = this._workers.length; - this._running = threads; - if (threads === 1) { - this._workers[0].postMessage({ - 'buffers': buffers, - 'meta': job.meta, - 'imageOps': this._imageOps, - 'width': width, - 'height': height - }, buffers); - } else { - var length = job.inputs[0].data.length; - var segmentLength = 4 * Math.ceil(length / 4 / threads); - for (var i = 0; i < threads; ++i) { - var offset = i * segmentLength; - var slices = []; - for (var j = 0, jj = buffers.length; j < jj; ++j) { - slices.push(buffers[i].slice(offset, offset + segmentLength)); - } - this._workers[i].postMessage({ - 'buffers': slices, - 'meta': job.meta, - 'imageOps': this._imageOps, - 'width': width, - 'height': height - }, slices); - } - } - } -}; -Processor.prototype._onWorkerMessage = function(index, event) { - if (this._destroyed) { - return; - } - this._dataLookup[index] = event.data; - --this._running; - if (this._running === 0) { - this._resolveJob(); - } -}; -Processor.prototype._resolveJob = function() { - var job = this._job; - var threads = this._workers.length; - var data, meta; - if (threads === 1) { - data = new Uint8ClampedArray(this._dataLookup[0]['buffer']); - meta = this._dataLookup[0]['meta']; - } else { - var length = job.inputs[0].data.length; - data = new Uint8ClampedArray(length); - meta = new Array(length); - var segmentLength = 4 * Math.ceil(length / 4 / threads); - for (var i = 0; i < threads; ++i) { - var buffer = this._dataLookup[i]['buffer']; - var offset = i * segmentLength; - data.set(new Uint8ClampedArray(buffer), offset); - meta[i] = this._dataLookup[i]['meta']; - } - } - this._job = null; - this._dataLookup = {}; - job.callback(null, - newImageData(data, job.inputs[0].width, job.inputs[0].height), meta); - this._dispatch(); -}; -var processor = Processor; - -var Processor_1 = processor; -var index = { - Processor: Processor_1 -}; - -exports['default'] = index; -exports.Processor = Processor_1; - -}((this.pixelworks = this.pixelworks || {})));}).call(ol.ext); - -goog.provide('ol.source.RasterOperationType'); - -/** - * Raster operation type. Supported values are `'pixel'` and `'image'`. - * @enum {string} - */ -ol.source.RasterOperationType = { - PIXEL: 'pixel', - IMAGE: 'image' -}; - -goog.provide('ol.source.Raster'); - -goog.require('ol'); -goog.require('ol.ImageCanvas'); -goog.require('ol.TileQueue'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.events.Event'); -goog.require('ol.events.EventType'); -goog.require('ol.ext.pixelworks.Processor'); -goog.require('ol.extent'); -goog.require('ol.layer.Image'); -goog.require('ol.layer.Tile'); -goog.require('ol.obj'); -goog.require('ol.renderer.canvas.ImageLayer'); -goog.require('ol.renderer.canvas.TileLayer'); -goog.require('ol.source.Image'); -goog.require('ol.source.RasterOperationType'); -goog.require('ol.source.State'); -goog.require('ol.source.Tile'); -goog.require('ol.transform'); - - -/** - * @classdesc - * A source that transforms data from any number of input sources using an array - * of {@link ol.RasterOperation} functions to transform input pixel values into - * output pixel values. - * - * @constructor - * @extends {ol.source.Image} - * @fires ol.source.Raster.Event - * @param {olx.source.RasterOptions} options Options. - * @api - */ -ol.source.Raster = function(options) { - - /** - * @private - * @type {*} - */ - this.worker_ = null; - - /** - * @private - * @type {ol.source.RasterOperationType} - */ - this.operationType_ = options.operationType !== undefined ? - options.operationType : ol.source.RasterOperationType.PIXEL; - - /** - * @private - * @type {number} - */ - this.threads_ = options.threads !== undefined ? options.threads : 1; - - /** - * @private - * @type {Array.<ol.renderer.canvas.Layer>} - */ - this.renderers_ = ol.source.Raster.createRenderers_(options.sources); - - for (var r = 0, rr = this.renderers_.length; r < rr; ++r) { - ol.events.listen(this.renderers_[r], ol.events.EventType.CHANGE, - this.changed, this); - } - - /** - * @private - * @type {ol.TileQueue} - */ - this.tileQueue_ = new ol.TileQueue( - function() { - return 1; - }, - this.changed.bind(this)); - - var layerStatesArray = ol.source.Raster.getLayerStatesArray_(this.renderers_); - var layerStates = {}; - for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerStates[ol.getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; - } - - /** - * The most recently requested frame state. - * @type {olx.FrameState} - * @private - */ - this.requestedFrameState_; - - /** - * The most recently rendered image canvas. - * @type {ol.ImageCanvas} - * @private - */ - this.renderedImageCanvas_ = null; - - /** - * The most recently rendered revision. - * @type {number} - */ - this.renderedRevision_; - - /** - * @private - * @type {olx.FrameState} - */ - this.frameState_ = { - animate: false, - attributions: {}, - coordinateToPixelTransform: ol.transform.create(), - extent: null, - focus: null, - index: 0, - layerStates: layerStates, - layerStatesArray: layerStatesArray, - logos: {}, - pixelRatio: 1, - pixelToCoordinateTransform: ol.transform.create(), - postRenderFunctions: [], - size: [0, 0], - skippedFeatureUids: {}, - tileQueue: this.tileQueue_, - time: Date.now(), - usedTiles: {}, - viewState: /** @type {olx.ViewState} */ ({ - rotation: 0 - }), - viewHints: [], - wantedTiles: {} - }; - - ol.source.Image.call(this, {}); - - if (options.operation !== undefined) { - this.setOperation(options.operation, options.lib); - } - -}; -ol.inherits(ol.source.Raster, ol.source.Image); - - -/** - * Set the operation. - * @param {ol.RasterOperation} operation New operation. - * @param {Object=} opt_lib Functions that will be available to operations run - * in a worker. - * @api - */ -ol.source.Raster.prototype.setOperation = function(operation, opt_lib) { - this.worker_ = new ol.ext.pixelworks.Processor({ - operation: operation, - imageOps: this.operationType_ === ol.source.RasterOperationType.IMAGE, - queue: 1, - lib: opt_lib, - threads: this.threads_ - }); - this.changed(); -}; - - -/** - * Update the stored frame state. - * @param {ol.Extent} extent The view extent (in map units). - * @param {number} resolution The view resolution. - * @param {ol.proj.Projection} projection The view projection. - * @return {olx.FrameState} The updated frame state. - * @private - */ -ol.source.Raster.prototype.updateFrameState_ = function(extent, resolution, projection) { - - var frameState = /** @type {olx.FrameState} */ ( - ol.obj.assign({}, this.frameState_)); - - frameState.viewState = /** @type {olx.ViewState} */ ( - ol.obj.assign({}, frameState.viewState)); - - var center = ol.extent.getCenter(extent); - - frameState.extent = extent.slice(); - frameState.focus = center; - frameState.size[0] = Math.round(ol.extent.getWidth(extent) / resolution); - frameState.size[1] = Math.round(ol.extent.getHeight(extent) / resolution); - - var viewState = frameState.viewState; - viewState.center = center; - viewState.projection = projection; - viewState.resolution = resolution; - return frameState; -}; - - -/** - * Determine if all sources are ready. - * @return {boolean} All sources are ready. - * @private - */ -ol.source.Raster.prototype.allSourcesReady_ = function() { - var ready = true; - var source; - for (var i = 0, ii = this.renderers_.length; i < ii; ++i) { - source = this.renderers_[i].getLayer().getSource(); - if (source.getState() !== ol.source.State.READY) { - ready = false; - break; - } - } - return ready; -}; - - -/** - * @inheritDoc - */ -ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, projection) { - if (!this.allSourcesReady_()) { - return null; - } - - var frameState = this.updateFrameState_(extent, resolution, projection); - this.requestedFrameState_ = frameState; - - frameState.tileQueue.loadMoreTiles(16, 16); - - // check if we can't reuse the existing ol.ImageCanvas - if (this.renderedImageCanvas_) { - var renderedResolution = this.renderedImageCanvas_.getResolution(); - var renderedExtent = this.renderedImageCanvas_.getExtent(); - if (resolution !== renderedResolution || !ol.extent.equals(extent, renderedExtent)) { - this.renderedImageCanvas_ = null; - } - } - - if (!this.renderedImageCanvas_ || this.getRevision() !== this.renderedRevision_) { - this.processSources_(); - } - - return this.renderedImageCanvas_; -}; - - -/** - * Start processing source data. - * @private - */ -ol.source.Raster.prototype.processSources_ = function() { - var frameState = this.requestedFrameState_; - var len = this.renderers_.length; - var imageDatas = new Array(len); - for (var i = 0; i < len; ++i) { - var imageData = ol.source.Raster.getImageData_( - this.renderers_[i], frameState, frameState.layerStatesArray[i]); - if (imageData) { - imageDatas[i] = imageData; - } else { - return; - } - } - - var data = {}; - this.dispatchEvent(new ol.source.Raster.Event( - ol.source.Raster.EventType_.BEFOREOPERATIONS, frameState, data)); - this.worker_.process(imageDatas, data, - this.onWorkerComplete_.bind(this, frameState)); -}; - - -/** - * Called when pixel processing is complete. - * @param {olx.FrameState} frameState The frame state. - * @param {Error} err Any error during processing. - * @param {ImageData} output The output image data. - * @param {Object} data The user data. - * @private - */ -ol.source.Raster.prototype.onWorkerComplete_ = function(frameState, err, output, data) { - if (err || !output) { - return; - } - - // do nothing if extent or resolution changed - var extent = frameState.extent; - var resolution = frameState.viewState.resolution; - if (resolution !== this.requestedFrameState_.viewState.resolution || - !ol.extent.equals(extent, this.requestedFrameState_.extent)) { - return; - } - - var context; - if (this.renderedImageCanvas_) { - context = this.renderedImageCanvas_.getImage().getContext('2d'); - } else { - var width = Math.round(ol.extent.getWidth(extent) / resolution); - var height = Math.round(ol.extent.getHeight(extent) / resolution); - context = ol.dom.createCanvasContext2D(width, height); - this.renderedImageCanvas_ = new ol.ImageCanvas( - extent, resolution, 1, this.getAttributions(), context.canvas); - } - context.putImageData(output, 0, 0); - - this.changed(); - this.renderedRevision_ = this.getRevision(); - - this.dispatchEvent(new ol.source.Raster.Event( - ol.source.Raster.EventType_.AFTEROPERATIONS, frameState, data)); -}; - - -/** - * Get image data from a renderer. - * @param {ol.renderer.canvas.Layer} renderer Layer renderer. - * @param {olx.FrameState} frameState The frame state. - * @param {ol.LayerState} layerState The layer state. - * @return {ImageData} The image data. - * @private - */ -ol.source.Raster.getImageData_ = function(renderer, frameState, layerState) { - if (!renderer.prepareFrame(frameState, layerState)) { - return null; - } - var width = frameState.size[0]; - var height = frameState.size[1]; - if (!ol.source.Raster.context_) { - ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height); - } else { - var canvas = ol.source.Raster.context_.canvas; - if (canvas.width !== width || canvas.height !== height) { - ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height); - } else { - ol.source.Raster.context_.clearRect(0, 0, width, height); - } - } - renderer.composeFrame(frameState, layerState, ol.source.Raster.context_); - return ol.source.Raster.context_.getImageData(0, 0, width, height); -}; - - -/** - * A reusable canvas context. - * @type {CanvasRenderingContext2D} - * @private - */ -ol.source.Raster.context_ = null; - - -/** - * Get a list of layer states from a list of renderers. - * @param {Array.<ol.renderer.canvas.Layer>} renderers Layer renderers. - * @return {Array.<ol.LayerState>} The layer states. - * @private - */ -ol.source.Raster.getLayerStatesArray_ = function(renderers) { - return renderers.map(function(renderer) { - return renderer.getLayer().getLayerState(); - }); -}; - - -/** - * Create renderers for all sources. - * @param {Array.<ol.source.Source>} sources The sources. - * @return {Array.<ol.renderer.canvas.Layer>} Array of layer renderers. - * @private - */ -ol.source.Raster.createRenderers_ = function(sources) { - var len = sources.length; - var renderers = new Array(len); - for (var i = 0; i < len; ++i) { - renderers[i] = ol.source.Raster.createRenderer_(sources[i]); - } - return renderers; -}; - - -/** - * Create a renderer for the provided source. - * @param {ol.source.Source} source The source. - * @return {ol.renderer.canvas.Layer} The renderer. - * @private - */ -ol.source.Raster.createRenderer_ = function(source) { - var renderer = null; - if (source instanceof ol.source.Tile) { - renderer = ol.source.Raster.createTileRenderer_(source); - } else if (source instanceof ol.source.Image) { - renderer = ol.source.Raster.createImageRenderer_(source); - } - return renderer; -}; - - -/** - * Create an image renderer for the provided source. - * @param {ol.source.Image} source The source. - * @return {ol.renderer.canvas.Layer} The renderer. - * @private - */ -ol.source.Raster.createImageRenderer_ = function(source) { - var layer = new ol.layer.Image({source: source}); - return new ol.renderer.canvas.ImageLayer(layer); -}; - - -/** - * Create a tile renderer for the provided source. - * @param {ol.source.Tile} source The source. - * @return {ol.renderer.canvas.Layer} The renderer. - * @private - */ -ol.source.Raster.createTileRenderer_ = function(source) { - var layer = new ol.layer.Tile({source: source}); - return new ol.renderer.canvas.TileLayer(layer); -}; - - -/** - * @classdesc - * Events emitted by {@link ol.source.Raster} instances are instances of this - * type. - * - * @constructor - * @extends {ol.events.Event} - * @implements {oli.source.RasterEvent} - * @param {string} type Type. - * @param {olx.FrameState} frameState The frame state. - * @param {Object} data An object made available to operations. - */ -ol.source.Raster.Event = function(type, frameState, data) { - ol.events.Event.call(this, type); - - /** - * The raster extent. - * @type {ol.Extent} - * @api - */ - this.extent = frameState.extent; - - /** - * The pixel resolution (map units per pixel). - * @type {number} - * @api - */ - this.resolution = frameState.viewState.resolution / frameState.pixelRatio; - - /** - * An object made available to all operations. This can be used by operations - * as a storage object (e.g. for calculating statistics). - * @type {Object} - * @api - */ - this.data = data; - -}; -ol.inherits(ol.source.Raster.Event, ol.events.Event); - - -/** - * @override - */ -ol.source.Raster.prototype.getImageInternal = function() { - return null; // not implemented -}; - - -/** - * @enum {string} - * @private - */ -ol.source.Raster.EventType_ = { - /** - * Triggered before operations are run. - * @event ol.source.Raster.Event#beforeoperations - * @api - */ - BEFOREOPERATIONS: 'beforeoperations', - - /** - * Triggered after operations are run. - * @event ol.source.Raster.Event#afteroperations - * @api - */ - AFTEROPERATIONS: 'afteroperations' -}; - -goog.provide('ol.source.Stamen'); - -goog.require('ol'); -goog.require('ol.Attribution'); -goog.require('ol.source.OSM'); -goog.require('ol.source.XYZ'); - - -/** - * @classdesc - * Layer source for the Stamen tile server. - * - * @constructor - * @extends {ol.source.XYZ} - * @param {olx.source.StamenOptions} options Stamen options. - * @api - */ -ol.source.Stamen = function(options) { - var i = options.layer.indexOf('-'); - var provider = i == -1 ? options.layer : options.layer.slice(0, i); - var providerConfig = ol.source.Stamen.ProviderConfig[provider]; - - var layerConfig = ol.source.Stamen.LayerConfig[options.layer]; - - var url = options.url !== undefined ? options.url : - 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' + options.layer + - '/{z}/{x}/{y}.' + layerConfig.extension; - - ol.source.XYZ.call(this, { - attributions: ol.source.Stamen.ATTRIBUTIONS, - cacheSize: options.cacheSize, - crossOrigin: 'anonymous', - maxZoom: options.maxZoom != undefined ? options.maxZoom : providerConfig.maxZoom, - minZoom: options.minZoom != undefined ? options.minZoom : providerConfig.minZoom, - opaque: layerConfig.opaque, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileLoadFunction: options.tileLoadFunction, - url: url, - wrapX: options.wrapX - }); -}; -ol.inherits(ol.source.Stamen, ol.source.XYZ); - - -/** - * @const - * @type {Array.<ol.Attribution>} - */ -ol.source.Stamen.ATTRIBUTIONS = [ - new ol.Attribution({ - html: 'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, ' + - 'under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY' + - ' 3.0</a>.' - }), - ol.source.OSM.ATTRIBUTION -]; - -/** - * @type {Object.<string, {extension: string, opaque: boolean}>} - */ -ol.source.Stamen.LayerConfig = { - 'terrain': { - extension: 'jpg', - opaque: true - }, - 'terrain-background': { - extension: 'jpg', - opaque: true - }, - 'terrain-labels': { - extension: 'png', - opaque: false - }, - 'terrain-lines': { - extension: 'png', - opaque: false - }, - 'toner-background': { - extension: 'png', - opaque: true - }, - 'toner': { - extension: 'png', - opaque: true - }, - 'toner-hybrid': { - extension: 'png', - opaque: false - }, - 'toner-labels': { - extension: 'png', - opaque: false - }, - 'toner-lines': { - extension: 'png', - opaque: false - }, - 'toner-lite': { - extension: 'png', - opaque: true - }, - 'watercolor': { - extension: 'jpg', - opaque: true - } -}; - -/** - * @type {Object.<string, {minZoom: number, maxZoom: number}>} - */ -ol.source.Stamen.ProviderConfig = { - 'terrain': { - minZoom: 4, - maxZoom: 18 - }, - 'toner': { - minZoom: 0, - maxZoom: 20 - }, - 'watercolor': { - minZoom: 1, - maxZoom: 16 - } -}; - -goog.provide('ol.source.TileArcGISRest'); - -goog.require('ol'); -goog.require('ol.extent'); -goog.require('ol.math'); -goog.require('ol.obj'); -goog.require('ol.size'); -goog.require('ol.source.TileImage'); -goog.require('ol.tilecoord'); -goog.require('ol.uri'); - - -/** - * @classdesc - * Layer source for tile data from ArcGIS Rest services. Map and Image - * Services are supported. - * - * For cached ArcGIS services, better performance is available using the - * {@link ol.source.XYZ} data source. - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.TileArcGISRestOptions=} opt_options Tile ArcGIS Rest - * options. - * @api - */ -ol.source.TileArcGISRest = function(opt_options) { - - var options = opt_options || {}; - - ol.source.TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - logo: options.logo, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - - /** - * @private - * @type {!Object} - */ - this.params_ = options.params || {}; - - /** - * @private - * @type {ol.Extent} - */ - this.tmpExtent_ = ol.extent.createEmpty(); - - this.setKey(this.getKeyForParams_()); -}; -ol.inherits(ol.source.TileArcGISRest, ol.source.TileImage); - - -/** - * @private - * @return {string} The key for the current params. - */ -ol.source.TileArcGISRest.prototype.getKeyForParams_ = function() { - var i = 0; - var res = []; - for (var key in this.params_) { - res[i++] = key + '-' + this.params_[key]; - } - return res.join('/'); -}; - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ol.source.TileArcGISRest.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.Size} tileSize Tile size. - * @param {ol.Extent} tileExtent Tile extent. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {Object} params Params. - * @return {string|undefined} Request URL. - * @private - */ -ol.source.TileArcGISRest.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent, - pixelRatio, projection, params) { - - var urls = this.urls; - if (!urls) { - return undefined; - } - - // ArcGIS Server only wants the numeric portion of the projection ID. - var srid = projection.getCode().split(':').pop(); - - params['SIZE'] = tileSize[0] + ',' + tileSize[1]; - params['BBOX'] = tileExtent.join(','); - params['BBOXSR'] = srid; - params['IMAGESR'] = srid; - params['DPI'] = Math.round( - params['DPI'] ? params['DPI'] * pixelRatio : 90 * pixelRatio - ); - - var url; - if (urls.length == 1) { - url = urls[0]; - } else { - var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length); - url = urls[index]; - } - - var modifiedUrl = url - .replace(/MapServer\/?$/, 'MapServer/export') - .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); - return ol.uri.appendParams(modifiedUrl, params); -}; - - -/** - * @inheritDoc - */ -ol.source.TileArcGISRest.prototype.getTilePixelRatio = function(pixelRatio) { - return /** @type {number} */ (pixelRatio); -}; - - -/** - * @inheritDoc - */ -ol.source.TileArcGISRest.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) { - - var tileGrid = this.getTileGrid(); - if (!tileGrid) { - tileGrid = this.getTileGridForProjection(projection); - } - - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return undefined; - } - - var tileExtent = tileGrid.getTileCoordExtent( - tileCoord, this.tmpExtent_); - var tileSize = ol.size.toSize( - tileGrid.getTileSize(tileCoord[0]), this.tmpSize); - - if (pixelRatio != 1) { - tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize); - } - - // Apply default params and override with user specified values. - var baseParams = { - 'F': 'image', - 'FORMAT': 'PNG32', - 'TRANSPARENT': true - }; - ol.obj.assign(baseParams, this.params_); - - return this.getRequestUrl_(tileCoord, tileSize, tileExtent, - pixelRatio, projection, baseParams); -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ol.source.TileArcGISRest.prototype.updateParams = function(params) { - ol.obj.assign(this.params_, params); - this.setKey(this.getKeyForParams_()); -}; - -goog.provide('ol.source.TileDebug'); - -goog.require('ol'); -goog.require('ol.Tile'); -goog.require('ol.TileState'); -goog.require('ol.dom'); -goog.require('ol.size'); -goog.require('ol.source.Tile'); - - -/** - * @classdesc - * A pseudo tile source, which does not fetch tiles from a server, but renders - * a grid outline for the tile grid/projection along with the coordinates for - * each tile. See examples/canvas-tiles for an example. - * - * Uses Canvas context2d, so requires Canvas support. - * - * @constructor - * @extends {ol.source.Tile} - * @param {olx.source.TileDebugOptions} options Debug tile options. - * @api - */ -ol.source.TileDebug = function(options) { - - ol.source.Tile.call(this, { - opaque: false, - projection: options.projection, - tileGrid: options.tileGrid, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - -}; -ol.inherits(ol.source.TileDebug, ol.source.Tile); - - -/** - * @inheritDoc - */ -ol.source.TileDebug.prototype.getTile = function(z, x, y) { - var tileCoordKey = this.getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - return /** @type {!ol.source.TileDebug.Tile_} */ (this.tileCache.get(tileCoordKey)); - } else { - var tileSize = ol.size.toSize(this.tileGrid.getTileSize(z)); - var tileCoord = [z, x, y]; - var textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord); - var text = !textTileCoord ? '' : - this.getTileCoordForTileUrlFunction(textTileCoord).toString(); - var tile = new ol.source.TileDebug.Tile_(tileCoord, tileSize, text); - this.tileCache.set(tileCoordKey, tile); - return tile; - } -}; - - -/** - * @constructor - * @extends {ol.Tile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.Size} tileSize Tile size. - * @param {string} text Text. - * @private - */ -ol.source.TileDebug.Tile_ = function(tileCoord, tileSize, text) { - - ol.Tile.call(this, tileCoord, ol.TileState.LOADED); - - /** - * @private - * @type {ol.Size} - */ - this.tileSize_ = tileSize; - - /** - * @private - * @type {string} - */ - this.text_ = text; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; - -}; -ol.inherits(ol.source.TileDebug.Tile_, ol.Tile); - - -/** - * Get the image element for this tile. - * @return {HTMLCanvasElement} Image. - */ -ol.source.TileDebug.Tile_.prototype.getImage = function() { - if (this.canvas_) { - return this.canvas_; - } else { - var tileSize = this.tileSize_; - var context = ol.dom.createCanvasContext2D(tileSize[0], tileSize[1]); - - context.strokeStyle = 'black'; - context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5); - - context.fillStyle = 'black'; - context.textAlign = 'center'; - context.textBaseline = 'middle'; - context.font = '24px sans-serif'; - context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2); - - this.canvas_ = context.canvas; - return context.canvas; - } -}; - - -/** - * @override - */ -ol.source.TileDebug.Tile_.prototype.load = function() {}; - -// FIXME check order of async callbacks - -/** - * @see http://mapbox.com/developers/api/ - */ - -goog.provide('ol.source.TileJSON'); - -goog.require('ol'); -goog.require('ol.Attribution'); -goog.require('ol.TileUrlFunction'); -goog.require('ol.asserts'); -goog.require('ol.extent'); -goog.require('ol.net'); -goog.require('ol.proj'); -goog.require('ol.source.State'); -goog.require('ol.source.TileImage'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * Layer source for tile data in TileJSON format. - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.TileJSONOptions} options TileJSON options. - * @api - */ -ol.source.TileJSON = function(options) { - - /** - * @type {TileJSON} - * @private - */ - this.tileJSON_ = null; - - ol.source.TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - projection: ol.proj.get('EPSG:3857'), - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - state: ol.source.State.LOADING, - tileLoadFunction: options.tileLoadFunction, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - - if (options.url) { - if (options.jsonp) { - ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this), - this.handleTileJSONError.bind(this)); - } else { - var client = new XMLHttpRequest(); - client.addEventListener('load', this.onXHRLoad_.bind(this)); - client.addEventListener('error', this.onXHRError_.bind(this)); - client.open('GET', options.url); - client.send(); - } - } else if (options.tileJSON) { - this.handleTileJSONResponse(options.tileJSON); - } else { - ol.asserts.assert(false, 51); // Either `url` or `tileJSON` options must be provided - } - -}; -ol.inherits(ol.source.TileJSON, ol.source.TileImage); - - -/** - * @private - * @param {Event} event The load event. - */ -ol.source.TileJSON.prototype.onXHRLoad_ = function(event) { - var client = /** @type {XMLHttpRequest} */ (event.target); - // status will be 0 for file:// urls - if (!client.status || client.status >= 200 && client.status < 300) { - var response; - try { - response = /** @type {TileJSON} */(JSON.parse(client.responseText)); - } catch (err) { - this.handleTileJSONError(); - return; - } - this.handleTileJSONResponse(response); - } else { - this.handleTileJSONError(); - } -}; - - -/** - * @private - * @param {Event} event The error event. - */ -ol.source.TileJSON.prototype.onXHRError_ = function(event) { - this.handleTileJSONError(); -}; - - -/** - * @return {TileJSON} The tilejson object. - * @api - */ -ol.source.TileJSON.prototype.getTileJSON = function() { - return this.tileJSON_; -}; - - -/** - * @protected - * @param {TileJSON} tileJSON Tile JSON. - */ -ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) { - - var epsg4326Projection = ol.proj.get('EPSG:4326'); - - var sourceProjection = this.getProjection(); - var extent; - if (tileJSON.bounds !== undefined) { - var transform = ol.proj.getTransformFromProjections( - epsg4326Projection, sourceProjection); - extent = ol.extent.applyTransform(tileJSON.bounds, transform); - } - - var minZoom = tileJSON.minzoom || 0; - var maxZoom = tileJSON.maxzoom || 22; - var tileGrid = ol.tilegrid.createXYZ({ - extent: ol.tilegrid.extentFromProjection(sourceProjection), - maxZoom: maxZoom, - minZoom: minZoom - }); - this.tileGrid = tileGrid; - - this.tileUrlFunction = - ol.TileUrlFunction.createFromTemplates(tileJSON.tiles, tileGrid); - - if (tileJSON.attribution !== undefined && !this.getAttributions()) { - var attributionExtent = extent !== undefined ? - extent : epsg4326Projection.getExtent(); - /** @type {Object.<string, Array.<ol.TileRange>>} */ - var tileRanges = {}; - var z, zKey; - for (z = minZoom; z <= maxZoom; ++z) { - zKey = z.toString(); - tileRanges[zKey] = - [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; - } - this.setAttributions([ - new ol.Attribution({ - html: tileJSON.attribution, - tileRanges: tileRanges - }) - ]); - } - this.tileJSON_ = tileJSON; - this.setState(ol.source.State.READY); - -}; - - -/** - * @protected - */ -ol.source.TileJSON.prototype.handleTileJSONError = function() { - this.setState(ol.source.State.ERROR); -}; - -goog.provide('ol.source.TileUTFGrid'); - -goog.require('ol'); -goog.require('ol.Attribution'); -goog.require('ol.Tile'); -goog.require('ol.TileState'); -goog.require('ol.TileUrlFunction'); -goog.require('ol.asserts'); -goog.require('ol.events'); -goog.require('ol.events.EventType'); -goog.require('ol.extent'); -goog.require('ol.net'); -goog.require('ol.proj'); -goog.require('ol.source.State'); -goog.require('ol.source.Tile'); -goog.require('ol.tilegrid'); - - -/** - * @classdesc - * Layer source for UTFGrid interaction data loaded from TileJSON format. - * - * @constructor - * @extends {ol.source.Tile} - * @param {olx.source.TileUTFGridOptions} options Source options. - * @api - */ -ol.source.TileUTFGrid = function(options) { - ol.source.Tile.call(this, { - projection: ol.proj.get('EPSG:3857'), - state: ol.source.State.LOADING - }); - - /** - * @private - * @type {boolean} - */ - this.preemptive_ = options.preemptive !== undefined ? - options.preemptive : true; - - /** - * @private - * @type {!ol.TileUrlFunctionType} - */ - this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction; - - /** - * @private - * @type {string|undefined} - */ - this.template_ = undefined; - - /** - * @private - * @type {boolean} - */ - this.jsonp_ = options.jsonp || false; - - if (options.url) { - if (this.jsonp_) { - ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this), - this.handleTileJSONError.bind(this)); - } else { - var client = new XMLHttpRequest(); - client.addEventListener('load', this.onXHRLoad_.bind(this)); - client.addEventListener('error', this.onXHRError_.bind(this)); - client.open('GET', options.url); - client.send(); - } - } else if (options.tileJSON) { - this.handleTileJSONResponse(options.tileJSON); - } else { - ol.asserts.assert(false, 51); // Either `url` or `tileJSON` options must be provided - } -}; -ol.inherits(ol.source.TileUTFGrid, ol.source.Tile); - - -/** - * @private - * @param {Event} event The load event. - */ -ol.source.TileUTFGrid.prototype.onXHRLoad_ = function(event) { - var client = /** @type {XMLHttpRequest} */ (event.target); - // status will be 0 for file:// urls - if (!client.status || client.status >= 200 && client.status < 300) { - var response; - try { - response = /** @type {TileJSON} */(JSON.parse(client.responseText)); - } catch (err) { - this.handleTileJSONError(); - return; - } - this.handleTileJSONResponse(response); - } else { - this.handleTileJSONError(); - } -}; - - -/** - * @private - * @param {Event} event The error event. - */ -ol.source.TileUTFGrid.prototype.onXHRError_ = function(event) { - this.handleTileJSONError(); -}; - - -/** - * Return the template from TileJSON. - * @return {string|undefined} The template from TileJSON. - * @api - */ -ol.source.TileUTFGrid.prototype.getTemplate = function() { - return this.template_; -}; - - -/** - * Calls the callback (synchronously by default) with the available data - * for given coordinate and resolution (or `null` if not yet loaded or - * in case of an error). - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {function(this: T, *)} callback Callback. - * @param {T=} opt_this The object to use as `this` in the callback. - * @param {boolean=} opt_request If `true` the callback is always async. - * The tile data is requested if not yet loaded. - * @template T - * @api - */ -ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( - coordinate, resolution, callback, opt_this, opt_request) { - if (this.tileGrid) { - var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( - coordinate, resolution); - var tile = /** @type {!ol.source.TileUTFGrid.Tile_} */(this.getTile( - tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection())); - tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_request); - } else { - if (opt_request === true) { - setTimeout(function() { - callback.call(opt_this, null); - }, 0); - } else { - callback.call(opt_this, null); - } - } -}; - - -/** - * @protected - */ -ol.source.TileUTFGrid.prototype.handleTileJSONError = function() { - this.setState(ol.source.State.ERROR); -}; - - -/** - * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse - * @protected - * @param {TileJSON} tileJSON Tile JSON. - */ -ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { - - var epsg4326Projection = ol.proj.get('EPSG:4326'); - - var sourceProjection = this.getProjection(); - var extent; - if (tileJSON.bounds !== undefined) { - var transform = ol.proj.getTransformFromProjections( - epsg4326Projection, sourceProjection); - extent = ol.extent.applyTransform(tileJSON.bounds, transform); - } - - var minZoom = tileJSON.minzoom || 0; - var maxZoom = tileJSON.maxzoom || 22; - var tileGrid = ol.tilegrid.createXYZ({ - extent: ol.tilegrid.extentFromProjection(sourceProjection), - maxZoom: maxZoom, - minZoom: minZoom - }); - this.tileGrid = tileGrid; - - this.template_ = tileJSON.template; - - var grids = tileJSON.grids; - if (!grids) { - this.setState(ol.source.State.ERROR); - return; - } - - this.tileUrlFunction_ = - ol.TileUrlFunction.createFromTemplates(grids, tileGrid); - - if (tileJSON.attribution !== undefined) { - var attributionExtent = extent !== undefined ? - extent : epsg4326Projection.getExtent(); - /** @type {Object.<string, Array.<ol.TileRange>>} */ - var tileRanges = {}; - var z, zKey; - for (z = minZoom; z <= maxZoom; ++z) { - zKey = z.toString(); - tileRanges[zKey] = - [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; - } - this.setAttributions([ - new ol.Attribution({ - html: tileJSON.attribution, - tileRanges: tileRanges - }) - ]); - } - - this.setState(ol.source.State.READY); - -}; - - -/** - * @inheritDoc - */ -ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projection) { - var tileCoordKey = this.getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); - } else { - var tileCoord = [z, x, y]; - var urlTileCoord = - this.getTileCoordForTileUrlFunction(tileCoord, projection); - var tileUrl = this.tileUrlFunction_(urlTileCoord, pixelRatio, projection); - var tile = new ol.source.TileUTFGrid.Tile_( - tileCoord, - tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, - tileUrl !== undefined ? tileUrl : '', - this.tileGrid.getTileCoordExtent(tileCoord), - this.preemptive_, - this.jsonp_); - this.tileCache.set(tileCoordKey, tile); - return tile; - } -}; - - -/** - * @inheritDoc - */ -ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { - var tileCoordKey = this.getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - this.tileCache.get(tileCoordKey); - } -}; - - -/** - * @constructor - * @extends {ol.Tile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileState} state State. - * @param {string} src Image source URI. - * @param {ol.Extent} extent Extent of the tile. - * @param {boolean} preemptive Load the tile when visible (before it's needed). - * @param {boolean} jsonp Load the tile as a script. - * @private - */ -ol.source.TileUTFGrid.Tile_ = function(tileCoord, state, src, extent, preemptive, jsonp) { - - ol.Tile.call(this, tileCoord, state); - - /** - * @private - * @type {string} - */ - this.src_ = src; - - /** - * @private - * @type {ol.Extent} - */ - this.extent_ = extent; - - /** - * @private - * @type {boolean} - */ - this.preemptive_ = preemptive; - - /** - * @private - * @type {Array.<string>} - */ - this.grid_ = null; - - /** - * @private - * @type {Array.<string>} - */ - this.keys_ = null; - - /** - * @private - * @type {Object.<string, Object>|undefined} - */ - this.data_ = null; - - - /** - * @private - * @type {boolean} - */ - this.jsonp_ = jsonp; - -}; -ol.inherits(ol.source.TileUTFGrid.Tile_, ol.Tile); - - -/** - * Get the image element for this tile. - * @return {Image} Image. - */ -ol.source.TileUTFGrid.Tile_.prototype.getImage = function() { - return null; -}; - - -/** - * Synchronously returns data at given coordinate (if available). - * @param {ol.Coordinate} coordinate Coordinate. - * @return {*} The data. - */ -ol.source.TileUTFGrid.Tile_.prototype.getData = function(coordinate) { - if (!this.grid_ || !this.keys_) { - return null; - } - var xRelative = (coordinate[0] - this.extent_[0]) / - (this.extent_[2] - this.extent_[0]); - var yRelative = (coordinate[1] - this.extent_[1]) / - (this.extent_[3] - this.extent_[1]); - - var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)]; - - if (typeof row !== 'string') { - return null; - } - - var code = row.charCodeAt(Math.floor(xRelative * row.length)); - if (code >= 93) { - code--; - } - if (code >= 35) { - code--; - } - code -= 32; - - var data = null; - if (code in this.keys_) { - var id = this.keys_[code]; - if (this.data_ && id in this.data_) { - data = this.data_[id]; - } else { - data = id; - } - } - return data; -}; - - -/** - * Calls the callback (synchronously by default) with the available data - * for given coordinate (or `null` if not yet loaded). - * @param {ol.Coordinate} coordinate Coordinate. - * @param {function(this: T, *)} callback Callback. - * @param {T=} opt_this The object to use as `this` in the callback. - * @param {boolean=} opt_request If `true` the callback is always async. - * The tile data is requested if not yet loaded. - * @template T - */ -ol.source.TileUTFGrid.Tile_.prototype.forDataAtCoordinate = function(coordinate, callback, opt_this, opt_request) { - if (this.state == ol.TileState.IDLE && opt_request === true) { - ol.events.listenOnce(this, ol.events.EventType.CHANGE, function(e) { - callback.call(opt_this, this.getData(coordinate)); - }, this); - this.loadInternal_(); - } else { - if (opt_request === true) { - setTimeout(function() { - callback.call(opt_this, this.getData(coordinate)); - }.bind(this), 0); - } else { - callback.call(opt_this, this.getData(coordinate)); - } - } -}; - - -/** - * @inheritDoc - */ -ol.source.TileUTFGrid.Tile_.prototype.getKey = function() { - return this.src_; -}; - - -/** - * @private - */ -ol.source.TileUTFGrid.Tile_.prototype.handleError_ = function() { - this.state = ol.TileState.ERROR; - this.changed(); -}; - - -/** - * @param {!UTFGridJSON} json UTFGrid data. - * @private - */ -ol.source.TileUTFGrid.Tile_.prototype.handleLoad_ = function(json) { - this.grid_ = json.grid; - this.keys_ = json.keys; - this.data_ = json.data; - - this.state = ol.TileState.EMPTY; - this.changed(); -}; - - -/** - * @private - */ -ol.source.TileUTFGrid.Tile_.prototype.loadInternal_ = function() { - if (this.state == ol.TileState.IDLE) { - this.state = ol.TileState.LOADING; - if (this.jsonp_) { - ol.net.jsonp(this.src_, this.handleLoad_.bind(this), - this.handleError_.bind(this)); - } else { - var client = new XMLHttpRequest(); - client.addEventListener('load', this.onXHRLoad_.bind(this)); - client.addEventListener('error', this.onXHRError_.bind(this)); - client.open('GET', this.src_); - client.send(); - } - } -}; - - -/** - * @private - * @param {Event} event The load event. - */ -ol.source.TileUTFGrid.Tile_.prototype.onXHRLoad_ = function(event) { - var client = /** @type {XMLHttpRequest} */ (event.target); - // status will be 0 for file:// urls - if (!client.status || client.status >= 200 && client.status < 300) { - var response; - try { - response = /** @type {!UTFGridJSON} */(JSON.parse(client.responseText)); - } catch (err) { - this.handleError_(); - return; - } - this.handleLoad_(response); - } else { - this.handleError_(); - } -}; - - -/** - * @private - * @param {Event} event The error event. - */ -ol.source.TileUTFGrid.Tile_.prototype.onXHRError_ = function(event) { - this.handleError_(); -}; - - -/** - * @override - */ -ol.source.TileUTFGrid.Tile_.prototype.load = function() { - if (this.preemptive_) { - this.loadInternal_(); - } -}; - -// FIXME add minZoom support -// FIXME add date line wrap (tile coord transform) -// FIXME cannot be shared between maps with different projections - -goog.provide('ol.source.TileWMS'); - -goog.require('ol'); -goog.require('ol.asserts'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.math'); -goog.require('ol.proj'); -goog.require('ol.size'); -goog.require('ol.source.TileImage'); -goog.require('ol.source.WMSServerType'); -goog.require('ol.tilecoord'); -goog.require('ol.string'); -goog.require('ol.uri'); - -/** - * @classdesc - * Layer source for tile data from WMS servers. - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.TileWMSOptions=} opt_options Tile WMS options. - * @api - */ -ol.source.TileWMS = function(opt_options) { - - var options = opt_options || {}; - - var params = options.params || {}; - - var transparent = 'TRANSPARENT' in params ? params['TRANSPARENT'] : true; - - ol.source.TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - logo: options.logo, - opaque: !transparent, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); - - /** - * @private - * @type {number} - */ - this.gutter_ = options.gutter !== undefined ? options.gutter : 0; - - /** - * @private - * @type {!Object} - */ - this.params_ = params; - - /** - * @private - * @type {boolean} - */ - this.v13_ = true; - - /** - * @private - * @type {ol.source.WMSServerType|undefined} - */ - this.serverType_ = - /** @type {ol.source.WMSServerType|undefined} */ (options.serverType); - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {string} - */ - this.coordKeyPrefix_ = ''; - this.resetCoordKeyPrefix_(); - - /** - * @private - * @type {ol.Extent} - */ - this.tmpExtent_ = ol.extent.createEmpty(); - - this.updateV13_(); - this.setKey(this.getKeyForParams_()); - -}; -ol.inherits(ol.source.TileWMS, ol.source.TileImage); - - -/** - * Return the GetFeatureInfo URL for the passed coordinate, resolution, and - * projection. Return `undefined` if the GetFeatureInfo URL cannot be - * constructed. - * @param {ol.Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {ol.ProjectionLike} projection Projection. - * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should - * be provided. If `QUERY_LAYERS` is not provided then the layers specified - * in the `LAYERS` parameter will be used. `VERSION` should not be - * specified here. - * @return {string|undefined} GetFeatureInfo URL. - * @api - */ -ol.source.TileWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { - var projectionObj = ol.proj.get(projection); - - var tileGrid = this.getTileGrid(); - if (!tileGrid) { - tileGrid = this.getTileGridForProjection(projectionObj); - } - - var tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, resolution); - - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return undefined; - } - - var tileResolution = tileGrid.getResolution(tileCoord[0]); - var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); - var tileSize = ol.size.toSize( - tileGrid.getTileSize(tileCoord[0]), this.tmpSize); - - var gutter = this.gutter_; - if (gutter !== 0) { - tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize); - tileExtent = ol.extent.buffer(tileExtent, - tileResolution * gutter, tileExtent); - } - - var baseParams = { - 'SERVICE': 'WMS', - 'VERSION': ol.DEFAULT_WMS_VERSION, - 'REQUEST': 'GetFeatureInfo', - 'FORMAT': 'image/png', - 'TRANSPARENT': true, - 'QUERY_LAYERS': this.params_['LAYERS'] - }; - ol.obj.assign(baseParams, this.params_, params); - - var x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution); - var y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution); - - baseParams[this.v13_ ? 'I' : 'X'] = x; - baseParams[this.v13_ ? 'J' : 'Y'] = y; - - return this.getRequestUrl_(tileCoord, tileSize, tileExtent, - 1, projectionObj, baseParams); -}; - - -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.getGutterInternal = function() { - return this.gutter_; -}; - - -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.getKeyZXY = function(z, x, y) { - return this.coordKeyPrefix_ + ol.source.TileImage.prototype.getKeyZXY.call(this, z, x, y); -}; - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ol.source.TileWMS.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.Size} tileSize Tile size. - * @param {ol.Extent} tileExtent Tile extent. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {Object} params Params. - * @return {string|undefined} Request URL. - * @private - */ -ol.source.TileWMS.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent, - pixelRatio, projection, params) { - - var urls = this.urls; - if (!urls) { - return undefined; - } - - params['WIDTH'] = tileSize[0]; - params['HEIGHT'] = tileSize[1]; - - params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); - - if (!('STYLES' in this.params_)) { - params['STYLES'] = ''; - } - - if (pixelRatio != 1) { - switch (this.serverType_) { - case ol.source.WMSServerType.GEOSERVER: - var dpi = (90 * pixelRatio + 0.5) | 0; - if ('FORMAT_OPTIONS' in params) { - params['FORMAT_OPTIONS'] += ';dpi:' + dpi; - } else { - params['FORMAT_OPTIONS'] = 'dpi:' + dpi; - } - break; - case ol.source.WMSServerType.MAPSERVER: - params['MAP_RESOLUTION'] = 90 * pixelRatio; - break; - case ol.source.WMSServerType.CARMENTA_SERVER: - case ol.source.WMSServerType.QGIS: - params['DPI'] = 90 * pixelRatio; - break; - default: - ol.asserts.assert(false, 52); // Unknown `serverType` configured - break; - } - } - - var axisOrientation = projection.getAxisOrientation(); - var bbox = tileExtent; - if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { - var tmp; - tmp = tileExtent[0]; - bbox[0] = tileExtent[1]; - bbox[1] = tmp; - tmp = tileExtent[2]; - bbox[2] = tileExtent[3]; - bbox[3] = tmp; - } - params['BBOX'] = bbox.join(','); - - var url; - if (urls.length == 1) { - url = urls[0]; - } else { - var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length); - url = urls[index]; - } - return ol.uri.appendParams(url, params); -}; - - -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.getTilePixelRatio = function(pixelRatio) { - return (!this.hidpi_ || this.serverType_ === undefined) ? 1 : - /** @type {number} */ (pixelRatio); -}; - - -/** - * @private - */ -ol.source.TileWMS.prototype.resetCoordKeyPrefix_ = function() { - var i = 0; - var res = []; - - if (this.urls) { - var j, jj; - for (j = 0, jj = this.urls.length; j < jj; ++j) { - res[i++] = this.urls[j]; - } - } - - this.coordKeyPrefix_ = res.join('#'); -}; - - -/** - * @private - * @return {string} The key for the current params. - */ -ol.source.TileWMS.prototype.getKeyForParams_ = function() { - var i = 0; - var res = []; - for (var key in this.params_) { - res[i++] = key + '-' + this.params_[key]; - } - return res.join('/'); -}; - - -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) { - - var tileGrid = this.getTileGrid(); - if (!tileGrid) { - tileGrid = this.getTileGridForProjection(projection); - } - - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return undefined; - } - - if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { - pixelRatio = 1; - } - - var tileResolution = tileGrid.getResolution(tileCoord[0]); - var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); - var tileSize = ol.size.toSize( - tileGrid.getTileSize(tileCoord[0]), this.tmpSize); - - var gutter = this.gutter_; - if (gutter !== 0) { - tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize); - tileExtent = ol.extent.buffer(tileExtent, - tileResolution * gutter, tileExtent); - } - - if (pixelRatio != 1) { - tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize); - } - - var baseParams = { - 'SERVICE': 'WMS', - 'VERSION': ol.DEFAULT_WMS_VERSION, - 'REQUEST': 'GetMap', - 'FORMAT': 'image/png', - 'TRANSPARENT': true - }; - ol.obj.assign(baseParams, this.params_); - - return this.getRequestUrl_(tileCoord, tileSize, tileExtent, - pixelRatio, projection, baseParams); -}; - -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.setUrls = function(urls) { - ol.source.TileImage.prototype.setUrls.call(this, urls); - this.resetCoordKeyPrefix_(); -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ol.source.TileWMS.prototype.updateParams = function(params) { - ol.obj.assign(this.params_, params); - this.resetCoordKeyPrefix_(); - this.updateV13_(); - this.setKey(this.getKeyForParams_()); -}; - - -/** - * @private - */ -ol.source.TileWMS.prototype.updateV13_ = function() { - var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION; - this.v13_ = ol.string.compareVersions(version, '1.3') >= 0; -}; - -goog.provide('ol.VectorImageTile'); - -goog.require('ol'); -goog.require('ol.Tile'); -goog.require('ol.TileState'); -goog.require('ol.array'); -goog.require('ol.dom'); -goog.require('ol.events'); -goog.require('ol.extent'); -goog.require('ol.events.EventType'); -goog.require('ol.featureloader'); - - -/** - * @constructor - * @extends {ol.Tile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileState} state State. - * @param {string} src Data source url. - * @param {ol.format.Feature} format Feature format. - * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. - * @param {ol.TileCoord} urlTileCoord Wrapped tile coordinate for source urls. - * @param {ol.TileUrlFunctionType} tileUrlFunction Tile url function. - * @param {ol.tilegrid.TileGrid} sourceTileGrid Tile grid of the source. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid of the renderer. - * @param {Object.<string,ol.VectorTile>} sourceTiles Source tiles. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @param {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string, - * ol.format.Feature, ol.TileLoadFunctionType)} tileClass Class to - * instantiate for source tiles. - * @param {function(this: ol.source.VectorTile, ol.events.Event)} handleTileChange - * Function to call when a source tile's state changes. - */ -ol.VectorImageTile = function(tileCoord, state, src, format, tileLoadFunction, - urlTileCoord, tileUrlFunction, sourceTileGrid, tileGrid, sourceTiles, - pixelRatio, projection, tileClass, handleTileChange) { - - ol.Tile.call(this, tileCoord, state); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = null; - - /** - * @private - * @type {ol.FeatureLoader} - */ - this.loader_; - - /** - * @private - * @type {ol.TileReplayState} - */ - this.replayState_ = { - dirty: false, - renderedRenderOrder: null, - renderedRevision: -1, - renderedTileRevision: -1 - }; - - /** - * @private - * @type {Object.<string,ol.VectorTile>} - */ - this.sourceTiles_ = sourceTiles; - - /** - * Keys of source tiles used by this tile. Use with {@link #getTile}. - * @type {Array.<string>} - */ - this.tileKeys = []; - - /** - * @type {string} - */ - this.src_ = src; - - /** - * @type {ol.TileCoord} - */ - this.wrappedTileCoord = urlTileCoord; - - /** - * @type {Array.<ol.EventsKey>} - */ - this.loadListenerKeys_ = []; - - /** - * @type {Array.<ol.EventsKey>} - */ - this.sourceTileListenerKeys_ = []; - - if (urlTileCoord) { - var extent = tileGrid.getTileCoordExtent(urlTileCoord); - var resolution = tileGrid.getResolution(tileCoord[0]); - var sourceZ = sourceTileGrid.getZForResolution(resolution); - sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { - var sharedExtent = ol.extent.getIntersection(extent, - sourceTileGrid.getTileCoordExtent(sourceTileCoord)); - if (ol.extent.getWidth(sharedExtent) / resolution >= 0.5 && - ol.extent.getHeight(sharedExtent) / resolution >= 0.5) { - // only include source tile if overlap is at least 1 pixel - var sourceTileKey = sourceTileCoord.toString(); - var sourceTile = sourceTiles[sourceTileKey]; - if (!sourceTile) { - var tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection); - sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, - tileUrl == undefined ? ol.TileState.EMPTY : ol.TileState.IDLE, - tileUrl == undefined ? '' : tileUrl, - format, tileLoadFunction); - this.sourceTileListenerKeys_.push( - ol.events.listen(sourceTile, ol.events.EventType.CHANGE, handleTileChange)); - } - sourceTile.consumers++; - this.tileKeys.push(sourceTileKey); - } - }.bind(this)); - } - -}; -ol.inherits(ol.VectorImageTile, ol.Tile); - - -/** - * @inheritDoc - */ -ol.VectorImageTile.prototype.disposeInternal = function() { - for (var i = 0, ii = this.tileKeys.length; i < ii; ++i) { - var sourceTileKey = this.tileKeys[i]; - var sourceTile = this.getTile(sourceTileKey); - sourceTile.consumers--; - if (sourceTile.consumers == 0) { - delete this.sourceTiles_[sourceTileKey]; - sourceTile.dispose(); - } - } - this.tileKeys.length = 0; - this.sourceTiles_ = null; - if (this.state == ol.TileState.LOADING) { - this.loadListenerKeys_.forEach(ol.events.unlistenByKey); - this.loadListenerKeys_.length = 0; - } - if (this.interimTile) { - this.interimTile.dispose(); - } - this.state = ol.TileState.ABORT; - this.changed(); - this.sourceTileListenerKeys_.forEach(ol.events.unlistenByKey); - this.sourceTileListenerKeys_.length = 0; - ol.Tile.prototype.disposeInternal.call(this); -}; - - -/** - * @return {CanvasRenderingContext2D} The rendering context. - */ -ol.VectorImageTile.prototype.getContext = function() { - if (!this.context_) { - this.context_ = ol.dom.createCanvasContext2D(); - } - return this.context_; -}; - - -/** - * Get the Canvas for this tile. - * @return {HTMLCanvasElement} Canvas. - */ -ol.VectorImageTile.prototype.getImage = function() { - return this.replayState_.renderedTileRevision == -1 ? - null : this.context_.canvas; -}; - - -/** - * @return {ol.TileReplayState} The replay state. - */ -ol.VectorImageTile.prototype.getReplayState = function() { - return this.replayState_; -}; - - -/** - * @inheritDoc - */ -ol.VectorImageTile.prototype.getKey = function() { - return this.tileKeys.join('/') + '/' + this.src_; -}; - - -/** - * @param {string} tileKey Key (tileCoord) of the source tile. - * @return {ol.VectorTile} Source tile. - */ -ol.VectorImageTile.prototype.getTile = function(tileKey) { - return this.sourceTiles_[tileKey]; -}; - - -/** - * @inheritDoc - */ -ol.VectorImageTile.prototype.load = function() { - var leftToLoad = 0; - var errors = false; - if (this.state == ol.TileState.IDLE) { - this.setState(ol.TileState.LOADING); - } - if (this.state == ol.TileState.LOADING) { - this.tileKeys.forEach(function(sourceTileKey) { - var sourceTile = this.getTile(sourceTileKey); - if (sourceTile.state == ol.TileState.IDLE) { - sourceTile.setLoader(this.loader_); - sourceTile.load(); - } else if (sourceTile.state == ol.TileState.ERROR) { - errors = true; - } else if (sourceTile.state == ol.TileState.EMPTY) { - ol.array.remove(this.tileKeys, sourceTileKey); - } - if (sourceTile.state == ol.TileState.LOADING) { - var key = ol.events.listen(sourceTile, ol.events.EventType.CHANGE, function(e) { - var state = sourceTile.getState(); - if (state == ol.TileState.LOADED || - state == ol.TileState.ERROR) { - --leftToLoad; - ol.events.unlistenByKey(key); - ol.array.remove(this.loadListenerKeys_, key); - if (state == ol.TileState.ERROR) { - ol.array.remove(this.tileKeys, sourceTileKey); - errors = true; - } - if (leftToLoad == 0) { - this.setState(this.tileKeys.length > 0 ? - ol.TileState.LOADED : ol.TileState.ERROR); - } - } - }.bind(this)); - this.loadListenerKeys_.push(key); - ++leftToLoad; - } - }.bind(this)); - } - if (leftToLoad == 0) { - setTimeout(function() { - this.setState(this.tileKeys.length > 0 ? - ol.TileState.LOADED : - (errors ? ol.TileState.ERROR : ol.TileState.EMPTY)); - }.bind(this), 0); - } -}; - - -/** - * Sets the loader for a tile. - * @param {ol.VectorTile} tile Vector tile. - * @param {string} url URL. - */ -ol.VectorImageTile.defaultLoadFunction = function(tile, url) { - var loader = ol.featureloader.loadFeaturesXhr( - url, tile.getFormat(), tile.onLoad_.bind(tile), tile.onError_.bind(tile)); - - tile.setLoader(loader); -}; - -goog.provide('ol.VectorTile'); - -goog.require('ol'); -goog.require('ol.Tile'); -goog.require('ol.TileState'); - - -/** - * @constructor - * @extends {ol.Tile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileState} state State. - * @param {string} src Data source url. - * @param {ol.format.Feature} format Feature format. - * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. - */ -ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) { - - ol.Tile.call(this, tileCoord, state); - - /** - * @type {number} - */ - this.consumers = 0; - - /** - * @private - * @type {ol.format.Feature} - */ - this.format_ = format; - - /** - * @private - * @type {Array.<ol.Feature>} - */ - this.features_ = null; - - /** - * @private - * @type {ol.FeatureLoader} - */ - this.loader_; - - /** - * Data projection - * @private - * @type {ol.proj.Projection} - */ - this.projection_; - - this.replayGroups_ = {}; - - /** - * @private - * @type {ol.TileLoadFunctionType} - */ - this.tileLoadFunction_ = tileLoadFunction; - - /** - * @private - * @type {string} - */ - this.url_ = src; - -}; -ol.inherits(ol.VectorTile, ol.Tile); - - -/** - * @inheritDoc - */ -ol.VectorTile.prototype.disposeInternal = function() { - this.features_ = null; - this.replayGroups_ = {}; - this.state = ol.TileState.ABORT; - this.changed(); - ol.Tile.prototype.disposeInternal.call(this); -}; - - -/** - * Get the feature format assigned for reading this tile's features. - * @return {ol.format.Feature} Feature format. - * @api - */ -ol.VectorTile.prototype.getFormat = function() { - return this.format_; -}; - - -/** - * Get the features for this tile. Geometries will be in the projection returned - * by {@link #getProjection}. - * @return {Array.<ol.Feature|ol.render.Feature>} Features. - * @api - */ -ol.VectorTile.prototype.getFeatures = function() { - return this.features_; -}; - - -/** - * @inheritDoc - */ -ol.VectorTile.prototype.getKey = function() { - return this.url_; -}; - - -/** - * Get the feature projection of features returned by {@link #getFeatures}. - * @return {ol.proj.Projection} Feature projection. - * @api - */ -ol.VectorTile.prototype.getProjection = function() { - return this.projection_; -}; - - -ol.VectorTile.prototype.getReplayGroup = function(key) { - return this.replayGroups_[key]; -}; - - -/** - * @inheritDoc - */ -ol.VectorTile.prototype.load = function() { - if (this.state == ol.TileState.IDLE) { - this.setState(ol.TileState.LOADING); - this.tileLoadFunction_(this, this.url_); - this.loader_(null, NaN, null); - } -}; - - -/** - * Handler for successful tile load. - * @param {Array.<ol.Feature>} features The loaded features. - * @param {ol.proj.Projection} dataProjection Data projection. - */ -ol.VectorTile.prototype.onLoad_ = function(features, dataProjection) { - this.setProjection(dataProjection); - this.setFeatures(features); -}; - - -/** - * Handler for tile load errors. - */ -ol.VectorTile.prototype.onError_ = function() { - this.setState(ol.TileState.ERROR); -}; - - -/** - * @param {Array.<ol.Feature>} features Features. - * @api - */ -ol.VectorTile.prototype.setFeatures = function(features) { - this.features_ = features; - this.setState(ol.TileState.LOADED); -}; - - -/** - * Set the projection of the features that were added with {@link #setFeatures}. - * @param {ol.proj.Projection} projection Feature projection. - * @api - */ -ol.VectorTile.prototype.setProjection = function(projection) { - this.projection_ = projection; -}; - - -ol.VectorTile.prototype.setReplayGroup = function(key, replayGroup) { - this.replayGroups_[key] = replayGroup; -}; - - -/** - * Set the feature loader for reading this tile's features. - * @param {ol.FeatureLoader} loader Feature loader. - * @api - */ -ol.VectorTile.prototype.setLoader = function(loader) { - this.loader_ = loader; -}; - -goog.provide('ol.source.VectorTile'); - -goog.require('ol'); -goog.require('ol.TileState'); -goog.require('ol.VectorImageTile'); -goog.require('ol.VectorTile'); -goog.require('ol.proj'); -goog.require('ol.size'); -goog.require('ol.tilegrid'); -goog.require('ol.source.UrlTile'); - - -/** - * @classdesc - * Class for layer sources providing vector data divided into a tile grid, to be - * used with {@link ol.layer.VectorTile}. Although this source receives tiles - * with vector features from the server, it is not meant for feature editing. - * Features are optimized for rendering, their geometries are clipped at or near - * tile boundaries and simplified for a view resolution. See - * {@link ol.source.Vector} for vector sources that are suitable for feature - * editing. - * - * @constructor - * @fires ol.source.Tile.Event - * @extends {ol.source.UrlTile} - * @param {olx.source.VectorTileOptions} options Vector tile options. - * @api - */ -ol.source.VectorTile = function(options) { - - ol.source.UrlTile.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128, - extent: options.extent, - logo: options.logo, - opaque: false, - projection: options.projection, - state: options.state, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction ? - options.tileLoadFunction : ol.VectorImageTile.defaultLoadFunction, - tileUrlFunction: options.tileUrlFunction, - tilePixelRatio: options.tilePixelRatio, - url: options.url, - urls: options.urls, - wrapX: options.wrapX === undefined ? true : options.wrapX - }); - - /** - * @private - * @type {ol.format.Feature} - */ - this.format_ = options.format ? options.format : null; - - /** - * @private - * @type {Object.<string,ol.VectorTile>} - */ - this.sourceTiles_ = {}; - - /** - * @private - * @type {boolean} - */ - this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; - - /** - * @protected - * @type {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string, - * ol.format.Feature, ol.TileLoadFunctionType)} - */ - this.tileClass = options.tileClass ? options.tileClass : ol.VectorTile; - - /** - * @private - * @type {Object.<string,ol.tilegrid.TileGrid>} - */ - this.tileGrids_ = {}; - - if (!this.tileGrid) { - this.tileGrid = this.getTileGridForProjection(ol.proj.get(options.projection || 'EPSG:3857')); - } - -}; -ol.inherits(ol.source.VectorTile, ol.source.UrlTile); - - -/** - * @return {boolean} The source can have overlapping geometries. - */ -ol.source.VectorTile.prototype.getOverlaps = function() { - return this.overlaps_; -}; - - -/** - * @inheritDoc - */ -ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projection) { - var tileCoordKey = this.getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); - } else { - var tileCoord = [z, x, y]; - var urlTileCoord = this.getTileCoordForTileUrlFunction( - tileCoord, projection); - var tileUrl = urlTileCoord ? - this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; - var tile = new ol.VectorImageTile( - tileCoord, - tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, - tileUrl !== undefined ? tileUrl : '', - this.format_, this.tileLoadFunction, urlTileCoord, this.tileUrlFunction, - this.tileGrid, this.getTileGridForProjection(projection), - this.sourceTiles_, pixelRatio, projection, this.tileClass, - this.handleTileChange.bind(this)); - - this.tileCache.set(tileCoordKey, tile); - return tile; - } -}; - - -/** - * @inheritDoc - */ -ol.source.VectorTile.prototype.getTileGridForProjection = function(projection) { - var code = projection.getCode(); - var tileGrid = this.tileGrids_[code]; - if (!tileGrid) { - // A tile grid that matches the tile size of the source tile grid is more - // likely to have 1:1 relationships between source tiles and rendered tiles. - var sourceTileGrid = this.tileGrid; - tileGrid = this.tileGrids_[code] = ol.tilegrid.createForProjection(projection, undefined, - sourceTileGrid ? sourceTileGrid.getTileSize(sourceTileGrid.getMinZoom()) : undefined); - } - return tileGrid; -}; - - -/** - * @inheritDoc - */ -ol.source.VectorTile.prototype.getTilePixelRatio = function(opt_pixelRatio) { - return opt_pixelRatio == undefined ? - ol.source.UrlTile.prototype.getTilePixelRatio.call(this, opt_pixelRatio) : - opt_pixelRatio; -}; - - -/** - * @inheritDoc - */ -ol.source.VectorTile.prototype.getTilePixelSize = function(z, pixelRatio, projection) { - var tileSize = ol.size.toSize(this.getTileGridForProjection(projection).getTileSize(z)); - return [Math.round(tileSize[0] * pixelRatio), Math.round(tileSize[1] * pixelRatio)]; -}; - -goog.provide('ol.source.WMTSRequestEncoding'); - -/** - * Request encoding. One of 'KVP', 'REST'. - * @enum {string} - */ -ol.source.WMTSRequestEncoding = { - KVP: 'KVP', // see spec §8 - REST: 'REST' // see spec §10 -}; - -goog.provide('ol.tilegrid.WMTS'); - -goog.require('ol'); -goog.require('ol.array'); -goog.require('ol.proj'); -goog.require('ol.tilegrid.TileGrid'); - - -/** - * @classdesc - * Set the grid pattern for sources accessing WMTS tiled-image servers. - * - * @constructor - * @extends {ol.tilegrid.TileGrid} - * @param {olx.tilegrid.WMTSOptions} options WMTS options. - * @struct - * @api - */ -ol.tilegrid.WMTS = function(options) { - /** - * @private - * @type {!Array.<string>} - */ - this.matrixIds_ = options.matrixIds; - // FIXME: should the matrixIds become optional? - - ol.tilegrid.TileGrid.call(this, { - extent: options.extent, - origin: options.origin, - origins: options.origins, - resolutions: options.resolutions, - tileSize: options.tileSize, - tileSizes: options.tileSizes, - sizes: options.sizes - }); -}; -ol.inherits(ol.tilegrid.WMTS, ol.tilegrid.TileGrid); - - -/** - * @param {number} z Z. - * @return {string} MatrixId.. - */ -ol.tilegrid.WMTS.prototype.getMatrixId = function(z) { - return this.matrixIds_[z]; -}; - - -/** - * Get the list of matrix identifiers. - * @return {Array.<string>} MatrixIds. - * @api - */ -ol.tilegrid.WMTS.prototype.getMatrixIds = function() { - return this.matrixIds_; -}; - - -/** - * Create a tile grid from a WMTS capabilities matrix set and an - * optional TileMatrixSetLimits. - * @param {Object} matrixSet An object representing a matrixSet in the - * capabilities document. - * @param {ol.Extent=} opt_extent An optional extent to restrict the tile - * ranges the server provides. - * @param {Array.<Object>=} opt_matrixLimits An optional object representing - * the available matrices for tileGrid. - * @return {ol.tilegrid.WMTS} WMTS tileGrid instance. - * @api - */ -ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = function(matrixSet, opt_extent, - opt_matrixLimits) { - - /** @type {!Array.<number>} */ - var resolutions = []; - /** @type {!Array.<string>} */ - var matrixIds = []; - /** @type {!Array.<ol.Coordinate>} */ - var origins = []; - /** @type {!Array.<ol.Size>} */ - var tileSizes = []; - /** @type {!Array.<ol.Size>} */ - var sizes = []; - - var matrixLimits = opt_matrixLimits !== undefined ? opt_matrixLimits : []; - - var supportedCRSPropName = 'SupportedCRS'; - var matrixIdsPropName = 'TileMatrix'; - var identifierPropName = 'Identifier'; - var scaleDenominatorPropName = 'ScaleDenominator'; - var topLeftCornerPropName = 'TopLeftCorner'; - var tileWidthPropName = 'TileWidth'; - var tileHeightPropName = 'TileHeight'; - - var projection; - projection = ol.proj.get(matrixSet[supportedCRSPropName].replace( - /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')); - var metersPerUnit = projection.getMetersPerUnit(); - // swap origin x and y coordinates if axis orientation is lat/long - var switchOriginXY = projection.getAxisOrientation().substr(0, 2) == 'ne'; - - matrixSet[matrixIdsPropName].sort(function(a, b) { - return b[scaleDenominatorPropName] - a[scaleDenominatorPropName]; - }); - - matrixSet[matrixIdsPropName].forEach(function(elt, index, array) { - - var matrixAvailable; - // use of matrixLimits to filter TileMatrices from GetCapabilities - // TileMatrixSet from unavailable matrix levels. - if (matrixLimits.length > 0) { - matrixAvailable = ol.array.find(matrixLimits, - function(elt_ml, index_ml, array_ml) { - return elt[identifierPropName] == elt_ml[matrixIdsPropName]; - }); - } else { - matrixAvailable = true; - } - - if (matrixAvailable) { - matrixIds.push(elt[identifierPropName]); - var resolution = elt[scaleDenominatorPropName] * 0.28E-3 / metersPerUnit; - var tileWidth = elt[tileWidthPropName]; - var tileHeight = elt[tileHeightPropName]; - if (switchOriginXY) { - origins.push([elt[topLeftCornerPropName][1], - elt[topLeftCornerPropName][0]]); - } else { - origins.push(elt[topLeftCornerPropName]); - } - resolutions.push(resolution); - tileSizes.push(tileWidth == tileHeight ? - tileWidth : [tileWidth, tileHeight]); - // top-left origin, so height is negative - sizes.push([elt['MatrixWidth'], -elt['MatrixHeight']]); - } - }); - - return new ol.tilegrid.WMTS({ - extent: opt_extent, - origins: origins, - resolutions: resolutions, - matrixIds: matrixIds, - tileSizes: tileSizes, - sizes: sizes - }); -}; - -goog.provide('ol.source.WMTS'); - -goog.require('ol'); -goog.require('ol.TileUrlFunction'); -goog.require('ol.array'); -goog.require('ol.extent'); -goog.require('ol.obj'); -goog.require('ol.proj'); -goog.require('ol.source.TileImage'); -goog.require('ol.source.WMTSRequestEncoding'); -goog.require('ol.tilegrid.WMTS'); -goog.require('ol.uri'); - - -/** - * @classdesc - * Layer source for tile data from WMTS servers. - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.WMTSOptions} options WMTS options. - * @api - */ -ol.source.WMTS = function(options) { - - // TODO: add support for TileMatrixLimits - - /** - * @private - * @type {string} - */ - this.version_ = options.version !== undefined ? options.version : '1.0.0'; - - /** - * @private - * @type {string} - */ - this.format_ = options.format !== undefined ? options.format : 'image/jpeg'; - - /** - * @private - * @type {!Object} - */ - this.dimensions_ = options.dimensions !== undefined ? options.dimensions : {}; - - /** - * @private - * @type {string} - */ - this.layer_ = options.layer; - - /** - * @private - * @type {string} - */ - this.matrixSet_ = options.matrixSet; - - /** - * @private - * @type {string} - */ - this.style_ = options.style; - - var urls = options.urls; - if (urls === undefined && options.url !== undefined) { - urls = ol.TileUrlFunction.expandUrl(options.url); - } - - // FIXME: should we guess this requestEncoding from options.url(s) - // structure? that would mean KVP only if a template is not provided. - - /** - * @private - * @type {ol.source.WMTSRequestEncoding} - */ - this.requestEncoding_ = options.requestEncoding !== undefined ? - /** @type {ol.source.WMTSRequestEncoding} */ (options.requestEncoding) : - ol.source.WMTSRequestEncoding.KVP; - - var requestEncoding = this.requestEncoding_; - - // FIXME: should we create a default tileGrid? - // we could issue a getCapabilities xhr to retrieve missing configuration - var tileGrid = options.tileGrid; - - // context property names are lower case to allow for a case insensitive - // replacement as some services use different naming conventions - var context = { - 'layer': this.layer_, - 'style': this.style_, - 'tilematrixset': this.matrixSet_ - }; - - if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) { - ol.obj.assign(context, { - 'Service': 'WMTS', - 'Request': 'GetTile', - 'Version': this.version_, - 'Format': this.format_ - }); - } - - var dimensions = this.dimensions_; - - /** - * @param {string} template Template. - * @return {ol.TileUrlFunctionType} Tile URL function. - */ - function createFromWMTSTemplate(template) { - - // TODO: we may want to create our own appendParams function so that params - // order conforms to wmts spec guidance, and so that we can avoid to escape - // special template params - - template = (requestEncoding == ol.source.WMTSRequestEncoding.KVP) ? - ol.uri.appendParams(template, context) : - template.replace(/\{(\w+?)\}/g, function(m, p) { - return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; - }); - - return ( - /** - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - var localContext = { - 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]), - 'TileCol': tileCoord[1], - 'TileRow': -tileCoord[2] - 1 - }; - ol.obj.assign(localContext, dimensions); - var url = template; - if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) { - url = ol.uri.appendParams(url, localContext); - } else { - url = url.replace(/\{(\w+?)\}/g, function(m, p) { - return localContext[p]; - }); - } - return url; - } - }); - } - - var tileUrlFunction = (urls && urls.length > 0) ? - ol.TileUrlFunction.createFromTileUrlFunctions( - urls.map(createFromWMTSTemplate)) : - ol.TileUrlFunction.nullTileUrlFunction; - - ol.source.TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - logo: options.logo, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileClass: options.tileClass, - tileGrid: tileGrid, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: tileUrlFunction, - urls: urls, - wrapX: options.wrapX !== undefined ? options.wrapX : false - }); - - this.setKey(this.getKeyForDimensions_()); - -}; -ol.inherits(ol.source.WMTS, ol.source.TileImage); - - -/** - * Get the dimensions, i.e. those passed to the constructor through the - * "dimensions" option, and possibly updated using the updateDimensions - * method. - * @return {!Object} Dimensions. - * @api - */ -ol.source.WMTS.prototype.getDimensions = function() { - return this.dimensions_; -}; - - -/** - * Return the image format of the WMTS source. - * @return {string} Format. - * @api - */ -ol.source.WMTS.prototype.getFormat = function() { - return this.format_; -}; - - -/** - * Return the layer of the WMTS source. - * @return {string} Layer. - * @api - */ -ol.source.WMTS.prototype.getLayer = function() { - return this.layer_; -}; - - -/** - * Return the matrix set of the WMTS source. - * @return {string} MatrixSet. - * @api - */ -ol.source.WMTS.prototype.getMatrixSet = function() { - return this.matrixSet_; -}; - - -/** - * Return the request encoding, either "KVP" or "REST". - * @return {ol.source.WMTSRequestEncoding} Request encoding. - * @api - */ -ol.source.WMTS.prototype.getRequestEncoding = function() { - return this.requestEncoding_; -}; - - -/** - * Return the style of the WMTS source. - * @return {string} Style. - * @api - */ -ol.source.WMTS.prototype.getStyle = function() { - return this.style_; -}; - - -/** - * Return the version of the WMTS source. - * @return {string} Version. - * @api - */ -ol.source.WMTS.prototype.getVersion = function() { - return this.version_; -}; - - -/** - * @private - * @return {string} The key for the current dimensions. - */ -ol.source.WMTS.prototype.getKeyForDimensions_ = function() { - var i = 0; - var res = []; - for (var key in this.dimensions_) { - res[i++] = key + '-' + this.dimensions_[key]; - } - return res.join('/'); -}; - - -/** - * Update the dimensions. - * @param {Object} dimensions Dimensions. - * @api - */ -ol.source.WMTS.prototype.updateDimensions = function(dimensions) { - ol.obj.assign(this.dimensions_, dimensions); - this.setKey(this.getKeyForDimensions_()); -}; - - -/** - * Generate source options from a capabilities object. - * @param {Object} wmtsCap An object representing the capabilities document. - * @param {Object} config Configuration properties for the layer. Defaults for - * the layer will apply if not provided. - * - * Required config properties: - * - layer - {string} The layer identifier. - * - * Optional config properties: - * - matrixSet - {string} The matrix set identifier, required if there is - * more than one matrix set in the layer capabilities. - * - projection - {string} The desired CRS when no matrixSet is specified. - * eg: "EPSG:3857". If the desired projection is not available, - * an error is thrown. - * - requestEncoding - {string} url encoding format for the layer. Default is - * the first tile url format found in the GetCapabilities response. - * - style - {string} The name of the style - * - format - {string} Image format for the layer. Default is the first - * format returned in the GetCapabilities response. - * - crossOrigin - {string|null|undefined} Cross origin. Default is `undefined`. - * @return {?olx.source.WMTSOptions} WMTS source options object or `null` if the layer was not found. - * @api - */ -ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { - var layers = wmtsCap['Contents']['Layer']; - var l = ol.array.find(layers, function(elt, index, array) { - return elt['Identifier'] == config['layer']; - }); - if (l === null) { - return null; - } - var tileMatrixSets = wmtsCap['Contents']['TileMatrixSet']; - var idx, matrixSet, matrixLimits; - if (l['TileMatrixSetLink'].length > 1) { - if ('projection' in config) { - idx = ol.array.findIndex(l['TileMatrixSetLink'], - function(elt, index, array) { - var tileMatrixSet = ol.array.find(tileMatrixSets, function(el) { - return el['Identifier'] == elt['TileMatrixSet']; - }); - var supportedCRS = tileMatrixSet['SupportedCRS'].replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'); - var proj1 = ol.proj.get(supportedCRS); - var proj2 = ol.proj.get(config['projection']); - if (proj1 && proj2) { - return ol.proj.equivalent(proj1, proj2); - } else { - return supportedCRS == config['projection']; - } - }); - } else { - idx = ol.array.findIndex(l['TileMatrixSetLink'], - function(elt, index, array) { - return elt['TileMatrixSet'] == config['matrixSet']; - }); - } - } else { - idx = 0; - } - if (idx < 0) { - idx = 0; - } - matrixSet = /** @type {string} */ - (l['TileMatrixSetLink'][idx]['TileMatrixSet']); - matrixLimits = /** @type {Array.<Object>} */ - (l['TileMatrixSetLink'][idx]['TileMatrixSetLimits']); - - var format = /** @type {string} */ (l['Format'][0]); - if ('format' in config) { - format = config['format']; - } - idx = ol.array.findIndex(l['Style'], function(elt, index, array) { - if ('style' in config) { - return elt['Title'] == config['style']; - } else { - return elt['isDefault']; - } - }); - if (idx < 0) { - idx = 0; - } - var style = /** @type {string} */ (l['Style'][idx]['Identifier']); - - var dimensions = {}; - if ('Dimension' in l) { - l['Dimension'].forEach(function(elt, index, array) { - var key = elt['Identifier']; - var value = elt['Default']; - if (value === undefined) { - value = elt['Value'][0]; - } - dimensions[key] = value; - }); - } - - var matrixSets = wmtsCap['Contents']['TileMatrixSet']; - var matrixSetObj = ol.array.find(matrixSets, function(elt, index, array) { - return elt['Identifier'] == matrixSet; - }); - - var projection; - if ('projection' in config) { - projection = ol.proj.get(config['projection']); - } else { - projection = ol.proj.get(matrixSetObj['SupportedCRS'].replace( - /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')); - } - - var wgs84BoundingBox = l['WGS84BoundingBox']; - var extent, wrapX; - if (wgs84BoundingBox !== undefined) { - var wgs84ProjectionExtent = ol.proj.get('EPSG:4326').getExtent(); - wrapX = (wgs84BoundingBox[0] == wgs84ProjectionExtent[0] && - wgs84BoundingBox[2] == wgs84ProjectionExtent[2]); - extent = ol.proj.transformExtent( - wgs84BoundingBox, 'EPSG:4326', projection); - var projectionExtent = projection.getExtent(); - if (projectionExtent) { - // If possible, do a sanity check on the extent - it should never be - // bigger than the validity extent of the projection of a matrix set. - if (!ol.extent.containsExtent(projectionExtent, extent)) { - extent = undefined; - } - } - } - - var tileGrid = ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet( - matrixSetObj, extent, matrixLimits); - - /** @type {!Array.<string>} */ - var urls = []; - var requestEncoding = config['requestEncoding']; - requestEncoding = requestEncoding !== undefined ? requestEncoding : ''; - - if ('OperationsMetadata' in wmtsCap && 'GetTile' in wmtsCap['OperationsMetadata']) { - var gets = wmtsCap['OperationsMetadata']['GetTile']['DCP']['HTTP']['Get']; - - for (var i = 0, ii = gets.length; i < ii; ++i) { - var constraint = ol.array.find(gets[i]['Constraint'], function(element) { - return element['name'] == 'GetEncoding'; - }); - var encodings = constraint['AllowedValues']['Value']; - - if (requestEncoding === '') { - // requestEncoding not provided, use the first encoding from the list - requestEncoding = encodings[0]; - } - if (requestEncoding === ol.source.WMTSRequestEncoding.KVP) { - if (ol.array.includes(encodings, ol.source.WMTSRequestEncoding.KVP)) { - urls.push(/** @type {string} */ (gets[i]['href'])); - } - } else { - break; - } - } - } - if (urls.length === 0) { - requestEncoding = ol.source.WMTSRequestEncoding.REST; - l['ResourceURL'].forEach(function(element) { - if (element['resourceType'] === 'tile') { - format = element['format']; - urls.push(/** @type {string} */ (element['template'])); - } - }); - } - - return { - urls: urls, - layer: config['layer'], - matrixSet: matrixSet, - format: format, - projection: projection, - requestEncoding: requestEncoding, - tileGrid: tileGrid, - style: style, - dimensions: dimensions, - wrapX: wrapX, - crossOrigin: config['crossOrigin'] - }; -}; - -goog.provide('ol.source.Zoomify'); - -goog.require('ol'); -goog.require('ol.ImageTile'); -goog.require('ol.TileState'); -goog.require('ol.TileUrlFunction'); -goog.require('ol.asserts'); -goog.require('ol.dom'); -goog.require('ol.extent'); -goog.require('ol.source.TileImage'); -goog.require('ol.tilegrid.TileGrid'); - - -/** - * @classdesc - * Layer source for tile data in Zoomify format. - * - * @constructor - * @extends {ol.source.TileImage} - * @param {olx.source.ZoomifyOptions=} opt_options Options. - * @api - */ -ol.source.Zoomify = function(opt_options) { - - var options = opt_options || {}; - - var size = options.size; - var tierSizeCalculation = options.tierSizeCalculation !== undefined ? - options.tierSizeCalculation : - ol.source.Zoomify.TierSizeCalculation_.DEFAULT; - - var imageWidth = size[0]; - var imageHeight = size[1]; - var tierSizeInTiles = []; - var tileSize = ol.DEFAULT_TILE_SIZE; - - switch (tierSizeCalculation) { - case ol.source.Zoomify.TierSizeCalculation_.DEFAULT: - while (imageWidth > tileSize || imageHeight > tileSize) { - tierSizeInTiles.push([ - Math.ceil(imageWidth / tileSize), - Math.ceil(imageHeight / tileSize) - ]); - tileSize += tileSize; - } - break; - case ol.source.Zoomify.TierSizeCalculation_.TRUNCATED: - var width = imageWidth; - var height = imageHeight; - while (width > tileSize || height > tileSize) { - tierSizeInTiles.push([ - Math.ceil(width / tileSize), - Math.ceil(height / tileSize) - ]); - width >>= 1; - height >>= 1; - } - break; - default: - ol.asserts.assert(false, 53); // Unknown `tierSizeCalculation` configured - break; - } - - tierSizeInTiles.push([1, 1]); - tierSizeInTiles.reverse(); - - var resolutions = [1]; - var tileCountUpToTier = [0]; - var i, ii; - for (i = 1, ii = tierSizeInTiles.length; i < ii; i++) { - resolutions.push(1 << i); - tileCountUpToTier.push( - tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] + - tileCountUpToTier[i - 1] - ); - } - resolutions.reverse(); - - var extent = [0, -size[1], size[0], 0]; - var tileGrid = new ol.tilegrid.TileGrid({ - extent: extent, - origin: ol.extent.getTopLeft(extent), - resolutions: resolutions - }); - - var url = options.url; - if (url && url.indexOf('{TileGroup}') == -1) { - url += '{TileGroup}/{z}-{x}-{y}.jpg'; - } - var urls = ol.TileUrlFunction.expandUrl(url); - - /** - * @param {string} template Template. - * @return {ol.TileUrlFunctionType} Tile URL function. - */ - function createFromTemplate(template) { - - return ( - /** - * @param {ol.TileCoord} tileCoord Tile Coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {ol.proj.Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - var tileCoordZ = tileCoord[0]; - var tileCoordX = tileCoord[1]; - var tileCoordY = -tileCoord[2] - 1; - var tileIndex = - tileCoordX + - tileCoordY * tierSizeInTiles[tileCoordZ][0] + - tileCountUpToTier[tileCoordZ]; - var tileGroup = (tileIndex / ol.DEFAULT_TILE_SIZE) | 0; - var localContext = { - 'z': tileCoordZ, - 'x': tileCoordX, - 'y': tileCoordY, - 'TileGroup': 'TileGroup' + tileGroup - }; - return template.replace(/\{(\w+?)\}/g, function(m, p) { - return localContext[p]; - }); - } - }); - } - - var tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions(urls.map(createFromTemplate)); - - ol.source.TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - logo: options.logo, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileClass: ol.source.Zoomify.Tile_, - tileGrid: tileGrid, - tileUrlFunction: tileUrlFunction - }); - -}; -ol.inherits(ol.source.Zoomify, ol.source.TileImage); - - -/** - * @constructor - * @extends {ol.ImageTile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.TileState} state State. - * @param {string} src Image source URI. - * @param {?string} crossOrigin Cross origin. - * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. - * @private - */ -ol.source.Zoomify.Tile_ = function( - tileCoord, state, src, crossOrigin, tileLoadFunction) { - - ol.ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction); - - /** - * @private - * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} - */ - this.zoomifyImage_ = null; - -}; -ol.inherits(ol.source.Zoomify.Tile_, ol.ImageTile); - - -/** - * @inheritDoc - */ -ol.source.Zoomify.Tile_.prototype.getImage = function() { - if (this.zoomifyImage_) { - return this.zoomifyImage_; - } - var tileSize = ol.DEFAULT_TILE_SIZE; - var image = ol.ImageTile.prototype.getImage.call(this); - if (this.state == ol.TileState.LOADED) { - if (image.width == tileSize && image.height == tileSize) { - this.zoomifyImage_ = image; - return image; - } else { - var context = ol.dom.createCanvasContext2D(tileSize, tileSize); - context.drawImage(image, 0, 0); - this.zoomifyImage_ = context.canvas; - return context.canvas; - } - } else { - return image; - } -}; - - -/** - * @enum {string} - * @private - */ -ol.source.Zoomify.TierSizeCalculation_ = { - DEFAULT: 'default', - TRUNCATED: 'truncated' -}; - -goog.provide('ol.style.Atlas'); - -goog.require('ol.dom'); - - -/** - * This class facilitates the creation of image atlases. - * - * Images added to an atlas will be rendered onto a single - * atlas canvas. The distribution of images on the canvas is - * managed with the bin packing algorithm described in: - * http://www.blackpawn.com/texts/lightmaps/ - * - * @constructor - * @struct - * @param {number} size The size in pixels of the sprite image. - * @param {number} space The space in pixels between images. - * Because texture coordinates are float values, the edges of - * images might not be completely correct (in a way that the - * edges overlap when being rendered). To avoid this we add a - * padding around each image. - */ -ol.style.Atlas = function(size, space) { - - /** - * @private - * @type {number} - */ - this.space_ = space; - - /** - * @private - * @type {Array.<ol.AtlasBlock>} - */ - this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}]; - - /** - * @private - * @type {Object.<string, ol.AtlasInfo>} - */ - this.entries_ = {}; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = ol.dom.createCanvasContext2D(size, size); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = this.context_.canvas; -}; - - -/** - * @param {string} id The identifier of the entry to check. - * @return {?ol.AtlasInfo} The atlas info. - */ -ol.style.Atlas.prototype.get = function(id) { - return this.entries_[id] || null; -}; - - -/** - * @param {string} id The identifier of the entry to add. - * @param {number} width The width. - * @param {number} height The height. - * @param {function(CanvasRenderingContext2D, number, number)} renderCallback - * Called to render the new image onto an atlas image. - * @param {Object=} opt_this Value to use as `this` when executing - * `renderCallback`. - * @return {?ol.AtlasInfo} The position and atlas image for the entry. - */ -ol.style.Atlas.prototype.add = function(id, width, height, renderCallback, opt_this) { - var block, i, ii; - for (i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) { - block = this.emptyBlocks_[i]; - if (block.width >= width + this.space_ && - block.height >= height + this.space_) { - // we found a block that is big enough for our entry - var entry = { - offsetX: block.x + this.space_, - offsetY: block.y + this.space_, - image: this.canvas_ - }; - this.entries_[id] = entry; - - // render the image on the atlas image - renderCallback.call(opt_this, this.context_, - block.x + this.space_, block.y + this.space_); - - // split the block after the insertion, either horizontally or vertically - this.split_(i, block, width + this.space_, height + this.space_); - - return entry; - } - } - - // there is no space for the new entry in this atlas - return null; -}; - - -/** - * @private - * @param {number} index The index of the block. - * @param {ol.AtlasBlock} block The block to split. - * @param {number} width The width of the entry to insert. - * @param {number} height The height of the entry to insert. - */ -ol.style.Atlas.prototype.split_ = function(index, block, width, height) { - var deltaWidth = block.width - width; - var deltaHeight = block.height - height; - - /** @type {ol.AtlasBlock} */ - var newBlock1; - /** @type {ol.AtlasBlock} */ - var newBlock2; - - if (deltaWidth > deltaHeight) { - // split vertically - // block right of the inserted entry - newBlock1 = { - x: block.x + width, - y: block.y, - width: block.width - width, - height: block.height - }; - - // block below the inserted entry - newBlock2 = { - x: block.x, - y: block.y + height, - width: width, - height: block.height - height - }; - this.updateBlocks_(index, newBlock1, newBlock2); - } else { - // split horizontally - // block right of the inserted entry - newBlock1 = { - x: block.x + width, - y: block.y, - width: block.width - width, - height: height - }; - - // block below the inserted entry - newBlock2 = { - x: block.x, - y: block.y + height, - width: block.width, - height: block.height - height - }; - this.updateBlocks_(index, newBlock1, newBlock2); - } -}; - - -/** - * Remove the old block and insert new blocks at the same array position. - * The new blocks are inserted at the same position, so that splitted - * blocks (that are potentially smaller) are filled first. - * @private - * @param {number} index The index of the block to remove. - * @param {ol.AtlasBlock} newBlock1 The 1st block to add. - * @param {ol.AtlasBlock} newBlock2 The 2nd block to add. - */ -ol.style.Atlas.prototype.updateBlocks_ = function(index, newBlock1, newBlock2) { - var args = [index, 1]; - if (newBlock1.width > 0 && newBlock1.height > 0) { - args.push(newBlock1); - } - if (newBlock2.width > 0 && newBlock2.height > 0) { - args.push(newBlock2); - } - this.emptyBlocks_.splice.apply(this.emptyBlocks_, args); -}; - -goog.provide('ol.style.AtlasManager'); - -goog.require('ol'); -goog.require('ol.style.Atlas'); - - -/** - * Manages the creation of image atlases. - * - * Images added to this manager will be inserted into an atlas, which - * will be used for rendering. - * The `size` given in the constructor is the size for the first - * atlas. After that, when new atlases are created, they will have - * twice the size as the latest atlas (until `maxSize` is reached). - * - * If an application uses many images or very large images, it is recommended - * to set a higher `size` value to avoid the creation of too many atlases. - * - * @constructor - * @struct - * @api - * @param {olx.style.AtlasManagerOptions=} opt_options Options. - */ -ol.style.AtlasManager = function(opt_options) { - - var options = opt_options || {}; - - /** - * The size in pixels of the latest atlas image. - * @private - * @type {number} - */ - this.currentSize_ = options.initialSize !== undefined ? - options.initialSize : ol.INITIAL_ATLAS_SIZE; - - /** - * The maximum size in pixels of atlas images. - * @private - * @type {number} - */ - this.maxSize_ = options.maxSize !== undefined ? - options.maxSize : ol.MAX_ATLAS_SIZE != -1 ? - ol.MAX_ATLAS_SIZE : ol.WEBGL_MAX_TEXTURE_SIZE !== undefined ? - ol.WEBGL_MAX_TEXTURE_SIZE : 2048; - - /** - * The size in pixels between images. - * @private - * @type {number} - */ - this.space_ = options.space !== undefined ? options.space : 1; - - /** - * @private - * @type {Array.<ol.style.Atlas>} - */ - this.atlases_ = [new ol.style.Atlas(this.currentSize_, this.space_)]; - - /** - * The size in pixels of the latest atlas image for hit-detection images. - * @private - * @type {number} - */ - this.currentHitSize_ = this.currentSize_; - - /** - * @private - * @type {Array.<ol.style.Atlas>} - */ - this.hitAtlases_ = [new ol.style.Atlas(this.currentHitSize_, this.space_)]; -}; - - -/** - * @param {string} id The identifier of the entry to check. - * @return {?ol.AtlasManagerInfo} The position and atlas image for the - * entry, or `null` if the entry is not part of the atlas manager. - */ -ol.style.AtlasManager.prototype.getInfo = function(id) { - /** @type {?ol.AtlasInfo} */ - var info = this.getInfo_(this.atlases_, id); - - if (!info) { - return null; - } - var hitInfo = /** @type {ol.AtlasInfo} */ (this.getInfo_(this.hitAtlases_, id)); - - return this.mergeInfos_(info, hitInfo); -}; - - -/** - * @private - * @param {Array.<ol.style.Atlas>} atlases The atlases to search. - * @param {string} id The identifier of the entry to check. - * @return {?ol.AtlasInfo} The position and atlas image for the entry, - * or `null` if the entry is not part of the atlases. - */ -ol.style.AtlasManager.prototype.getInfo_ = function(atlases, id) { - var atlas, info, i, ii; - for (i = 0, ii = atlases.length; i < ii; ++i) { - atlas = atlases[i]; - info = atlas.get(id); - if (info) { - return info; - } - } - return null; -}; - - -/** - * @private - * @param {ol.AtlasInfo} info The info for the real image. - * @param {ol.AtlasInfo} hitInfo The info for the hit-detection - * image. - * @return {?ol.AtlasManagerInfo} The position and atlas image for the - * entry, or `null` if the entry is not part of the atlases. - */ -ol.style.AtlasManager.prototype.mergeInfos_ = function(info, hitInfo) { - return /** @type {ol.AtlasManagerInfo} */ ({ - offsetX: info.offsetX, - offsetY: info.offsetY, - image: info.image, - hitImage: hitInfo.image - }); -}; - - -/** - * Add an image to the atlas manager. - * - * If an entry for the given id already exists, the entry will - * be overridden (but the space on the atlas graphic will not be freed). - * - * If `renderHitCallback` is provided, the image (or the hit-detection version - * of the image) will be rendered into a separate hit-detection atlas image. - * - * @param {string} id The identifier of the entry to add. - * @param {number} width The width. - * @param {number} height The height. - * @param {function(CanvasRenderingContext2D, number, number)} renderCallback - * Called to render the new image onto an atlas image. - * @param {function(CanvasRenderingContext2D, number, number)=} - * opt_renderHitCallback Called to render a hit-detection image onto a hit - * detection atlas image. - * @param {Object=} opt_this Value to use as `this` when executing - * `renderCallback` and `renderHitCallback`. - * @return {?ol.AtlasManagerInfo} The position and atlas image for the - * entry, or `null` if the image is too big. - */ -ol.style.AtlasManager.prototype.add = function(id, width, height, - renderCallback, opt_renderHitCallback, opt_this) { - if (width + this.space_ > this.maxSize_ || - height + this.space_ > this.maxSize_) { - return null; - } - - /** @type {?ol.AtlasInfo} */ - var info = this.add_(false, - id, width, height, renderCallback, opt_this); - if (!info) { - return null; - } - - // even if no hit-detection entry is requested, we insert a fake entry into - // the hit-detection atlas, to make sure that the offset is the same for - // the original image and the hit-detection image. - var renderHitCallback = opt_renderHitCallback !== undefined ? - opt_renderHitCallback : ol.nullFunction; - - var hitInfo = /** @type {ol.AtlasInfo} */ (this.add_(true, - id, width, height, renderHitCallback, opt_this)); - - return this.mergeInfos_(info, hitInfo); -}; - - -/** - * @private - * @param {boolean} isHitAtlas If the hit-detection atlases are used. - * @param {string} id The identifier of the entry to add. - * @param {number} width The width. - * @param {number} height The height. - * @param {function(CanvasRenderingContext2D, number, number)} renderCallback - * Called to render the new image onto an atlas image. - * @param {Object=} opt_this Value to use as `this` when executing - * `renderCallback` and `renderHitCallback`. - * @return {?ol.AtlasInfo} The position and atlas image for the entry, - * or `null` if the image is too big. - */ -ol.style.AtlasManager.prototype.add_ = function(isHitAtlas, id, width, height, - renderCallback, opt_this) { - var atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_; - var atlas, info, i, ii; - for (i = 0, ii = atlases.length; i < ii; ++i) { - atlas = atlases[i]; - info = atlas.add(id, width, height, renderCallback, opt_this); - if (info) { - return info; - } else if (!info && i === ii - 1) { - // the entry could not be added to one of the existing atlases, - // create a new atlas that is twice as big and try to add to this one. - var size; - if (isHitAtlas) { - size = Math.min(this.currentHitSize_ * 2, this.maxSize_); - this.currentHitSize_ = size; - } else { - size = Math.min(this.currentSize_ * 2, this.maxSize_); - this.currentSize_ = size; - } - atlas = new ol.style.Atlas(size, this.space_); - atlases.push(atlas); - // run the loop another time - ++ii; - } - } - return null; -}; - -// Copyright 2009 The Closure Library Authors. -// All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file has been auto-generated by GenJsDeps, please do not edit. - -goog.addDependency( - 'demos/editor/equationeditor.js', ['goog.demos.editor.EquationEditor'], - ['goog.ui.equation.EquationEditorDialog']); -goog.addDependency( - 'demos/editor/helloworld.js', ['goog.demos.editor.HelloWorld'], - ['goog.dom', 'goog.dom.TagName', 'goog.editor.Plugin']); -goog.addDependency( - 'demos/editor/helloworlddialog.js', - [ - 'goog.demos.editor.HelloWorldDialog', - 'goog.demos.editor.HelloWorldDialog.OkEvent' - ], - [ - 'goog.dom.TagName', 'goog.events.Event', 'goog.string', - 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.AbstractDialog.Builder', - 'goog.ui.editor.AbstractDialog.EventType' - ]); -goog.addDependency( - 'demos/editor/helloworlddialogplugin.js', - [ - 'goog.demos.editor.HelloWorldDialogPlugin', - 'goog.demos.editor.HelloWorldDialogPlugin.Command' - ], - [ - 'goog.demos.editor.HelloWorldDialog', 'goog.dom.TagName', - 'goog.editor.plugins.AbstractDialogPlugin', 'goog.editor.range', - 'goog.functions', 'goog.ui.editor.AbstractDialog.EventType' - ]); - -/** - * @fileoverview Custom exports file. - * @suppress {checkVars,extraRequire} - */ - -goog.require('ol'); -goog.require('ol.AssertionError'); -goog.require('ol.Attribution'); -goog.require('ol.Collection'); -goog.require('ol.DeviceOrientation'); -goog.require('ol.Feature'); -goog.require('ol.Geolocation'); -goog.require('ol.Graticule'); -goog.require('ol.Image'); -goog.require('ol.ImageTile'); -goog.require('ol.Kinetic'); -goog.require('ol.Map'); -goog.require('ol.MapBrowserEvent'); -goog.require('ol.MapEvent'); -goog.require('ol.Object'); -goog.require('ol.Observable'); -goog.require('ol.Overlay'); -goog.require('ol.Sphere'); -goog.require('ol.Tile'); -goog.require('ol.VectorTile'); -goog.require('ol.View'); -goog.require('ol.color'); -goog.require('ol.colorlike'); -goog.require('ol.control'); -goog.require('ol.control.Attribution'); -goog.require('ol.control.Control'); -goog.require('ol.control.FullScreen'); -goog.require('ol.control.MousePosition'); -goog.require('ol.control.OverviewMap'); -goog.require('ol.control.Rotate'); -goog.require('ol.control.ScaleLine'); -goog.require('ol.control.Zoom'); -goog.require('ol.control.ZoomSlider'); -goog.require('ol.control.ZoomToExtent'); -goog.require('ol.coordinate'); -goog.require('ol.easing'); -goog.require('ol.events.Event'); -goog.require('ol.events.condition'); -goog.require('ol.extent'); -goog.require('ol.featureloader'); -goog.require('ol.format.EsriJSON'); -goog.require('ol.format.Feature'); -goog.require('ol.format.GML'); -goog.require('ol.format.GML2'); -goog.require('ol.format.GML3'); -goog.require('ol.format.GMLBase'); -goog.require('ol.format.GPX'); -goog.require('ol.format.GeoJSON'); -goog.require('ol.format.IGC'); -goog.require('ol.format.KML'); -goog.require('ol.format.MVT'); -goog.require('ol.format.OSMXML'); -goog.require('ol.format.Polyline'); -goog.require('ol.format.TopoJSON'); -goog.require('ol.format.WFS'); -goog.require('ol.format.WKT'); -goog.require('ol.format.WMSCapabilities'); -goog.require('ol.format.WMSGetFeatureInfo'); -goog.require('ol.format.WMTSCapabilities'); -goog.require('ol.format.filter'); -goog.require('ol.format.filter.And'); -goog.require('ol.format.filter.Bbox'); -goog.require('ol.format.filter.Comparison'); -goog.require('ol.format.filter.ComparisonBinary'); -goog.require('ol.format.filter.During'); -goog.require('ol.format.filter.EqualTo'); -goog.require('ol.format.filter.Filter'); -goog.require('ol.format.filter.GreaterThan'); -goog.require('ol.format.filter.GreaterThanOrEqualTo'); -goog.require('ol.format.filter.Intersects'); -goog.require('ol.format.filter.IsBetween'); -goog.require('ol.format.filter.IsLike'); -goog.require('ol.format.filter.IsNull'); -goog.require('ol.format.filter.LessThan'); -goog.require('ol.format.filter.LessThanOrEqualTo'); -goog.require('ol.format.filter.Not'); -goog.require('ol.format.filter.NotEqualTo'); -goog.require('ol.format.filter.Or'); -goog.require('ol.format.filter.Spatial'); -goog.require('ol.format.filter.Within'); -goog.require('ol.geom.Circle'); -goog.require('ol.geom.Geometry'); -goog.require('ol.geom.GeometryCollection'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.LinearRing'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.geom.SimpleGeometry'); -goog.require('ol.has'); -goog.require('ol.interaction'); -goog.require('ol.interaction.DoubleClickZoom'); -goog.require('ol.interaction.DragAndDrop'); -goog.require('ol.interaction.DragBox'); -goog.require('ol.interaction.DragPan'); -goog.require('ol.interaction.DragRotate'); -goog.require('ol.interaction.DragRotateAndZoom'); -goog.require('ol.interaction.DragZoom'); -goog.require('ol.interaction.Draw'); -goog.require('ol.interaction.Extent'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.interaction.KeyboardPan'); -goog.require('ol.interaction.KeyboardZoom'); -goog.require('ol.interaction.Modify'); -goog.require('ol.interaction.MouseWheelZoom'); -goog.require('ol.interaction.PinchRotate'); -goog.require('ol.interaction.PinchZoom'); -goog.require('ol.interaction.Pointer'); -goog.require('ol.interaction.Select'); -goog.require('ol.interaction.Snap'); -goog.require('ol.interaction.Translate'); -goog.require('ol.layer.Base'); -goog.require('ol.layer.Group'); -goog.require('ol.layer.Heatmap'); -goog.require('ol.layer.Image'); -goog.require('ol.layer.Layer'); -goog.require('ol.layer.Tile'); -goog.require('ol.layer.Vector'); -goog.require('ol.layer.VectorTile'); -goog.require('ol.loadingstrategy'); -goog.require('ol.proj'); -goog.require('ol.proj.Projection'); -goog.require('ol.proj.Units'); -goog.require('ol.proj.common'); -goog.require('ol.render'); -goog.require('ol.render.Event'); -goog.require('ol.render.Feature'); -goog.require('ol.render.VectorContext'); -goog.require('ol.render.canvas.Immediate'); -goog.require('ol.render.webgl.Immediate'); -goog.require('ol.size'); -goog.require('ol.source.BingMaps'); -goog.require('ol.source.CartoDB'); -goog.require('ol.source.Cluster'); -goog.require('ol.source.Image'); -goog.require('ol.source.ImageArcGISRest'); -goog.require('ol.source.ImageCanvas'); -goog.require('ol.source.ImageMapGuide'); -goog.require('ol.source.ImageStatic'); -goog.require('ol.source.ImageVector'); -goog.require('ol.source.ImageWMS'); -goog.require('ol.source.OSM'); -goog.require('ol.source.Raster'); -goog.require('ol.source.Source'); -goog.require('ol.source.Stamen'); -goog.require('ol.source.Tile'); -goog.require('ol.source.TileArcGISRest'); -goog.require('ol.source.TileDebug'); -goog.require('ol.source.TileImage'); -goog.require('ol.source.TileJSON'); -goog.require('ol.source.TileUTFGrid'); -goog.require('ol.source.TileWMS'); -goog.require('ol.source.UrlTile'); -goog.require('ol.source.Vector'); -goog.require('ol.source.VectorTile'); -goog.require('ol.source.WMTS'); -goog.require('ol.source.XYZ'); -goog.require('ol.source.Zoomify'); -goog.require('ol.style.AtlasManager'); -goog.require('ol.style.Circle'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Icon'); -goog.require('ol.style.Image'); -goog.require('ol.style.RegularShape'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); -goog.require('ol.style.Text'); -goog.require('ol.tilegrid'); -goog.require('ol.tilegrid.TileGrid'); -goog.require('ol.tilegrid.WMTS'); -goog.require('ol.webgl.Context'); -goog.require('ol.xml'); - - - -goog.exportProperty( - ol.AssertionError.prototype, - 'code', - ol.AssertionError.prototype.code); - -goog.exportSymbol( - 'ol.Attribution', - ol.Attribution, - OPENLAYERS); - -goog.exportProperty( - ol.Attribution.prototype, - 'getHTML', - ol.Attribution.prototype.getHTML); - -goog.exportSymbol( - 'ol.Collection', - ol.Collection, - OPENLAYERS); - -goog.exportProperty( - ol.Collection.prototype, - 'clear', - ol.Collection.prototype.clear); - -goog.exportProperty( - ol.Collection.prototype, - 'extend', - ol.Collection.prototype.extend); - -goog.exportProperty( - ol.Collection.prototype, - 'forEach', - ol.Collection.prototype.forEach); - -goog.exportProperty( - ol.Collection.prototype, - 'getArray', - ol.Collection.prototype.getArray); - -goog.exportProperty( - ol.Collection.prototype, - 'item', - ol.Collection.prototype.item); - -goog.exportProperty( - ol.Collection.prototype, - 'getLength', - ol.Collection.prototype.getLength); - -goog.exportProperty( - ol.Collection.prototype, - 'insertAt', - ol.Collection.prototype.insertAt); - -goog.exportProperty( - ol.Collection.prototype, - 'pop', - ol.Collection.prototype.pop); - -goog.exportProperty( - ol.Collection.prototype, - 'push', - ol.Collection.prototype.push); - -goog.exportProperty( - ol.Collection.prototype, - 'remove', - ol.Collection.prototype.remove); - -goog.exportProperty( - ol.Collection.prototype, - 'removeAt', - ol.Collection.prototype.removeAt); - -goog.exportProperty( - ol.Collection.prototype, - 'setAt', - ol.Collection.prototype.setAt); - -goog.exportProperty( - ol.Collection.Event.prototype, - 'element', - ol.Collection.Event.prototype.element); - -goog.exportSymbol( - 'ol.color.asArray', - ol.color.asArray, - OPENLAYERS); - -goog.exportSymbol( - 'ol.color.asString', - ol.color.asString, - OPENLAYERS); - -goog.exportSymbol( - 'ol.colorlike.asColorLike', - ol.colorlike.asColorLike, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.defaults', - ol.control.defaults, - OPENLAYERS); - -goog.exportSymbol( - 'ol.coordinate.add', - ol.coordinate.add, - OPENLAYERS); - -goog.exportSymbol( - 'ol.coordinate.createStringXY', - ol.coordinate.createStringXY, - OPENLAYERS); - -goog.exportSymbol( - 'ol.coordinate.format', - ol.coordinate.format, - OPENLAYERS); - -goog.exportSymbol( - 'ol.coordinate.rotate', - ol.coordinate.rotate, - OPENLAYERS); - -goog.exportSymbol( - 'ol.coordinate.toStringHDMS', - ol.coordinate.toStringHDMS, - OPENLAYERS); - -goog.exportSymbol( - 'ol.coordinate.toStringXY', - ol.coordinate.toStringXY, - OPENLAYERS); - -goog.exportSymbol( - 'ol.DeviceOrientation', - ol.DeviceOrientation, - OPENLAYERS); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getAlpha', - ol.DeviceOrientation.prototype.getAlpha); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getBeta', - ol.DeviceOrientation.prototype.getBeta); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getGamma', - ol.DeviceOrientation.prototype.getGamma); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getHeading', - ol.DeviceOrientation.prototype.getHeading); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getTracking', - ol.DeviceOrientation.prototype.getTracking); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'setTracking', - ol.DeviceOrientation.prototype.setTracking); - -goog.exportSymbol( - 'ol.easing.easeIn', - ol.easing.easeIn, - OPENLAYERS); - -goog.exportSymbol( - 'ol.easing.easeOut', - ol.easing.easeOut, - OPENLAYERS); - -goog.exportSymbol( - 'ol.easing.inAndOut', - ol.easing.inAndOut, - OPENLAYERS); - -goog.exportSymbol( - 'ol.easing.linear', - ol.easing.linear, - OPENLAYERS); - -goog.exportSymbol( - 'ol.easing.upAndDown', - ol.easing.upAndDown, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.boundingExtent', - ol.extent.boundingExtent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.buffer', - ol.extent.buffer, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.containsCoordinate', - ol.extent.containsCoordinate, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.containsExtent', - ol.extent.containsExtent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.containsXY', - ol.extent.containsXY, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.createEmpty', - ol.extent.createEmpty, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.equals', - ol.extent.equals, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.extend', - ol.extent.extend, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getArea', - ol.extent.getArea, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getBottomLeft', - ol.extent.getBottomLeft, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getBottomRight', - ol.extent.getBottomRight, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getCenter', - ol.extent.getCenter, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getHeight', - ol.extent.getHeight, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getIntersection', - ol.extent.getIntersection, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getSize', - ol.extent.getSize, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getTopLeft', - ol.extent.getTopLeft, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getTopRight', - ol.extent.getTopRight, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.getWidth', - ol.extent.getWidth, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.intersects', - ol.extent.intersects, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.isEmpty', - ol.extent.isEmpty, - OPENLAYERS); - -goog.exportSymbol( - 'ol.extent.applyTransform', - ol.extent.applyTransform, - OPENLAYERS); - -goog.exportSymbol( - 'ol.Feature', - ol.Feature, - OPENLAYERS); - -goog.exportProperty( - ol.Feature.prototype, - 'clone', - ol.Feature.prototype.clone); - -goog.exportProperty( - ol.Feature.prototype, - 'getGeometry', - ol.Feature.prototype.getGeometry); - -goog.exportProperty( - ol.Feature.prototype, - 'getId', - ol.Feature.prototype.getId); - -goog.exportProperty( - ol.Feature.prototype, - 'getGeometryName', - ol.Feature.prototype.getGeometryName); - -goog.exportProperty( - ol.Feature.prototype, - 'getStyle', - ol.Feature.prototype.getStyle); - -goog.exportProperty( - ol.Feature.prototype, - 'getStyleFunction', - ol.Feature.prototype.getStyleFunction); - -goog.exportProperty( - ol.Feature.prototype, - 'setGeometry', - ol.Feature.prototype.setGeometry); - -goog.exportProperty( - ol.Feature.prototype, - 'setStyle', - ol.Feature.prototype.setStyle); - -goog.exportProperty( - ol.Feature.prototype, - 'setId', - ol.Feature.prototype.setId); - -goog.exportProperty( - ol.Feature.prototype, - 'setGeometryName', - ol.Feature.prototype.setGeometryName); - -goog.exportSymbol( - 'ol.featureloader.xhr', - ol.featureloader.xhr, - OPENLAYERS); - -goog.exportSymbol( - 'ol.Geolocation', - ol.Geolocation, - OPENLAYERS); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getAccuracy', - ol.Geolocation.prototype.getAccuracy); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getAccuracyGeometry', - ol.Geolocation.prototype.getAccuracyGeometry); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getAltitude', - ol.Geolocation.prototype.getAltitude); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getAltitudeAccuracy', - ol.Geolocation.prototype.getAltitudeAccuracy); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getHeading', - ol.Geolocation.prototype.getHeading); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getPosition', - ol.Geolocation.prototype.getPosition); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getProjection', - ol.Geolocation.prototype.getProjection); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getSpeed', - ol.Geolocation.prototype.getSpeed); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getTracking', - ol.Geolocation.prototype.getTracking); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getTrackingOptions', - ol.Geolocation.prototype.getTrackingOptions); - -goog.exportProperty( - ol.Geolocation.prototype, - 'setProjection', - ol.Geolocation.prototype.setProjection); - -goog.exportProperty( - ol.Geolocation.prototype, - 'setTracking', - ol.Geolocation.prototype.setTracking); - -goog.exportProperty( - ol.Geolocation.prototype, - 'setTrackingOptions', - ol.Geolocation.prototype.setTrackingOptions); - -goog.exportSymbol( - 'ol.Graticule', - ol.Graticule, - OPENLAYERS); - -goog.exportProperty( - ol.Graticule.prototype, - 'getMap', - ol.Graticule.prototype.getMap); - -goog.exportProperty( - ol.Graticule.prototype, - 'getMeridians', - ol.Graticule.prototype.getMeridians); - -goog.exportProperty( - ol.Graticule.prototype, - 'getParallels', - ol.Graticule.prototype.getParallels); - -goog.exportProperty( - ol.Graticule.prototype, - 'setMap', - ol.Graticule.prototype.setMap); - -goog.exportSymbol( - 'ol.has.DEVICE_PIXEL_RATIO', - ol.has.DEVICE_PIXEL_RATIO, - OPENLAYERS); - -goog.exportSymbol( - 'ol.has.CANVAS', - ol.has.CANVAS, - OPENLAYERS); - -goog.exportSymbol( - 'ol.has.DEVICE_ORIENTATION', - ol.has.DEVICE_ORIENTATION, - OPENLAYERS); - -goog.exportSymbol( - 'ol.has.GEOLOCATION', - ol.has.GEOLOCATION, - OPENLAYERS); - -goog.exportSymbol( - 'ol.has.TOUCH', - ol.has.TOUCH, - OPENLAYERS); - -goog.exportSymbol( - 'ol.has.WEBGL', - ol.has.WEBGL, - OPENLAYERS); - -goog.exportProperty( - ol.Image.prototype, - 'getImage', - ol.Image.prototype.getImage); - -goog.exportProperty( - ol.Image.prototype, - 'load', - ol.Image.prototype.load); - -goog.exportProperty( - ol.ImageTile.prototype, - 'getImage', - ol.ImageTile.prototype.getImage); - -goog.exportSymbol( - 'ol.inherits', - ol.inherits, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.defaults', - ol.interaction.defaults, - OPENLAYERS); - -goog.exportSymbol( - 'ol.Kinetic', - ol.Kinetic, - OPENLAYERS); - -goog.exportSymbol( - 'ol.loadingstrategy.all', - ol.loadingstrategy.all, - OPENLAYERS); - -goog.exportSymbol( - 'ol.loadingstrategy.bbox', - ol.loadingstrategy.bbox, - OPENLAYERS); - -goog.exportSymbol( - 'ol.loadingstrategy.tile', - ol.loadingstrategy.tile, - OPENLAYERS); - -goog.exportSymbol( - 'ol.Map', - ol.Map, - OPENLAYERS); - -goog.exportProperty( - ol.Map.prototype, - 'addControl', - ol.Map.prototype.addControl); - -goog.exportProperty( - ol.Map.prototype, - 'addInteraction', - ol.Map.prototype.addInteraction); - -goog.exportProperty( - ol.Map.prototype, - 'addLayer', - ol.Map.prototype.addLayer); - -goog.exportProperty( - ol.Map.prototype, - 'addOverlay', - ol.Map.prototype.addOverlay); - -goog.exportProperty( - ol.Map.prototype, - 'forEachFeatureAtPixel', - ol.Map.prototype.forEachFeatureAtPixel); - -goog.exportProperty( - ol.Map.prototype, - 'forEachLayerAtPixel', - ol.Map.prototype.forEachLayerAtPixel); - -goog.exportProperty( - ol.Map.prototype, - 'hasFeatureAtPixel', - ol.Map.prototype.hasFeatureAtPixel); - -goog.exportProperty( - ol.Map.prototype, - 'getEventCoordinate', - ol.Map.prototype.getEventCoordinate); - -goog.exportProperty( - ol.Map.prototype, - 'getEventPixel', - ol.Map.prototype.getEventPixel); - -goog.exportProperty( - ol.Map.prototype, - 'getTarget', - ol.Map.prototype.getTarget); - -goog.exportProperty( - ol.Map.prototype, - 'getTargetElement', - ol.Map.prototype.getTargetElement); - -goog.exportProperty( - ol.Map.prototype, - 'getCoordinateFromPixel', - ol.Map.prototype.getCoordinateFromPixel); - -goog.exportProperty( - ol.Map.prototype, - 'getControls', - ol.Map.prototype.getControls); - -goog.exportProperty( - ol.Map.prototype, - 'getOverlays', - ol.Map.prototype.getOverlays); - -goog.exportProperty( - ol.Map.prototype, - 'getOverlayById', - ol.Map.prototype.getOverlayById); - -goog.exportProperty( - ol.Map.prototype, - 'getInteractions', - ol.Map.prototype.getInteractions); - -goog.exportProperty( - ol.Map.prototype, - 'getLayerGroup', - ol.Map.prototype.getLayerGroup); - -goog.exportProperty( - ol.Map.prototype, - 'getLayers', - ol.Map.prototype.getLayers); - -goog.exportProperty( - ol.Map.prototype, - 'getPixelFromCoordinate', - ol.Map.prototype.getPixelFromCoordinate); - -goog.exportProperty( - ol.Map.prototype, - 'getSize', - ol.Map.prototype.getSize); - -goog.exportProperty( - ol.Map.prototype, - 'getView', - ol.Map.prototype.getView); - -goog.exportProperty( - ol.Map.prototype, - 'getViewport', - ol.Map.prototype.getViewport); - -goog.exportProperty( - ol.Map.prototype, - 'renderSync', - ol.Map.prototype.renderSync); - -goog.exportProperty( - ol.Map.prototype, - 'render', - ol.Map.prototype.render); - -goog.exportProperty( - ol.Map.prototype, - 'removeControl', - ol.Map.prototype.removeControl); - -goog.exportProperty( - ol.Map.prototype, - 'removeInteraction', - ol.Map.prototype.removeInteraction); - -goog.exportProperty( - ol.Map.prototype, - 'removeLayer', - ol.Map.prototype.removeLayer); - -goog.exportProperty( - ol.Map.prototype, - 'removeOverlay', - ol.Map.prototype.removeOverlay); - -goog.exportProperty( - ol.Map.prototype, - 'setLayerGroup', - ol.Map.prototype.setLayerGroup); - -goog.exportProperty( - ol.Map.prototype, - 'setSize', - ol.Map.prototype.setSize); - -goog.exportProperty( - ol.Map.prototype, - 'setTarget', - ol.Map.prototype.setTarget); - -goog.exportProperty( - ol.Map.prototype, - 'setView', - ol.Map.prototype.setView); - -goog.exportProperty( - ol.Map.prototype, - 'updateSize', - ol.Map.prototype.updateSize); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'originalEvent', - ol.MapBrowserEvent.prototype.originalEvent); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'pixel', - ol.MapBrowserEvent.prototype.pixel); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'coordinate', - ol.MapBrowserEvent.prototype.coordinate); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'dragging', - ol.MapBrowserEvent.prototype.dragging); - -goog.exportProperty( - ol.MapEvent.prototype, - 'map', - ol.MapEvent.prototype.map); - -goog.exportProperty( - ol.MapEvent.prototype, - 'frameState', - ol.MapEvent.prototype.frameState); - -goog.exportSymbol( - 'ol.Object', - ol.Object, - OPENLAYERS); - -goog.exportProperty( - ol.Object.prototype, - 'get', - ol.Object.prototype.get); - -goog.exportProperty( - ol.Object.prototype, - 'getKeys', - ol.Object.prototype.getKeys); - -goog.exportProperty( - ol.Object.prototype, - 'getProperties', - ol.Object.prototype.getProperties); - -goog.exportProperty( - ol.Object.prototype, - 'set', - ol.Object.prototype.set); - -goog.exportProperty( - ol.Object.prototype, - 'setProperties', - ol.Object.prototype.setProperties); - -goog.exportProperty( - ol.Object.prototype, - 'unset', - ol.Object.prototype.unset); - -goog.exportProperty( - ol.Object.Event.prototype, - 'key', - ol.Object.Event.prototype.key); - -goog.exportProperty( - ol.Object.Event.prototype, - 'oldValue', - ol.Object.Event.prototype.oldValue); - -goog.exportSymbol( - 'ol.Observable', - ol.Observable, - OPENLAYERS); - -goog.exportSymbol( - 'ol.Observable.unByKey', - ol.Observable.unByKey, - OPENLAYERS); - -goog.exportProperty( - ol.Observable.prototype, - 'changed', - ol.Observable.prototype.changed); - -goog.exportProperty( - ol.Observable.prototype, - 'dispatchEvent', - ol.Observable.prototype.dispatchEvent); - -goog.exportProperty( - ol.Observable.prototype, - 'getRevision', - ol.Observable.prototype.getRevision); - -goog.exportProperty( - ol.Observable.prototype, - 'on', - ol.Observable.prototype.on); - -goog.exportProperty( - ol.Observable.prototype, - 'once', - ol.Observable.prototype.once); - -goog.exportProperty( - ol.Observable.prototype, - 'un', - ol.Observable.prototype.un); - -goog.exportSymbol( - 'ol.Overlay', - ol.Overlay, - OPENLAYERS); - -goog.exportProperty( - ol.Overlay.prototype, - 'getElement', - ol.Overlay.prototype.getElement); - -goog.exportProperty( - ol.Overlay.prototype, - 'getId', - ol.Overlay.prototype.getId); - -goog.exportProperty( - ol.Overlay.prototype, - 'getMap', - ol.Overlay.prototype.getMap); - -goog.exportProperty( - ol.Overlay.prototype, - 'getOffset', - ol.Overlay.prototype.getOffset); - -goog.exportProperty( - ol.Overlay.prototype, - 'getPosition', - ol.Overlay.prototype.getPosition); - -goog.exportProperty( - ol.Overlay.prototype, - 'getPositioning', - ol.Overlay.prototype.getPositioning); - -goog.exportProperty( - ol.Overlay.prototype, - 'setElement', - ol.Overlay.prototype.setElement); - -goog.exportProperty( - ol.Overlay.prototype, - 'setMap', - ol.Overlay.prototype.setMap); - -goog.exportProperty( - ol.Overlay.prototype, - 'setOffset', - ol.Overlay.prototype.setOffset); - -goog.exportProperty( - ol.Overlay.prototype, - 'setPosition', - ol.Overlay.prototype.setPosition); - -goog.exportProperty( - ol.Overlay.prototype, - 'setPositioning', - ol.Overlay.prototype.setPositioning); - -goog.exportSymbol( - 'ol.proj.METERS_PER_UNIT', - ol.proj.METERS_PER_UNIT, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.setProj4', - ol.proj.setProj4, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.getPointResolution', - ol.proj.getPointResolution, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.addEquivalentProjections', - ol.proj.addEquivalentProjections, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.addProjection', - ol.proj.addProjection, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.addCoordinateTransforms', - ol.proj.addCoordinateTransforms, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.fromLonLat', - ol.proj.fromLonLat, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.toLonLat', - ol.proj.toLonLat, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.get', - ol.proj.get, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.equivalent', - ol.proj.equivalent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.getTransform', - ol.proj.getTransform, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.transform', - ol.proj.transform, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.transformExtent', - ol.proj.transformExtent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.render.toContext', - ol.render.toContext, - OPENLAYERS); - -goog.exportSymbol( - 'ol.size.toSize', - ol.size.toSize, - OPENLAYERS); - -goog.exportSymbol( - 'ol.Sphere', - ol.Sphere, - OPENLAYERS); - -goog.exportProperty( - ol.Sphere.prototype, - 'geodesicArea', - ol.Sphere.prototype.geodesicArea); - -goog.exportProperty( - ol.Sphere.prototype, - 'haversineDistance', - ol.Sphere.prototype.haversineDistance); - -goog.exportProperty( - ol.Tile.prototype, - 'getTileCoord', - ol.Tile.prototype.getTileCoord); - -goog.exportProperty( - ol.Tile.prototype, - 'load', - ol.Tile.prototype.load); - -goog.exportSymbol( - 'ol.tilegrid.createXYZ', - ol.tilegrid.createXYZ, - OPENLAYERS); - -goog.exportProperty( - ol.VectorTile.prototype, - 'getFormat', - ol.VectorTile.prototype.getFormat); - -goog.exportProperty( - ol.VectorTile.prototype, - 'getFeatures', - ol.VectorTile.prototype.getFeatures); - -goog.exportProperty( - ol.VectorTile.prototype, - 'getProjection', - ol.VectorTile.prototype.getProjection); - -goog.exportProperty( - ol.VectorTile.prototype, - 'setFeatures', - ol.VectorTile.prototype.setFeatures); - -goog.exportProperty( - ol.VectorTile.prototype, - 'setProjection', - ol.VectorTile.prototype.setProjection); - -goog.exportProperty( - ol.VectorTile.prototype, - 'setLoader', - ol.VectorTile.prototype.setLoader); - -goog.exportSymbol( - 'ol.View', - ol.View, - OPENLAYERS); - -goog.exportProperty( - ol.View.prototype, - 'animate', - ol.View.prototype.animate); - -goog.exportProperty( - ol.View.prototype, - 'getAnimating', - ol.View.prototype.getAnimating); - -goog.exportProperty( - ol.View.prototype, - 'getInteracting', - ol.View.prototype.getInteracting); - -goog.exportProperty( - ol.View.prototype, - 'cancelAnimations', - ol.View.prototype.cancelAnimations); - -goog.exportProperty( - ol.View.prototype, - 'constrainCenter', - ol.View.prototype.constrainCenter); - -goog.exportProperty( - ol.View.prototype, - 'constrainResolution', - ol.View.prototype.constrainResolution); - -goog.exportProperty( - ol.View.prototype, - 'constrainRotation', - ol.View.prototype.constrainRotation); - -goog.exportProperty( - ol.View.prototype, - 'getCenter', - ol.View.prototype.getCenter); - -goog.exportProperty( - ol.View.prototype, - 'calculateExtent', - ol.View.prototype.calculateExtent); - -goog.exportProperty( - ol.View.prototype, - 'getMaxResolution', - ol.View.prototype.getMaxResolution); - -goog.exportProperty( - ol.View.prototype, - 'getMinResolution', - ol.View.prototype.getMinResolution); - -goog.exportProperty( - ol.View.prototype, - 'getMaxZoom', - ol.View.prototype.getMaxZoom); - -goog.exportProperty( - ol.View.prototype, - 'setMaxZoom', - ol.View.prototype.setMaxZoom); - -goog.exportProperty( - ol.View.prototype, - 'getMinZoom', - ol.View.prototype.getMinZoom); - -goog.exportProperty( - ol.View.prototype, - 'setMinZoom', - ol.View.prototype.setMinZoom); - -goog.exportProperty( - ol.View.prototype, - 'getProjection', - ol.View.prototype.getProjection); - -goog.exportProperty( - ol.View.prototype, - 'getResolution', - ol.View.prototype.getResolution); - -goog.exportProperty( - ol.View.prototype, - 'getResolutions', - ol.View.prototype.getResolutions); - -goog.exportProperty( - ol.View.prototype, - 'getResolutionForExtent', - ol.View.prototype.getResolutionForExtent); - -goog.exportProperty( - ol.View.prototype, - 'getRotation', - ol.View.prototype.getRotation); - -goog.exportProperty( - ol.View.prototype, - 'getZoom', - ol.View.prototype.getZoom); - -goog.exportProperty( - ol.View.prototype, - 'getZoomForResolution', - ol.View.prototype.getZoomForResolution); - -goog.exportProperty( - ol.View.prototype, - 'fit', - ol.View.prototype.fit); - -goog.exportProperty( - ol.View.prototype, - 'centerOn', - ol.View.prototype.centerOn); - -goog.exportProperty( - ol.View.prototype, - 'rotate', - ol.View.prototype.rotate); - -goog.exportProperty( - ol.View.prototype, - 'setCenter', - ol.View.prototype.setCenter); - -goog.exportProperty( - ol.View.prototype, - 'setResolution', - ol.View.prototype.setResolution); - -goog.exportProperty( - ol.View.prototype, - 'setRotation', - ol.View.prototype.setRotation); - -goog.exportProperty( - ol.View.prototype, - 'setZoom', - ol.View.prototype.setZoom); - -goog.exportSymbol( - 'ol.xml.getAllTextContent', - ol.xml.getAllTextContent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.xml.parse', - ol.xml.parse, - OPENLAYERS); - -goog.exportProperty( - ol.webgl.Context.prototype, - 'getGL', - ol.webgl.Context.prototype.getGL); - -goog.exportProperty( - ol.webgl.Context.prototype, - 'useProgram', - ol.webgl.Context.prototype.useProgram); - -goog.exportSymbol( - 'ol.tilegrid.TileGrid', - ol.tilegrid.TileGrid, - OPENLAYERS); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'forEachTileCoord', - ol.tilegrid.TileGrid.prototype.forEachTileCoord); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getMaxZoom', - ol.tilegrid.TileGrid.prototype.getMaxZoom); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getMinZoom', - ol.tilegrid.TileGrid.prototype.getMinZoom); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getOrigin', - ol.tilegrid.TileGrid.prototype.getOrigin); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getResolution', - ol.tilegrid.TileGrid.prototype.getResolution); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getResolutions', - ol.tilegrid.TileGrid.prototype.getResolutions); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getTileCoordExtent', - ol.tilegrid.TileGrid.prototype.getTileCoordExtent); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getTileCoordForCoordAndResolution', - ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getTileCoordForCoordAndZ', - ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getTileSize', - ol.tilegrid.TileGrid.prototype.getTileSize); - -goog.exportProperty( - ol.tilegrid.TileGrid.prototype, - 'getZForResolution', - ol.tilegrid.TileGrid.prototype.getZForResolution); - -goog.exportSymbol( - 'ol.tilegrid.WMTS', - ol.tilegrid.WMTS, - OPENLAYERS); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getMatrixIds', - ol.tilegrid.WMTS.prototype.getMatrixIds); - -goog.exportSymbol( - 'ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet', - ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet, - OPENLAYERS); - -goog.exportSymbol( - 'ol.style.AtlasManager', - ol.style.AtlasManager, - OPENLAYERS); - -goog.exportSymbol( - 'ol.style.Circle', - ol.style.Circle, - OPENLAYERS); - -goog.exportProperty( - ol.style.Circle.prototype, - 'setRadius', - ol.style.Circle.prototype.setRadius); - -goog.exportSymbol( - 'ol.style.Fill', - ol.style.Fill, - OPENLAYERS); - -goog.exportProperty( - ol.style.Fill.prototype, - 'clone', - ol.style.Fill.prototype.clone); - -goog.exportProperty( - ol.style.Fill.prototype, - 'getColor', - ol.style.Fill.prototype.getColor); - -goog.exportProperty( - ol.style.Fill.prototype, - 'setColor', - ol.style.Fill.prototype.setColor); - -goog.exportSymbol( - 'ol.style.Icon', - ol.style.Icon, - OPENLAYERS); - -goog.exportProperty( - ol.style.Icon.prototype, - 'clone', - ol.style.Icon.prototype.clone); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getAnchor', - ol.style.Icon.prototype.getAnchor); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getColor', - ol.style.Icon.prototype.getColor); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getImage', - ol.style.Icon.prototype.getImage); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getOrigin', - ol.style.Icon.prototype.getOrigin); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getSrc', - ol.style.Icon.prototype.getSrc); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getSize', - ol.style.Icon.prototype.getSize); - -goog.exportProperty( - ol.style.Icon.prototype, - 'load', - ol.style.Icon.prototype.load); - -goog.exportSymbol( - 'ol.style.Image', - ol.style.Image, - OPENLAYERS); - -goog.exportProperty( - ol.style.Image.prototype, - 'getOpacity', - ol.style.Image.prototype.getOpacity); - -goog.exportProperty( - ol.style.Image.prototype, - 'getRotateWithView', - ol.style.Image.prototype.getRotateWithView); - -goog.exportProperty( - ol.style.Image.prototype, - 'getRotation', - ol.style.Image.prototype.getRotation); - -goog.exportProperty( - ol.style.Image.prototype, - 'getScale', - ol.style.Image.prototype.getScale); - -goog.exportProperty( - ol.style.Image.prototype, - 'getSnapToPixel', - ol.style.Image.prototype.getSnapToPixel); - -goog.exportProperty( - ol.style.Image.prototype, - 'setOpacity', - ol.style.Image.prototype.setOpacity); - -goog.exportProperty( - ol.style.Image.prototype, - 'setRotation', - ol.style.Image.prototype.setRotation); - -goog.exportProperty( - ol.style.Image.prototype, - 'setScale', - ol.style.Image.prototype.setScale); - -goog.exportSymbol( - 'ol.style.RegularShape', - ol.style.RegularShape, - OPENLAYERS); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'clone', - ol.style.RegularShape.prototype.clone); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getAnchor', - ol.style.RegularShape.prototype.getAnchor); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getAngle', - ol.style.RegularShape.prototype.getAngle); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getFill', - ol.style.RegularShape.prototype.getFill); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getImage', - ol.style.RegularShape.prototype.getImage); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getOrigin', - ol.style.RegularShape.prototype.getOrigin); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getPoints', - ol.style.RegularShape.prototype.getPoints); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getRadius', - ol.style.RegularShape.prototype.getRadius); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getRadius2', - ol.style.RegularShape.prototype.getRadius2); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getSize', - ol.style.RegularShape.prototype.getSize); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getStroke', - ol.style.RegularShape.prototype.getStroke); - -goog.exportSymbol( - 'ol.style.Stroke', - ol.style.Stroke, - OPENLAYERS); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'clone', - ol.style.Stroke.prototype.clone); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getColor', - ol.style.Stroke.prototype.getColor); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getLineCap', - ol.style.Stroke.prototype.getLineCap); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getLineDash', - ol.style.Stroke.prototype.getLineDash); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getLineDashOffset', - ol.style.Stroke.prototype.getLineDashOffset); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getLineJoin', - ol.style.Stroke.prototype.getLineJoin); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getMiterLimit', - ol.style.Stroke.prototype.getMiterLimit); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'getWidth', - ol.style.Stroke.prototype.getWidth); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setColor', - ol.style.Stroke.prototype.setColor); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setLineCap', - ol.style.Stroke.prototype.setLineCap); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setLineDash', - ol.style.Stroke.prototype.setLineDash); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setLineDashOffset', - ol.style.Stroke.prototype.setLineDashOffset); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setLineJoin', - ol.style.Stroke.prototype.setLineJoin); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setMiterLimit', - ol.style.Stroke.prototype.setMiterLimit); - -goog.exportProperty( - ol.style.Stroke.prototype, - 'setWidth', - ol.style.Stroke.prototype.setWidth); - -goog.exportSymbol( - 'ol.style.Style', - ol.style.Style, - OPENLAYERS); - -goog.exportProperty( - ol.style.Style.prototype, - 'clone', - ol.style.Style.prototype.clone); - -goog.exportProperty( - ol.style.Style.prototype, - 'getGeometry', - ol.style.Style.prototype.getGeometry); - -goog.exportProperty( - ol.style.Style.prototype, - 'getGeometryFunction', - ol.style.Style.prototype.getGeometryFunction); - -goog.exportProperty( - ol.style.Style.prototype, - 'getFill', - ol.style.Style.prototype.getFill); - -goog.exportProperty( - ol.style.Style.prototype, - 'setFill', - ol.style.Style.prototype.setFill); - -goog.exportProperty( - ol.style.Style.prototype, - 'getImage', - ol.style.Style.prototype.getImage); - -goog.exportProperty( - ol.style.Style.prototype, - 'setImage', - ol.style.Style.prototype.setImage); - -goog.exportProperty( - ol.style.Style.prototype, - 'getStroke', - ol.style.Style.prototype.getStroke); - -goog.exportProperty( - ol.style.Style.prototype, - 'setStroke', - ol.style.Style.prototype.setStroke); - -goog.exportProperty( - ol.style.Style.prototype, - 'getText', - ol.style.Style.prototype.getText); - -goog.exportProperty( - ol.style.Style.prototype, - 'setText', - ol.style.Style.prototype.setText); - -goog.exportProperty( - ol.style.Style.prototype, - 'getZIndex', - ol.style.Style.prototype.getZIndex); - -goog.exportProperty( - ol.style.Style.prototype, - 'setGeometry', - ol.style.Style.prototype.setGeometry); - -goog.exportProperty( - ol.style.Style.prototype, - 'setZIndex', - ol.style.Style.prototype.setZIndex); - -goog.exportSymbol( - 'ol.style.Text', - ol.style.Text, - OPENLAYERS); - -goog.exportProperty( - ol.style.Text.prototype, - 'clone', - ol.style.Text.prototype.clone); - -goog.exportProperty( - ol.style.Text.prototype, - 'getFont', - ol.style.Text.prototype.getFont); - -goog.exportProperty( - ol.style.Text.prototype, - 'getOffsetX', - ol.style.Text.prototype.getOffsetX); - -goog.exportProperty( - ol.style.Text.prototype, - 'getOffsetY', - ol.style.Text.prototype.getOffsetY); - -goog.exportProperty( - ol.style.Text.prototype, - 'getFill', - ol.style.Text.prototype.getFill); - -goog.exportProperty( - ol.style.Text.prototype, - 'getRotateWithView', - ol.style.Text.prototype.getRotateWithView); - -goog.exportProperty( - ol.style.Text.prototype, - 'getRotation', - ol.style.Text.prototype.getRotation); - -goog.exportProperty( - ol.style.Text.prototype, - 'getScale', - ol.style.Text.prototype.getScale); - -goog.exportProperty( - ol.style.Text.prototype, - 'getStroke', - ol.style.Text.prototype.getStroke); - -goog.exportProperty( - ol.style.Text.prototype, - 'getText', - ol.style.Text.prototype.getText); - -goog.exportProperty( - ol.style.Text.prototype, - 'getTextAlign', - ol.style.Text.prototype.getTextAlign); - -goog.exportProperty( - ol.style.Text.prototype, - 'getTextBaseline', - ol.style.Text.prototype.getTextBaseline); - -goog.exportProperty( - ol.style.Text.prototype, - 'setFont', - ol.style.Text.prototype.setFont); - -goog.exportProperty( - ol.style.Text.prototype, - 'setOffsetX', - ol.style.Text.prototype.setOffsetX); - -goog.exportProperty( - ol.style.Text.prototype, - 'setOffsetY', - ol.style.Text.prototype.setOffsetY); - -goog.exportProperty( - ol.style.Text.prototype, - 'setFill', - ol.style.Text.prototype.setFill); - -goog.exportProperty( - ol.style.Text.prototype, - 'setRotation', - ol.style.Text.prototype.setRotation); - -goog.exportProperty( - ol.style.Text.prototype, - 'setScale', - ol.style.Text.prototype.setScale); - -goog.exportProperty( - ol.style.Text.prototype, - 'setStroke', - ol.style.Text.prototype.setStroke); - -goog.exportProperty( - ol.style.Text.prototype, - 'setText', - ol.style.Text.prototype.setText); - -goog.exportProperty( - ol.style.Text.prototype, - 'setTextAlign', - ol.style.Text.prototype.setTextAlign); - -goog.exportProperty( - ol.style.Text.prototype, - 'setTextBaseline', - ol.style.Text.prototype.setTextBaseline); - -goog.exportSymbol( - 'ol.source.BingMaps', - ol.source.BingMaps, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.BingMaps.TOS_ATTRIBUTION', - ol.source.BingMaps.TOS_ATTRIBUTION, - OPENLAYERS); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getApiKey', - ol.source.BingMaps.prototype.getApiKey); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getImagerySet', - ol.source.BingMaps.prototype.getImagerySet); - -goog.exportSymbol( - 'ol.source.CartoDB', - ol.source.CartoDB, - OPENLAYERS); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getConfig', - ol.source.CartoDB.prototype.getConfig); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'updateConfig', - ol.source.CartoDB.prototype.updateConfig); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setConfig', - ol.source.CartoDB.prototype.setConfig); - -goog.exportSymbol( - 'ol.source.Cluster', - ol.source.Cluster, - OPENLAYERS); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getDistance', - ol.source.Cluster.prototype.getDistance); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getSource', - ol.source.Cluster.prototype.getSource); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'setDistance', - ol.source.Cluster.prototype.setDistance); - -goog.exportSymbol( - 'ol.source.Image', - ol.source.Image, - OPENLAYERS); - -goog.exportProperty( - ol.source.Image.Event.prototype, - 'image', - ol.source.Image.Event.prototype.image); - -goog.exportSymbol( - 'ol.source.ImageArcGISRest', - ol.source.ImageArcGISRest, - OPENLAYERS); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getParams', - ol.source.ImageArcGISRest.prototype.getParams); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getImageLoadFunction', - ol.source.ImageArcGISRest.prototype.getImageLoadFunction); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getUrl', - ol.source.ImageArcGISRest.prototype.getUrl); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'setImageLoadFunction', - ol.source.ImageArcGISRest.prototype.setImageLoadFunction); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'setUrl', - ol.source.ImageArcGISRest.prototype.setUrl); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'updateParams', - ol.source.ImageArcGISRest.prototype.updateParams); - -goog.exportSymbol( - 'ol.source.ImageCanvas', - ol.source.ImageCanvas, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.ImageMapGuide', - ol.source.ImageMapGuide, - OPENLAYERS); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getParams', - ol.source.ImageMapGuide.prototype.getParams); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getImageLoadFunction', - ol.source.ImageMapGuide.prototype.getImageLoadFunction); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'updateParams', - ol.source.ImageMapGuide.prototype.updateParams); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'setImageLoadFunction', - ol.source.ImageMapGuide.prototype.setImageLoadFunction); - -goog.exportSymbol( - 'ol.source.ImageStatic', - ol.source.ImageStatic, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.ImageVector', - ol.source.ImageVector, - OPENLAYERS); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getSource', - ol.source.ImageVector.prototype.getSource); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getStyle', - ol.source.ImageVector.prototype.getStyle); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getStyleFunction', - ol.source.ImageVector.prototype.getStyleFunction); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'setStyle', - ol.source.ImageVector.prototype.setStyle); - -goog.exportSymbol( - 'ol.source.ImageWMS', - ol.source.ImageWMS, - OPENLAYERS); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getGetFeatureInfoUrl', - ol.source.ImageWMS.prototype.getGetFeatureInfoUrl); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getParams', - ol.source.ImageWMS.prototype.getParams); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getImageLoadFunction', - ol.source.ImageWMS.prototype.getImageLoadFunction); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getUrl', - ol.source.ImageWMS.prototype.getUrl); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'setImageLoadFunction', - ol.source.ImageWMS.prototype.setImageLoadFunction); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'setUrl', - ol.source.ImageWMS.prototype.setUrl); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'updateParams', - ol.source.ImageWMS.prototype.updateParams); - -goog.exportSymbol( - 'ol.source.OSM', - ol.source.OSM, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.OSM.ATTRIBUTION', - ol.source.OSM.ATTRIBUTION, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.Raster', - ol.source.Raster, - OPENLAYERS); - -goog.exportProperty( - ol.source.Raster.prototype, - 'setOperation', - ol.source.Raster.prototype.setOperation); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'extent', - ol.source.Raster.Event.prototype.extent); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'resolution', - ol.source.Raster.Event.prototype.resolution); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'data', - ol.source.Raster.Event.prototype.data); - -goog.exportSymbol( - 'ol.source.Source', - ol.source.Source, - OPENLAYERS); - -goog.exportProperty( - ol.source.Source.prototype, - 'getAttributions', - ol.source.Source.prototype.getAttributions); - -goog.exportProperty( - ol.source.Source.prototype, - 'getLogo', - ol.source.Source.prototype.getLogo); - -goog.exportProperty( - ol.source.Source.prototype, - 'getProjection', - ol.source.Source.prototype.getProjection); - -goog.exportProperty( - ol.source.Source.prototype, - 'getState', - ol.source.Source.prototype.getState); - -goog.exportProperty( - ol.source.Source.prototype, - 'refresh', - ol.source.Source.prototype.refresh); - -goog.exportProperty( - ol.source.Source.prototype, - 'setAttributions', - ol.source.Source.prototype.setAttributions); - -goog.exportSymbol( - 'ol.source.Stamen', - ol.source.Stamen, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.Tile', - ol.source.Tile, - OPENLAYERS); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getTileGrid', - ol.source.Tile.prototype.getTileGrid); - -goog.exportProperty( - ol.source.Tile.Event.prototype, - 'tile', - ol.source.Tile.Event.prototype.tile); - -goog.exportSymbol( - 'ol.source.TileArcGISRest', - ol.source.TileArcGISRest, - OPENLAYERS); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getParams', - ol.source.TileArcGISRest.prototype.getParams); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'updateParams', - ol.source.TileArcGISRest.prototype.updateParams); - -goog.exportSymbol( - 'ol.source.TileDebug', - ol.source.TileDebug, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.TileImage', - ol.source.TileImage, - OPENLAYERS); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setRenderReprojectionEdges', - ol.source.TileImage.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setTileGridForProjection', - ol.source.TileImage.prototype.setTileGridForProjection); - -goog.exportSymbol( - 'ol.source.TileJSON', - ol.source.TileJSON, - OPENLAYERS); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getTileJSON', - ol.source.TileJSON.prototype.getTileJSON); - -goog.exportSymbol( - 'ol.source.TileUTFGrid', - ol.source.TileUTFGrid, - OPENLAYERS); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getTemplate', - ol.source.TileUTFGrid.prototype.getTemplate); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'forDataAtCoordinateAndResolution', - ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution); - -goog.exportSymbol( - 'ol.source.TileWMS', - ol.source.TileWMS, - OPENLAYERS); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getGetFeatureInfoUrl', - ol.source.TileWMS.prototype.getGetFeatureInfoUrl); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getParams', - ol.source.TileWMS.prototype.getParams); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'updateParams', - ol.source.TileWMS.prototype.updateParams); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getTileLoadFunction', - ol.source.UrlTile.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getTileUrlFunction', - ol.source.UrlTile.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getUrls', - ol.source.UrlTile.prototype.getUrls); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'setTileLoadFunction', - ol.source.UrlTile.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'setTileUrlFunction', - ol.source.UrlTile.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'setUrl', - ol.source.UrlTile.prototype.setUrl); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'setUrls', - ol.source.UrlTile.prototype.setUrls); - -goog.exportSymbol( - 'ol.source.Vector', - ol.source.Vector, - OPENLAYERS); - -goog.exportProperty( - ol.source.Vector.prototype, - 'addFeature', - ol.source.Vector.prototype.addFeature); - -goog.exportProperty( - ol.source.Vector.prototype, - 'addFeatures', - ol.source.Vector.prototype.addFeatures); - -goog.exportProperty( - ol.source.Vector.prototype, - 'clear', - ol.source.Vector.prototype.clear); - -goog.exportProperty( - ol.source.Vector.prototype, - 'forEachFeature', - ol.source.Vector.prototype.forEachFeature); - -goog.exportProperty( - ol.source.Vector.prototype, - 'forEachFeatureInExtent', - ol.source.Vector.prototype.forEachFeatureInExtent); - -goog.exportProperty( - ol.source.Vector.prototype, - 'forEachFeatureIntersectingExtent', - ol.source.Vector.prototype.forEachFeatureIntersectingExtent); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getFeaturesCollection', - ol.source.Vector.prototype.getFeaturesCollection); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getFeatures', - ol.source.Vector.prototype.getFeatures); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getFeaturesAtCoordinate', - ol.source.Vector.prototype.getFeaturesAtCoordinate); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getFeaturesInExtent', - ol.source.Vector.prototype.getFeaturesInExtent); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getClosestFeatureToCoordinate', - ol.source.Vector.prototype.getClosestFeatureToCoordinate); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getExtent', - ol.source.Vector.prototype.getExtent); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getFeatureById', - ol.source.Vector.prototype.getFeatureById); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getFormat', - ol.source.Vector.prototype.getFormat); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getUrl', - ol.source.Vector.prototype.getUrl); - -goog.exportProperty( - ol.source.Vector.prototype, - 'removeFeature', - ol.source.Vector.prototype.removeFeature); - -goog.exportProperty( - ol.source.Vector.Event.prototype, - 'feature', - ol.source.Vector.Event.prototype.feature); - -goog.exportSymbol( - 'ol.source.VectorTile', - ol.source.VectorTile, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.WMTS', - ol.source.WMTS, - OPENLAYERS); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getDimensions', - ol.source.WMTS.prototype.getDimensions); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getFormat', - ol.source.WMTS.prototype.getFormat); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getLayer', - ol.source.WMTS.prototype.getLayer); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getMatrixSet', - ol.source.WMTS.prototype.getMatrixSet); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getRequestEncoding', - ol.source.WMTS.prototype.getRequestEncoding); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getStyle', - ol.source.WMTS.prototype.getStyle); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getVersion', - ol.source.WMTS.prototype.getVersion); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'updateDimensions', - ol.source.WMTS.prototype.updateDimensions); - -goog.exportSymbol( - 'ol.source.WMTS.optionsFromCapabilities', - ol.source.WMTS.optionsFromCapabilities, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.XYZ', - ol.source.XYZ, - OPENLAYERS); - -goog.exportSymbol( - 'ol.source.Zoomify', - ol.source.Zoomify, - OPENLAYERS); - -goog.exportProperty( - ol.render.Event.prototype, - 'vectorContext', - ol.render.Event.prototype.vectorContext); - -goog.exportProperty( - ol.render.Event.prototype, - 'frameState', - ol.render.Event.prototype.frameState); - -goog.exportProperty( - ol.render.Event.prototype, - 'context', - ol.render.Event.prototype.context); - -goog.exportProperty( - ol.render.Event.prototype, - 'glContext', - ol.render.Event.prototype.glContext); - -goog.exportProperty( - ol.render.Feature.prototype, - 'get', - ol.render.Feature.prototype.get); - -goog.exportProperty( - ol.render.Feature.prototype, - 'getExtent', - ol.render.Feature.prototype.getExtent); - -goog.exportProperty( - ol.render.Feature.prototype, - 'getId', - ol.render.Feature.prototype.getId); - -goog.exportProperty( - ol.render.Feature.prototype, - 'getGeometry', - ol.render.Feature.prototype.getGeometry); - -goog.exportProperty( - ol.render.Feature.prototype, - 'getProperties', - ol.render.Feature.prototype.getProperties); - -goog.exportProperty( - ol.render.Feature.prototype, - 'getType', - ol.render.Feature.prototype.getType); - -goog.exportSymbol( - 'ol.render.VectorContext', - ol.render.VectorContext, - OPENLAYERS); - -goog.exportProperty( - ol.render.webgl.Immediate.prototype, - 'setStyle', - ol.render.webgl.Immediate.prototype.setStyle); - -goog.exportProperty( - ol.render.webgl.Immediate.prototype, - 'drawGeometry', - ol.render.webgl.Immediate.prototype.drawGeometry); - -goog.exportProperty( - ol.render.webgl.Immediate.prototype, - 'drawFeature', - ol.render.webgl.Immediate.prototype.drawFeature); - -goog.exportProperty( - ol.render.canvas.Immediate.prototype, - 'drawCircle', - ol.render.canvas.Immediate.prototype.drawCircle); - -goog.exportProperty( - ol.render.canvas.Immediate.prototype, - 'setStyle', - ol.render.canvas.Immediate.prototype.setStyle); - -goog.exportProperty( - ol.render.canvas.Immediate.prototype, - 'drawGeometry', - ol.render.canvas.Immediate.prototype.drawGeometry); - -goog.exportProperty( - ol.render.canvas.Immediate.prototype, - 'drawFeature', - ol.render.canvas.Immediate.prototype.drawFeature); - -goog.exportSymbol( - 'ol.proj.common.add', - ol.proj.common.add, - OPENLAYERS); - -goog.exportSymbol( - 'ol.proj.Projection', - ol.proj.Projection, - OPENLAYERS); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'getCode', - ol.proj.Projection.prototype.getCode); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'getExtent', - ol.proj.Projection.prototype.getExtent); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'getUnits', - ol.proj.Projection.prototype.getUnits); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'getMetersPerUnit', - ol.proj.Projection.prototype.getMetersPerUnit); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'getWorldExtent', - ol.proj.Projection.prototype.getWorldExtent); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'isGlobal', - ol.proj.Projection.prototype.isGlobal); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'setGlobal', - ol.proj.Projection.prototype.setGlobal); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'setExtent', - ol.proj.Projection.prototype.setExtent); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'setWorldExtent', - ol.proj.Projection.prototype.setWorldExtent); - -goog.exportProperty( - ol.proj.Projection.prototype, - 'setGetPointResolution', - ol.proj.Projection.prototype.setGetPointResolution); - -goog.exportSymbol( - 'ol.proj.Units.METERS_PER_UNIT', - ol.proj.Units.METERS_PER_UNIT, - OPENLAYERS); - -goog.exportSymbol( - 'ol.layer.Base', - ol.layer.Base, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getExtent', - ol.layer.Base.prototype.getExtent); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getMaxResolution', - ol.layer.Base.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getMinResolution', - ol.layer.Base.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getOpacity', - ol.layer.Base.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getVisible', - ol.layer.Base.prototype.getVisible); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getZIndex', - ol.layer.Base.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setExtent', - ol.layer.Base.prototype.setExtent); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setMaxResolution', - ol.layer.Base.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setMinResolution', - ol.layer.Base.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setOpacity', - ol.layer.Base.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setVisible', - ol.layer.Base.prototype.setVisible); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setZIndex', - ol.layer.Base.prototype.setZIndex); - -goog.exportSymbol( - 'ol.layer.Group', - ol.layer.Group, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getLayers', - ol.layer.Group.prototype.getLayers); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setLayers', - ol.layer.Group.prototype.setLayers); - -goog.exportSymbol( - 'ol.layer.Heatmap', - ol.layer.Heatmap, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getBlur', - ol.layer.Heatmap.prototype.getBlur); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getGradient', - ol.layer.Heatmap.prototype.getGradient); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getRadius', - ol.layer.Heatmap.prototype.getRadius); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setBlur', - ol.layer.Heatmap.prototype.setBlur); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setGradient', - ol.layer.Heatmap.prototype.setGradient); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setRadius', - ol.layer.Heatmap.prototype.setRadius); - -goog.exportSymbol( - 'ol.layer.Image', - ol.layer.Image, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getSource', - ol.layer.Image.prototype.getSource); - -goog.exportSymbol( - 'ol.layer.Layer', - ol.layer.Layer, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getSource', - ol.layer.Layer.prototype.getSource); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setMap', - ol.layer.Layer.prototype.setMap); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setSource', - ol.layer.Layer.prototype.setSource); - -goog.exportSymbol( - 'ol.layer.Tile', - ol.layer.Tile, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getPreload', - ol.layer.Tile.prototype.getPreload); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getSource', - ol.layer.Tile.prototype.getSource); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setPreload', - ol.layer.Tile.prototype.setPreload); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getUseInterimTilesOnError', - ol.layer.Tile.prototype.getUseInterimTilesOnError); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setUseInterimTilesOnError', - ol.layer.Tile.prototype.setUseInterimTilesOnError); - -goog.exportSymbol( - 'ol.layer.Vector', - ol.layer.Vector, - OPENLAYERS); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getSource', - ol.layer.Vector.prototype.getSource); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getStyle', - ol.layer.Vector.prototype.getStyle); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getStyleFunction', - ol.layer.Vector.prototype.getStyleFunction); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setStyle', - ol.layer.Vector.prototype.setStyle); - -goog.exportSymbol( - 'ol.layer.VectorTile', - ol.layer.VectorTile, - OPENLAYERS); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getPreload', - ol.layer.VectorTile.prototype.getPreload); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getUseInterimTilesOnError', - ol.layer.VectorTile.prototype.getUseInterimTilesOnError); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setPreload', - ol.layer.VectorTile.prototype.setPreload); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setUseInterimTilesOnError', - ol.layer.VectorTile.prototype.setUseInterimTilesOnError); - -goog.exportSymbol( - 'ol.interaction.DoubleClickZoom', - ol.interaction.DoubleClickZoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.DoubleClickZoom.handleEvent', - ol.interaction.DoubleClickZoom.handleEvent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.DragAndDrop', - ol.interaction.DragAndDrop, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.DragAndDrop.handleEvent', - ol.interaction.DragAndDrop.handleEvent, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'features', - ol.interaction.DragAndDrop.Event.prototype.features); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'file', - ol.interaction.DragAndDrop.Event.prototype.file); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'projection', - ol.interaction.DragAndDrop.Event.prototype.projection); - -goog.exportSymbol( - 'ol.interaction.DragBox', - ol.interaction.DragBox, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'getGeometry', - ol.interaction.DragBox.prototype.getGeometry); - -goog.exportProperty( - ol.interaction.DragBox.Event.prototype, - 'coordinate', - ol.interaction.DragBox.Event.prototype.coordinate); - -goog.exportProperty( - ol.interaction.DragBox.Event.prototype, - 'mapBrowserEvent', - ol.interaction.DragBox.Event.prototype.mapBrowserEvent); - -goog.exportSymbol( - 'ol.interaction.DragPan', - ol.interaction.DragPan, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.DragRotate', - ol.interaction.DragRotate, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.DragRotateAndZoom', - ol.interaction.DragRotateAndZoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.DragZoom', - ol.interaction.DragZoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Draw', - ol.interaction.Draw, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Draw.handleEvent', - ol.interaction.Draw.handleEvent, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'removeLastPoint', - ol.interaction.Draw.prototype.removeLastPoint); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'finishDrawing', - ol.interaction.Draw.prototype.finishDrawing); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'extend', - ol.interaction.Draw.prototype.extend); - -goog.exportSymbol( - 'ol.interaction.Draw.createRegularPolygon', - ol.interaction.Draw.createRegularPolygon, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Draw.createBox', - ol.interaction.Draw.createBox, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Draw.Event.prototype, - 'feature', - ol.interaction.Draw.Event.prototype.feature); - -goog.exportSymbol( - 'ol.interaction.Extent', - ol.interaction.Extent, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'getExtent', - ol.interaction.Extent.prototype.getExtent); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'setExtent', - ol.interaction.Extent.prototype.setExtent); - -goog.exportProperty( - ol.interaction.Extent.Event.prototype, - 'extent_', - ol.interaction.Extent.Event.prototype.extent_); - -goog.exportSymbol( - 'ol.interaction.Interaction', - ol.interaction.Interaction, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'getActive', - ol.interaction.Interaction.prototype.getActive); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'getMap', - ol.interaction.Interaction.prototype.getMap); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'setActive', - ol.interaction.Interaction.prototype.setActive); - -goog.exportSymbol( - 'ol.interaction.KeyboardPan', - ol.interaction.KeyboardPan, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.KeyboardPan.handleEvent', - ol.interaction.KeyboardPan.handleEvent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.KeyboardZoom', - ol.interaction.KeyboardZoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.KeyboardZoom.handleEvent', - ol.interaction.KeyboardZoom.handleEvent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Modify', - ol.interaction.Modify, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Modify.handleEvent', - ol.interaction.Modify.handleEvent, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'removePoint', - ol.interaction.Modify.prototype.removePoint); - -goog.exportProperty( - ol.interaction.Modify.Event.prototype, - 'features', - ol.interaction.Modify.Event.prototype.features); - -goog.exportProperty( - ol.interaction.Modify.Event.prototype, - 'mapBrowserEvent', - ol.interaction.Modify.Event.prototype.mapBrowserEvent); - -goog.exportSymbol( - 'ol.interaction.MouseWheelZoom', - ol.interaction.MouseWheelZoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.MouseWheelZoom.handleEvent', - ol.interaction.MouseWheelZoom.handleEvent, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'setMouseAnchor', - ol.interaction.MouseWheelZoom.prototype.setMouseAnchor); - -goog.exportSymbol( - 'ol.interaction.PinchRotate', - ol.interaction.PinchRotate, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.PinchZoom', - ol.interaction.PinchZoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Pointer', - ol.interaction.Pointer, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Pointer.handleEvent', - ol.interaction.Pointer.handleEvent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.interaction.Select', - ol.interaction.Select, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getFeatures', - ol.interaction.Select.prototype.getFeatures); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getHitTolerance', - ol.interaction.Select.prototype.getHitTolerance); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getLayer', - ol.interaction.Select.prototype.getLayer); - -goog.exportSymbol( - 'ol.interaction.Select.handleEvent', - ol.interaction.Select.handleEvent, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'setHitTolerance', - ol.interaction.Select.prototype.setHitTolerance); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'setMap', - ol.interaction.Select.prototype.setMap); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'selected', - ol.interaction.Select.Event.prototype.selected); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'deselected', - ol.interaction.Select.Event.prototype.deselected); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'mapBrowserEvent', - ol.interaction.Select.Event.prototype.mapBrowserEvent); - -goog.exportSymbol( - 'ol.interaction.Snap', - ol.interaction.Snap, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'addFeature', - ol.interaction.Snap.prototype.addFeature); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'removeFeature', - ol.interaction.Snap.prototype.removeFeature); - -goog.exportSymbol( - 'ol.interaction.Translate', - ol.interaction.Translate, - OPENLAYERS); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'getHitTolerance', - ol.interaction.Translate.prototype.getHitTolerance); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'setHitTolerance', - ol.interaction.Translate.prototype.setHitTolerance); - -goog.exportProperty( - ol.interaction.Translate.Event.prototype, - 'features', - ol.interaction.Translate.Event.prototype.features); - -goog.exportProperty( - ol.interaction.Translate.Event.prototype, - 'coordinate', - ol.interaction.Translate.Event.prototype.coordinate); - -goog.exportSymbol( - 'ol.geom.Circle', - ol.geom.Circle, - OPENLAYERS); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'clone', - ol.geom.Circle.prototype.clone); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getCenter', - ol.geom.Circle.prototype.getCenter); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getRadius', - ol.geom.Circle.prototype.getRadius); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getType', - ol.geom.Circle.prototype.getType); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'intersectsExtent', - ol.geom.Circle.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'setCenter', - ol.geom.Circle.prototype.setCenter); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'setCenterAndRadius', - ol.geom.Circle.prototype.setCenterAndRadius); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'setRadius', - ol.geom.Circle.prototype.setRadius); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'transform', - ol.geom.Circle.prototype.transform); - -goog.exportSymbol( - 'ol.geom.Geometry', - ol.geom.Geometry, - OPENLAYERS); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'getClosestPoint', - ol.geom.Geometry.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'intersectsCoordinate', - ol.geom.Geometry.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'getExtent', - ol.geom.Geometry.prototype.getExtent); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'rotate', - ol.geom.Geometry.prototype.rotate); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'scale', - ol.geom.Geometry.prototype.scale); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'simplify', - ol.geom.Geometry.prototype.simplify); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'transform', - ol.geom.Geometry.prototype.transform); - -goog.exportSymbol( - 'ol.geom.GeometryCollection', - ol.geom.GeometryCollection, - OPENLAYERS); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'clone', - ol.geom.GeometryCollection.prototype.clone); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getGeometries', - ol.geom.GeometryCollection.prototype.getGeometries); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getType', - ol.geom.GeometryCollection.prototype.getType); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'intersectsExtent', - ol.geom.GeometryCollection.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'setGeometries', - ol.geom.GeometryCollection.prototype.setGeometries); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'applyTransform', - ol.geom.GeometryCollection.prototype.applyTransform); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'translate', - ol.geom.GeometryCollection.prototype.translate); - -goog.exportSymbol( - 'ol.geom.LinearRing', - ol.geom.LinearRing, - OPENLAYERS); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'clone', - ol.geom.LinearRing.prototype.clone); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getArea', - ol.geom.LinearRing.prototype.getArea); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getCoordinates', - ol.geom.LinearRing.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getType', - ol.geom.LinearRing.prototype.getType); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'setCoordinates', - ol.geom.LinearRing.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.LineString', - ol.geom.LineString, - OPENLAYERS); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'appendCoordinate', - ol.geom.LineString.prototype.appendCoordinate); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'clone', - ol.geom.LineString.prototype.clone); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'forEachSegment', - ol.geom.LineString.prototype.forEachSegment); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getCoordinateAtM', - ol.geom.LineString.prototype.getCoordinateAtM); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getCoordinates', - ol.geom.LineString.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getCoordinateAt', - ol.geom.LineString.prototype.getCoordinateAt); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getLength', - ol.geom.LineString.prototype.getLength); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getType', - ol.geom.LineString.prototype.getType); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'intersectsExtent', - ol.geom.LineString.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'setCoordinates', - ol.geom.LineString.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.MultiLineString', - ol.geom.MultiLineString, - OPENLAYERS); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'appendLineString', - ol.geom.MultiLineString.prototype.appendLineString); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'clone', - ol.geom.MultiLineString.prototype.clone); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getCoordinateAtM', - ol.geom.MultiLineString.prototype.getCoordinateAtM); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getCoordinates', - ol.geom.MultiLineString.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getLineString', - ol.geom.MultiLineString.prototype.getLineString); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getLineStrings', - ol.geom.MultiLineString.prototype.getLineStrings); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getType', - ol.geom.MultiLineString.prototype.getType); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'intersectsExtent', - ol.geom.MultiLineString.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'setCoordinates', - ol.geom.MultiLineString.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.MultiPoint', - ol.geom.MultiPoint, - OPENLAYERS); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'appendPoint', - ol.geom.MultiPoint.prototype.appendPoint); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'clone', - ol.geom.MultiPoint.prototype.clone); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getCoordinates', - ol.geom.MultiPoint.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getPoint', - ol.geom.MultiPoint.prototype.getPoint); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getPoints', - ol.geom.MultiPoint.prototype.getPoints); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getType', - ol.geom.MultiPoint.prototype.getType); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'intersectsExtent', - ol.geom.MultiPoint.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'setCoordinates', - ol.geom.MultiPoint.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.MultiPolygon', - ol.geom.MultiPolygon, - OPENLAYERS); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'appendPolygon', - ol.geom.MultiPolygon.prototype.appendPolygon); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'clone', - ol.geom.MultiPolygon.prototype.clone); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getArea', - ol.geom.MultiPolygon.prototype.getArea); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getCoordinates', - ol.geom.MultiPolygon.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getInteriorPoints', - ol.geom.MultiPolygon.prototype.getInteriorPoints); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getPolygon', - ol.geom.MultiPolygon.prototype.getPolygon); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getPolygons', - ol.geom.MultiPolygon.prototype.getPolygons); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getType', - ol.geom.MultiPolygon.prototype.getType); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'intersectsExtent', - ol.geom.MultiPolygon.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'setCoordinates', - ol.geom.MultiPolygon.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.Point', - ol.geom.Point, - OPENLAYERS); - -goog.exportProperty( - ol.geom.Point.prototype, - 'clone', - ol.geom.Point.prototype.clone); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getCoordinates', - ol.geom.Point.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getType', - ol.geom.Point.prototype.getType); - -goog.exportProperty( - ol.geom.Point.prototype, - 'intersectsExtent', - ol.geom.Point.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.Point.prototype, - 'setCoordinates', - ol.geom.Point.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.Polygon', - ol.geom.Polygon, - OPENLAYERS); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'appendLinearRing', - ol.geom.Polygon.prototype.appendLinearRing); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'clone', - ol.geom.Polygon.prototype.clone); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getArea', - ol.geom.Polygon.prototype.getArea); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getCoordinates', - ol.geom.Polygon.prototype.getCoordinates); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getInteriorPoint', - ol.geom.Polygon.prototype.getInteriorPoint); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getLinearRingCount', - ol.geom.Polygon.prototype.getLinearRingCount); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getLinearRing', - ol.geom.Polygon.prototype.getLinearRing); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getLinearRings', - ol.geom.Polygon.prototype.getLinearRings); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getType', - ol.geom.Polygon.prototype.getType); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'intersectsExtent', - ol.geom.Polygon.prototype.intersectsExtent); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'setCoordinates', - ol.geom.Polygon.prototype.setCoordinates); - -goog.exportSymbol( - 'ol.geom.Polygon.circular', - ol.geom.Polygon.circular, - OPENLAYERS); - -goog.exportSymbol( - 'ol.geom.Polygon.fromExtent', - ol.geom.Polygon.fromExtent, - OPENLAYERS); - -goog.exportSymbol( - 'ol.geom.Polygon.fromCircle', - ol.geom.Polygon.fromCircle, - OPENLAYERS); - -goog.exportSymbol( - 'ol.geom.SimpleGeometry', - ol.geom.SimpleGeometry, - OPENLAYERS); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getFirstCoordinate', - ol.geom.SimpleGeometry.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getLastCoordinate', - ol.geom.SimpleGeometry.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getLayout', - ol.geom.SimpleGeometry.prototype.getLayout); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'applyTransform', - ol.geom.SimpleGeometry.prototype.applyTransform); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'translate', - ol.geom.SimpleGeometry.prototype.translate); - -goog.exportSymbol( - 'ol.format.EsriJSON', - ol.format.EsriJSON, - OPENLAYERS); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'readFeature', - ol.format.EsriJSON.prototype.readFeature); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'readFeatures', - ol.format.EsriJSON.prototype.readFeatures); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'readGeometry', - ol.format.EsriJSON.prototype.readGeometry); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'readProjection', - ol.format.EsriJSON.prototype.readProjection); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'writeGeometry', - ol.format.EsriJSON.prototype.writeGeometry); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'writeGeometryObject', - ol.format.EsriJSON.prototype.writeGeometryObject); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'writeFeature', - ol.format.EsriJSON.prototype.writeFeature); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'writeFeatureObject', - ol.format.EsriJSON.prototype.writeFeatureObject); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'writeFeatures', - ol.format.EsriJSON.prototype.writeFeatures); - -goog.exportProperty( - ol.format.EsriJSON.prototype, - 'writeFeaturesObject', - ol.format.EsriJSON.prototype.writeFeaturesObject); - -goog.exportSymbol( - 'ol.format.Feature', - ol.format.Feature, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.and', - ol.format.filter.and, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.or', - ol.format.filter.or, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.not', - ol.format.filter.not, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.bbox', - ol.format.filter.bbox, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.intersects', - ol.format.filter.intersects, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.within', - ol.format.filter.within, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.equalTo', - ol.format.filter.equalTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.notEqualTo', - ol.format.filter.notEqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.lessThan', - ol.format.filter.lessThan, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.lessThanOrEqualTo', - ol.format.filter.lessThanOrEqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.greaterThan', - ol.format.filter.greaterThan, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.greaterThanOrEqualTo', - ol.format.filter.greaterThanOrEqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.isNull', - ol.format.filter.isNull, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.between', - ol.format.filter.between, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.like', - ol.format.filter.like, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.during', - ol.format.filter.during, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.GeoJSON', - ol.format.GeoJSON, - OPENLAYERS); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'readFeature', - ol.format.GeoJSON.prototype.readFeature); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'readFeatures', - ol.format.GeoJSON.prototype.readFeatures); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'readGeometry', - ol.format.GeoJSON.prototype.readGeometry); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'readProjection', - ol.format.GeoJSON.prototype.readProjection); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'writeFeature', - ol.format.GeoJSON.prototype.writeFeature); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'writeFeatureObject', - ol.format.GeoJSON.prototype.writeFeatureObject); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'writeFeatures', - ol.format.GeoJSON.prototype.writeFeatures); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'writeFeaturesObject', - ol.format.GeoJSON.prototype.writeFeaturesObject); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'writeGeometry', - ol.format.GeoJSON.prototype.writeGeometry); - -goog.exportProperty( - ol.format.GeoJSON.prototype, - 'writeGeometryObject', - ol.format.GeoJSON.prototype.writeGeometryObject); - -goog.exportSymbol( - 'ol.format.GML', - ol.format.GML, - OPENLAYERS); - -goog.exportProperty( - ol.format.GML.prototype, - 'writeFeatures', - ol.format.GML.prototype.writeFeatures); - -goog.exportProperty( - ol.format.GML.prototype, - 'writeFeaturesNode', - ol.format.GML.prototype.writeFeaturesNode); - -goog.exportSymbol( - 'ol.format.GML2', - ol.format.GML2, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.GML3', - ol.format.GML3, - OPENLAYERS); - -goog.exportProperty( - ol.format.GML3.prototype, - 'writeGeometryNode', - ol.format.GML3.prototype.writeGeometryNode); - -goog.exportProperty( - ol.format.GML3.prototype, - 'writeFeatures', - ol.format.GML3.prototype.writeFeatures); - -goog.exportProperty( - ol.format.GML3.prototype, - 'writeFeaturesNode', - ol.format.GML3.prototype.writeFeaturesNode); - -goog.exportProperty( - ol.format.GMLBase.prototype, - 'readFeatures', - ol.format.GMLBase.prototype.readFeatures); - -goog.exportSymbol( - 'ol.format.GPX', - ol.format.GPX, - OPENLAYERS); - -goog.exportProperty( - ol.format.GPX.prototype, - 'readFeature', - ol.format.GPX.prototype.readFeature); - -goog.exportProperty( - ol.format.GPX.prototype, - 'readFeatures', - ol.format.GPX.prototype.readFeatures); - -goog.exportProperty( - ol.format.GPX.prototype, - 'readProjection', - ol.format.GPX.prototype.readProjection); - -goog.exportProperty( - ol.format.GPX.prototype, - 'writeFeatures', - ol.format.GPX.prototype.writeFeatures); - -goog.exportProperty( - ol.format.GPX.prototype, - 'writeFeaturesNode', - ol.format.GPX.prototype.writeFeaturesNode); - -goog.exportSymbol( - 'ol.format.IGC', - ol.format.IGC, - OPENLAYERS); - -goog.exportProperty( - ol.format.IGC.prototype, - 'readFeature', - ol.format.IGC.prototype.readFeature); - -goog.exportProperty( - ol.format.IGC.prototype, - 'readFeatures', - ol.format.IGC.prototype.readFeatures); - -goog.exportProperty( - ol.format.IGC.prototype, - 'readProjection', - ol.format.IGC.prototype.readProjection); - -goog.exportSymbol( - 'ol.format.KML', - ol.format.KML, - OPENLAYERS); - -goog.exportProperty( - ol.format.KML.prototype, - 'readFeature', - ol.format.KML.prototype.readFeature); - -goog.exportProperty( - ol.format.KML.prototype, - 'readFeatures', - ol.format.KML.prototype.readFeatures); - -goog.exportProperty( - ol.format.KML.prototype, - 'readName', - ol.format.KML.prototype.readName); - -goog.exportProperty( - ol.format.KML.prototype, - 'readNetworkLinks', - ol.format.KML.prototype.readNetworkLinks); - -goog.exportProperty( - ol.format.KML.prototype, - 'readRegion', - ol.format.KML.prototype.readRegion); - -goog.exportProperty( - ol.format.KML.prototype, - 'readRegionFromNode', - ol.format.KML.prototype.readRegionFromNode); - -goog.exportProperty( - ol.format.KML.prototype, - 'readProjection', - ol.format.KML.prototype.readProjection); - -goog.exportProperty( - ol.format.KML.prototype, - 'writeFeatures', - ol.format.KML.prototype.writeFeatures); - -goog.exportProperty( - ol.format.KML.prototype, - 'writeFeaturesNode', - ol.format.KML.prototype.writeFeaturesNode); - -goog.exportSymbol( - 'ol.format.MVT', - ol.format.MVT, - OPENLAYERS); - -goog.exportProperty( - ol.format.MVT.prototype, - 'readFeatures', - ol.format.MVT.prototype.readFeatures); - -goog.exportProperty( - ol.format.MVT.prototype, - 'readProjection', - ol.format.MVT.prototype.readProjection); - -goog.exportProperty( - ol.format.MVT.prototype, - 'setLayers', - ol.format.MVT.prototype.setLayers); - -goog.exportSymbol( - 'ol.format.OSMXML', - ol.format.OSMXML, - OPENLAYERS); - -goog.exportProperty( - ol.format.OSMXML.prototype, - 'readFeatures', - ol.format.OSMXML.prototype.readFeatures); - -goog.exportProperty( - ol.format.OSMXML.prototype, - 'readProjection', - ol.format.OSMXML.prototype.readProjection); - -goog.exportSymbol( - 'ol.format.Polyline', - ol.format.Polyline, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.Polyline.encodeDeltas', - ol.format.Polyline.encodeDeltas, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.Polyline.decodeDeltas', - ol.format.Polyline.decodeDeltas, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.Polyline.encodeFloats', - ol.format.Polyline.encodeFloats, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.Polyline.decodeFloats', - ol.format.Polyline.decodeFloats, - OPENLAYERS); - -goog.exportProperty( - ol.format.Polyline.prototype, - 'readFeature', - ol.format.Polyline.prototype.readFeature); - -goog.exportProperty( - ol.format.Polyline.prototype, - 'readFeatures', - ol.format.Polyline.prototype.readFeatures); - -goog.exportProperty( - ol.format.Polyline.prototype, - 'readGeometry', - ol.format.Polyline.prototype.readGeometry); - -goog.exportProperty( - ol.format.Polyline.prototype, - 'readProjection', - ol.format.Polyline.prototype.readProjection); - -goog.exportProperty( - ol.format.Polyline.prototype, - 'writeGeometry', - ol.format.Polyline.prototype.writeGeometry); - -goog.exportSymbol( - 'ol.format.TopoJSON', - ol.format.TopoJSON, - OPENLAYERS); - -goog.exportProperty( - ol.format.TopoJSON.prototype, - 'readFeatures', - ol.format.TopoJSON.prototype.readFeatures); - -goog.exportProperty( - ol.format.TopoJSON.prototype, - 'readProjection', - ol.format.TopoJSON.prototype.readProjection); - -goog.exportSymbol( - 'ol.format.WFS', - ol.format.WFS, - OPENLAYERS); - -goog.exportProperty( - ol.format.WFS.prototype, - 'readFeatures', - ol.format.WFS.prototype.readFeatures); - -goog.exportProperty( - ol.format.WFS.prototype, - 'readTransactionResponse', - ol.format.WFS.prototype.readTransactionResponse); - -goog.exportProperty( - ol.format.WFS.prototype, - 'readFeatureCollectionMetadata', - ol.format.WFS.prototype.readFeatureCollectionMetadata); - -goog.exportSymbol( - 'ol.format.WFS.writeFilter', - ol.format.WFS.writeFilter, - OPENLAYERS); - -goog.exportProperty( - ol.format.WFS.prototype, - 'writeGetFeature', - ol.format.WFS.prototype.writeGetFeature); - -goog.exportProperty( - ol.format.WFS.prototype, - 'writeTransaction', - ol.format.WFS.prototype.writeTransaction); - -goog.exportProperty( - ol.format.WFS.prototype, - 'readProjection', - ol.format.WFS.prototype.readProjection); - -goog.exportSymbol( - 'ol.format.WKT', - ol.format.WKT, - OPENLAYERS); - -goog.exportProperty( - ol.format.WKT.prototype, - 'readFeature', - ol.format.WKT.prototype.readFeature); - -goog.exportProperty( - ol.format.WKT.prototype, - 'readFeatures', - ol.format.WKT.prototype.readFeatures); - -goog.exportProperty( - ol.format.WKT.prototype, - 'readGeometry', - ol.format.WKT.prototype.readGeometry); - -goog.exportProperty( - ol.format.WKT.prototype, - 'writeFeature', - ol.format.WKT.prototype.writeFeature); - -goog.exportProperty( - ol.format.WKT.prototype, - 'writeFeatures', - ol.format.WKT.prototype.writeFeatures); - -goog.exportProperty( - ol.format.WKT.prototype, - 'writeGeometry', - ol.format.WKT.prototype.writeGeometry); - -goog.exportSymbol( - 'ol.format.WMSCapabilities', - ol.format.WMSCapabilities, - OPENLAYERS); - -goog.exportProperty( - ol.format.WMSCapabilities.prototype, - 'read', - ol.format.WMSCapabilities.prototype.read); - -goog.exportSymbol( - 'ol.format.WMSGetFeatureInfo', - ol.format.WMSGetFeatureInfo, - OPENLAYERS); - -goog.exportProperty( - ol.format.WMSGetFeatureInfo.prototype, - 'readFeatures', - ol.format.WMSGetFeatureInfo.prototype.readFeatures); - -goog.exportSymbol( - 'ol.format.WMTSCapabilities', - ol.format.WMTSCapabilities, - OPENLAYERS); - -goog.exportProperty( - ol.format.WMTSCapabilities.prototype, - 'read', - ol.format.WMTSCapabilities.prototype.read); - -goog.exportSymbol( - 'ol.format.filter.And', - ol.format.filter.And, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Bbox', - ol.format.filter.Bbox, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Comparison', - ol.format.filter.Comparison, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.ComparisonBinary', - ol.format.filter.ComparisonBinary, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.During', - ol.format.filter.During, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.EqualTo', - ol.format.filter.EqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Filter', - ol.format.filter.Filter, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.GreaterThan', - ol.format.filter.GreaterThan, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.GreaterThanOrEqualTo', - ol.format.filter.GreaterThanOrEqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Intersects', - ol.format.filter.Intersects, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.IsBetween', - ol.format.filter.IsBetween, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.IsLike', - ol.format.filter.IsLike, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.IsNull', - ol.format.filter.IsNull, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.LessThan', - ol.format.filter.LessThan, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.LessThanOrEqualTo', - ol.format.filter.LessThanOrEqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Not', - ol.format.filter.Not, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.NotEqualTo', - ol.format.filter.NotEqualTo, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Or', - ol.format.filter.Or, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Spatial', - ol.format.filter.Spatial, - OPENLAYERS); - -goog.exportSymbol( - 'ol.format.filter.Within', - ol.format.filter.Within, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.altKeyOnly', - ol.events.condition.altKeyOnly, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.altShiftKeysOnly', - ol.events.condition.altShiftKeysOnly, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.always', - ol.events.condition.always, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.click', - ol.events.condition.click, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.never', - ol.events.condition.never, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.pointerMove', - ol.events.condition.pointerMove, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.singleClick', - ol.events.condition.singleClick, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.doubleClick', - ol.events.condition.doubleClick, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.noModifierKeys', - ol.events.condition.noModifierKeys, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.platformModifierKeyOnly', - ol.events.condition.platformModifierKeyOnly, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.shiftKeyOnly', - ol.events.condition.shiftKeyOnly, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.targetNotEditable', - ol.events.condition.targetNotEditable, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.mouseOnly', - ol.events.condition.mouseOnly, - OPENLAYERS); - -goog.exportSymbol( - 'ol.events.condition.primaryAction', - ol.events.condition.primaryAction, - OPENLAYERS); - -goog.exportProperty( - ol.events.Event.prototype, - 'type', - ol.events.Event.prototype.type); - -goog.exportProperty( - ol.events.Event.prototype, - 'target', - ol.events.Event.prototype.target); - -goog.exportProperty( - ol.events.Event.prototype, - 'preventDefault', - ol.events.Event.prototype.preventDefault); - -goog.exportProperty( - ol.events.Event.prototype, - 'stopPropagation', - ol.events.Event.prototype.stopPropagation); - -goog.exportSymbol( - 'ol.control.Attribution', - ol.control.Attribution, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.Attribution.render', - ol.control.Attribution.render, - OPENLAYERS); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'getCollapsible', - ol.control.Attribution.prototype.getCollapsible); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'setCollapsible', - ol.control.Attribution.prototype.setCollapsible); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'setCollapsed', - ol.control.Attribution.prototype.setCollapsed); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'getCollapsed', - ol.control.Attribution.prototype.getCollapsed); - -goog.exportSymbol( - 'ol.control.Control', - ol.control.Control, - OPENLAYERS); - -goog.exportProperty( - ol.control.Control.prototype, - 'getMap', - ol.control.Control.prototype.getMap); - -goog.exportProperty( - ol.control.Control.prototype, - 'setMap', - ol.control.Control.prototype.setMap); - -goog.exportProperty( - ol.control.Control.prototype, - 'setTarget', - ol.control.Control.prototype.setTarget); - -goog.exportSymbol( - 'ol.control.FullScreen', - ol.control.FullScreen, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.MousePosition', - ol.control.MousePosition, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.MousePosition.render', - ol.control.MousePosition.render, - OPENLAYERS); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'getCoordinateFormat', - ol.control.MousePosition.prototype.getCoordinateFormat); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'getProjection', - ol.control.MousePosition.prototype.getProjection); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'setCoordinateFormat', - ol.control.MousePosition.prototype.setCoordinateFormat); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'setProjection', - ol.control.MousePosition.prototype.setProjection); - -goog.exportSymbol( - 'ol.control.OverviewMap', - ol.control.OverviewMap, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.OverviewMap.render', - ol.control.OverviewMap.render, - OPENLAYERS); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getCollapsible', - ol.control.OverviewMap.prototype.getCollapsible); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'setCollapsible', - ol.control.OverviewMap.prototype.setCollapsible); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'setCollapsed', - ol.control.OverviewMap.prototype.setCollapsed); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getCollapsed', - ol.control.OverviewMap.prototype.getCollapsed); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getOverviewMap', - ol.control.OverviewMap.prototype.getOverviewMap); - -goog.exportSymbol( - 'ol.control.Rotate', - ol.control.Rotate, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.Rotate.render', - ol.control.Rotate.render, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.ScaleLine', - ol.control.ScaleLine, - OPENLAYERS); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'getUnits', - ol.control.ScaleLine.prototype.getUnits); - -goog.exportSymbol( - 'ol.control.ScaleLine.render', - ol.control.ScaleLine.render, - OPENLAYERS); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'setUnits', - ol.control.ScaleLine.prototype.setUnits); - -goog.exportSymbol( - 'ol.control.Zoom', - ol.control.Zoom, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.ZoomSlider', - ol.control.ZoomSlider, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.ZoomSlider.render', - ol.control.ZoomSlider.render, - OPENLAYERS); - -goog.exportSymbol( - 'ol.control.ZoomToExtent', - ol.control.ZoomToExtent, - OPENLAYERS); - -goog.exportProperty( - ol.Object.prototype, - 'changed', - ol.Object.prototype.changed); - -goog.exportProperty( - ol.Object.prototype, - 'dispatchEvent', - ol.Object.prototype.dispatchEvent); - -goog.exportProperty( - ol.Object.prototype, - 'getRevision', - ol.Object.prototype.getRevision); - -goog.exportProperty( - ol.Object.prototype, - 'on', - ol.Object.prototype.on); - -goog.exportProperty( - ol.Object.prototype, - 'once', - ol.Object.prototype.once); - -goog.exportProperty( - ol.Object.prototype, - 'un', - ol.Object.prototype.un); - -goog.exportProperty( - ol.Collection.prototype, - 'get', - ol.Collection.prototype.get); - -goog.exportProperty( - ol.Collection.prototype, - 'getKeys', - ol.Collection.prototype.getKeys); - -goog.exportProperty( - ol.Collection.prototype, - 'getProperties', - ol.Collection.prototype.getProperties); - -goog.exportProperty( - ol.Collection.prototype, - 'set', - ol.Collection.prototype.set); - -goog.exportProperty( - ol.Collection.prototype, - 'setProperties', - ol.Collection.prototype.setProperties); - -goog.exportProperty( - ol.Collection.prototype, - 'unset', - ol.Collection.prototype.unset); - -goog.exportProperty( - ol.Collection.prototype, - 'changed', - ol.Collection.prototype.changed); - -goog.exportProperty( - ol.Collection.prototype, - 'dispatchEvent', - ol.Collection.prototype.dispatchEvent); - -goog.exportProperty( - ol.Collection.prototype, - 'getRevision', - ol.Collection.prototype.getRevision); - -goog.exportProperty( - ol.Collection.prototype, - 'on', - ol.Collection.prototype.on); - -goog.exportProperty( - ol.Collection.prototype, - 'once', - ol.Collection.prototype.once); - -goog.exportProperty( - ol.Collection.prototype, - 'un', - ol.Collection.prototype.un); - -goog.exportProperty( - ol.Collection.Event.prototype, - 'type', - ol.Collection.Event.prototype.type); - -goog.exportProperty( - ol.Collection.Event.prototype, - 'target', - ol.Collection.Event.prototype.target); - -goog.exportProperty( - ol.Collection.Event.prototype, - 'preventDefault', - ol.Collection.Event.prototype.preventDefault); - -goog.exportProperty( - ol.Collection.Event.prototype, - 'stopPropagation', - ol.Collection.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'get', - ol.DeviceOrientation.prototype.get); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getKeys', - ol.DeviceOrientation.prototype.getKeys); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getProperties', - ol.DeviceOrientation.prototype.getProperties); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'set', - ol.DeviceOrientation.prototype.set); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'setProperties', - ol.DeviceOrientation.prototype.setProperties); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'unset', - ol.DeviceOrientation.prototype.unset); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'changed', - ol.DeviceOrientation.prototype.changed); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'dispatchEvent', - ol.DeviceOrientation.prototype.dispatchEvent); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'getRevision', - ol.DeviceOrientation.prototype.getRevision); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'on', - ol.DeviceOrientation.prototype.on); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'once', - ol.DeviceOrientation.prototype.once); - -goog.exportProperty( - ol.DeviceOrientation.prototype, - 'un', - ol.DeviceOrientation.prototype.un); - -goog.exportProperty( - ol.Feature.prototype, - 'get', - ol.Feature.prototype.get); - -goog.exportProperty( - ol.Feature.prototype, - 'getKeys', - ol.Feature.prototype.getKeys); - -goog.exportProperty( - ol.Feature.prototype, - 'getProperties', - ol.Feature.prototype.getProperties); - -goog.exportProperty( - ol.Feature.prototype, - 'set', - ol.Feature.prototype.set); - -goog.exportProperty( - ol.Feature.prototype, - 'setProperties', - ol.Feature.prototype.setProperties); - -goog.exportProperty( - ol.Feature.prototype, - 'unset', - ol.Feature.prototype.unset); - -goog.exportProperty( - ol.Feature.prototype, - 'changed', - ol.Feature.prototype.changed); - -goog.exportProperty( - ol.Feature.prototype, - 'dispatchEvent', - ol.Feature.prototype.dispatchEvent); - -goog.exportProperty( - ol.Feature.prototype, - 'getRevision', - ol.Feature.prototype.getRevision); - -goog.exportProperty( - ol.Feature.prototype, - 'on', - ol.Feature.prototype.on); - -goog.exportProperty( - ol.Feature.prototype, - 'once', - ol.Feature.prototype.once); - -goog.exportProperty( - ol.Feature.prototype, - 'un', - ol.Feature.prototype.un); - -goog.exportProperty( - ol.Geolocation.prototype, - 'get', - ol.Geolocation.prototype.get); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getKeys', - ol.Geolocation.prototype.getKeys); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getProperties', - ol.Geolocation.prototype.getProperties); - -goog.exportProperty( - ol.Geolocation.prototype, - 'set', - ol.Geolocation.prototype.set); - -goog.exportProperty( - ol.Geolocation.prototype, - 'setProperties', - ol.Geolocation.prototype.setProperties); - -goog.exportProperty( - ol.Geolocation.prototype, - 'unset', - ol.Geolocation.prototype.unset); - -goog.exportProperty( - ol.Geolocation.prototype, - 'changed', - ol.Geolocation.prototype.changed); - -goog.exportProperty( - ol.Geolocation.prototype, - 'dispatchEvent', - ol.Geolocation.prototype.dispatchEvent); - -goog.exportProperty( - ol.Geolocation.prototype, - 'getRevision', - ol.Geolocation.prototype.getRevision); - -goog.exportProperty( - ol.Geolocation.prototype, - 'on', - ol.Geolocation.prototype.on); - -goog.exportProperty( - ol.Geolocation.prototype, - 'once', - ol.Geolocation.prototype.once); - -goog.exportProperty( - ol.Geolocation.prototype, - 'un', - ol.Geolocation.prototype.un); - -goog.exportProperty( - ol.ImageTile.prototype, - 'getTileCoord', - ol.ImageTile.prototype.getTileCoord); - -goog.exportProperty( - ol.ImageTile.prototype, - 'load', - ol.ImageTile.prototype.load); - -goog.exportProperty( - ol.Map.prototype, - 'get', - ol.Map.prototype.get); - -goog.exportProperty( - ol.Map.prototype, - 'getKeys', - ol.Map.prototype.getKeys); - -goog.exportProperty( - ol.Map.prototype, - 'getProperties', - ol.Map.prototype.getProperties); - -goog.exportProperty( - ol.Map.prototype, - 'set', - ol.Map.prototype.set); - -goog.exportProperty( - ol.Map.prototype, - 'setProperties', - ol.Map.prototype.setProperties); - -goog.exportProperty( - ol.Map.prototype, - 'unset', - ol.Map.prototype.unset); - -goog.exportProperty( - ol.Map.prototype, - 'changed', - ol.Map.prototype.changed); - -goog.exportProperty( - ol.Map.prototype, - 'dispatchEvent', - ol.Map.prototype.dispatchEvent); - -goog.exportProperty( - ol.Map.prototype, - 'getRevision', - ol.Map.prototype.getRevision); - -goog.exportProperty( - ol.Map.prototype, - 'on', - ol.Map.prototype.on); - -goog.exportProperty( - ol.Map.prototype, - 'once', - ol.Map.prototype.once); - -goog.exportProperty( - ol.Map.prototype, - 'un', - ol.Map.prototype.un); - -goog.exportProperty( - ol.MapEvent.prototype, - 'type', - ol.MapEvent.prototype.type); - -goog.exportProperty( - ol.MapEvent.prototype, - 'target', - ol.MapEvent.prototype.target); - -goog.exportProperty( - ol.MapEvent.prototype, - 'preventDefault', - ol.MapEvent.prototype.preventDefault); - -goog.exportProperty( - ol.MapEvent.prototype, - 'stopPropagation', - ol.MapEvent.prototype.stopPropagation); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'map', - ol.MapBrowserEvent.prototype.map); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'frameState', - ol.MapBrowserEvent.prototype.frameState); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'type', - ol.MapBrowserEvent.prototype.type); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'target', - ol.MapBrowserEvent.prototype.target); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'preventDefault', - ol.MapBrowserEvent.prototype.preventDefault); - -goog.exportProperty( - ol.MapBrowserEvent.prototype, - 'stopPropagation', - ol.MapBrowserEvent.prototype.stopPropagation); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'originalEvent', - ol.MapBrowserPointerEvent.prototype.originalEvent); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'pixel', - ol.MapBrowserPointerEvent.prototype.pixel); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'coordinate', - ol.MapBrowserPointerEvent.prototype.coordinate); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'dragging', - ol.MapBrowserPointerEvent.prototype.dragging); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'preventDefault', - ol.MapBrowserPointerEvent.prototype.preventDefault); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'stopPropagation', - ol.MapBrowserPointerEvent.prototype.stopPropagation); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'map', - ol.MapBrowserPointerEvent.prototype.map); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'frameState', - ol.MapBrowserPointerEvent.prototype.frameState); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'type', - ol.MapBrowserPointerEvent.prototype.type); - -goog.exportProperty( - ol.MapBrowserPointerEvent.prototype, - 'target', - ol.MapBrowserPointerEvent.prototype.target); - -goog.exportProperty( - ol.Object.Event.prototype, - 'type', - ol.Object.Event.prototype.type); - -goog.exportProperty( - ol.Object.Event.prototype, - 'target', - ol.Object.Event.prototype.target); - -goog.exportProperty( - ol.Object.Event.prototype, - 'preventDefault', - ol.Object.Event.prototype.preventDefault); - -goog.exportProperty( - ol.Object.Event.prototype, - 'stopPropagation', - ol.Object.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.Overlay.prototype, - 'get', - ol.Overlay.prototype.get); - -goog.exportProperty( - ol.Overlay.prototype, - 'getKeys', - ol.Overlay.prototype.getKeys); - -goog.exportProperty( - ol.Overlay.prototype, - 'getProperties', - ol.Overlay.prototype.getProperties); - -goog.exportProperty( - ol.Overlay.prototype, - 'set', - ol.Overlay.prototype.set); - -goog.exportProperty( - ol.Overlay.prototype, - 'setProperties', - ol.Overlay.prototype.setProperties); - -goog.exportProperty( - ol.Overlay.prototype, - 'unset', - ol.Overlay.prototype.unset); - -goog.exportProperty( - ol.Overlay.prototype, - 'changed', - ol.Overlay.prototype.changed); - -goog.exportProperty( - ol.Overlay.prototype, - 'dispatchEvent', - ol.Overlay.prototype.dispatchEvent); - -goog.exportProperty( - ol.Overlay.prototype, - 'getRevision', - ol.Overlay.prototype.getRevision); - -goog.exportProperty( - ol.Overlay.prototype, - 'on', - ol.Overlay.prototype.on); - -goog.exportProperty( - ol.Overlay.prototype, - 'once', - ol.Overlay.prototype.once); - -goog.exportProperty( - ol.Overlay.prototype, - 'un', - ol.Overlay.prototype.un); - -goog.exportProperty( - ol.VectorImageTile.prototype, - 'getTileCoord', - ol.VectorImageTile.prototype.getTileCoord); - -goog.exportProperty( - ol.VectorImageTile.prototype, - 'load', - ol.VectorImageTile.prototype.load); - -goog.exportProperty( - ol.VectorTile.prototype, - 'getTileCoord', - ol.VectorTile.prototype.getTileCoord); - -goog.exportProperty( - ol.VectorTile.prototype, - 'load', - ol.VectorTile.prototype.load); - -goog.exportProperty( - ol.View.prototype, - 'get', - ol.View.prototype.get); - -goog.exportProperty( - ol.View.prototype, - 'getKeys', - ol.View.prototype.getKeys); - -goog.exportProperty( - ol.View.prototype, - 'getProperties', - ol.View.prototype.getProperties); - -goog.exportProperty( - ol.View.prototype, - 'set', - ol.View.prototype.set); - -goog.exportProperty( - ol.View.prototype, - 'setProperties', - ol.View.prototype.setProperties); - -goog.exportProperty( - ol.View.prototype, - 'unset', - ol.View.prototype.unset); - -goog.exportProperty( - ol.View.prototype, - 'changed', - ol.View.prototype.changed); - -goog.exportProperty( - ol.View.prototype, - 'dispatchEvent', - ol.View.prototype.dispatchEvent); - -goog.exportProperty( - ol.View.prototype, - 'getRevision', - ol.View.prototype.getRevision); - -goog.exportProperty( - ol.View.prototype, - 'on', - ol.View.prototype.on); - -goog.exportProperty( - ol.View.prototype, - 'once', - ol.View.prototype.once); - -goog.exportProperty( - ol.View.prototype, - 'un', - ol.View.prototype.un); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'forEachTileCoord', - ol.tilegrid.WMTS.prototype.forEachTileCoord); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getMaxZoom', - ol.tilegrid.WMTS.prototype.getMaxZoom); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getMinZoom', - ol.tilegrid.WMTS.prototype.getMinZoom); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getOrigin', - ol.tilegrid.WMTS.prototype.getOrigin); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getResolution', - ol.tilegrid.WMTS.prototype.getResolution); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getResolutions', - ol.tilegrid.WMTS.prototype.getResolutions); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getTileCoordExtent', - ol.tilegrid.WMTS.prototype.getTileCoordExtent); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getTileCoordForCoordAndResolution', - ol.tilegrid.WMTS.prototype.getTileCoordForCoordAndResolution); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getTileCoordForCoordAndZ', - ol.tilegrid.WMTS.prototype.getTileCoordForCoordAndZ); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getTileSize', - ol.tilegrid.WMTS.prototype.getTileSize); - -goog.exportProperty( - ol.tilegrid.WMTS.prototype, - 'getZForResolution', - ol.tilegrid.WMTS.prototype.getZForResolution); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getOpacity', - ol.style.RegularShape.prototype.getOpacity); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getRotateWithView', - ol.style.RegularShape.prototype.getRotateWithView); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getRotation', - ol.style.RegularShape.prototype.getRotation); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getScale', - ol.style.RegularShape.prototype.getScale); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'getSnapToPixel', - ol.style.RegularShape.prototype.getSnapToPixel); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'setOpacity', - ol.style.RegularShape.prototype.setOpacity); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'setRotation', - ol.style.RegularShape.prototype.setRotation); - -goog.exportProperty( - ol.style.RegularShape.prototype, - 'setScale', - ol.style.RegularShape.prototype.setScale); - -goog.exportProperty( - ol.style.Circle.prototype, - 'clone', - ol.style.Circle.prototype.clone); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getAngle', - ol.style.Circle.prototype.getAngle); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getFill', - ol.style.Circle.prototype.getFill); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getPoints', - ol.style.Circle.prototype.getPoints); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getRadius', - ol.style.Circle.prototype.getRadius); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getRadius2', - ol.style.Circle.prototype.getRadius2); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getStroke', - ol.style.Circle.prototype.getStroke); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getOpacity', - ol.style.Circle.prototype.getOpacity); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getRotateWithView', - ol.style.Circle.prototype.getRotateWithView); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getRotation', - ol.style.Circle.prototype.getRotation); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getScale', - ol.style.Circle.prototype.getScale); - -goog.exportProperty( - ol.style.Circle.prototype, - 'getSnapToPixel', - ol.style.Circle.prototype.getSnapToPixel); - -goog.exportProperty( - ol.style.Circle.prototype, - 'setOpacity', - ol.style.Circle.prototype.setOpacity); - -goog.exportProperty( - ol.style.Circle.prototype, - 'setRotation', - ol.style.Circle.prototype.setRotation); - -goog.exportProperty( - ol.style.Circle.prototype, - 'setScale', - ol.style.Circle.prototype.setScale); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getOpacity', - ol.style.Icon.prototype.getOpacity); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getRotateWithView', - ol.style.Icon.prototype.getRotateWithView); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getRotation', - ol.style.Icon.prototype.getRotation); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getScale', - ol.style.Icon.prototype.getScale); - -goog.exportProperty( - ol.style.Icon.prototype, - 'getSnapToPixel', - ol.style.Icon.prototype.getSnapToPixel); - -goog.exportProperty( - ol.style.Icon.prototype, - 'setOpacity', - ol.style.Icon.prototype.setOpacity); - -goog.exportProperty( - ol.style.Icon.prototype, - 'setRotation', - ol.style.Icon.prototype.setRotation); - -goog.exportProperty( - ol.style.Icon.prototype, - 'setScale', - ol.style.Icon.prototype.setScale); - -goog.exportProperty( - ol.source.Source.prototype, - 'get', - ol.source.Source.prototype.get); - -goog.exportProperty( - ol.source.Source.prototype, - 'getKeys', - ol.source.Source.prototype.getKeys); - -goog.exportProperty( - ol.source.Source.prototype, - 'getProperties', - ol.source.Source.prototype.getProperties); - -goog.exportProperty( - ol.source.Source.prototype, - 'set', - ol.source.Source.prototype.set); - -goog.exportProperty( - ol.source.Source.prototype, - 'setProperties', - ol.source.Source.prototype.setProperties); - -goog.exportProperty( - ol.source.Source.prototype, - 'unset', - ol.source.Source.prototype.unset); - -goog.exportProperty( - ol.source.Source.prototype, - 'changed', - ol.source.Source.prototype.changed); - -goog.exportProperty( - ol.source.Source.prototype, - 'dispatchEvent', - ol.source.Source.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Source.prototype, - 'getRevision', - ol.source.Source.prototype.getRevision); - -goog.exportProperty( - ol.source.Source.prototype, - 'on', - ol.source.Source.prototype.on); - -goog.exportProperty( - ol.source.Source.prototype, - 'once', - ol.source.Source.prototype.once); - -goog.exportProperty( - ol.source.Source.prototype, - 'un', - ol.source.Source.prototype.un); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getAttributions', - ol.source.Tile.prototype.getAttributions); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getLogo', - ol.source.Tile.prototype.getLogo); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getProjection', - ol.source.Tile.prototype.getProjection); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getState', - ol.source.Tile.prototype.getState); - -goog.exportProperty( - ol.source.Tile.prototype, - 'refresh', - ol.source.Tile.prototype.refresh); - -goog.exportProperty( - ol.source.Tile.prototype, - 'setAttributions', - ol.source.Tile.prototype.setAttributions); - -goog.exportProperty( - ol.source.Tile.prototype, - 'get', - ol.source.Tile.prototype.get); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getKeys', - ol.source.Tile.prototype.getKeys); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getProperties', - ol.source.Tile.prototype.getProperties); - -goog.exportProperty( - ol.source.Tile.prototype, - 'set', - ol.source.Tile.prototype.set); - -goog.exportProperty( - ol.source.Tile.prototype, - 'setProperties', - ol.source.Tile.prototype.setProperties); - -goog.exportProperty( - ol.source.Tile.prototype, - 'unset', - ol.source.Tile.prototype.unset); - -goog.exportProperty( - ol.source.Tile.prototype, - 'changed', - ol.source.Tile.prototype.changed); - -goog.exportProperty( - ol.source.Tile.prototype, - 'dispatchEvent', - ol.source.Tile.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Tile.prototype, - 'getRevision', - ol.source.Tile.prototype.getRevision); - -goog.exportProperty( - ol.source.Tile.prototype, - 'on', - ol.source.Tile.prototype.on); - -goog.exportProperty( - ol.source.Tile.prototype, - 'once', - ol.source.Tile.prototype.once); - -goog.exportProperty( - ol.source.Tile.prototype, - 'un', - ol.source.Tile.prototype.un); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getTileGrid', - ol.source.UrlTile.prototype.getTileGrid); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'refresh', - ol.source.UrlTile.prototype.refresh); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getAttributions', - ol.source.UrlTile.prototype.getAttributions); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getLogo', - ol.source.UrlTile.prototype.getLogo); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getProjection', - ol.source.UrlTile.prototype.getProjection); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getState', - ol.source.UrlTile.prototype.getState); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'setAttributions', - ol.source.UrlTile.prototype.setAttributions); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'get', - ol.source.UrlTile.prototype.get); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getKeys', - ol.source.UrlTile.prototype.getKeys); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getProperties', - ol.source.UrlTile.prototype.getProperties); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'set', - ol.source.UrlTile.prototype.set); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'setProperties', - ol.source.UrlTile.prototype.setProperties); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'unset', - ol.source.UrlTile.prototype.unset); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'changed', - ol.source.UrlTile.prototype.changed); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'dispatchEvent', - ol.source.UrlTile.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'getRevision', - ol.source.UrlTile.prototype.getRevision); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'on', - ol.source.UrlTile.prototype.on); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'once', - ol.source.UrlTile.prototype.once); - -goog.exportProperty( - ol.source.UrlTile.prototype, - 'un', - ol.source.UrlTile.prototype.un); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getTileLoadFunction', - ol.source.TileImage.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getTileUrlFunction', - ol.source.TileImage.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getUrls', - ol.source.TileImage.prototype.getUrls); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setTileLoadFunction', - ol.source.TileImage.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setTileUrlFunction', - ol.source.TileImage.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setUrl', - ol.source.TileImage.prototype.setUrl); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setUrls', - ol.source.TileImage.prototype.setUrls); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getTileGrid', - ol.source.TileImage.prototype.getTileGrid); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'refresh', - ol.source.TileImage.prototype.refresh); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getAttributions', - ol.source.TileImage.prototype.getAttributions); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getLogo', - ol.source.TileImage.prototype.getLogo); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getProjection', - ol.source.TileImage.prototype.getProjection); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getState', - ol.source.TileImage.prototype.getState); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setAttributions', - ol.source.TileImage.prototype.setAttributions); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'get', - ol.source.TileImage.prototype.get); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getKeys', - ol.source.TileImage.prototype.getKeys); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getProperties', - ol.source.TileImage.prototype.getProperties); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'set', - ol.source.TileImage.prototype.set); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'setProperties', - ol.source.TileImage.prototype.setProperties); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'unset', - ol.source.TileImage.prototype.unset); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'changed', - ol.source.TileImage.prototype.changed); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'dispatchEvent', - ol.source.TileImage.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'getRevision', - ol.source.TileImage.prototype.getRevision); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'on', - ol.source.TileImage.prototype.on); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'once', - ol.source.TileImage.prototype.once); - -goog.exportProperty( - ol.source.TileImage.prototype, - 'un', - ol.source.TileImage.prototype.un); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setRenderReprojectionEdges', - ol.source.BingMaps.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setTileGridForProjection', - ol.source.BingMaps.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getTileLoadFunction', - ol.source.BingMaps.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getTileUrlFunction', - ol.source.BingMaps.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getUrls', - ol.source.BingMaps.prototype.getUrls); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setTileLoadFunction', - ol.source.BingMaps.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setTileUrlFunction', - ol.source.BingMaps.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setUrl', - ol.source.BingMaps.prototype.setUrl); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setUrls', - ol.source.BingMaps.prototype.setUrls); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getTileGrid', - ol.source.BingMaps.prototype.getTileGrid); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'refresh', - ol.source.BingMaps.prototype.refresh); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getAttributions', - ol.source.BingMaps.prototype.getAttributions); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getLogo', - ol.source.BingMaps.prototype.getLogo); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getProjection', - ol.source.BingMaps.prototype.getProjection); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getState', - ol.source.BingMaps.prototype.getState); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setAttributions', - ol.source.BingMaps.prototype.setAttributions); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'get', - ol.source.BingMaps.prototype.get); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getKeys', - ol.source.BingMaps.prototype.getKeys); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getProperties', - ol.source.BingMaps.prototype.getProperties); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'set', - ol.source.BingMaps.prototype.set); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'setProperties', - ol.source.BingMaps.prototype.setProperties); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'unset', - ol.source.BingMaps.prototype.unset); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'changed', - ol.source.BingMaps.prototype.changed); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'dispatchEvent', - ol.source.BingMaps.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'getRevision', - ol.source.BingMaps.prototype.getRevision); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'on', - ol.source.BingMaps.prototype.on); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'once', - ol.source.BingMaps.prototype.once); - -goog.exportProperty( - ol.source.BingMaps.prototype, - 'un', - ol.source.BingMaps.prototype.un); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setRenderReprojectionEdges', - ol.source.XYZ.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setTileGridForProjection', - ol.source.XYZ.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getTileLoadFunction', - ol.source.XYZ.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getTileUrlFunction', - ol.source.XYZ.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getUrls', - ol.source.XYZ.prototype.getUrls); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setTileLoadFunction', - ol.source.XYZ.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setTileUrlFunction', - ol.source.XYZ.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setUrl', - ol.source.XYZ.prototype.setUrl); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setUrls', - ol.source.XYZ.prototype.setUrls); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getTileGrid', - ol.source.XYZ.prototype.getTileGrid); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'refresh', - ol.source.XYZ.prototype.refresh); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getAttributions', - ol.source.XYZ.prototype.getAttributions); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getLogo', - ol.source.XYZ.prototype.getLogo); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getProjection', - ol.source.XYZ.prototype.getProjection); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getState', - ol.source.XYZ.prototype.getState); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setAttributions', - ol.source.XYZ.prototype.setAttributions); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'get', - ol.source.XYZ.prototype.get); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getKeys', - ol.source.XYZ.prototype.getKeys); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getProperties', - ol.source.XYZ.prototype.getProperties); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'set', - ol.source.XYZ.prototype.set); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'setProperties', - ol.source.XYZ.prototype.setProperties); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'unset', - ol.source.XYZ.prototype.unset); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'changed', - ol.source.XYZ.prototype.changed); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'dispatchEvent', - ol.source.XYZ.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'getRevision', - ol.source.XYZ.prototype.getRevision); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'on', - ol.source.XYZ.prototype.on); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'once', - ol.source.XYZ.prototype.once); - -goog.exportProperty( - ol.source.XYZ.prototype, - 'un', - ol.source.XYZ.prototype.un); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setRenderReprojectionEdges', - ol.source.CartoDB.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setTileGridForProjection', - ol.source.CartoDB.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getTileLoadFunction', - ol.source.CartoDB.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getTileUrlFunction', - ol.source.CartoDB.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getUrls', - ol.source.CartoDB.prototype.getUrls); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setTileLoadFunction', - ol.source.CartoDB.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setTileUrlFunction', - ol.source.CartoDB.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setUrl', - ol.source.CartoDB.prototype.setUrl); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setUrls', - ol.source.CartoDB.prototype.setUrls); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getTileGrid', - ol.source.CartoDB.prototype.getTileGrid); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'refresh', - ol.source.CartoDB.prototype.refresh); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getAttributions', - ol.source.CartoDB.prototype.getAttributions); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getLogo', - ol.source.CartoDB.prototype.getLogo); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getProjection', - ol.source.CartoDB.prototype.getProjection); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getState', - ol.source.CartoDB.prototype.getState); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setAttributions', - ol.source.CartoDB.prototype.setAttributions); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'get', - ol.source.CartoDB.prototype.get); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getKeys', - ol.source.CartoDB.prototype.getKeys); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getProperties', - ol.source.CartoDB.prototype.getProperties); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'set', - ol.source.CartoDB.prototype.set); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'setProperties', - ol.source.CartoDB.prototype.setProperties); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'unset', - ol.source.CartoDB.prototype.unset); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'changed', - ol.source.CartoDB.prototype.changed); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'dispatchEvent', - ol.source.CartoDB.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'getRevision', - ol.source.CartoDB.prototype.getRevision); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'on', - ol.source.CartoDB.prototype.on); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'once', - ol.source.CartoDB.prototype.once); - -goog.exportProperty( - ol.source.CartoDB.prototype, - 'un', - ol.source.CartoDB.prototype.un); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getAttributions', - ol.source.Vector.prototype.getAttributions); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getLogo', - ol.source.Vector.prototype.getLogo); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getProjection', - ol.source.Vector.prototype.getProjection); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getState', - ol.source.Vector.prototype.getState); - -goog.exportProperty( - ol.source.Vector.prototype, - 'refresh', - ol.source.Vector.prototype.refresh); - -goog.exportProperty( - ol.source.Vector.prototype, - 'setAttributions', - ol.source.Vector.prototype.setAttributions); - -goog.exportProperty( - ol.source.Vector.prototype, - 'get', - ol.source.Vector.prototype.get); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getKeys', - ol.source.Vector.prototype.getKeys); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getProperties', - ol.source.Vector.prototype.getProperties); - -goog.exportProperty( - ol.source.Vector.prototype, - 'set', - ol.source.Vector.prototype.set); - -goog.exportProperty( - ol.source.Vector.prototype, - 'setProperties', - ol.source.Vector.prototype.setProperties); - -goog.exportProperty( - ol.source.Vector.prototype, - 'unset', - ol.source.Vector.prototype.unset); - -goog.exportProperty( - ol.source.Vector.prototype, - 'changed', - ol.source.Vector.prototype.changed); - -goog.exportProperty( - ol.source.Vector.prototype, - 'dispatchEvent', - ol.source.Vector.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Vector.prototype, - 'getRevision', - ol.source.Vector.prototype.getRevision); - -goog.exportProperty( - ol.source.Vector.prototype, - 'on', - ol.source.Vector.prototype.on); - -goog.exportProperty( - ol.source.Vector.prototype, - 'once', - ol.source.Vector.prototype.once); - -goog.exportProperty( - ol.source.Vector.prototype, - 'un', - ol.source.Vector.prototype.un); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'addFeature', - ol.source.Cluster.prototype.addFeature); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'addFeatures', - ol.source.Cluster.prototype.addFeatures); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'clear', - ol.source.Cluster.prototype.clear); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'forEachFeature', - ol.source.Cluster.prototype.forEachFeature); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'forEachFeatureInExtent', - ol.source.Cluster.prototype.forEachFeatureInExtent); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'forEachFeatureIntersectingExtent', - ol.source.Cluster.prototype.forEachFeatureIntersectingExtent); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getFeaturesCollection', - ol.source.Cluster.prototype.getFeaturesCollection); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getFeatures', - ol.source.Cluster.prototype.getFeatures); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getFeaturesAtCoordinate', - ol.source.Cluster.prototype.getFeaturesAtCoordinate); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getFeaturesInExtent', - ol.source.Cluster.prototype.getFeaturesInExtent); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getClosestFeatureToCoordinate', - ol.source.Cluster.prototype.getClosestFeatureToCoordinate); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getExtent', - ol.source.Cluster.prototype.getExtent); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getFeatureById', - ol.source.Cluster.prototype.getFeatureById); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getFormat', - ol.source.Cluster.prototype.getFormat); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getUrl', - ol.source.Cluster.prototype.getUrl); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'removeFeature', - ol.source.Cluster.prototype.removeFeature); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getAttributions', - ol.source.Cluster.prototype.getAttributions); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getLogo', - ol.source.Cluster.prototype.getLogo); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getProjection', - ol.source.Cluster.prototype.getProjection); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getState', - ol.source.Cluster.prototype.getState); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'refresh', - ol.source.Cluster.prototype.refresh); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'setAttributions', - ol.source.Cluster.prototype.setAttributions); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'get', - ol.source.Cluster.prototype.get); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getKeys', - ol.source.Cluster.prototype.getKeys); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getProperties', - ol.source.Cluster.prototype.getProperties); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'set', - ol.source.Cluster.prototype.set); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'setProperties', - ol.source.Cluster.prototype.setProperties); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'unset', - ol.source.Cluster.prototype.unset); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'changed', - ol.source.Cluster.prototype.changed); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'dispatchEvent', - ol.source.Cluster.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'getRevision', - ol.source.Cluster.prototype.getRevision); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'on', - ol.source.Cluster.prototype.on); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'once', - ol.source.Cluster.prototype.once); - -goog.exportProperty( - ol.source.Cluster.prototype, - 'un', - ol.source.Cluster.prototype.un); - -goog.exportProperty( - ol.source.Image.prototype, - 'getAttributions', - ol.source.Image.prototype.getAttributions); - -goog.exportProperty( - ol.source.Image.prototype, - 'getLogo', - ol.source.Image.prototype.getLogo); - -goog.exportProperty( - ol.source.Image.prototype, - 'getProjection', - ol.source.Image.prototype.getProjection); - -goog.exportProperty( - ol.source.Image.prototype, - 'getState', - ol.source.Image.prototype.getState); - -goog.exportProperty( - ol.source.Image.prototype, - 'refresh', - ol.source.Image.prototype.refresh); - -goog.exportProperty( - ol.source.Image.prototype, - 'setAttributions', - ol.source.Image.prototype.setAttributions); - -goog.exportProperty( - ol.source.Image.prototype, - 'get', - ol.source.Image.prototype.get); - -goog.exportProperty( - ol.source.Image.prototype, - 'getKeys', - ol.source.Image.prototype.getKeys); - -goog.exportProperty( - ol.source.Image.prototype, - 'getProperties', - ol.source.Image.prototype.getProperties); - -goog.exportProperty( - ol.source.Image.prototype, - 'set', - ol.source.Image.prototype.set); - -goog.exportProperty( - ol.source.Image.prototype, - 'setProperties', - ol.source.Image.prototype.setProperties); - -goog.exportProperty( - ol.source.Image.prototype, - 'unset', - ol.source.Image.prototype.unset); - -goog.exportProperty( - ol.source.Image.prototype, - 'changed', - ol.source.Image.prototype.changed); - -goog.exportProperty( - ol.source.Image.prototype, - 'dispatchEvent', - ol.source.Image.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Image.prototype, - 'getRevision', - ol.source.Image.prototype.getRevision); - -goog.exportProperty( - ol.source.Image.prototype, - 'on', - ol.source.Image.prototype.on); - -goog.exportProperty( - ol.source.Image.prototype, - 'once', - ol.source.Image.prototype.once); - -goog.exportProperty( - ol.source.Image.prototype, - 'un', - ol.source.Image.prototype.un); - -goog.exportProperty( - ol.source.Image.Event.prototype, - 'type', - ol.source.Image.Event.prototype.type); - -goog.exportProperty( - ol.source.Image.Event.prototype, - 'target', - ol.source.Image.Event.prototype.target); - -goog.exportProperty( - ol.source.Image.Event.prototype, - 'preventDefault', - ol.source.Image.Event.prototype.preventDefault); - -goog.exportProperty( - ol.source.Image.Event.prototype, - 'stopPropagation', - ol.source.Image.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getAttributions', - ol.source.ImageArcGISRest.prototype.getAttributions); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getLogo', - ol.source.ImageArcGISRest.prototype.getLogo); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getProjection', - ol.source.ImageArcGISRest.prototype.getProjection); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getState', - ol.source.ImageArcGISRest.prototype.getState); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'refresh', - ol.source.ImageArcGISRest.prototype.refresh); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'setAttributions', - ol.source.ImageArcGISRest.prototype.setAttributions); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'get', - ol.source.ImageArcGISRest.prototype.get); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getKeys', - ol.source.ImageArcGISRest.prototype.getKeys); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getProperties', - ol.source.ImageArcGISRest.prototype.getProperties); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'set', - ol.source.ImageArcGISRest.prototype.set); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'setProperties', - ol.source.ImageArcGISRest.prototype.setProperties); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'unset', - ol.source.ImageArcGISRest.prototype.unset); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'changed', - ol.source.ImageArcGISRest.prototype.changed); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'dispatchEvent', - ol.source.ImageArcGISRest.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'getRevision', - ol.source.ImageArcGISRest.prototype.getRevision); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'on', - ol.source.ImageArcGISRest.prototype.on); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'once', - ol.source.ImageArcGISRest.prototype.once); - -goog.exportProperty( - ol.source.ImageArcGISRest.prototype, - 'un', - ol.source.ImageArcGISRest.prototype.un); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getAttributions', - ol.source.ImageCanvas.prototype.getAttributions); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getLogo', - ol.source.ImageCanvas.prototype.getLogo); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getProjection', - ol.source.ImageCanvas.prototype.getProjection); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getState', - ol.source.ImageCanvas.prototype.getState); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'refresh', - ol.source.ImageCanvas.prototype.refresh); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'setAttributions', - ol.source.ImageCanvas.prototype.setAttributions); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'get', - ol.source.ImageCanvas.prototype.get); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getKeys', - ol.source.ImageCanvas.prototype.getKeys); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getProperties', - ol.source.ImageCanvas.prototype.getProperties); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'set', - ol.source.ImageCanvas.prototype.set); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'setProperties', - ol.source.ImageCanvas.prototype.setProperties); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'unset', - ol.source.ImageCanvas.prototype.unset); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'changed', - ol.source.ImageCanvas.prototype.changed); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'dispatchEvent', - ol.source.ImageCanvas.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'getRevision', - ol.source.ImageCanvas.prototype.getRevision); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'on', - ol.source.ImageCanvas.prototype.on); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'once', - ol.source.ImageCanvas.prototype.once); - -goog.exportProperty( - ol.source.ImageCanvas.prototype, - 'un', - ol.source.ImageCanvas.prototype.un); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getAttributions', - ol.source.ImageMapGuide.prototype.getAttributions); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getLogo', - ol.source.ImageMapGuide.prototype.getLogo); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getProjection', - ol.source.ImageMapGuide.prototype.getProjection); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getState', - ol.source.ImageMapGuide.prototype.getState); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'refresh', - ol.source.ImageMapGuide.prototype.refresh); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'setAttributions', - ol.source.ImageMapGuide.prototype.setAttributions); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'get', - ol.source.ImageMapGuide.prototype.get); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getKeys', - ol.source.ImageMapGuide.prototype.getKeys); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getProperties', - ol.source.ImageMapGuide.prototype.getProperties); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'set', - ol.source.ImageMapGuide.prototype.set); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'setProperties', - ol.source.ImageMapGuide.prototype.setProperties); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'unset', - ol.source.ImageMapGuide.prototype.unset); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'changed', - ol.source.ImageMapGuide.prototype.changed); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'dispatchEvent', - ol.source.ImageMapGuide.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'getRevision', - ol.source.ImageMapGuide.prototype.getRevision); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'on', - ol.source.ImageMapGuide.prototype.on); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'once', - ol.source.ImageMapGuide.prototype.once); - -goog.exportProperty( - ol.source.ImageMapGuide.prototype, - 'un', - ol.source.ImageMapGuide.prototype.un); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getAttributions', - ol.source.ImageStatic.prototype.getAttributions); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getLogo', - ol.source.ImageStatic.prototype.getLogo); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getProjection', - ol.source.ImageStatic.prototype.getProjection); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getState', - ol.source.ImageStatic.prototype.getState); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'refresh', - ol.source.ImageStatic.prototype.refresh); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'setAttributions', - ol.source.ImageStatic.prototype.setAttributions); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'get', - ol.source.ImageStatic.prototype.get); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getKeys', - ol.source.ImageStatic.prototype.getKeys); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getProperties', - ol.source.ImageStatic.prototype.getProperties); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'set', - ol.source.ImageStatic.prototype.set); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'setProperties', - ol.source.ImageStatic.prototype.setProperties); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'unset', - ol.source.ImageStatic.prototype.unset); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'changed', - ol.source.ImageStatic.prototype.changed); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'dispatchEvent', - ol.source.ImageStatic.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'getRevision', - ol.source.ImageStatic.prototype.getRevision); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'on', - ol.source.ImageStatic.prototype.on); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'once', - ol.source.ImageStatic.prototype.once); - -goog.exportProperty( - ol.source.ImageStatic.prototype, - 'un', - ol.source.ImageStatic.prototype.un); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getAttributions', - ol.source.ImageVector.prototype.getAttributions); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getLogo', - ol.source.ImageVector.prototype.getLogo); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getProjection', - ol.source.ImageVector.prototype.getProjection); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getState', - ol.source.ImageVector.prototype.getState); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'refresh', - ol.source.ImageVector.prototype.refresh); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'setAttributions', - ol.source.ImageVector.prototype.setAttributions); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'get', - ol.source.ImageVector.prototype.get); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getKeys', - ol.source.ImageVector.prototype.getKeys); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getProperties', - ol.source.ImageVector.prototype.getProperties); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'set', - ol.source.ImageVector.prototype.set); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'setProperties', - ol.source.ImageVector.prototype.setProperties); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'unset', - ol.source.ImageVector.prototype.unset); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'changed', - ol.source.ImageVector.prototype.changed); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'dispatchEvent', - ol.source.ImageVector.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'getRevision', - ol.source.ImageVector.prototype.getRevision); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'on', - ol.source.ImageVector.prototype.on); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'once', - ol.source.ImageVector.prototype.once); - -goog.exportProperty( - ol.source.ImageVector.prototype, - 'un', - ol.source.ImageVector.prototype.un); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getAttributions', - ol.source.ImageWMS.prototype.getAttributions); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getLogo', - ol.source.ImageWMS.prototype.getLogo); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getProjection', - ol.source.ImageWMS.prototype.getProjection); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getState', - ol.source.ImageWMS.prototype.getState); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'refresh', - ol.source.ImageWMS.prototype.refresh); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'setAttributions', - ol.source.ImageWMS.prototype.setAttributions); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'get', - ol.source.ImageWMS.prototype.get); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getKeys', - ol.source.ImageWMS.prototype.getKeys); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getProperties', - ol.source.ImageWMS.prototype.getProperties); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'set', - ol.source.ImageWMS.prototype.set); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'setProperties', - ol.source.ImageWMS.prototype.setProperties); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'unset', - ol.source.ImageWMS.prototype.unset); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'changed', - ol.source.ImageWMS.prototype.changed); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'dispatchEvent', - ol.source.ImageWMS.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'getRevision', - ol.source.ImageWMS.prototype.getRevision); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'on', - ol.source.ImageWMS.prototype.on); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'once', - ol.source.ImageWMS.prototype.once); - -goog.exportProperty( - ol.source.ImageWMS.prototype, - 'un', - ol.source.ImageWMS.prototype.un); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setRenderReprojectionEdges', - ol.source.OSM.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setTileGridForProjection', - ol.source.OSM.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getTileLoadFunction', - ol.source.OSM.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getTileUrlFunction', - ol.source.OSM.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getUrls', - ol.source.OSM.prototype.getUrls); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setTileLoadFunction', - ol.source.OSM.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setTileUrlFunction', - ol.source.OSM.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setUrl', - ol.source.OSM.prototype.setUrl); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setUrls', - ol.source.OSM.prototype.setUrls); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getTileGrid', - ol.source.OSM.prototype.getTileGrid); - -goog.exportProperty( - ol.source.OSM.prototype, - 'refresh', - ol.source.OSM.prototype.refresh); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getAttributions', - ol.source.OSM.prototype.getAttributions); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getLogo', - ol.source.OSM.prototype.getLogo); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getProjection', - ol.source.OSM.prototype.getProjection); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getState', - ol.source.OSM.prototype.getState); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setAttributions', - ol.source.OSM.prototype.setAttributions); - -goog.exportProperty( - ol.source.OSM.prototype, - 'get', - ol.source.OSM.prototype.get); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getKeys', - ol.source.OSM.prototype.getKeys); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getProperties', - ol.source.OSM.prototype.getProperties); - -goog.exportProperty( - ol.source.OSM.prototype, - 'set', - ol.source.OSM.prototype.set); - -goog.exportProperty( - ol.source.OSM.prototype, - 'setProperties', - ol.source.OSM.prototype.setProperties); - -goog.exportProperty( - ol.source.OSM.prototype, - 'unset', - ol.source.OSM.prototype.unset); - -goog.exportProperty( - ol.source.OSM.prototype, - 'changed', - ol.source.OSM.prototype.changed); - -goog.exportProperty( - ol.source.OSM.prototype, - 'dispatchEvent', - ol.source.OSM.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.OSM.prototype, - 'getRevision', - ol.source.OSM.prototype.getRevision); - -goog.exportProperty( - ol.source.OSM.prototype, - 'on', - ol.source.OSM.prototype.on); - -goog.exportProperty( - ol.source.OSM.prototype, - 'once', - ol.source.OSM.prototype.once); - -goog.exportProperty( - ol.source.OSM.prototype, - 'un', - ol.source.OSM.prototype.un); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getAttributions', - ol.source.Raster.prototype.getAttributions); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getLogo', - ol.source.Raster.prototype.getLogo); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getProjection', - ol.source.Raster.prototype.getProjection); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getState', - ol.source.Raster.prototype.getState); - -goog.exportProperty( - ol.source.Raster.prototype, - 'refresh', - ol.source.Raster.prototype.refresh); - -goog.exportProperty( - ol.source.Raster.prototype, - 'setAttributions', - ol.source.Raster.prototype.setAttributions); - -goog.exportProperty( - ol.source.Raster.prototype, - 'get', - ol.source.Raster.prototype.get); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getKeys', - ol.source.Raster.prototype.getKeys); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getProperties', - ol.source.Raster.prototype.getProperties); - -goog.exportProperty( - ol.source.Raster.prototype, - 'set', - ol.source.Raster.prototype.set); - -goog.exportProperty( - ol.source.Raster.prototype, - 'setProperties', - ol.source.Raster.prototype.setProperties); - -goog.exportProperty( - ol.source.Raster.prototype, - 'unset', - ol.source.Raster.prototype.unset); - -goog.exportProperty( - ol.source.Raster.prototype, - 'changed', - ol.source.Raster.prototype.changed); - -goog.exportProperty( - ol.source.Raster.prototype, - 'dispatchEvent', - ol.source.Raster.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Raster.prototype, - 'getRevision', - ol.source.Raster.prototype.getRevision); - -goog.exportProperty( - ol.source.Raster.prototype, - 'on', - ol.source.Raster.prototype.on); - -goog.exportProperty( - ol.source.Raster.prototype, - 'once', - ol.source.Raster.prototype.once); - -goog.exportProperty( - ol.source.Raster.prototype, - 'un', - ol.source.Raster.prototype.un); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'type', - ol.source.Raster.Event.prototype.type); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'target', - ol.source.Raster.Event.prototype.target); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'preventDefault', - ol.source.Raster.Event.prototype.preventDefault); - -goog.exportProperty( - ol.source.Raster.Event.prototype, - 'stopPropagation', - ol.source.Raster.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setRenderReprojectionEdges', - ol.source.Stamen.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setTileGridForProjection', - ol.source.Stamen.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getTileLoadFunction', - ol.source.Stamen.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getTileUrlFunction', - ol.source.Stamen.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getUrls', - ol.source.Stamen.prototype.getUrls); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setTileLoadFunction', - ol.source.Stamen.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setTileUrlFunction', - ol.source.Stamen.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setUrl', - ol.source.Stamen.prototype.setUrl); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setUrls', - ol.source.Stamen.prototype.setUrls); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getTileGrid', - ol.source.Stamen.prototype.getTileGrid); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'refresh', - ol.source.Stamen.prototype.refresh); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getAttributions', - ol.source.Stamen.prototype.getAttributions); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getLogo', - ol.source.Stamen.prototype.getLogo); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getProjection', - ol.source.Stamen.prototype.getProjection); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getState', - ol.source.Stamen.prototype.getState); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setAttributions', - ol.source.Stamen.prototype.setAttributions); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'get', - ol.source.Stamen.prototype.get); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getKeys', - ol.source.Stamen.prototype.getKeys); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getProperties', - ol.source.Stamen.prototype.getProperties); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'set', - ol.source.Stamen.prototype.set); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'setProperties', - ol.source.Stamen.prototype.setProperties); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'unset', - ol.source.Stamen.prototype.unset); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'changed', - ol.source.Stamen.prototype.changed); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'dispatchEvent', - ol.source.Stamen.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'getRevision', - ol.source.Stamen.prototype.getRevision); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'on', - ol.source.Stamen.prototype.on); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'once', - ol.source.Stamen.prototype.once); - -goog.exportProperty( - ol.source.Stamen.prototype, - 'un', - ol.source.Stamen.prototype.un); - -goog.exportProperty( - ol.source.Tile.Event.prototype, - 'type', - ol.source.Tile.Event.prototype.type); - -goog.exportProperty( - ol.source.Tile.Event.prototype, - 'target', - ol.source.Tile.Event.prototype.target); - -goog.exportProperty( - ol.source.Tile.Event.prototype, - 'preventDefault', - ol.source.Tile.Event.prototype.preventDefault); - -goog.exportProperty( - ol.source.Tile.Event.prototype, - 'stopPropagation', - ol.source.Tile.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setRenderReprojectionEdges', - ol.source.TileArcGISRest.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setTileGridForProjection', - ol.source.TileArcGISRest.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getTileLoadFunction', - ol.source.TileArcGISRest.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getTileUrlFunction', - ol.source.TileArcGISRest.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getUrls', - ol.source.TileArcGISRest.prototype.getUrls); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setTileLoadFunction', - ol.source.TileArcGISRest.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setTileUrlFunction', - ol.source.TileArcGISRest.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setUrl', - ol.source.TileArcGISRest.prototype.setUrl); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setUrls', - ol.source.TileArcGISRest.prototype.setUrls); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getTileGrid', - ol.source.TileArcGISRest.prototype.getTileGrid); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'refresh', - ol.source.TileArcGISRest.prototype.refresh); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getAttributions', - ol.source.TileArcGISRest.prototype.getAttributions); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getLogo', - ol.source.TileArcGISRest.prototype.getLogo); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getProjection', - ol.source.TileArcGISRest.prototype.getProjection); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getState', - ol.source.TileArcGISRest.prototype.getState); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setAttributions', - ol.source.TileArcGISRest.prototype.setAttributions); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'get', - ol.source.TileArcGISRest.prototype.get); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getKeys', - ol.source.TileArcGISRest.prototype.getKeys); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getProperties', - ol.source.TileArcGISRest.prototype.getProperties); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'set', - ol.source.TileArcGISRest.prototype.set); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'setProperties', - ol.source.TileArcGISRest.prototype.setProperties); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'unset', - ol.source.TileArcGISRest.prototype.unset); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'changed', - ol.source.TileArcGISRest.prototype.changed); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'dispatchEvent', - ol.source.TileArcGISRest.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'getRevision', - ol.source.TileArcGISRest.prototype.getRevision); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'on', - ol.source.TileArcGISRest.prototype.on); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'once', - ol.source.TileArcGISRest.prototype.once); - -goog.exportProperty( - ol.source.TileArcGISRest.prototype, - 'un', - ol.source.TileArcGISRest.prototype.un); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getTileGrid', - ol.source.TileDebug.prototype.getTileGrid); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'refresh', - ol.source.TileDebug.prototype.refresh); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getAttributions', - ol.source.TileDebug.prototype.getAttributions); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getLogo', - ol.source.TileDebug.prototype.getLogo); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getProjection', - ol.source.TileDebug.prototype.getProjection); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getState', - ol.source.TileDebug.prototype.getState); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'setAttributions', - ol.source.TileDebug.prototype.setAttributions); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'get', - ol.source.TileDebug.prototype.get); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getKeys', - ol.source.TileDebug.prototype.getKeys); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getProperties', - ol.source.TileDebug.prototype.getProperties); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'set', - ol.source.TileDebug.prototype.set); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'setProperties', - ol.source.TileDebug.prototype.setProperties); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'unset', - ol.source.TileDebug.prototype.unset); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'changed', - ol.source.TileDebug.prototype.changed); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'dispatchEvent', - ol.source.TileDebug.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'getRevision', - ol.source.TileDebug.prototype.getRevision); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'on', - ol.source.TileDebug.prototype.on); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'once', - ol.source.TileDebug.prototype.once); - -goog.exportProperty( - ol.source.TileDebug.prototype, - 'un', - ol.source.TileDebug.prototype.un); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setRenderReprojectionEdges', - ol.source.TileJSON.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setTileGridForProjection', - ol.source.TileJSON.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getTileLoadFunction', - ol.source.TileJSON.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getTileUrlFunction', - ol.source.TileJSON.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getUrls', - ol.source.TileJSON.prototype.getUrls); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setTileLoadFunction', - ol.source.TileJSON.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setTileUrlFunction', - ol.source.TileJSON.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setUrl', - ol.source.TileJSON.prototype.setUrl); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setUrls', - ol.source.TileJSON.prototype.setUrls); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getTileGrid', - ol.source.TileJSON.prototype.getTileGrid); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'refresh', - ol.source.TileJSON.prototype.refresh); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getAttributions', - ol.source.TileJSON.prototype.getAttributions); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getLogo', - ol.source.TileJSON.prototype.getLogo); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getProjection', - ol.source.TileJSON.prototype.getProjection); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getState', - ol.source.TileJSON.prototype.getState); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setAttributions', - ol.source.TileJSON.prototype.setAttributions); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'get', - ol.source.TileJSON.prototype.get); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getKeys', - ol.source.TileJSON.prototype.getKeys); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getProperties', - ol.source.TileJSON.prototype.getProperties); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'set', - ol.source.TileJSON.prototype.set); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'setProperties', - ol.source.TileJSON.prototype.setProperties); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'unset', - ol.source.TileJSON.prototype.unset); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'changed', - ol.source.TileJSON.prototype.changed); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'dispatchEvent', - ol.source.TileJSON.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'getRevision', - ol.source.TileJSON.prototype.getRevision); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'on', - ol.source.TileJSON.prototype.on); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'once', - ol.source.TileJSON.prototype.once); - -goog.exportProperty( - ol.source.TileJSON.prototype, - 'un', - ol.source.TileJSON.prototype.un); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getTileGrid', - ol.source.TileUTFGrid.prototype.getTileGrid); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'refresh', - ol.source.TileUTFGrid.prototype.refresh); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getAttributions', - ol.source.TileUTFGrid.prototype.getAttributions); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getLogo', - ol.source.TileUTFGrid.prototype.getLogo); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getProjection', - ol.source.TileUTFGrid.prototype.getProjection); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getState', - ol.source.TileUTFGrid.prototype.getState); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'setAttributions', - ol.source.TileUTFGrid.prototype.setAttributions); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'get', - ol.source.TileUTFGrid.prototype.get); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getKeys', - ol.source.TileUTFGrid.prototype.getKeys); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getProperties', - ol.source.TileUTFGrid.prototype.getProperties); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'set', - ol.source.TileUTFGrid.prototype.set); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'setProperties', - ol.source.TileUTFGrid.prototype.setProperties); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'unset', - ol.source.TileUTFGrid.prototype.unset); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'changed', - ol.source.TileUTFGrid.prototype.changed); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'dispatchEvent', - ol.source.TileUTFGrid.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'getRevision', - ol.source.TileUTFGrid.prototype.getRevision); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'on', - ol.source.TileUTFGrid.prototype.on); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'once', - ol.source.TileUTFGrid.prototype.once); - -goog.exportProperty( - ol.source.TileUTFGrid.prototype, - 'un', - ol.source.TileUTFGrid.prototype.un); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setRenderReprojectionEdges', - ol.source.TileWMS.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setTileGridForProjection', - ol.source.TileWMS.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getTileLoadFunction', - ol.source.TileWMS.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getTileUrlFunction', - ol.source.TileWMS.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getUrls', - ol.source.TileWMS.prototype.getUrls); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setTileLoadFunction', - ol.source.TileWMS.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setTileUrlFunction', - ol.source.TileWMS.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setUrl', - ol.source.TileWMS.prototype.setUrl); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setUrls', - ol.source.TileWMS.prototype.setUrls); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getTileGrid', - ol.source.TileWMS.prototype.getTileGrid); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'refresh', - ol.source.TileWMS.prototype.refresh); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getAttributions', - ol.source.TileWMS.prototype.getAttributions); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getLogo', - ol.source.TileWMS.prototype.getLogo); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getProjection', - ol.source.TileWMS.prototype.getProjection); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getState', - ol.source.TileWMS.prototype.getState); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setAttributions', - ol.source.TileWMS.prototype.setAttributions); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'get', - ol.source.TileWMS.prototype.get); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getKeys', - ol.source.TileWMS.prototype.getKeys); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getProperties', - ol.source.TileWMS.prototype.getProperties); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'set', - ol.source.TileWMS.prototype.set); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'setProperties', - ol.source.TileWMS.prototype.setProperties); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'unset', - ol.source.TileWMS.prototype.unset); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'changed', - ol.source.TileWMS.prototype.changed); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'dispatchEvent', - ol.source.TileWMS.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'getRevision', - ol.source.TileWMS.prototype.getRevision); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'on', - ol.source.TileWMS.prototype.on); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'once', - ol.source.TileWMS.prototype.once); - -goog.exportProperty( - ol.source.TileWMS.prototype, - 'un', - ol.source.TileWMS.prototype.un); - -goog.exportProperty( - ol.source.Vector.Event.prototype, - 'type', - ol.source.Vector.Event.prototype.type); - -goog.exportProperty( - ol.source.Vector.Event.prototype, - 'target', - ol.source.Vector.Event.prototype.target); - -goog.exportProperty( - ol.source.Vector.Event.prototype, - 'preventDefault', - ol.source.Vector.Event.prototype.preventDefault); - -goog.exportProperty( - ol.source.Vector.Event.prototype, - 'stopPropagation', - ol.source.Vector.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getTileLoadFunction', - ol.source.VectorTile.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getTileUrlFunction', - ol.source.VectorTile.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getUrls', - ol.source.VectorTile.prototype.getUrls); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'setTileLoadFunction', - ol.source.VectorTile.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'setTileUrlFunction', - ol.source.VectorTile.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'setUrl', - ol.source.VectorTile.prototype.setUrl); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'setUrls', - ol.source.VectorTile.prototype.setUrls); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getTileGrid', - ol.source.VectorTile.prototype.getTileGrid); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'refresh', - ol.source.VectorTile.prototype.refresh); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getAttributions', - ol.source.VectorTile.prototype.getAttributions); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getLogo', - ol.source.VectorTile.prototype.getLogo); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getProjection', - ol.source.VectorTile.prototype.getProjection); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getState', - ol.source.VectorTile.prototype.getState); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'setAttributions', - ol.source.VectorTile.prototype.setAttributions); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'get', - ol.source.VectorTile.prototype.get); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getKeys', - ol.source.VectorTile.prototype.getKeys); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getProperties', - ol.source.VectorTile.prototype.getProperties); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'set', - ol.source.VectorTile.prototype.set); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'setProperties', - ol.source.VectorTile.prototype.setProperties); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'unset', - ol.source.VectorTile.prototype.unset); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'changed', - ol.source.VectorTile.prototype.changed); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'dispatchEvent', - ol.source.VectorTile.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'getRevision', - ol.source.VectorTile.prototype.getRevision); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'on', - ol.source.VectorTile.prototype.on); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'once', - ol.source.VectorTile.prototype.once); - -goog.exportProperty( - ol.source.VectorTile.prototype, - 'un', - ol.source.VectorTile.prototype.un); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setRenderReprojectionEdges', - ol.source.WMTS.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setTileGridForProjection', - ol.source.WMTS.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getTileLoadFunction', - ol.source.WMTS.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getTileUrlFunction', - ol.source.WMTS.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getUrls', - ol.source.WMTS.prototype.getUrls); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setTileLoadFunction', - ol.source.WMTS.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setTileUrlFunction', - ol.source.WMTS.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setUrl', - ol.source.WMTS.prototype.setUrl); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setUrls', - ol.source.WMTS.prototype.setUrls); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getTileGrid', - ol.source.WMTS.prototype.getTileGrid); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'refresh', - ol.source.WMTS.prototype.refresh); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getAttributions', - ol.source.WMTS.prototype.getAttributions); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getLogo', - ol.source.WMTS.prototype.getLogo); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getProjection', - ol.source.WMTS.prototype.getProjection); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getState', - ol.source.WMTS.prototype.getState); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setAttributions', - ol.source.WMTS.prototype.setAttributions); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'get', - ol.source.WMTS.prototype.get); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getKeys', - ol.source.WMTS.prototype.getKeys); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getProperties', - ol.source.WMTS.prototype.getProperties); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'set', - ol.source.WMTS.prototype.set); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'setProperties', - ol.source.WMTS.prototype.setProperties); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'unset', - ol.source.WMTS.prototype.unset); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'changed', - ol.source.WMTS.prototype.changed); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'dispatchEvent', - ol.source.WMTS.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'getRevision', - ol.source.WMTS.prototype.getRevision); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'on', - ol.source.WMTS.prototype.on); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'once', - ol.source.WMTS.prototype.once); - -goog.exportProperty( - ol.source.WMTS.prototype, - 'un', - ol.source.WMTS.prototype.un); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setRenderReprojectionEdges', - ol.source.Zoomify.prototype.setRenderReprojectionEdges); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setTileGridForProjection', - ol.source.Zoomify.prototype.setTileGridForProjection); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getTileLoadFunction', - ol.source.Zoomify.prototype.getTileLoadFunction); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getTileUrlFunction', - ol.source.Zoomify.prototype.getTileUrlFunction); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getUrls', - ol.source.Zoomify.prototype.getUrls); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setTileLoadFunction', - ol.source.Zoomify.prototype.setTileLoadFunction); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setTileUrlFunction', - ol.source.Zoomify.prototype.setTileUrlFunction); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setUrl', - ol.source.Zoomify.prototype.setUrl); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setUrls', - ol.source.Zoomify.prototype.setUrls); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getTileGrid', - ol.source.Zoomify.prototype.getTileGrid); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'refresh', - ol.source.Zoomify.prototype.refresh); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getAttributions', - ol.source.Zoomify.prototype.getAttributions); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getLogo', - ol.source.Zoomify.prototype.getLogo); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getProjection', - ol.source.Zoomify.prototype.getProjection); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getState', - ol.source.Zoomify.prototype.getState); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setAttributions', - ol.source.Zoomify.prototype.setAttributions); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'get', - ol.source.Zoomify.prototype.get); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getKeys', - ol.source.Zoomify.prototype.getKeys); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getProperties', - ol.source.Zoomify.prototype.getProperties); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'set', - ol.source.Zoomify.prototype.set); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'setProperties', - ol.source.Zoomify.prototype.setProperties); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'unset', - ol.source.Zoomify.prototype.unset); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'changed', - ol.source.Zoomify.prototype.changed); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'dispatchEvent', - ol.source.Zoomify.prototype.dispatchEvent); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'getRevision', - ol.source.Zoomify.prototype.getRevision); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'on', - ol.source.Zoomify.prototype.on); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'once', - ol.source.Zoomify.prototype.once); - -goog.exportProperty( - ol.source.Zoomify.prototype, - 'un', - ol.source.Zoomify.prototype.un); - -goog.exportProperty( - ol.reproj.Tile.prototype, - 'getTileCoord', - ol.reproj.Tile.prototype.getTileCoord); - -goog.exportProperty( - ol.reproj.Tile.prototype, - 'load', - ol.reproj.Tile.prototype.load); - -goog.exportProperty( - ol.renderer.Layer.prototype, - 'changed', - ol.renderer.Layer.prototype.changed); - -goog.exportProperty( - ol.renderer.Layer.prototype, - 'dispatchEvent', - ol.renderer.Layer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.Layer.prototype, - 'getRevision', - ol.renderer.Layer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.Layer.prototype, - 'on', - ol.renderer.Layer.prototype.on); - -goog.exportProperty( - ol.renderer.Layer.prototype, - 'once', - ol.renderer.Layer.prototype.once); - -goog.exportProperty( - ol.renderer.Layer.prototype, - 'un', - ol.renderer.Layer.prototype.un); - -goog.exportProperty( - ol.renderer.webgl.Layer.prototype, - 'changed', - ol.renderer.webgl.Layer.prototype.changed); - -goog.exportProperty( - ol.renderer.webgl.Layer.prototype, - 'dispatchEvent', - ol.renderer.webgl.Layer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.webgl.Layer.prototype, - 'getRevision', - ol.renderer.webgl.Layer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.webgl.Layer.prototype, - 'on', - ol.renderer.webgl.Layer.prototype.on); - -goog.exportProperty( - ol.renderer.webgl.Layer.prototype, - 'once', - ol.renderer.webgl.Layer.prototype.once); - -goog.exportProperty( - ol.renderer.webgl.Layer.prototype, - 'un', - ol.renderer.webgl.Layer.prototype.un); - -goog.exportProperty( - ol.renderer.webgl.ImageLayer.prototype, - 'changed', - ol.renderer.webgl.ImageLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.webgl.ImageLayer.prototype, - 'dispatchEvent', - ol.renderer.webgl.ImageLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.webgl.ImageLayer.prototype, - 'getRevision', - ol.renderer.webgl.ImageLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.webgl.ImageLayer.prototype, - 'on', - ol.renderer.webgl.ImageLayer.prototype.on); - -goog.exportProperty( - ol.renderer.webgl.ImageLayer.prototype, - 'once', - ol.renderer.webgl.ImageLayer.prototype.once); - -goog.exportProperty( - ol.renderer.webgl.ImageLayer.prototype, - 'un', - ol.renderer.webgl.ImageLayer.prototype.un); - -goog.exportProperty( - ol.renderer.webgl.TileLayer.prototype, - 'changed', - ol.renderer.webgl.TileLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.webgl.TileLayer.prototype, - 'dispatchEvent', - ol.renderer.webgl.TileLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.webgl.TileLayer.prototype, - 'getRevision', - ol.renderer.webgl.TileLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.webgl.TileLayer.prototype, - 'on', - ol.renderer.webgl.TileLayer.prototype.on); - -goog.exportProperty( - ol.renderer.webgl.TileLayer.prototype, - 'once', - ol.renderer.webgl.TileLayer.prototype.once); - -goog.exportProperty( - ol.renderer.webgl.TileLayer.prototype, - 'un', - ol.renderer.webgl.TileLayer.prototype.un); - -goog.exportProperty( - ol.renderer.webgl.VectorLayer.prototype, - 'changed', - ol.renderer.webgl.VectorLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.webgl.VectorLayer.prototype, - 'dispatchEvent', - ol.renderer.webgl.VectorLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.webgl.VectorLayer.prototype, - 'getRevision', - ol.renderer.webgl.VectorLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.webgl.VectorLayer.prototype, - 'on', - ol.renderer.webgl.VectorLayer.prototype.on); - -goog.exportProperty( - ol.renderer.webgl.VectorLayer.prototype, - 'once', - ol.renderer.webgl.VectorLayer.prototype.once); - -goog.exportProperty( - ol.renderer.webgl.VectorLayer.prototype, - 'un', - ol.renderer.webgl.VectorLayer.prototype.un); - -goog.exportProperty( - ol.renderer.canvas.Layer.prototype, - 'changed', - ol.renderer.canvas.Layer.prototype.changed); - -goog.exportProperty( - ol.renderer.canvas.Layer.prototype, - 'dispatchEvent', - ol.renderer.canvas.Layer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.canvas.Layer.prototype, - 'getRevision', - ol.renderer.canvas.Layer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.canvas.Layer.prototype, - 'on', - ol.renderer.canvas.Layer.prototype.on); - -goog.exportProperty( - ol.renderer.canvas.Layer.prototype, - 'once', - ol.renderer.canvas.Layer.prototype.once); - -goog.exportProperty( - ol.renderer.canvas.Layer.prototype, - 'un', - ol.renderer.canvas.Layer.prototype.un); - -goog.exportProperty( - ol.renderer.canvas.IntermediateCanvas.prototype, - 'changed', - ol.renderer.canvas.IntermediateCanvas.prototype.changed); - -goog.exportProperty( - ol.renderer.canvas.IntermediateCanvas.prototype, - 'dispatchEvent', - ol.renderer.canvas.IntermediateCanvas.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.canvas.IntermediateCanvas.prototype, - 'getRevision', - ol.renderer.canvas.IntermediateCanvas.prototype.getRevision); - -goog.exportProperty( - ol.renderer.canvas.IntermediateCanvas.prototype, - 'on', - ol.renderer.canvas.IntermediateCanvas.prototype.on); - -goog.exportProperty( - ol.renderer.canvas.IntermediateCanvas.prototype, - 'once', - ol.renderer.canvas.IntermediateCanvas.prototype.once); - -goog.exportProperty( - ol.renderer.canvas.IntermediateCanvas.prototype, - 'un', - ol.renderer.canvas.IntermediateCanvas.prototype.un); - -goog.exportProperty( - ol.renderer.canvas.ImageLayer.prototype, - 'changed', - ol.renderer.canvas.ImageLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.canvas.ImageLayer.prototype, - 'dispatchEvent', - ol.renderer.canvas.ImageLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.canvas.ImageLayer.prototype, - 'getRevision', - ol.renderer.canvas.ImageLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.canvas.ImageLayer.prototype, - 'on', - ol.renderer.canvas.ImageLayer.prototype.on); - -goog.exportProperty( - ol.renderer.canvas.ImageLayer.prototype, - 'once', - ol.renderer.canvas.ImageLayer.prototype.once); - -goog.exportProperty( - ol.renderer.canvas.ImageLayer.prototype, - 'un', - ol.renderer.canvas.ImageLayer.prototype.un); - -goog.exportProperty( - ol.renderer.canvas.TileLayer.prototype, - 'changed', - ol.renderer.canvas.TileLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.canvas.TileLayer.prototype, - 'dispatchEvent', - ol.renderer.canvas.TileLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.canvas.TileLayer.prototype, - 'getRevision', - ol.renderer.canvas.TileLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.canvas.TileLayer.prototype, - 'on', - ol.renderer.canvas.TileLayer.prototype.on); - -goog.exportProperty( - ol.renderer.canvas.TileLayer.prototype, - 'once', - ol.renderer.canvas.TileLayer.prototype.once); - -goog.exportProperty( - ol.renderer.canvas.TileLayer.prototype, - 'un', - ol.renderer.canvas.TileLayer.prototype.un); - -goog.exportProperty( - ol.renderer.canvas.VectorLayer.prototype, - 'changed', - ol.renderer.canvas.VectorLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.canvas.VectorLayer.prototype, - 'dispatchEvent', - ol.renderer.canvas.VectorLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.canvas.VectorLayer.prototype, - 'getRevision', - ol.renderer.canvas.VectorLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.canvas.VectorLayer.prototype, - 'on', - ol.renderer.canvas.VectorLayer.prototype.on); - -goog.exportProperty( - ol.renderer.canvas.VectorLayer.prototype, - 'once', - ol.renderer.canvas.VectorLayer.prototype.once); - -goog.exportProperty( - ol.renderer.canvas.VectorLayer.prototype, - 'un', - ol.renderer.canvas.VectorLayer.prototype.un); - -goog.exportProperty( - ol.renderer.canvas.VectorTileLayer.prototype, - 'changed', - ol.renderer.canvas.VectorTileLayer.prototype.changed); - -goog.exportProperty( - ol.renderer.canvas.VectorTileLayer.prototype, - 'dispatchEvent', - ol.renderer.canvas.VectorTileLayer.prototype.dispatchEvent); - -goog.exportProperty( - ol.renderer.canvas.VectorTileLayer.prototype, - 'getRevision', - ol.renderer.canvas.VectorTileLayer.prototype.getRevision); - -goog.exportProperty( - ol.renderer.canvas.VectorTileLayer.prototype, - 'on', - ol.renderer.canvas.VectorTileLayer.prototype.on); - -goog.exportProperty( - ol.renderer.canvas.VectorTileLayer.prototype, - 'once', - ol.renderer.canvas.VectorTileLayer.prototype.once); - -goog.exportProperty( - ol.renderer.canvas.VectorTileLayer.prototype, - 'un', - ol.renderer.canvas.VectorTileLayer.prototype.un); - -goog.exportProperty( - ol.render.Event.prototype, - 'type', - ol.render.Event.prototype.type); - -goog.exportProperty( - ol.render.Event.prototype, - 'target', - ol.render.Event.prototype.target); - -goog.exportProperty( - ol.render.Event.prototype, - 'preventDefault', - ol.render.Event.prototype.preventDefault); - -goog.exportProperty( - ol.render.Event.prototype, - 'stopPropagation', - ol.render.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.pointer.PointerEvent.prototype, - 'type', - ol.pointer.PointerEvent.prototype.type); - -goog.exportProperty( - ol.pointer.PointerEvent.prototype, - 'target', - ol.pointer.PointerEvent.prototype.target); - -goog.exportProperty( - ol.pointer.PointerEvent.prototype, - 'preventDefault', - ol.pointer.PointerEvent.prototype.preventDefault); - -goog.exportProperty( - ol.pointer.PointerEvent.prototype, - 'stopPropagation', - ol.pointer.PointerEvent.prototype.stopPropagation); - -goog.exportProperty( - ol.layer.Base.prototype, - 'get', - ol.layer.Base.prototype.get); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getKeys', - ol.layer.Base.prototype.getKeys); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getProperties', - ol.layer.Base.prototype.getProperties); - -goog.exportProperty( - ol.layer.Base.prototype, - 'set', - ol.layer.Base.prototype.set); - -goog.exportProperty( - ol.layer.Base.prototype, - 'setProperties', - ol.layer.Base.prototype.setProperties); - -goog.exportProperty( - ol.layer.Base.prototype, - 'unset', - ol.layer.Base.prototype.unset); - -goog.exportProperty( - ol.layer.Base.prototype, - 'changed', - ol.layer.Base.prototype.changed); - -goog.exportProperty( - ol.layer.Base.prototype, - 'dispatchEvent', - ol.layer.Base.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Base.prototype, - 'getRevision', - ol.layer.Base.prototype.getRevision); - -goog.exportProperty( - ol.layer.Base.prototype, - 'on', - ol.layer.Base.prototype.on); - -goog.exportProperty( - ol.layer.Base.prototype, - 'once', - ol.layer.Base.prototype.once); - -goog.exportProperty( - ol.layer.Base.prototype, - 'un', - ol.layer.Base.prototype.un); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getExtent', - ol.layer.Group.prototype.getExtent); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getMaxResolution', - ol.layer.Group.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getMinResolution', - ol.layer.Group.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getOpacity', - ol.layer.Group.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getVisible', - ol.layer.Group.prototype.getVisible); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getZIndex', - ol.layer.Group.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setExtent', - ol.layer.Group.prototype.setExtent); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setMaxResolution', - ol.layer.Group.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setMinResolution', - ol.layer.Group.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setOpacity', - ol.layer.Group.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setVisible', - ol.layer.Group.prototype.setVisible); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setZIndex', - ol.layer.Group.prototype.setZIndex); - -goog.exportProperty( - ol.layer.Group.prototype, - 'get', - ol.layer.Group.prototype.get); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getKeys', - ol.layer.Group.prototype.getKeys); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getProperties', - ol.layer.Group.prototype.getProperties); - -goog.exportProperty( - ol.layer.Group.prototype, - 'set', - ol.layer.Group.prototype.set); - -goog.exportProperty( - ol.layer.Group.prototype, - 'setProperties', - ol.layer.Group.prototype.setProperties); - -goog.exportProperty( - ol.layer.Group.prototype, - 'unset', - ol.layer.Group.prototype.unset); - -goog.exportProperty( - ol.layer.Group.prototype, - 'changed', - ol.layer.Group.prototype.changed); - -goog.exportProperty( - ol.layer.Group.prototype, - 'dispatchEvent', - ol.layer.Group.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Group.prototype, - 'getRevision', - ol.layer.Group.prototype.getRevision); - -goog.exportProperty( - ol.layer.Group.prototype, - 'on', - ol.layer.Group.prototype.on); - -goog.exportProperty( - ol.layer.Group.prototype, - 'once', - ol.layer.Group.prototype.once); - -goog.exportProperty( - ol.layer.Group.prototype, - 'un', - ol.layer.Group.prototype.un); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getExtent', - ol.layer.Layer.prototype.getExtent); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getMaxResolution', - ol.layer.Layer.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getMinResolution', - ol.layer.Layer.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getOpacity', - ol.layer.Layer.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getVisible', - ol.layer.Layer.prototype.getVisible); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getZIndex', - ol.layer.Layer.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setExtent', - ol.layer.Layer.prototype.setExtent); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setMaxResolution', - ol.layer.Layer.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setMinResolution', - ol.layer.Layer.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setOpacity', - ol.layer.Layer.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setVisible', - ol.layer.Layer.prototype.setVisible); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setZIndex', - ol.layer.Layer.prototype.setZIndex); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'get', - ol.layer.Layer.prototype.get); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getKeys', - ol.layer.Layer.prototype.getKeys); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getProperties', - ol.layer.Layer.prototype.getProperties); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'set', - ol.layer.Layer.prototype.set); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'setProperties', - ol.layer.Layer.prototype.setProperties); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'unset', - ol.layer.Layer.prototype.unset); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'changed', - ol.layer.Layer.prototype.changed); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'dispatchEvent', - ol.layer.Layer.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'getRevision', - ol.layer.Layer.prototype.getRevision); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'on', - ol.layer.Layer.prototype.on); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'once', - ol.layer.Layer.prototype.once); - -goog.exportProperty( - ol.layer.Layer.prototype, - 'un', - ol.layer.Layer.prototype.un); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setMap', - ol.layer.Vector.prototype.setMap); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setSource', - ol.layer.Vector.prototype.setSource); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getExtent', - ol.layer.Vector.prototype.getExtent); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getMaxResolution', - ol.layer.Vector.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getMinResolution', - ol.layer.Vector.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getOpacity', - ol.layer.Vector.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getVisible', - ol.layer.Vector.prototype.getVisible); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getZIndex', - ol.layer.Vector.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setExtent', - ol.layer.Vector.prototype.setExtent); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setMaxResolution', - ol.layer.Vector.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setMinResolution', - ol.layer.Vector.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setOpacity', - ol.layer.Vector.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setVisible', - ol.layer.Vector.prototype.setVisible); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setZIndex', - ol.layer.Vector.prototype.setZIndex); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'get', - ol.layer.Vector.prototype.get); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getKeys', - ol.layer.Vector.prototype.getKeys); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getProperties', - ol.layer.Vector.prototype.getProperties); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'set', - ol.layer.Vector.prototype.set); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'setProperties', - ol.layer.Vector.prototype.setProperties); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'unset', - ol.layer.Vector.prototype.unset); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'changed', - ol.layer.Vector.prototype.changed); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'dispatchEvent', - ol.layer.Vector.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'getRevision', - ol.layer.Vector.prototype.getRevision); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'on', - ol.layer.Vector.prototype.on); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'once', - ol.layer.Vector.prototype.once); - -goog.exportProperty( - ol.layer.Vector.prototype, - 'un', - ol.layer.Vector.prototype.un); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getSource', - ol.layer.Heatmap.prototype.getSource); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getStyle', - ol.layer.Heatmap.prototype.getStyle); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getStyleFunction', - ol.layer.Heatmap.prototype.getStyleFunction); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setStyle', - ol.layer.Heatmap.prototype.setStyle); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setMap', - ol.layer.Heatmap.prototype.setMap); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setSource', - ol.layer.Heatmap.prototype.setSource); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getExtent', - ol.layer.Heatmap.prototype.getExtent); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getMaxResolution', - ol.layer.Heatmap.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getMinResolution', - ol.layer.Heatmap.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getOpacity', - ol.layer.Heatmap.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getVisible', - ol.layer.Heatmap.prototype.getVisible); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getZIndex', - ol.layer.Heatmap.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setExtent', - ol.layer.Heatmap.prototype.setExtent); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setMaxResolution', - ol.layer.Heatmap.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setMinResolution', - ol.layer.Heatmap.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setOpacity', - ol.layer.Heatmap.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setVisible', - ol.layer.Heatmap.prototype.setVisible); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setZIndex', - ol.layer.Heatmap.prototype.setZIndex); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'get', - ol.layer.Heatmap.prototype.get); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getKeys', - ol.layer.Heatmap.prototype.getKeys); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getProperties', - ol.layer.Heatmap.prototype.getProperties); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'set', - ol.layer.Heatmap.prototype.set); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'setProperties', - ol.layer.Heatmap.prototype.setProperties); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'unset', - ol.layer.Heatmap.prototype.unset); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'changed', - ol.layer.Heatmap.prototype.changed); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'dispatchEvent', - ol.layer.Heatmap.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'getRevision', - ol.layer.Heatmap.prototype.getRevision); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'on', - ol.layer.Heatmap.prototype.on); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'once', - ol.layer.Heatmap.prototype.once); - -goog.exportProperty( - ol.layer.Heatmap.prototype, - 'un', - ol.layer.Heatmap.prototype.un); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setMap', - ol.layer.Image.prototype.setMap); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setSource', - ol.layer.Image.prototype.setSource); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getExtent', - ol.layer.Image.prototype.getExtent); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getMaxResolution', - ol.layer.Image.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getMinResolution', - ol.layer.Image.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getOpacity', - ol.layer.Image.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getVisible', - ol.layer.Image.prototype.getVisible); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getZIndex', - ol.layer.Image.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setExtent', - ol.layer.Image.prototype.setExtent); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setMaxResolution', - ol.layer.Image.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setMinResolution', - ol.layer.Image.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setOpacity', - ol.layer.Image.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setVisible', - ol.layer.Image.prototype.setVisible); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setZIndex', - ol.layer.Image.prototype.setZIndex); - -goog.exportProperty( - ol.layer.Image.prototype, - 'get', - ol.layer.Image.prototype.get); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getKeys', - ol.layer.Image.prototype.getKeys); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getProperties', - ol.layer.Image.prototype.getProperties); - -goog.exportProperty( - ol.layer.Image.prototype, - 'set', - ol.layer.Image.prototype.set); - -goog.exportProperty( - ol.layer.Image.prototype, - 'setProperties', - ol.layer.Image.prototype.setProperties); - -goog.exportProperty( - ol.layer.Image.prototype, - 'unset', - ol.layer.Image.prototype.unset); - -goog.exportProperty( - ol.layer.Image.prototype, - 'changed', - ol.layer.Image.prototype.changed); - -goog.exportProperty( - ol.layer.Image.prototype, - 'dispatchEvent', - ol.layer.Image.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Image.prototype, - 'getRevision', - ol.layer.Image.prototype.getRevision); - -goog.exportProperty( - ol.layer.Image.prototype, - 'on', - ol.layer.Image.prototype.on); - -goog.exportProperty( - ol.layer.Image.prototype, - 'once', - ol.layer.Image.prototype.once); - -goog.exportProperty( - ol.layer.Image.prototype, - 'un', - ol.layer.Image.prototype.un); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setMap', - ol.layer.Tile.prototype.setMap); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setSource', - ol.layer.Tile.prototype.setSource); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getExtent', - ol.layer.Tile.prototype.getExtent); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getMaxResolution', - ol.layer.Tile.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getMinResolution', - ol.layer.Tile.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getOpacity', - ol.layer.Tile.prototype.getOpacity); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getVisible', - ol.layer.Tile.prototype.getVisible); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getZIndex', - ol.layer.Tile.prototype.getZIndex); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setExtent', - ol.layer.Tile.prototype.setExtent); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setMaxResolution', - ol.layer.Tile.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setMinResolution', - ol.layer.Tile.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setOpacity', - ol.layer.Tile.prototype.setOpacity); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setVisible', - ol.layer.Tile.prototype.setVisible); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setZIndex', - ol.layer.Tile.prototype.setZIndex); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'get', - ol.layer.Tile.prototype.get); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getKeys', - ol.layer.Tile.prototype.getKeys); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getProperties', - ol.layer.Tile.prototype.getProperties); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'set', - ol.layer.Tile.prototype.set); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'setProperties', - ol.layer.Tile.prototype.setProperties); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'unset', - ol.layer.Tile.prototype.unset); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'changed', - ol.layer.Tile.prototype.changed); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'dispatchEvent', - ol.layer.Tile.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'getRevision', - ol.layer.Tile.prototype.getRevision); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'on', - ol.layer.Tile.prototype.on); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'once', - ol.layer.Tile.prototype.once); - -goog.exportProperty( - ol.layer.Tile.prototype, - 'un', - ol.layer.Tile.prototype.un); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getSource', - ol.layer.VectorTile.prototype.getSource); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getStyle', - ol.layer.VectorTile.prototype.getStyle); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getStyleFunction', - ol.layer.VectorTile.prototype.getStyleFunction); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setStyle', - ol.layer.VectorTile.prototype.setStyle); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setMap', - ol.layer.VectorTile.prototype.setMap); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setSource', - ol.layer.VectorTile.prototype.setSource); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getExtent', - ol.layer.VectorTile.prototype.getExtent); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getMaxResolution', - ol.layer.VectorTile.prototype.getMaxResolution); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getMinResolution', - ol.layer.VectorTile.prototype.getMinResolution); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getOpacity', - ol.layer.VectorTile.prototype.getOpacity); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getVisible', - ol.layer.VectorTile.prototype.getVisible); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getZIndex', - ol.layer.VectorTile.prototype.getZIndex); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setExtent', - ol.layer.VectorTile.prototype.setExtent); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setMaxResolution', - ol.layer.VectorTile.prototype.setMaxResolution); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setMinResolution', - ol.layer.VectorTile.prototype.setMinResolution); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setOpacity', - ol.layer.VectorTile.prototype.setOpacity); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setVisible', - ol.layer.VectorTile.prototype.setVisible); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setZIndex', - ol.layer.VectorTile.prototype.setZIndex); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'get', - ol.layer.VectorTile.prototype.get); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getKeys', - ol.layer.VectorTile.prototype.getKeys); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getProperties', - ol.layer.VectorTile.prototype.getProperties); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'set', - ol.layer.VectorTile.prototype.set); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'setProperties', - ol.layer.VectorTile.prototype.setProperties); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'unset', - ol.layer.VectorTile.prototype.unset); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'changed', - ol.layer.VectorTile.prototype.changed); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'dispatchEvent', - ol.layer.VectorTile.prototype.dispatchEvent); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'getRevision', - ol.layer.VectorTile.prototype.getRevision); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'on', - ol.layer.VectorTile.prototype.on); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'once', - ol.layer.VectorTile.prototype.once); - -goog.exportProperty( - ol.layer.VectorTile.prototype, - 'un', - ol.layer.VectorTile.prototype.un); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'get', - ol.interaction.Interaction.prototype.get); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'getKeys', - ol.interaction.Interaction.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'getProperties', - ol.interaction.Interaction.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'set', - ol.interaction.Interaction.prototype.set); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'setProperties', - ol.interaction.Interaction.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'unset', - ol.interaction.Interaction.prototype.unset); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'changed', - ol.interaction.Interaction.prototype.changed); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'dispatchEvent', - ol.interaction.Interaction.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'getRevision', - ol.interaction.Interaction.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'on', - ol.interaction.Interaction.prototype.on); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'once', - ol.interaction.Interaction.prototype.once); - -goog.exportProperty( - ol.interaction.Interaction.prototype, - 'un', - ol.interaction.Interaction.prototype.un); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'getActive', - ol.interaction.DoubleClickZoom.prototype.getActive); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'getMap', - ol.interaction.DoubleClickZoom.prototype.getMap); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'setActive', - ol.interaction.DoubleClickZoom.prototype.setActive); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'get', - ol.interaction.DoubleClickZoom.prototype.get); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'getKeys', - ol.interaction.DoubleClickZoom.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'getProperties', - ol.interaction.DoubleClickZoom.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'set', - ol.interaction.DoubleClickZoom.prototype.set); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'setProperties', - ol.interaction.DoubleClickZoom.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'unset', - ol.interaction.DoubleClickZoom.prototype.unset); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'changed', - ol.interaction.DoubleClickZoom.prototype.changed); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'dispatchEvent', - ol.interaction.DoubleClickZoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'getRevision', - ol.interaction.DoubleClickZoom.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'on', - ol.interaction.DoubleClickZoom.prototype.on); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'once', - ol.interaction.DoubleClickZoom.prototype.once); - -goog.exportProperty( - ol.interaction.DoubleClickZoom.prototype, - 'un', - ol.interaction.DoubleClickZoom.prototype.un); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'getActive', - ol.interaction.DragAndDrop.prototype.getActive); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'getMap', - ol.interaction.DragAndDrop.prototype.getMap); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'setActive', - ol.interaction.DragAndDrop.prototype.setActive); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'get', - ol.interaction.DragAndDrop.prototype.get); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'getKeys', - ol.interaction.DragAndDrop.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'getProperties', - ol.interaction.DragAndDrop.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'set', - ol.interaction.DragAndDrop.prototype.set); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'setProperties', - ol.interaction.DragAndDrop.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'unset', - ol.interaction.DragAndDrop.prototype.unset); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'changed', - ol.interaction.DragAndDrop.prototype.changed); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'dispatchEvent', - ol.interaction.DragAndDrop.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'getRevision', - ol.interaction.DragAndDrop.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'on', - ol.interaction.DragAndDrop.prototype.on); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'once', - ol.interaction.DragAndDrop.prototype.once); - -goog.exportProperty( - ol.interaction.DragAndDrop.prototype, - 'un', - ol.interaction.DragAndDrop.prototype.un); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'type', - ol.interaction.DragAndDrop.Event.prototype.type); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'target', - ol.interaction.DragAndDrop.Event.prototype.target); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'preventDefault', - ol.interaction.DragAndDrop.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.DragAndDrop.Event.prototype, - 'stopPropagation', - ol.interaction.DragAndDrop.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'getActive', - ol.interaction.Pointer.prototype.getActive); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'getMap', - ol.interaction.Pointer.prototype.getMap); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'setActive', - ol.interaction.Pointer.prototype.setActive); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'get', - ol.interaction.Pointer.prototype.get); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'getKeys', - ol.interaction.Pointer.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'getProperties', - ol.interaction.Pointer.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'set', - ol.interaction.Pointer.prototype.set); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'setProperties', - ol.interaction.Pointer.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'unset', - ol.interaction.Pointer.prototype.unset); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'changed', - ol.interaction.Pointer.prototype.changed); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'dispatchEvent', - ol.interaction.Pointer.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'getRevision', - ol.interaction.Pointer.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'on', - ol.interaction.Pointer.prototype.on); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'once', - ol.interaction.Pointer.prototype.once); - -goog.exportProperty( - ol.interaction.Pointer.prototype, - 'un', - ol.interaction.Pointer.prototype.un); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'getActive', - ol.interaction.DragBox.prototype.getActive); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'getMap', - ol.interaction.DragBox.prototype.getMap); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'setActive', - ol.interaction.DragBox.prototype.setActive); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'get', - ol.interaction.DragBox.prototype.get); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'getKeys', - ol.interaction.DragBox.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'getProperties', - ol.interaction.DragBox.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'set', - ol.interaction.DragBox.prototype.set); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'setProperties', - ol.interaction.DragBox.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'unset', - ol.interaction.DragBox.prototype.unset); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'changed', - ol.interaction.DragBox.prototype.changed); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'dispatchEvent', - ol.interaction.DragBox.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'getRevision', - ol.interaction.DragBox.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'on', - ol.interaction.DragBox.prototype.on); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'once', - ol.interaction.DragBox.prototype.once); - -goog.exportProperty( - ol.interaction.DragBox.prototype, - 'un', - ol.interaction.DragBox.prototype.un); - -goog.exportProperty( - ol.interaction.DragBox.Event.prototype, - 'type', - ol.interaction.DragBox.Event.prototype.type); - -goog.exportProperty( - ol.interaction.DragBox.Event.prototype, - 'target', - ol.interaction.DragBox.Event.prototype.target); - -goog.exportProperty( - ol.interaction.DragBox.Event.prototype, - 'preventDefault', - ol.interaction.DragBox.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.DragBox.Event.prototype, - 'stopPropagation', - ol.interaction.DragBox.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'getActive', - ol.interaction.DragPan.prototype.getActive); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'getMap', - ol.interaction.DragPan.prototype.getMap); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'setActive', - ol.interaction.DragPan.prototype.setActive); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'get', - ol.interaction.DragPan.prototype.get); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'getKeys', - ol.interaction.DragPan.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'getProperties', - ol.interaction.DragPan.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'set', - ol.interaction.DragPan.prototype.set); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'setProperties', - ol.interaction.DragPan.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'unset', - ol.interaction.DragPan.prototype.unset); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'changed', - ol.interaction.DragPan.prototype.changed); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'dispatchEvent', - ol.interaction.DragPan.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'getRevision', - ol.interaction.DragPan.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'on', - ol.interaction.DragPan.prototype.on); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'once', - ol.interaction.DragPan.prototype.once); - -goog.exportProperty( - ol.interaction.DragPan.prototype, - 'un', - ol.interaction.DragPan.prototype.un); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'getActive', - ol.interaction.DragRotate.prototype.getActive); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'getMap', - ol.interaction.DragRotate.prototype.getMap); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'setActive', - ol.interaction.DragRotate.prototype.setActive); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'get', - ol.interaction.DragRotate.prototype.get); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'getKeys', - ol.interaction.DragRotate.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'getProperties', - ol.interaction.DragRotate.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'set', - ol.interaction.DragRotate.prototype.set); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'setProperties', - ol.interaction.DragRotate.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'unset', - ol.interaction.DragRotate.prototype.unset); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'changed', - ol.interaction.DragRotate.prototype.changed); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'dispatchEvent', - ol.interaction.DragRotate.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'getRevision', - ol.interaction.DragRotate.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'on', - ol.interaction.DragRotate.prototype.on); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'once', - ol.interaction.DragRotate.prototype.once); - -goog.exportProperty( - ol.interaction.DragRotate.prototype, - 'un', - ol.interaction.DragRotate.prototype.un); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'getActive', - ol.interaction.DragRotateAndZoom.prototype.getActive); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'getMap', - ol.interaction.DragRotateAndZoom.prototype.getMap); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'setActive', - ol.interaction.DragRotateAndZoom.prototype.setActive); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'get', - ol.interaction.DragRotateAndZoom.prototype.get); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'getKeys', - ol.interaction.DragRotateAndZoom.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'getProperties', - ol.interaction.DragRotateAndZoom.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'set', - ol.interaction.DragRotateAndZoom.prototype.set); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'setProperties', - ol.interaction.DragRotateAndZoom.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'unset', - ol.interaction.DragRotateAndZoom.prototype.unset); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'changed', - ol.interaction.DragRotateAndZoom.prototype.changed); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'dispatchEvent', - ol.interaction.DragRotateAndZoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'getRevision', - ol.interaction.DragRotateAndZoom.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'on', - ol.interaction.DragRotateAndZoom.prototype.on); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'once', - ol.interaction.DragRotateAndZoom.prototype.once); - -goog.exportProperty( - ol.interaction.DragRotateAndZoom.prototype, - 'un', - ol.interaction.DragRotateAndZoom.prototype.un); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'getGeometry', - ol.interaction.DragZoom.prototype.getGeometry); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'getActive', - ol.interaction.DragZoom.prototype.getActive); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'getMap', - ol.interaction.DragZoom.prototype.getMap); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'setActive', - ol.interaction.DragZoom.prototype.setActive); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'get', - ol.interaction.DragZoom.prototype.get); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'getKeys', - ol.interaction.DragZoom.prototype.getKeys); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'getProperties', - ol.interaction.DragZoom.prototype.getProperties); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'set', - ol.interaction.DragZoom.prototype.set); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'setProperties', - ol.interaction.DragZoom.prototype.setProperties); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'unset', - ol.interaction.DragZoom.prototype.unset); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'changed', - ol.interaction.DragZoom.prototype.changed); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'dispatchEvent', - ol.interaction.DragZoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'getRevision', - ol.interaction.DragZoom.prototype.getRevision); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'on', - ol.interaction.DragZoom.prototype.on); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'once', - ol.interaction.DragZoom.prototype.once); - -goog.exportProperty( - ol.interaction.DragZoom.prototype, - 'un', - ol.interaction.DragZoom.prototype.un); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'getActive', - ol.interaction.Draw.prototype.getActive); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'getMap', - ol.interaction.Draw.prototype.getMap); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'setActive', - ol.interaction.Draw.prototype.setActive); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'get', - ol.interaction.Draw.prototype.get); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'getKeys', - ol.interaction.Draw.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'getProperties', - ol.interaction.Draw.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'set', - ol.interaction.Draw.prototype.set); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'setProperties', - ol.interaction.Draw.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'unset', - ol.interaction.Draw.prototype.unset); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'changed', - ol.interaction.Draw.prototype.changed); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'dispatchEvent', - ol.interaction.Draw.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'getRevision', - ol.interaction.Draw.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'on', - ol.interaction.Draw.prototype.on); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'once', - ol.interaction.Draw.prototype.once); - -goog.exportProperty( - ol.interaction.Draw.prototype, - 'un', - ol.interaction.Draw.prototype.un); - -goog.exportProperty( - ol.interaction.Draw.Event.prototype, - 'type', - ol.interaction.Draw.Event.prototype.type); - -goog.exportProperty( - ol.interaction.Draw.Event.prototype, - 'target', - ol.interaction.Draw.Event.prototype.target); - -goog.exportProperty( - ol.interaction.Draw.Event.prototype, - 'preventDefault', - ol.interaction.Draw.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.Draw.Event.prototype, - 'stopPropagation', - ol.interaction.Draw.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'getActive', - ol.interaction.Extent.prototype.getActive); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'getMap', - ol.interaction.Extent.prototype.getMap); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'setActive', - ol.interaction.Extent.prototype.setActive); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'get', - ol.interaction.Extent.prototype.get); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'getKeys', - ol.interaction.Extent.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'getProperties', - ol.interaction.Extent.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'set', - ol.interaction.Extent.prototype.set); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'setProperties', - ol.interaction.Extent.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'unset', - ol.interaction.Extent.prototype.unset); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'changed', - ol.interaction.Extent.prototype.changed); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'dispatchEvent', - ol.interaction.Extent.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'getRevision', - ol.interaction.Extent.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'on', - ol.interaction.Extent.prototype.on); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'once', - ol.interaction.Extent.prototype.once); - -goog.exportProperty( - ol.interaction.Extent.prototype, - 'un', - ol.interaction.Extent.prototype.un); - -goog.exportProperty( - ol.interaction.Extent.Event.prototype, - 'type', - ol.interaction.Extent.Event.prototype.type); - -goog.exportProperty( - ol.interaction.Extent.Event.prototype, - 'target', - ol.interaction.Extent.Event.prototype.target); - -goog.exportProperty( - ol.interaction.Extent.Event.prototype, - 'preventDefault', - ol.interaction.Extent.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.Extent.Event.prototype, - 'stopPropagation', - ol.interaction.Extent.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'getActive', - ol.interaction.KeyboardPan.prototype.getActive); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'getMap', - ol.interaction.KeyboardPan.prototype.getMap); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'setActive', - ol.interaction.KeyboardPan.prototype.setActive); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'get', - ol.interaction.KeyboardPan.prototype.get); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'getKeys', - ol.interaction.KeyboardPan.prototype.getKeys); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'getProperties', - ol.interaction.KeyboardPan.prototype.getProperties); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'set', - ol.interaction.KeyboardPan.prototype.set); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'setProperties', - ol.interaction.KeyboardPan.prototype.setProperties); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'unset', - ol.interaction.KeyboardPan.prototype.unset); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'changed', - ol.interaction.KeyboardPan.prototype.changed); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'dispatchEvent', - ol.interaction.KeyboardPan.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'getRevision', - ol.interaction.KeyboardPan.prototype.getRevision); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'on', - ol.interaction.KeyboardPan.prototype.on); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'once', - ol.interaction.KeyboardPan.prototype.once); - -goog.exportProperty( - ol.interaction.KeyboardPan.prototype, - 'un', - ol.interaction.KeyboardPan.prototype.un); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'getActive', - ol.interaction.KeyboardZoom.prototype.getActive); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'getMap', - ol.interaction.KeyboardZoom.prototype.getMap); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'setActive', - ol.interaction.KeyboardZoom.prototype.setActive); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'get', - ol.interaction.KeyboardZoom.prototype.get); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'getKeys', - ol.interaction.KeyboardZoom.prototype.getKeys); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'getProperties', - ol.interaction.KeyboardZoom.prototype.getProperties); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'set', - ol.interaction.KeyboardZoom.prototype.set); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'setProperties', - ol.interaction.KeyboardZoom.prototype.setProperties); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'unset', - ol.interaction.KeyboardZoom.prototype.unset); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'changed', - ol.interaction.KeyboardZoom.prototype.changed); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'dispatchEvent', - ol.interaction.KeyboardZoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'getRevision', - ol.interaction.KeyboardZoom.prototype.getRevision); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'on', - ol.interaction.KeyboardZoom.prototype.on); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'once', - ol.interaction.KeyboardZoom.prototype.once); - -goog.exportProperty( - ol.interaction.KeyboardZoom.prototype, - 'un', - ol.interaction.KeyboardZoom.prototype.un); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'getActive', - ol.interaction.Modify.prototype.getActive); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'getMap', - ol.interaction.Modify.prototype.getMap); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'setActive', - ol.interaction.Modify.prototype.setActive); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'get', - ol.interaction.Modify.prototype.get); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'getKeys', - ol.interaction.Modify.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'getProperties', - ol.interaction.Modify.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'set', - ol.interaction.Modify.prototype.set); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'setProperties', - ol.interaction.Modify.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'unset', - ol.interaction.Modify.prototype.unset); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'changed', - ol.interaction.Modify.prototype.changed); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'dispatchEvent', - ol.interaction.Modify.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'getRevision', - ol.interaction.Modify.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'on', - ol.interaction.Modify.prototype.on); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'once', - ol.interaction.Modify.prototype.once); - -goog.exportProperty( - ol.interaction.Modify.prototype, - 'un', - ol.interaction.Modify.prototype.un); - -goog.exportProperty( - ol.interaction.Modify.Event.prototype, - 'type', - ol.interaction.Modify.Event.prototype.type); - -goog.exportProperty( - ol.interaction.Modify.Event.prototype, - 'target', - ol.interaction.Modify.Event.prototype.target); - -goog.exportProperty( - ol.interaction.Modify.Event.prototype, - 'preventDefault', - ol.interaction.Modify.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.Modify.Event.prototype, - 'stopPropagation', - ol.interaction.Modify.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'getActive', - ol.interaction.MouseWheelZoom.prototype.getActive); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'getMap', - ol.interaction.MouseWheelZoom.prototype.getMap); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'setActive', - ol.interaction.MouseWheelZoom.prototype.setActive); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'get', - ol.interaction.MouseWheelZoom.prototype.get); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'getKeys', - ol.interaction.MouseWheelZoom.prototype.getKeys); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'getProperties', - ol.interaction.MouseWheelZoom.prototype.getProperties); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'set', - ol.interaction.MouseWheelZoom.prototype.set); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'setProperties', - ol.interaction.MouseWheelZoom.prototype.setProperties); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'unset', - ol.interaction.MouseWheelZoom.prototype.unset); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'changed', - ol.interaction.MouseWheelZoom.prototype.changed); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'dispatchEvent', - ol.interaction.MouseWheelZoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'getRevision', - ol.interaction.MouseWheelZoom.prototype.getRevision); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'on', - ol.interaction.MouseWheelZoom.prototype.on); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'once', - ol.interaction.MouseWheelZoom.prototype.once); - -goog.exportProperty( - ol.interaction.MouseWheelZoom.prototype, - 'un', - ol.interaction.MouseWheelZoom.prototype.un); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'getActive', - ol.interaction.PinchRotate.prototype.getActive); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'getMap', - ol.interaction.PinchRotate.prototype.getMap); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'setActive', - ol.interaction.PinchRotate.prototype.setActive); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'get', - ol.interaction.PinchRotate.prototype.get); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'getKeys', - ol.interaction.PinchRotate.prototype.getKeys); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'getProperties', - ol.interaction.PinchRotate.prototype.getProperties); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'set', - ol.interaction.PinchRotate.prototype.set); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'setProperties', - ol.interaction.PinchRotate.prototype.setProperties); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'unset', - ol.interaction.PinchRotate.prototype.unset); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'changed', - ol.interaction.PinchRotate.prototype.changed); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'dispatchEvent', - ol.interaction.PinchRotate.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'getRevision', - ol.interaction.PinchRotate.prototype.getRevision); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'on', - ol.interaction.PinchRotate.prototype.on); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'once', - ol.interaction.PinchRotate.prototype.once); - -goog.exportProperty( - ol.interaction.PinchRotate.prototype, - 'un', - ol.interaction.PinchRotate.prototype.un); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'getActive', - ol.interaction.PinchZoom.prototype.getActive); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'getMap', - ol.interaction.PinchZoom.prototype.getMap); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'setActive', - ol.interaction.PinchZoom.prototype.setActive); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'get', - ol.interaction.PinchZoom.prototype.get); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'getKeys', - ol.interaction.PinchZoom.prototype.getKeys); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'getProperties', - ol.interaction.PinchZoom.prototype.getProperties); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'set', - ol.interaction.PinchZoom.prototype.set); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'setProperties', - ol.interaction.PinchZoom.prototype.setProperties); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'unset', - ol.interaction.PinchZoom.prototype.unset); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'changed', - ol.interaction.PinchZoom.prototype.changed); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'dispatchEvent', - ol.interaction.PinchZoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'getRevision', - ol.interaction.PinchZoom.prototype.getRevision); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'on', - ol.interaction.PinchZoom.prototype.on); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'once', - ol.interaction.PinchZoom.prototype.once); - -goog.exportProperty( - ol.interaction.PinchZoom.prototype, - 'un', - ol.interaction.PinchZoom.prototype.un); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getActive', - ol.interaction.Select.prototype.getActive); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getMap', - ol.interaction.Select.prototype.getMap); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'setActive', - ol.interaction.Select.prototype.setActive); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'get', - ol.interaction.Select.prototype.get); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getKeys', - ol.interaction.Select.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getProperties', - ol.interaction.Select.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'set', - ol.interaction.Select.prototype.set); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'setProperties', - ol.interaction.Select.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'unset', - ol.interaction.Select.prototype.unset); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'changed', - ol.interaction.Select.prototype.changed); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'dispatchEvent', - ol.interaction.Select.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'getRevision', - ol.interaction.Select.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'on', - ol.interaction.Select.prototype.on); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'once', - ol.interaction.Select.prototype.once); - -goog.exportProperty( - ol.interaction.Select.prototype, - 'un', - ol.interaction.Select.prototype.un); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'type', - ol.interaction.Select.Event.prototype.type); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'target', - ol.interaction.Select.Event.prototype.target); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'preventDefault', - ol.interaction.Select.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.Select.Event.prototype, - 'stopPropagation', - ol.interaction.Select.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'getActive', - ol.interaction.Snap.prototype.getActive); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'getMap', - ol.interaction.Snap.prototype.getMap); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'setActive', - ol.interaction.Snap.prototype.setActive); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'get', - ol.interaction.Snap.prototype.get); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'getKeys', - ol.interaction.Snap.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'getProperties', - ol.interaction.Snap.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'set', - ol.interaction.Snap.prototype.set); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'setProperties', - ol.interaction.Snap.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'unset', - ol.interaction.Snap.prototype.unset); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'changed', - ol.interaction.Snap.prototype.changed); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'dispatchEvent', - ol.interaction.Snap.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'getRevision', - ol.interaction.Snap.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'on', - ol.interaction.Snap.prototype.on); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'once', - ol.interaction.Snap.prototype.once); - -goog.exportProperty( - ol.interaction.Snap.prototype, - 'un', - ol.interaction.Snap.prototype.un); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'getActive', - ol.interaction.Translate.prototype.getActive); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'getMap', - ol.interaction.Translate.prototype.getMap); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'setActive', - ol.interaction.Translate.prototype.setActive); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'get', - ol.interaction.Translate.prototype.get); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'getKeys', - ol.interaction.Translate.prototype.getKeys); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'getProperties', - ol.interaction.Translate.prototype.getProperties); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'set', - ol.interaction.Translate.prototype.set); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'setProperties', - ol.interaction.Translate.prototype.setProperties); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'unset', - ol.interaction.Translate.prototype.unset); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'changed', - ol.interaction.Translate.prototype.changed); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'dispatchEvent', - ol.interaction.Translate.prototype.dispatchEvent); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'getRevision', - ol.interaction.Translate.prototype.getRevision); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'on', - ol.interaction.Translate.prototype.on); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'once', - ol.interaction.Translate.prototype.once); - -goog.exportProperty( - ol.interaction.Translate.prototype, - 'un', - ol.interaction.Translate.prototype.un); - -goog.exportProperty( - ol.interaction.Translate.Event.prototype, - 'type', - ol.interaction.Translate.Event.prototype.type); - -goog.exportProperty( - ol.interaction.Translate.Event.prototype, - 'target', - ol.interaction.Translate.Event.prototype.target); - -goog.exportProperty( - ol.interaction.Translate.Event.prototype, - 'preventDefault', - ol.interaction.Translate.Event.prototype.preventDefault); - -goog.exportProperty( - ol.interaction.Translate.Event.prototype, - 'stopPropagation', - ol.interaction.Translate.Event.prototype.stopPropagation); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'get', - ol.geom.Geometry.prototype.get); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'getKeys', - ol.geom.Geometry.prototype.getKeys); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'getProperties', - ol.geom.Geometry.prototype.getProperties); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'set', - ol.geom.Geometry.prototype.set); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'setProperties', - ol.geom.Geometry.prototype.setProperties); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'unset', - ol.geom.Geometry.prototype.unset); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'changed', - ol.geom.Geometry.prototype.changed); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'dispatchEvent', - ol.geom.Geometry.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'getRevision', - ol.geom.Geometry.prototype.getRevision); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'on', - ol.geom.Geometry.prototype.on); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'once', - ol.geom.Geometry.prototype.once); - -goog.exportProperty( - ol.geom.Geometry.prototype, - 'un', - ol.geom.Geometry.prototype.un); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getClosestPoint', - ol.geom.SimpleGeometry.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'intersectsCoordinate', - ol.geom.SimpleGeometry.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getExtent', - ol.geom.SimpleGeometry.prototype.getExtent); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'rotate', - ol.geom.SimpleGeometry.prototype.rotate); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'scale', - ol.geom.SimpleGeometry.prototype.scale); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'simplify', - ol.geom.SimpleGeometry.prototype.simplify); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'transform', - ol.geom.SimpleGeometry.prototype.transform); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'get', - ol.geom.SimpleGeometry.prototype.get); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getKeys', - ol.geom.SimpleGeometry.prototype.getKeys); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getProperties', - ol.geom.SimpleGeometry.prototype.getProperties); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'set', - ol.geom.SimpleGeometry.prototype.set); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'setProperties', - ol.geom.SimpleGeometry.prototype.setProperties); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'unset', - ol.geom.SimpleGeometry.prototype.unset); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'changed', - ol.geom.SimpleGeometry.prototype.changed); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'dispatchEvent', - ol.geom.SimpleGeometry.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'getRevision', - ol.geom.SimpleGeometry.prototype.getRevision); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'on', - ol.geom.SimpleGeometry.prototype.on); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'once', - ol.geom.SimpleGeometry.prototype.once); - -goog.exportProperty( - ol.geom.SimpleGeometry.prototype, - 'un', - ol.geom.SimpleGeometry.prototype.un); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getFirstCoordinate', - ol.geom.Circle.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getLastCoordinate', - ol.geom.Circle.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getLayout', - ol.geom.Circle.prototype.getLayout); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'rotate', - ol.geom.Circle.prototype.rotate); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'scale', - ol.geom.Circle.prototype.scale); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getClosestPoint', - ol.geom.Circle.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'intersectsCoordinate', - ol.geom.Circle.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getExtent', - ol.geom.Circle.prototype.getExtent); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'simplify', - ol.geom.Circle.prototype.simplify); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'get', - ol.geom.Circle.prototype.get); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getKeys', - ol.geom.Circle.prototype.getKeys); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getProperties', - ol.geom.Circle.prototype.getProperties); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'set', - ol.geom.Circle.prototype.set); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'setProperties', - ol.geom.Circle.prototype.setProperties); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'unset', - ol.geom.Circle.prototype.unset); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'changed', - ol.geom.Circle.prototype.changed); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'dispatchEvent', - ol.geom.Circle.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'getRevision', - ol.geom.Circle.prototype.getRevision); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'on', - ol.geom.Circle.prototype.on); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'once', - ol.geom.Circle.prototype.once); - -goog.exportProperty( - ol.geom.Circle.prototype, - 'un', - ol.geom.Circle.prototype.un); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getClosestPoint', - ol.geom.GeometryCollection.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'intersectsCoordinate', - ol.geom.GeometryCollection.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getExtent', - ol.geom.GeometryCollection.prototype.getExtent); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'rotate', - ol.geom.GeometryCollection.prototype.rotate); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'scale', - ol.geom.GeometryCollection.prototype.scale); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'simplify', - ol.geom.GeometryCollection.prototype.simplify); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'transform', - ol.geom.GeometryCollection.prototype.transform); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'get', - ol.geom.GeometryCollection.prototype.get); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getKeys', - ol.geom.GeometryCollection.prototype.getKeys); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getProperties', - ol.geom.GeometryCollection.prototype.getProperties); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'set', - ol.geom.GeometryCollection.prototype.set); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'setProperties', - ol.geom.GeometryCollection.prototype.setProperties); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'unset', - ol.geom.GeometryCollection.prototype.unset); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'changed', - ol.geom.GeometryCollection.prototype.changed); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'dispatchEvent', - ol.geom.GeometryCollection.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'getRevision', - ol.geom.GeometryCollection.prototype.getRevision); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'on', - ol.geom.GeometryCollection.prototype.on); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'once', - ol.geom.GeometryCollection.prototype.once); - -goog.exportProperty( - ol.geom.GeometryCollection.prototype, - 'un', - ol.geom.GeometryCollection.prototype.un); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getFirstCoordinate', - ol.geom.LinearRing.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getLastCoordinate', - ol.geom.LinearRing.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getLayout', - ol.geom.LinearRing.prototype.getLayout); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'rotate', - ol.geom.LinearRing.prototype.rotate); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'scale', - ol.geom.LinearRing.prototype.scale); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getClosestPoint', - ol.geom.LinearRing.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'intersectsCoordinate', - ol.geom.LinearRing.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getExtent', - ol.geom.LinearRing.prototype.getExtent); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'simplify', - ol.geom.LinearRing.prototype.simplify); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'transform', - ol.geom.LinearRing.prototype.transform); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'get', - ol.geom.LinearRing.prototype.get); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getKeys', - ol.geom.LinearRing.prototype.getKeys); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getProperties', - ol.geom.LinearRing.prototype.getProperties); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'set', - ol.geom.LinearRing.prototype.set); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'setProperties', - ol.geom.LinearRing.prototype.setProperties); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'unset', - ol.geom.LinearRing.prototype.unset); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'changed', - ol.geom.LinearRing.prototype.changed); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'dispatchEvent', - ol.geom.LinearRing.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'getRevision', - ol.geom.LinearRing.prototype.getRevision); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'on', - ol.geom.LinearRing.prototype.on); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'once', - ol.geom.LinearRing.prototype.once); - -goog.exportProperty( - ol.geom.LinearRing.prototype, - 'un', - ol.geom.LinearRing.prototype.un); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getFirstCoordinate', - ol.geom.LineString.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getLastCoordinate', - ol.geom.LineString.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getLayout', - ol.geom.LineString.prototype.getLayout); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'rotate', - ol.geom.LineString.prototype.rotate); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'scale', - ol.geom.LineString.prototype.scale); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getClosestPoint', - ol.geom.LineString.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'intersectsCoordinate', - ol.geom.LineString.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getExtent', - ol.geom.LineString.prototype.getExtent); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'simplify', - ol.geom.LineString.prototype.simplify); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'transform', - ol.geom.LineString.prototype.transform); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'get', - ol.geom.LineString.prototype.get); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getKeys', - ol.geom.LineString.prototype.getKeys); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getProperties', - ol.geom.LineString.prototype.getProperties); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'set', - ol.geom.LineString.prototype.set); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'setProperties', - ol.geom.LineString.prototype.setProperties); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'unset', - ol.geom.LineString.prototype.unset); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'changed', - ol.geom.LineString.prototype.changed); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'dispatchEvent', - ol.geom.LineString.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'getRevision', - ol.geom.LineString.prototype.getRevision); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'on', - ol.geom.LineString.prototype.on); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'once', - ol.geom.LineString.prototype.once); - -goog.exportProperty( - ol.geom.LineString.prototype, - 'un', - ol.geom.LineString.prototype.un); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getFirstCoordinate', - ol.geom.MultiLineString.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getLastCoordinate', - ol.geom.MultiLineString.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getLayout', - ol.geom.MultiLineString.prototype.getLayout); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'rotate', - ol.geom.MultiLineString.prototype.rotate); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'scale', - ol.geom.MultiLineString.prototype.scale); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getClosestPoint', - ol.geom.MultiLineString.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'intersectsCoordinate', - ol.geom.MultiLineString.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getExtent', - ol.geom.MultiLineString.prototype.getExtent); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'simplify', - ol.geom.MultiLineString.prototype.simplify); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'transform', - ol.geom.MultiLineString.prototype.transform); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'get', - ol.geom.MultiLineString.prototype.get); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getKeys', - ol.geom.MultiLineString.prototype.getKeys); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getProperties', - ol.geom.MultiLineString.prototype.getProperties); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'set', - ol.geom.MultiLineString.prototype.set); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'setProperties', - ol.geom.MultiLineString.prototype.setProperties); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'unset', - ol.geom.MultiLineString.prototype.unset); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'changed', - ol.geom.MultiLineString.prototype.changed); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'dispatchEvent', - ol.geom.MultiLineString.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'getRevision', - ol.geom.MultiLineString.prototype.getRevision); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'on', - ol.geom.MultiLineString.prototype.on); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'once', - ol.geom.MultiLineString.prototype.once); - -goog.exportProperty( - ol.geom.MultiLineString.prototype, - 'un', - ol.geom.MultiLineString.prototype.un); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getFirstCoordinate', - ol.geom.MultiPoint.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getLastCoordinate', - ol.geom.MultiPoint.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getLayout', - ol.geom.MultiPoint.prototype.getLayout); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'rotate', - ol.geom.MultiPoint.prototype.rotate); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'scale', - ol.geom.MultiPoint.prototype.scale); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getClosestPoint', - ol.geom.MultiPoint.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'intersectsCoordinate', - ol.geom.MultiPoint.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getExtent', - ol.geom.MultiPoint.prototype.getExtent); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'simplify', - ol.geom.MultiPoint.prototype.simplify); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'transform', - ol.geom.MultiPoint.prototype.transform); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'get', - ol.geom.MultiPoint.prototype.get); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getKeys', - ol.geom.MultiPoint.prototype.getKeys); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getProperties', - ol.geom.MultiPoint.prototype.getProperties); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'set', - ol.geom.MultiPoint.prototype.set); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'setProperties', - ol.geom.MultiPoint.prototype.setProperties); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'unset', - ol.geom.MultiPoint.prototype.unset); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'changed', - ol.geom.MultiPoint.prototype.changed); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'dispatchEvent', - ol.geom.MultiPoint.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'getRevision', - ol.geom.MultiPoint.prototype.getRevision); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'on', - ol.geom.MultiPoint.prototype.on); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'once', - ol.geom.MultiPoint.prototype.once); - -goog.exportProperty( - ol.geom.MultiPoint.prototype, - 'un', - ol.geom.MultiPoint.prototype.un); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getFirstCoordinate', - ol.geom.MultiPolygon.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getLastCoordinate', - ol.geom.MultiPolygon.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getLayout', - ol.geom.MultiPolygon.prototype.getLayout); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'rotate', - ol.geom.MultiPolygon.prototype.rotate); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'scale', - ol.geom.MultiPolygon.prototype.scale); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getClosestPoint', - ol.geom.MultiPolygon.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'intersectsCoordinate', - ol.geom.MultiPolygon.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getExtent', - ol.geom.MultiPolygon.prototype.getExtent); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'simplify', - ol.geom.MultiPolygon.prototype.simplify); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'transform', - ol.geom.MultiPolygon.prototype.transform); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'get', - ol.geom.MultiPolygon.prototype.get); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getKeys', - ol.geom.MultiPolygon.prototype.getKeys); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getProperties', - ol.geom.MultiPolygon.prototype.getProperties); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'set', - ol.geom.MultiPolygon.prototype.set); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'setProperties', - ol.geom.MultiPolygon.prototype.setProperties); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'unset', - ol.geom.MultiPolygon.prototype.unset); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'changed', - ol.geom.MultiPolygon.prototype.changed); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'dispatchEvent', - ol.geom.MultiPolygon.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'getRevision', - ol.geom.MultiPolygon.prototype.getRevision); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'on', - ol.geom.MultiPolygon.prototype.on); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'once', - ol.geom.MultiPolygon.prototype.once); - -goog.exportProperty( - ol.geom.MultiPolygon.prototype, - 'un', - ol.geom.MultiPolygon.prototype.un); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getFirstCoordinate', - ol.geom.Point.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getLastCoordinate', - ol.geom.Point.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getLayout', - ol.geom.Point.prototype.getLayout); - -goog.exportProperty( - ol.geom.Point.prototype, - 'rotate', - ol.geom.Point.prototype.rotate); - -goog.exportProperty( - ol.geom.Point.prototype, - 'scale', - ol.geom.Point.prototype.scale); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getClosestPoint', - ol.geom.Point.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.Point.prototype, - 'intersectsCoordinate', - ol.geom.Point.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getExtent', - ol.geom.Point.prototype.getExtent); - -goog.exportProperty( - ol.geom.Point.prototype, - 'simplify', - ol.geom.Point.prototype.simplify); - -goog.exportProperty( - ol.geom.Point.prototype, - 'transform', - ol.geom.Point.prototype.transform); - -goog.exportProperty( - ol.geom.Point.prototype, - 'get', - ol.geom.Point.prototype.get); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getKeys', - ol.geom.Point.prototype.getKeys); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getProperties', - ol.geom.Point.prototype.getProperties); - -goog.exportProperty( - ol.geom.Point.prototype, - 'set', - ol.geom.Point.prototype.set); - -goog.exportProperty( - ol.geom.Point.prototype, - 'setProperties', - ol.geom.Point.prototype.setProperties); - -goog.exportProperty( - ol.geom.Point.prototype, - 'unset', - ol.geom.Point.prototype.unset); - -goog.exportProperty( - ol.geom.Point.prototype, - 'changed', - ol.geom.Point.prototype.changed); - -goog.exportProperty( - ol.geom.Point.prototype, - 'dispatchEvent', - ol.geom.Point.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.Point.prototype, - 'getRevision', - ol.geom.Point.prototype.getRevision); - -goog.exportProperty( - ol.geom.Point.prototype, - 'on', - ol.geom.Point.prototype.on); - -goog.exportProperty( - ol.geom.Point.prototype, - 'once', - ol.geom.Point.prototype.once); - -goog.exportProperty( - ol.geom.Point.prototype, - 'un', - ol.geom.Point.prototype.un); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getFirstCoordinate', - ol.geom.Polygon.prototype.getFirstCoordinate); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getLastCoordinate', - ol.geom.Polygon.prototype.getLastCoordinate); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getLayout', - ol.geom.Polygon.prototype.getLayout); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'rotate', - ol.geom.Polygon.prototype.rotate); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'scale', - ol.geom.Polygon.prototype.scale); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getClosestPoint', - ol.geom.Polygon.prototype.getClosestPoint); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'intersectsCoordinate', - ol.geom.Polygon.prototype.intersectsCoordinate); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getExtent', - ol.geom.Polygon.prototype.getExtent); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'simplify', - ol.geom.Polygon.prototype.simplify); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'transform', - ol.geom.Polygon.prototype.transform); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'get', - ol.geom.Polygon.prototype.get); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getKeys', - ol.geom.Polygon.prototype.getKeys); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getProperties', - ol.geom.Polygon.prototype.getProperties); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'set', - ol.geom.Polygon.prototype.set); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'setProperties', - ol.geom.Polygon.prototype.setProperties); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'unset', - ol.geom.Polygon.prototype.unset); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'changed', - ol.geom.Polygon.prototype.changed); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'dispatchEvent', - ol.geom.Polygon.prototype.dispatchEvent); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'getRevision', - ol.geom.Polygon.prototype.getRevision); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'on', - ol.geom.Polygon.prototype.on); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'once', - ol.geom.Polygon.prototype.once); - -goog.exportProperty( - ol.geom.Polygon.prototype, - 'un', - ol.geom.Polygon.prototype.un); - -goog.exportProperty( - ol.format.GML.prototype, - 'readFeatures', - ol.format.GML.prototype.readFeatures); - -goog.exportProperty( - ol.format.GML2.prototype, - 'readFeatures', - ol.format.GML2.prototype.readFeatures); - -goog.exportProperty( - ol.format.GML3.prototype, - 'readFeatures', - ol.format.GML3.prototype.readFeatures); - -goog.exportProperty( - ol.control.Control.prototype, - 'get', - ol.control.Control.prototype.get); - -goog.exportProperty( - ol.control.Control.prototype, - 'getKeys', - ol.control.Control.prototype.getKeys); - -goog.exportProperty( - ol.control.Control.prototype, - 'getProperties', - ol.control.Control.prototype.getProperties); - -goog.exportProperty( - ol.control.Control.prototype, - 'set', - ol.control.Control.prototype.set); - -goog.exportProperty( - ol.control.Control.prototype, - 'setProperties', - ol.control.Control.prototype.setProperties); - -goog.exportProperty( - ol.control.Control.prototype, - 'unset', - ol.control.Control.prototype.unset); - -goog.exportProperty( - ol.control.Control.prototype, - 'changed', - ol.control.Control.prototype.changed); - -goog.exportProperty( - ol.control.Control.prototype, - 'dispatchEvent', - ol.control.Control.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.Control.prototype, - 'getRevision', - ol.control.Control.prototype.getRevision); - -goog.exportProperty( - ol.control.Control.prototype, - 'on', - ol.control.Control.prototype.on); - -goog.exportProperty( - ol.control.Control.prototype, - 'once', - ol.control.Control.prototype.once); - -goog.exportProperty( - ol.control.Control.prototype, - 'un', - ol.control.Control.prototype.un); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'getMap', - ol.control.Attribution.prototype.getMap); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'setMap', - ol.control.Attribution.prototype.setMap); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'setTarget', - ol.control.Attribution.prototype.setTarget); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'get', - ol.control.Attribution.prototype.get); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'getKeys', - ol.control.Attribution.prototype.getKeys); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'getProperties', - ol.control.Attribution.prototype.getProperties); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'set', - ol.control.Attribution.prototype.set); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'setProperties', - ol.control.Attribution.prototype.setProperties); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'unset', - ol.control.Attribution.prototype.unset); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'changed', - ol.control.Attribution.prototype.changed); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'dispatchEvent', - ol.control.Attribution.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'getRevision', - ol.control.Attribution.prototype.getRevision); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'on', - ol.control.Attribution.prototype.on); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'once', - ol.control.Attribution.prototype.once); - -goog.exportProperty( - ol.control.Attribution.prototype, - 'un', - ol.control.Attribution.prototype.un); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'getMap', - ol.control.FullScreen.prototype.getMap); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'setMap', - ol.control.FullScreen.prototype.setMap); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'setTarget', - ol.control.FullScreen.prototype.setTarget); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'get', - ol.control.FullScreen.prototype.get); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'getKeys', - ol.control.FullScreen.prototype.getKeys); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'getProperties', - ol.control.FullScreen.prototype.getProperties); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'set', - ol.control.FullScreen.prototype.set); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'setProperties', - ol.control.FullScreen.prototype.setProperties); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'unset', - ol.control.FullScreen.prototype.unset); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'changed', - ol.control.FullScreen.prototype.changed); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'dispatchEvent', - ol.control.FullScreen.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'getRevision', - ol.control.FullScreen.prototype.getRevision); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'on', - ol.control.FullScreen.prototype.on); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'once', - ol.control.FullScreen.prototype.once); - -goog.exportProperty( - ol.control.FullScreen.prototype, - 'un', - ol.control.FullScreen.prototype.un); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'getMap', - ol.control.MousePosition.prototype.getMap); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'setMap', - ol.control.MousePosition.prototype.setMap); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'setTarget', - ol.control.MousePosition.prototype.setTarget); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'get', - ol.control.MousePosition.prototype.get); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'getKeys', - ol.control.MousePosition.prototype.getKeys); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'getProperties', - ol.control.MousePosition.prototype.getProperties); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'set', - ol.control.MousePosition.prototype.set); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'setProperties', - ol.control.MousePosition.prototype.setProperties); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'unset', - ol.control.MousePosition.prototype.unset); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'changed', - ol.control.MousePosition.prototype.changed); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'dispatchEvent', - ol.control.MousePosition.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'getRevision', - ol.control.MousePosition.prototype.getRevision); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'on', - ol.control.MousePosition.prototype.on); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'once', - ol.control.MousePosition.prototype.once); - -goog.exportProperty( - ol.control.MousePosition.prototype, - 'un', - ol.control.MousePosition.prototype.un); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getMap', - ol.control.OverviewMap.prototype.getMap); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'setMap', - ol.control.OverviewMap.prototype.setMap); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'setTarget', - ol.control.OverviewMap.prototype.setTarget); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'get', - ol.control.OverviewMap.prototype.get); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getKeys', - ol.control.OverviewMap.prototype.getKeys); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getProperties', - ol.control.OverviewMap.prototype.getProperties); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'set', - ol.control.OverviewMap.prototype.set); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'setProperties', - ol.control.OverviewMap.prototype.setProperties); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'unset', - ol.control.OverviewMap.prototype.unset); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'changed', - ol.control.OverviewMap.prototype.changed); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'dispatchEvent', - ol.control.OverviewMap.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'getRevision', - ol.control.OverviewMap.prototype.getRevision); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'on', - ol.control.OverviewMap.prototype.on); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'once', - ol.control.OverviewMap.prototype.once); - -goog.exportProperty( - ol.control.OverviewMap.prototype, - 'un', - ol.control.OverviewMap.prototype.un); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'getMap', - ol.control.Rotate.prototype.getMap); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'setMap', - ol.control.Rotate.prototype.setMap); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'setTarget', - ol.control.Rotate.prototype.setTarget); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'get', - ol.control.Rotate.prototype.get); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'getKeys', - ol.control.Rotate.prototype.getKeys); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'getProperties', - ol.control.Rotate.prototype.getProperties); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'set', - ol.control.Rotate.prototype.set); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'setProperties', - ol.control.Rotate.prototype.setProperties); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'unset', - ol.control.Rotate.prototype.unset); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'changed', - ol.control.Rotate.prototype.changed); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'dispatchEvent', - ol.control.Rotate.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'getRevision', - ol.control.Rotate.prototype.getRevision); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'on', - ol.control.Rotate.prototype.on); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'once', - ol.control.Rotate.prototype.once); - -goog.exportProperty( - ol.control.Rotate.prototype, - 'un', - ol.control.Rotate.prototype.un); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'getMap', - ol.control.ScaleLine.prototype.getMap); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'setMap', - ol.control.ScaleLine.prototype.setMap); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'setTarget', - ol.control.ScaleLine.prototype.setTarget); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'get', - ol.control.ScaleLine.prototype.get); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'getKeys', - ol.control.ScaleLine.prototype.getKeys); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'getProperties', - ol.control.ScaleLine.prototype.getProperties); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'set', - ol.control.ScaleLine.prototype.set); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'setProperties', - ol.control.ScaleLine.prototype.setProperties); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'unset', - ol.control.ScaleLine.prototype.unset); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'changed', - ol.control.ScaleLine.prototype.changed); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'dispatchEvent', - ol.control.ScaleLine.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'getRevision', - ol.control.ScaleLine.prototype.getRevision); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'on', - ol.control.ScaleLine.prototype.on); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'once', - ol.control.ScaleLine.prototype.once); - -goog.exportProperty( - ol.control.ScaleLine.prototype, - 'un', - ol.control.ScaleLine.prototype.un); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'getMap', - ol.control.Zoom.prototype.getMap); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'setMap', - ol.control.Zoom.prototype.setMap); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'setTarget', - ol.control.Zoom.prototype.setTarget); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'get', - ol.control.Zoom.prototype.get); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'getKeys', - ol.control.Zoom.prototype.getKeys); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'getProperties', - ol.control.Zoom.prototype.getProperties); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'set', - ol.control.Zoom.prototype.set); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'setProperties', - ol.control.Zoom.prototype.setProperties); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'unset', - ol.control.Zoom.prototype.unset); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'changed', - ol.control.Zoom.prototype.changed); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'dispatchEvent', - ol.control.Zoom.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'getRevision', - ol.control.Zoom.prototype.getRevision); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'on', - ol.control.Zoom.prototype.on); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'once', - ol.control.Zoom.prototype.once); - -goog.exportProperty( - ol.control.Zoom.prototype, - 'un', - ol.control.Zoom.prototype.un); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'getMap', - ol.control.ZoomSlider.prototype.getMap); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'setMap', - ol.control.ZoomSlider.prototype.setMap); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'setTarget', - ol.control.ZoomSlider.prototype.setTarget); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'get', - ol.control.ZoomSlider.prototype.get); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'getKeys', - ol.control.ZoomSlider.prototype.getKeys); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'getProperties', - ol.control.ZoomSlider.prototype.getProperties); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'set', - ol.control.ZoomSlider.prototype.set); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'setProperties', - ol.control.ZoomSlider.prototype.setProperties); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'unset', - ol.control.ZoomSlider.prototype.unset); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'changed', - ol.control.ZoomSlider.prototype.changed); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'dispatchEvent', - ol.control.ZoomSlider.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'getRevision', - ol.control.ZoomSlider.prototype.getRevision); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'on', - ol.control.ZoomSlider.prototype.on); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'once', - ol.control.ZoomSlider.prototype.once); - -goog.exportProperty( - ol.control.ZoomSlider.prototype, - 'un', - ol.control.ZoomSlider.prototype.un); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'getMap', - ol.control.ZoomToExtent.prototype.getMap); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'setMap', - ol.control.ZoomToExtent.prototype.setMap); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'setTarget', - ol.control.ZoomToExtent.prototype.setTarget); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'get', - ol.control.ZoomToExtent.prototype.get); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'getKeys', - ol.control.ZoomToExtent.prototype.getKeys); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'getProperties', - ol.control.ZoomToExtent.prototype.getProperties); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'set', - ol.control.ZoomToExtent.prototype.set); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'setProperties', - ol.control.ZoomToExtent.prototype.setProperties); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'unset', - ol.control.ZoomToExtent.prototype.unset); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'changed', - ol.control.ZoomToExtent.prototype.changed); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'dispatchEvent', - ol.control.ZoomToExtent.prototype.dispatchEvent); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'getRevision', - ol.control.ZoomToExtent.prototype.getRevision); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'on', - ol.control.ZoomToExtent.prototype.on); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'once', - ol.control.ZoomToExtent.prototype.once); - -goog.exportProperty( - ol.control.ZoomToExtent.prototype, - 'un', - ol.control.ZoomToExtent.prototype.un); -ol.VERSION = 'v4.2.0'; -OPENLAYERS.ol = ol; - - return OPENLAYERS.ol; -})); diff --git a/themes/bootstrap3/js/vendor/ol/ol.js b/themes/bootstrap3/js/vendor/ol/ol.js deleted file mode 100644 index e3f70fc8346f28ea02fda5791bc57778e87a5f20..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/js/vendor/ol/ol.js +++ /dev/null @@ -1,1038 +0,0 @@ -// OpenLayers. See https://openlayers.org/ -// License: https://raw.githubusercontent.com/openlayers/openlayers/master/LICENSE.md -// Version: v4.2.0 -;(function (root, factory) { - if (typeof exports === "object") { - module.exports = factory(); - } else if (typeof define === "function" && define.amd) { - define([], factory); - } else { - root.ol = factory(); - } -}(this, function () { - var OPENLAYERS = {}; - var k,aa=this;function t(a,b){var c=OPENLAYERS;a=a.split(".");c=c||aa;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b};var ea,fa;function ia(a,b){return a>b?1:a<b?-1:0}function ja(a,b){return 0<=a.indexOf(b)}function ka(a,b,c){var d=a.length;if(a[0]<=b)return 0;if(!(b<=a[d-1]))if(0<c)for(c=1;c<d;++c){if(a[c]<b)return c-1}else if(0>c)for(c=1;c<d;++c){if(a[c]<=b)return c}else for(c=1;c<d;++c){if(a[c]==b)return c;if(a[c]<b)return a[c-1]-b<b-a[c]?c-1:c}return d-1}function la(a,b){var c=Array.isArray(b)?b:[b],d=c.length;for(b=0;b<d;b++)a[a.length]=c[b]}function ma(a,b){b=a.indexOf(b);-1<b&&a.splice(b,1)} -function na(a,b){for(var c=a.length>>>0,d,e=0;e<c;e++)if(d=a[e],b(d,e,a))return d;return null}function pa(a,b){var c=a.length;if(c!==b.length)return!1;for(var d=0;d<c;d++)if(a[d]!==b[d])return!1;return!0}function qa(a){var b=ra,c=a.length,d=Array(a.length),e;for(e=0;e<c;e++)d[e]={index:e,value:a[e]};d.sort(function(a,c){return b(a.value,c.value)||a.index-c.index});for(e=0;e<a.length;e++)a[e]=d[e].value}function sa(a,b){var c;return a.every(function(d,e){c=e;return!b(d,e,a)})?-1:c} -function ta(a,b){var c=b||ia;return a.every(function(b,e){if(!e)return!0;b=c(a[e-1],b);return!(0<b||0===b)})};function v(a,b){a.prototype=Object.create(b.prototype);a.prototype.constructor=a}function ua(){}function w(a){return a.Vo||(a.Vo=++va)}var va=0;function wa(a){this.message="Assertion failed. See https://openlayers.org/en/v4.2.0/doc/errors/#"+a+" for details.";this.code=a;this.name="AssertionError"}v(wa,Error);function xa(a,b){if(!a)throw new wa(b);};function ya(a,b,c,d){this.ca=a;this.$=b;this.da=c;this.ia=d}function za(a,b,c){return a.ca<=b&&b<=a.$&&a.da<=c&&c<=a.ia}function Aa(a,b){return a.ca==b.ca&&a.da==b.da&&a.$==b.$&&a.ia==b.ia}function Ba(a,b){return a.ca<=b.$&&a.$>=b.ca&&a.da<=b.ia&&a.ia>=b.da};function Ca(a,b,c){return Math.min(Math.max(a,b),c)}var Da=function(){var a;"cosh"in Math?a=Math.cosh:a=function(a){a=Math.exp(a);return(a+1/a)/2};return a}();function Ea(a){xa(0<a,29);return Math.pow(2,Math.ceil(Math.log(a)/Math.LN2))}function Fa(a,b,c,d,e,f){var g=e-c,h=f-d;if(g||h){var l=((a-c)*g+(b-d)*h)/(g*g+h*h);1<l?(c=e,d=f):0<l&&(c+=g*l,d+=h*l)}return Ga(a,b,c,d)}function Ga(a,b,c,d){a=c-a;b=d-b;return a*a+b*b}function Ha(a){return a*Math.PI/180}function Ia(a,b){a%=b;return 0>a*b?a+b:a} -function Ja(a,b,c){return a+c*(b-a)};function Ka(a,b,c){void 0===c&&(c=[0,0]);c[0]=a[0]+2*b;c[1]=a[1]+2*b;return c}function La(a,b,c){void 0===c&&(c=[0,0]);c[0]=a[0]*b+.5|0;c[1]=a[1]*b+.5|0;return c}function Ma(a,b){if(Array.isArray(a))return a;void 0===b?b=[a,a]:b[0]=b[1]=a;return b};function Na(a){for(var b=Oa(),c=0,d=a.length;c<d;++c)Pa(b,a[c]);return b}function Qa(a,b,c){return c?(c[0]=a[0]-b,c[1]=a[1]-b,c[2]=a[2]+b,c[3]=a[3]+b,c):[a[0]-b,a[1]-b,a[2]+b,a[3]+b]}function Ra(a,b){return b?(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b):a.slice()}function Sa(a,b,c){b=b<a[0]?a[0]-b:a[2]<b?b-a[2]:0;a=c<a[1]?a[1]-c:a[3]<c?c-a[3]:0;return b*b+a*a}function Ta(a,b){return Ua(a,b[0],b[1])}function Va(a,b){return a[0]<=b[0]&&b[2]<=a[2]&&a[1]<=b[1]&&b[3]<=a[3]} -function Ua(a,b,c){return a[0]<=b&&b<=a[2]&&a[1]<=c&&c<=a[3]}function Wa(a,b){var c=a[1],d=a[2],e=a[3],f=b[0];b=b[1];var g=0;f<a[0]?g|=16:f>d&&(g|=4);b<c?g|=8:b>e&&(g|=2);g||(g=1);return g}function Oa(){return[Infinity,Infinity,-Infinity,-Infinity]}function Xa(a,b,c,d,e){return e?(e[0]=a,e[1]=b,e[2]=c,e[3]=d,e):[a,b,c,d]}function Ya(a){return Xa(Infinity,Infinity,-Infinity,-Infinity,a)}function Za(a,b){var c=a[0];a=a[1];return Xa(c,a,c,a,b)}function $a(a,b,c,d,e){e=Ya(e);return ab(e,a,b,c,d)} -function bb(a,b){return a[0]==b[0]&&a[2]==b[2]&&a[1]==b[1]&&a[3]==b[3]}function cb(a,b){b[0]<a[0]&&(a[0]=b[0]);b[2]>a[2]&&(a[2]=b[2]);b[1]<a[1]&&(a[1]=b[1]);b[3]>a[3]&&(a[3]=b[3]);return a}function Pa(a,b){b[0]<a[0]&&(a[0]=b[0]);b[0]>a[2]&&(a[2]=b[0]);b[1]<a[1]&&(a[1]=b[1]);b[1]>a[3]&&(a[3]=b[1])}function ab(a,b,c,d,e){for(;c<d;c+=e){var f=a,g=b[c],h=b[c+1];f[0]=Math.min(f[0],g);f[1]=Math.min(f[1],h);f[2]=Math.max(f[2],g);f[3]=Math.max(f[3],h)}return a} -function db(a,b,c){var d;return(d=b.call(c,eb(a)))||(d=b.call(c,gb(a)))||(d=b.call(c,hb(a)))?d:(d=b.call(c,ib(a)))?d:!1}function jb(a){var b=0;kb(a)||(b=lb(a)*mb(a));return b}function eb(a){return[a[0],a[1]]}function gb(a){return[a[2],a[1]]}function nb(a){return[(a[0]+a[2])/2,(a[1]+a[3])/2]} -function ob(a,b,c,d,e){var f=b*d[0]/2;d=b*d[1]/2;b=Math.cos(c);var g=Math.sin(c);c=f*b;f*=g;b*=d;var h=d*g,l=a[0],m=a[1];a=l-c+h;d=l-c-h;g=l+c-h;c=l+c+h;var h=m-f-b,l=m-f+b,n=m+f+b,f=m+f-b;return Xa(Math.min(a,d,g,c),Math.min(h,l,n,f),Math.max(a,d,g,c),Math.max(h,l,n,f),e)}function mb(a){return a[3]-a[1]}function pb(a,b,c){c=c?c:Oa();qb(a,b)&&(c[0]=a[0]>b[0]?a[0]:b[0],c[1]=a[1]>b[1]?a[1]:b[1],c[2]=a[2]<b[2]?a[2]:b[2],c[3]=a[3]<b[3]?a[3]:b[3]);return c}function ib(a){return[a[0],a[3]]} -function hb(a){return[a[2],a[3]]}function lb(a){return a[2]-a[0]}function qb(a,b){return a[0]<=b[2]&&a[2]>=b[0]&&a[1]<=b[3]&&a[3]>=b[1]}function kb(a){return a[2]<a[0]||a[3]<a[1]}function rb(a,b){var c=(a[2]-a[0])/2*(b-1);b=(a[3]-a[1])/2*(b-1);a[0]-=c;a[2]+=c;a[1]-=b;a[3]+=b} -function sb(a,b,c){a=[a[0],a[1],a[0],a[3],a[2],a[1],a[2],a[3]];b(a,a,2);var d=[a[0],a[2],a[4],a[6]],e=[a[1],a[3],a[5],a[7]];b=Math.min.apply(null,d);a=Math.min.apply(null,e);d=Math.max.apply(null,d);e=Math.max.apply(null,e);return Xa(b,a,d,e,c)};var tb="function"===typeof Object.assign?Object.assign:function(a,b){if(!a||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var c=Object(a),d=1,e=arguments.length;d<e;++d){var f=arguments[d];if(void 0!==f&&null!==f)for(var g in f)f.hasOwnProperty(g)&&(c[g]=f[g])}return c};function ub(a){for(var b in a)delete a[b]}function vb(a){var b=[],c;for(c in a)b.push(a[c]);return b}function wb(a){for(var b in a)return!1;return!b};/* - - Latitude/longitude spherical geodesy formulae taken from - http://www.movable-type.co.uk/scripts/latlong.html - Licensed under CC-BY-3.0. -*/ -function xb(a){this.radius=a}xb.prototype.a=function(a){for(var b=0,c=a.length,d=a[c-1][0],e=a[c-1][1],f=0;f<c;f++)var g=a[f][0],h=a[f][1],b=b+Ha(g-d)*(2+Math.sin(Ha(e))+Math.sin(Ha(h))),d=g,e=h;return b*this.radius*this.radius/2};xb.prototype.b=function(a,b){var c=Ha(a[1]),d=Ha(b[1]),e=(d-c)/2;a=Ha(b[0]-a[0])/2;c=Math.sin(e)*Math.sin(e)+Math.sin(a)*Math.sin(a)*Math.cos(c)*Math.cos(d);return 2*this.radius*Math.atan2(Math.sqrt(c),Math.sqrt(1-c))}; -xb.prototype.offset=function(a,b,c){var d=Ha(a[1]);b/=this.radius;var e=Math.asin(Math.sin(d)*Math.cos(b)+Math.cos(d)*Math.sin(b)*Math.cos(c));return[180*(Ha(a[0])+Math.atan2(Math.sin(c)*Math.sin(b)*Math.cos(d),Math.cos(b)-Math.sin(d)*Math.sin(e)))/Math.PI,180*e/Math.PI]};var yb=new xb(6370997);var zb={};zb.degrees=2*Math.PI*yb.radius/360;zb.ft=.3048;zb.m=1;zb["us-ft"]=1200/3937;var Ab=null;function Bb(a){this.mb=a.code;this.a=a.units;this.f=void 0!==a.extent?a.extent:null;this.g=void 0!==a.worldExtent?a.worldExtent:null;this.b=void 0!==a.axisOrientation?a.axisOrientation:"enu";this.c=void 0!==a.global?a.global:!1;this.i=!(!this.c||!this.f);this.o=a.getPointResolution;this.j=null;this.l=a.metersPerUnit;var b=a.code,c=Ab||window.proj4;"function"==typeof c&&(b=c.defs(b),void 0!==b&&(void 0!==b.axis&&void 0===a.axisOrientation&&(this.b=b.axis),void 0===a.metersPerUnit&&(this.l=b.to_meter), -void 0===a.units&&(this.a=b.units)))}k=Bb.prototype;k.Jk=function(){return this.mb};k.G=function(){return this.f};k.Un=function(){return this.a};k.sc=function(){return this.l||zb[this.a]};k.tl=function(){return this.g};k.dm=function(){return this.c};k.$p=function(a){this.c=a;this.i=!(!a||!this.f)};k.Vn=function(a){this.f=a;this.i=!(!this.c||!a)};k.kq=function(a){this.g=a};k.Zp=function(a){this.o=a};function Cb(a){Bb.call(this,{code:a,units:"m",extent:Db,global:!0,worldExtent:Eb,getPointResolution:function(a,c){return a/Da(c[1]/6378137)}})}v(Cb,Bb);var Fb=6378137*Math.PI,Db=[-Fb,-Fb,Fb,Fb],Eb=[-180,-85,180,85],Gb="EPSG:3857 EPSG:102100 EPSG:102113 EPSG:900913 urn:ogc:def:crs:EPSG:6.18:3:3857 urn:ogc:def:crs:EPSG::3857 http://www.opengis.net/gml/srs/epsg.xml#3857".split(" ").map(function(a){return new Cb(a)}); -function Hb(a,b,c){var d=a.length;c=1<c?c:2;void 0===b&&(2<c?b=a.slice():b=Array(d));for(var e=0;e<d;e+=c){b[e]=Fb*a[e]/180;var f=6378137*Math.log(Math.tan(Math.PI*(a[e+1]+90)/360));f>Fb?f=Fb:f<-Fb&&(f=-Fb);b[e+1]=f}return b}function Ib(a,b,c){var d=a.length;c=1<c?c:2;void 0===b&&(2<c?b=a.slice():b=Array(d));for(var e=0;e<d;e+=c)b[e]=180*a[e]/Fb,b[e+1]=360*Math.atan(Math.exp(a[e+1]/6378137))/Math.PI-90;return b};var Jb=new xb(6378137);function Kb(a,b){Bb.call(this,{code:a,units:"degrees",extent:Lb,axisOrientation:b,global:!0,metersPerUnit:Mb,worldExtent:Lb})}v(Kb,Bb);var Lb=[-180,-90,180,90],Mb=Math.PI*Jb.radius/180,Nb=[new Kb("CRS:84"),new Kb("EPSG:4326","neu"),new Kb("urn:ogc:def:crs:EPSG::4326","neu"),new Kb("urn:ogc:def:crs:EPSG:6.6:4326","neu"),new Kb("urn:ogc:def:crs:OGC:1.3:CRS84"),new Kb("urn:ogc:def:crs:OGC:2:84"),new Kb("http://www.opengis.net/gml/srs/epsg.xml#4326","neu"),new Kb("urn:x-ogc:def:crs:EPSG:4326","neu")];var Ob={};var Pb={};function Qb(a,b,c){a=a.mb;b=b.mb;a in Pb||(Pb[a]={});Pb[a][b]=c}function Rb(a,b){var c;a in Pb&&b in Pb[a]&&(c=Pb[a][b]);return c};function Sb(a,b,c){a=Tb(a);var d=a.o;d?b=d(b,c):"degrees"!=a.a&&(d=Vb(a,Tb("EPSG:4326")),b=[c[0]-b/2,c[1],c[0]+b/2,c[1],c[0],c[1]-b/2,c[0],c[1]+b/2],b=d(b,b,2),b=(yb.b(b.slice(0,2),b.slice(2,4))+yb.b(b.slice(4,6),b.slice(6,8)))/2,a=a.sc(),void 0!==a&&(b/=a));return b}function Wb(a){a.forEach(Xb);a.forEach(function(b){a.forEach(function(a){b!==a&&Qb(b,a,Yb)})})}function Zb(){Nb.forEach(function(a){Gb.forEach(function(b){Qb(a,b,Hb);Qb(b,a,Ib)})})}function Xb(a){Ob[a.mb]=a;Qb(a,a,Yb)} -function $b(a){return a?"string"===typeof a?Tb(a):a:Tb("EPSG:3857")}function ac(a,b,c,d){a=Tb(a);b=Tb(b);Qb(a,b,cc(c));Qb(b,a,cc(d))}function cc(a){return function(b,c,d){var e=b.length;d=void 0!==d?d:2;c=void 0!==c?c:Array(e);var f;for(f=0;f<e;f+=d){var g=a([b[f],b[f+1]]);c[f]=g[0];c[f+1]=g[1];for(g=d-1;2<=g;--g)c[f+g]=b[f+g]}return c}} -function Tb(a){var b=null;if(a instanceof Bb)b=a;else if("string"===typeof a){var b=Ob[a]||null,c=Ab||window.proj4;b||"function"!=typeof c||void 0===c.defs(a)||(b=new Bb({code:a}),Xb(b))}return b}function dc(a,b){if(a===b)return!0;var c=a.a===b.a;return a.mb===b.mb?c:Vb(a,b)===Yb&&c}function ec(a,b){a=Tb(a);b=Tb(b);return Vb(a,b)} -function Vb(a,b){var c=a.mb,d=b.mb,e=Rb(c,d);if(!e){var f=Ab||window.proj4;if("function"==typeof f){var g=f.defs(c),h=f.defs(d);void 0!==g&&void 0!==h&&(g===h?Wb([b,a]):(e=f(d,c),ac(b,a,e.forward,e.inverse)),e=Rb(c,d))}}e||(e=fc);return e}function fc(a,b){if(void 0!==b&&a!==b){for(var c=0,d=a.length;c<d;++c)b[c]=a[c];a=b}return a}function Yb(a,b){if(void 0!==b){for(var c=0,d=a.length;c<d;++c)b[c]=a[c];a=b}else a=a.slice();return a}function gc(a,b,c){return ec(b,c)(a,void 0,a.length)} -function hc(a,b,c){b=ec(b,c);return sb(a,b)}function ic(){Wb(Gb);Wb(Nb);Zb()}ic();function jc(a,b,c,d){return void 0!==d?(d[0]=a,d[1]=b,d[2]=c,d):[a,b,c]}function kc(a){var b=a[0],c=Array(b),d=1<<b-1,e;for(e=0;e<b;++e){var f=48;a[1]&d&&(f+=1);a[2]&d&&(f+=2);c[e]=String.fromCharCode(f);d>>=1}return c.join("")};function lc(a){this.minZoom=void 0!==a.minZoom?a.minZoom:0;this.b=a.resolutions;xa(ta(this.b,function(a,b){return b-a}),17);this.maxZoom=this.b.length-1;this.i=void 0!==a.origin?a.origin:null;this.c=null;void 0!==a.origins&&(this.c=a.origins,xa(this.c.length==this.b.length,20));var b=a.extent;void 0===b||this.i||this.c||(this.i=ib(b));xa(!this.i&&this.c||this.i&&!this.c,18);this.f=null;void 0!==a.tileSizes&&(this.f=a.tileSizes,xa(this.f.length==this.b.length,19));this.g=void 0!==a.tileSize?a.tileSize: -this.f?null:256;xa(!this.g&&this.f||this.g&&!this.f,22);this.v=void 0!==b?b:null;this.a=null;this.j=[0,0];void 0!==a.sizes?this.a=a.sizes.map(function(a){return new ya(Math.min(0,a[0]),Math.max(a[0]-1,-1),Math.min(0,a[1]),Math.max(a[1]-1,-1))},this):b&&mc(this,b)}var nc=[0,0,0];k=lc.prototype;k.Rf=function(a,b,c){a=oc(this,a,b);for(var d=a.ca,e=a.$;d<=e;++d)for(var f=a.da,g=a.ia;f<=g;++f)c([b,d,f])}; -function pc(a,b,c,d,e){e=a.Aa(b,e);for(b=b[0]-1;b>=a.minZoom;){if(c.call(null,b,oc(a,e,b,d)))return!0;--b}return!1}k.G=function(){return this.v};k.Ti=function(){return this.maxZoom};k.Ui=function(){return this.minZoom};k.Pc=function(a){return this.i?this.i:this.c[a]};k.Da=function(a){return this.b[a]};k.Vi=function(){return this.b};function qc(a,b,c,d){return b[0]<a.maxZoom?(d=a.Aa(b,d),oc(a,d,b[0]+1,c)):null} -function rc(a,b,c,d){sc(a,b[0],b[1],c,!1,nc);var e=nc[1],f=nc[2];sc(a,b[2],b[3],c,!0,nc);a=nc[1];b=nc[2];void 0!==d?(d.ca=e,d.$=a,d.da=f,d.ia=b):d=new ya(e,a,f,b);return d}function oc(a,b,c,d){return rc(a,b,a.Da(c),d)}function tc(a,b){var c=a.Pc(b[0]),d=a.Da(b[0]);a=Ma(a.gb(b[0]),a.j);return[c[0]+(b[1]+.5)*a[0]*d,c[1]+(b[2]+.5)*a[1]*d]}k.Aa=function(a,b){var c=this.Pc(a[0]),d=this.Da(a[0]),e=Ma(this.gb(a[0]),this.j),f=c[0]+a[1]*e[0]*d;a=c[1]+a[2]*e[1]*d;return Xa(f,a,f+e[0]*d,a+e[1]*d,b)}; -k.Be=function(a,b,c){return sc(this,a[0],a[1],b,!1,c)};function sc(a,b,c,d,e,f){var g=a.tc(d),h=d/a.Da(g),l=a.Pc(g);a=Ma(a.gb(g),a.j);b=h*Math.floor((b-l[0])/d+(e?.5:0))/a[0];c=h*Math.floor((c-l[1])/d+(e?0:.5))/a[1];e?(b=Math.ceil(b)-1,c=Math.ceil(c)-1):(b=Math.floor(b),c=Math.floor(c));return jc(g,b,c,f)}k.bg=function(a,b,c){return sc(this,a[0],a[1],this.Da(b),!1,c)};k.gb=function(a){return this.g?this.g:this.f[a]};k.tc=function(a,b){return Ca(ka(this.b,a,b||0),this.minZoom,this.maxZoom)}; -function mc(a,b){for(var c=a.b.length,d=Array(c),e=a.minZoom;e<c;++e)d[e]=oc(a,b,e);a.a=d};function vc(a){var b=a.j;b||(b=wc(a),a.j=b);return b}function xc(a){var b={};tb(b,a?a:{});void 0===b.extent&&(b.extent=Tb("EPSG:3857").G());b.resolutions=yc(b.extent,b.maxZoom,b.tileSize);delete b.maxZoom;return new lc(b)}function yc(a,b,c){b=void 0!==b?b:42;var d=mb(a);a=lb(a);c=Ma(void 0!==c?c:256);c=Math.max(a/c[0],d/c[1]);b+=1;d=Array(b);for(a=0;a<b;++a)d[a]=c/Math.pow(2,a);return d}function wc(a,b,c){a=zc(a);b=yc(a,b,c);return new lc({extent:a,origin:ib(a),resolutions:b,tileSize:c})} -function zc(a){a=Tb(a);var b=a.G();b||(a=180*zb.degrees/a.sc(),b=Xa(-a,-a,a,a));return b};function Ac(a){this.b=a.html;this.a=a.tileRanges?a.tileRanges:null}Ac.prototype.i=function(){return this.b};function Bc(a){return function(b){if(b)return[Ca(b[0],a[0],a[2]),Ca(b[1],a[1],a[3])]}}function Cc(a){return a};function Dc(a){function b(b){var c=a.listener,e=a.lh||a.target;a.nh&&Ec(a);return c.call(e,b)}return a.mh=b}function Fc(a,b,c,d){for(var e,f=0,g=a.length;f<g;++f)if(e=a[f],e.listener===b&&e.lh===c)return d&&(e.deleteIndex=f),e}function Gc(a,b){return(a=a.fb)?a[b]:void 0}function Hc(a){var b=a.fb;b||(b=a.fb={});return b}function Ic(a,b){var c=Gc(a,b);if(c){for(var d=0,e=c.length;d<e;++d)a.removeEventListener(b,c[d].mh),ub(c[d]);c.length=0;if(c=a.fb)delete c[b],Object.keys(c).length||delete a.fb}} -function y(a,b,c,d,e){var f=Hc(a),g=f[b];g||(g=f[b]=[]);(f=Fc(g,c,d,!1))?e||(f.nh=!1):(f={lh:d,nh:!!e,listener:c,target:a,type:b},a.addEventListener(b,Dc(f)),g.push(f));return f}function Jc(a,b,c,d){return y(a,b,c,d,!0)}function Kc(a,b,c,d){(a=Gc(a,b))&&(c=Fc(a,c,d,!0))&&Ec(c)}function Ec(a){if(a&&a.target){a.target.removeEventListener(a.type,a.mh);var b=Gc(a.target,a.type);if(b){var c="deleteIndex"in a?a.deleteIndex:b.indexOf(a);-1!==c&&b.splice(c,1);b.length||Ic(a.target,a.type)}ub(a)}} -function Lc(a){var b=Hc(a),c;for(c in b)Ic(a,c)};function Mc(){}Mc.prototype.Jb=!1;function Nc(a){a.Jb||(a.Jb=!0,a.ka())}Mc.prototype.ka=ua;function Oc(a){this.type=a;this.target=null}Oc.prototype.preventDefault=Oc.prototype.stopPropagation=function(){this.qp=!0};function Pc(a){a.stopPropagation()};function Qc(){this.Ua={};this.ra={};this.oa={}}v(Qc,Mc);Qc.prototype.addEventListener=function(a,b){var c=this.oa[a];c||(c=this.oa[a]=[]);-1===c.indexOf(b)&&c.push(b)}; -Qc.prototype.b=function(a){var b="string"===typeof a?new Oc(a):a;a=b.type;b.target=this;var c=this.oa[a];if(c){a in this.ra||(this.ra[a]=0,this.Ua[a]=0);++this.ra[a];for(var d=0,e=c.length;d<e;++d)if(!1===c[d].call(this,b)||b.qp){var f=!1;break}--this.ra[a];if(!this.ra[a]){b=this.Ua[a];for(delete this.Ua[a];b--;)this.removeEventListener(a,ua);delete this.ra[a]}return f}};Qc.prototype.ka=function(){Lc(this)};function Rc(a,b){return b?b in a.oa:0<Object.keys(a.oa).length} -Qc.prototype.removeEventListener=function(a,b){var c=this.oa[a];c&&(b=c.indexOf(b),a in this.Ua?(c[b]=ua,++this.Ua[a]):(c.splice(b,1),c.length||delete this.oa[a]))};function Sc(){Qc.call(this);this.i=0}v(Sc,Qc);k=Sc.prototype;k.s=function(){++this.i;this.b("change")};k.L=function(){return this.i};k.J=function(a,b,c){if(Array.isArray(a)){for(var d=a.length,e=Array(d),f=0;f<d;++f)e[f]=y(this,a[f],b,c);return e}return y(this,a,b,c)};k.once=function(a,b,c){if(Array.isArray(a)){for(var d=a.length,e=Array(d),f=0;f<d;++f)e[f]=Jc(this,a[f],b,c);return e}return Jc(this,a,b,c)}; -k.K=function(a,b,c){if(Array.isArray(a))for(var d=0,e=a.length;d<e;++d)Kc(this,a[d],b,c);else Kc(this,a,b,c)};function Tc(a){Sc.call(this);w(this);this.S={};void 0!==a&&this.H(a)}v(Tc,Sc);var Uc={};function Vc(a){return Uc.hasOwnProperty(a)?Uc[a]:Uc[a]="change:"+a}k=Tc.prototype;k.get=function(a){var b;this.S.hasOwnProperty(a)&&(b=this.S[a]);return b};k.O=function(){return Object.keys(this.S)};k.N=function(){return tb({},this.S)};function Wc(a,b,c){var d=Vc(b);a.b(new Xc(d,b,c));a.b(new Xc("propertychange",b,c))}k.set=function(a,b,c){c?this.S[a]=b:(c=this.S[a],this.S[a]=b,c!==b&&Wc(this,a,c))}; -k.H=function(a,b){for(var c in a)this.set(c,a[c],b)};k.P=function(a,b){if(a in this.S){var c=this.S[a];delete this.S[a];b||Wc(this,a,c)}};function Xc(a,b,c){Oc.call(this,a);this.key=b;this.oldValue=c}v(Xc,Oc);function Yc(a,b){Tc.call(this);this.c=!!(b||{}).unique;this.a=a?a:[];if(this.c)for(a=0,b=this.a.length;a<b;++a)Zc(this,this.a[a],a);$c(this)}v(Yc,Tc);k=Yc.prototype;k.clear=function(){for(;0<this.dc();)this.pop()};k.fg=function(a){var b;var c=0;for(b=a.length;c<b;++c)this.push(a[c]);return this};k.forEach=function(a,b){this.a.forEach(a,b)};k.tm=function(){return this.a};k.item=function(a){return this.a[a]};k.dc=function(){return this.get(ad)}; -k.He=function(a,b){this.c&&Zc(this,b);this.a.splice(a,0,b);$c(this);this.b(new bd("add",b))};k.pop=function(){return this.Hg(this.dc()-1)};k.push=function(a){this.c&&Zc(this,a);var b=this.dc();this.He(b,a);return this.dc()};k.remove=function(a){var b=this.a,c;var d=0;for(c=b.length;d<c;++d)if(b[d]===a)return this.Hg(d)};k.Hg=function(a){var b=this.a[a];this.a.splice(a,1);$c(this);this.b(new bd("remove",b));return b}; -k.Wp=function(a,b){var c=this.dc();if(a<c)this.c&&Zc(this,b,a),c=this.a[a],this.a[a]=b,this.b(new bd("remove",c)),this.b(new bd("add",b));else{for(;c<a;++c)this.He(c,void 0);this.He(a,b)}};function $c(a){a.set(ad,a.a.length)}function Zc(a,b,c){for(var d=0,e=a.a.length;d<e;++d)if(a.a[d]===b&&d!==c)throw new wa(58);}var ad="length";function bd(a,b){Oc.call(this,a);this.element=b}v(bd,Oc);var cd=/^#(?:[0-9a-f]{3}){1,2}$/i,dd=/^([a-z]*)$/i;function ed(a){return Array.isArray(a)?a:fd(a)}function gd(a){if("string"!==typeof a){var b=a[0];b!=(b|0)&&(b=b+.5|0);var c=a[1];c!=(c|0)&&(c=c+.5|0);var d=a[2];d!=(d|0)&&(d=d+.5|0);a="rgba("+b+","+c+","+d+","+(void 0===a[3]?1:a[3])+")"}return a} -var fd=function(){var a={},b=0;return function(c){if(a.hasOwnProperty(c))var d=a[c];else{if(1024<=b){d=0;for(var e in a)d++&3||(delete a[e],--b)}d=c;dd.exec(d)&&(e=document.createElement("div"),e.style.color=d,document.body.appendChild(e),d=getComputedStyle(e).color,document.body.removeChild(e));if(cd.exec(d)){var f=d.length-1;xa(3==f||6==f,54);var g=3==f?1:2;f=parseInt(d.substr(1+0*g,g),16);e=parseInt(d.substr(1+1*g,g),16);d=parseInt(d.substr(1+2*g,g),16);1==g&&(f=(f<<4)+f,e=(e<<4)+e,d=(d<<4)+d); -f=[f,e,d,1]}else d.indexOf("rgba(")?d.indexOf("rgb(")?xa(!1,14):(d=d.slice(4,-1).split(",").map(Number),d.push(1),f=hd(d)):(d=d.slice(5,-1).split(",").map(Number),f=hd(d));d=f;a[c]=d;++b}return d}}();function hd(a){var b=[];b[0]=Ca(a[0]+.5|0,0,255);b[1]=Ca(a[1]+.5|0,0,255);b[2]=Ca(a[2]+.5|0,0,255);b[3]=Ca(a[3],0,1);return b};function id(a){return"string"===typeof a||a instanceof CanvasPattern||a instanceof CanvasGradient?a:gd(a)};function jd(a,b){var c=document.createElement("CANVAS");a&&(c.width=a);b&&(c.height=b);return c.getContext("2d")}function kd(a,b){var c=b.parentNode;c&&c.replaceChild(a,b)}function ld(a){a&&a.parentNode&&a.parentNode.removeChild(a)};function md(a){Tc.call(this);this.element=a.element?a.element:null;this.a=this.R=null;this.v=[];this.render=a.render?a.render:ua;a.target&&this.f(a.target)}v(md,Tc);md.prototype.ka=function(){ld(this.element);Tc.prototype.ka.call(this)};md.prototype.g=function(){return this.a}; -md.prototype.setMap=function(a){this.a&&ld(this.element);for(var b=0,c=this.v.length;b<c;++b)Ec(this.v[b]);this.v.length=0;if(this.a=a)(this.R?this.R:a.D).appendChild(this.element),this.render!==ua&&this.v.push(y(a,"postrender",this.render,this)),a.render()};md.prototype.f=function(a){this.R="string"===typeof a?document.getElementById(a):a};function nd(a){a=a?a:{};this.I=document.createElement("UL");this.u=document.createElement("LI");this.I.appendChild(this.u);this.u.style.display="none";this.c=void 0!==a.collapsed?a.collapsed:!0;this.o=void 0!==a.collapsible?a.collapsible:!0;this.o||(this.c=!1);var b=void 0!==a.className?a.className:"ol-attribution",c=void 0!==a.tipLabel?a.tipLabel:"Attributions",d=void 0!==a.collapseLabel?a.collapseLabel:"\u00bb";"string"===typeof d?(this.D=document.createElement("span"),this.D.textContent=d):this.D= -d;d=void 0!==a.label?a.label:"i";"string"===typeof d?(this.C=document.createElement("span"),this.C.textContent=d):this.C=d;var e=this.o&&!this.c?this.D:this.C,d=document.createElement("button");d.setAttribute("type","button");d.title=c;d.appendChild(e);y(d,"click",this.Vm,this);c=document.createElement("div");c.className=b+" ol-unselectable ol-control"+(this.c&&this.o?" ol-collapsed":"")+(this.o?"":" ol-uncollapsible");c.appendChild(this.I);c.appendChild(d);md.call(this,{element:c,render:a.render? -a.render:od,target:a.target});this.B=!0;this.l={};this.j={};this.T={}}v(nd,md); -function od(a){if(a=a.frameState){var b,c,d,e,f,g=a.layerStatesArray,h=tb({},a.attributions),l={},m={},n=a.viewState.projection;var p=0;for(b=g.length;p<b;p++)if(e=g[p].layer.ha()){var q=w(e).toString();if(f=e.j){var r=0;for(c=f.length;r<c;r++){var u=f[r];var x=w(u).toString();if(!(x in h)){if(d=a.usedTiles[q]){var B=e.Ta(n);a:{var E=void 0;var A,L=u,oa=B,ha=n;if(L.a){for(E in d)if(E in L.a){var B=d[E];var ga=0;for(A=L.a[E].length;ga<A;++ga){var z=L.a[E][ga];if(Ba(z,B)){E=!0;break a}var M=oc(oa,zc(ha), -parseInt(E,10)),ba=M.$-M.ca+1;if(B.ca<M.ca||B.$>M.$)if(Ba(z,new ya(Ia(B.ca,ba),Ia(B.$,ba),B.da,B.ia))||B.$-B.ca+1>ba&&Ba(z,M)){E=!0;break a}}}E=!1}else E=!0}}else E=!1;E?(x in l&&delete l[x],E=u.b,E in m||(m[E]=!0,h[x]=u)):l[x]=u}}}}b=[h,l];p=b[0];b=b[1];for(var da in this.l)da in p?(this.j[da]||(this.l[da].style.display="",this.j[da]=!0),delete p[da]):da in b?(this.j[da]&&(this.l[da].style.display="none",delete this.j[da]),delete b[da]):(ld(this.l[da]),delete this.l[da],delete this.j[da]);for(da in p)r= -document.createElement("LI"),r.innerHTML=p[da].b,this.I.appendChild(r),this.l[da]=r,this.j[da]=!0;for(da in b)r=document.createElement("LI"),r.innerHTML=b[da].b,r.style.display="none",this.I.appendChild(r),this.l[da]=r;da=!wb(this.j)||!wb(a.logos);this.B!=da&&(this.element.style.display=da?"":"none",this.B=da);da&&wb(this.j)?this.element.classList.add("ol-logo-only"):this.element.classList.remove("ol-logo-only");a=a.logos;da=this.T;for(ca in da)ca in a||(ld(da[ca]),delete da[ca]);for(var fb in a)if(b= -a[fb],b instanceof HTMLElement&&(this.u.appendChild(b),da[fb]=b),!(fb in da)){var ca=new Image;ca.src=fb;""===b?p=ca:(p=document.createElement("a"),p.href=b,p.appendChild(ca));this.u.appendChild(p);da[fb]=p}this.u.style.display=wb(a)?"none":""}else this.B&&(this.element.style.display="none",this.B=!1)}k=nd.prototype;k.Vm=function(a){a.preventDefault();pd(this)};function pd(a){a.element.classList.toggle("ol-collapsed");a.c?kd(a.D,a.C):kd(a.C,a.D);a.c=!a.c}k.Um=function(){return this.o}; -k.Xm=function(a){this.o!==a&&(this.o=a,this.element.classList.toggle("ol-uncollapsible"),!a&&this.c&&pd(this))};k.Wm=function(a){this.o&&this.c!==a&&pd(this)};k.Tm=function(){return this.c};function qd(a){return Math.pow(a,3)}function rd(a){return 1-qd(1-a)}function sd(a){return 3*a*a-2*a*a*a}function td(a){return a};function ud(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-rotate",c=void 0!==a.label?a.label:"\u21e7";this.c=null;"string"===typeof c?(this.c=document.createElement("span"),this.c.className="ol-compass",this.c.textContent=c):(this.c=c,this.c.classList.add("ol-compass"));var d=a.tipLabel?a.tipLabel:"Reset rotation",c=document.createElement("button");c.className=b+"-reset";c.setAttribute("type","button");c.title=d;c.appendChild(this.c);y(c,"click",ud.prototype.D,this);d=document.createElement("div"); -d.className=b+" ol-unselectable ol-control";d.appendChild(c);b=a.render?a.render:vd;this.o=a.resetNorth?a.resetNorth:void 0;md.call(this,{element:d,render:b,target:a.target});this.l=void 0!==a.duration?a.duration:250;this.j=void 0!==a.autoHide?a.autoHide:!0;this.u=void 0;this.j&&this.element.classList.add("ol-hidden")}v(ud,md);ud.prototype.D=function(a){a.preventDefault();this.o?this.o():(a=this.a.Z())&&void 0!==a.Qa()&&(0<this.l?a.animate({rotation:0,duration:this.l,easing:rd}):a.Oe(0))}; -function vd(a){if(a=a.frameState){a=a.viewState.rotation;if(a!=this.u){var b="rotate("+a+"rad)";if(this.j){var c=this.element.classList.contains("ol-hidden");c||a?c&&a&&this.element.classList.remove("ol-hidden"):this.element.classList.add("ol-hidden")}this.c.style.msTransform=b;this.c.style.webkitTransform=b;this.c.style.transform=b}this.u=a}};function wd(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-zoom",c=void 0!==a.delta?a.delta:1,d=void 0!==a.zoomInLabel?a.zoomInLabel:"+",e=void 0!==a.zoomOutLabel?a.zoomOutLabel:"\u2212",f=void 0!==a.zoomInTipLabel?a.zoomInTipLabel:"Zoom in",g=void 0!==a.zoomOutTipLabel?a.zoomOutTipLabel:"Zoom out",h=document.createElement("button");h.className=b+"-in";h.setAttribute("type","button");h.title=f;h.appendChild("string"===typeof d?document.createTextNode(d):d);y(h,"click",wd.prototype.j.bind(this, -c));d=document.createElement("button");d.className=b+"-out";d.setAttribute("type","button");d.title=g;d.appendChild("string"===typeof e?document.createTextNode(e):e);y(d,"click",wd.prototype.j.bind(this,-c));c=document.createElement("div");c.className=b+" ol-unselectable ol-control";c.appendChild(h);c.appendChild(d);md.call(this,{element:c,target:a.target});this.c=void 0!==a.duration?a.duration:250}v(wd,md); -wd.prototype.j=function(a,b){b.preventDefault();if(b=this.a.Z()){var c=b.Pa();c&&(a=b.constrainResolution(c,a),0<this.c?(b.Ic()&&b.ed(),b.animate({resolution:a,duration:this.c,easing:rd})):b.Vc(a))}};function xd(a){a=a?a:{};var b=new Yc;(void 0!==a.zoom?a.zoom:1)&&b.push(new wd(a.zoomOptions));(void 0!==a.rotate?a.rotate:1)&&b.push(new ud(a.rotateOptions));(void 0!==a.attribution?a.attribution:1)&&b.push(new nd(a.attributionOptions));return b};function yd(a){a=a?a:{};this.c=void 0!==a.className?a.className:"ol-full-screen";var b=void 0!==a.label?a.label:"\u2922";this.o="string"===typeof b?document.createTextNode(b):b;b=void 0!==a.labelActive?a.labelActive:"\u00d7";this.l="string"===typeof b?document.createTextNode(b):b;var c=a.tipLabel?a.tipLabel:"Toggle full-screen",b=document.createElement("button");b.className=this.c+"-"+zd();b.setAttribute("type","button");b.title=c;b.appendChild(this.o);y(b,"click",this.C,this);c=document.createElement("div"); -c.className=this.c+" ol-unselectable ol-control "+(Ad()?"":"ol-unsupported");c.appendChild(b);md.call(this,{element:c,target:a.target});this.D=void 0!==a.keys?a.keys:!1;this.j=a.source}v(yd,md); -yd.prototype.C=function(a){a.preventDefault();Ad()&&(a=this.a)&&(zd()?document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen():(a=this.j?"string"===typeof this.j?document.getElementById(this.j):this.j:a.jd(),this.D?a.mozRequestFullScreenWithKeys?a.mozRequestFullScreenWithKeys():a.webkitRequestFullscreen?a.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT): -Bd(a):Bd(a)))};yd.prototype.u=function(){var a=this.element.firstElementChild,b=this.a;zd()?(a.className=this.c+"-true",kd(this.l,this.o)):(a.className=this.c+"-false",kd(this.o,this.l));b&&b.Ad()};yd.prototype.setMap=function(a){md.prototype.setMap.call(this,a);a&&this.v.push(y(document,Cd(),this.u,this))}; -function Ad(){var a=document.body;return!!(a.webkitRequestFullscreen||a.mozRequestFullScreen&&document.mozFullScreenEnabled||a.msRequestFullscreen&&document.msFullscreenEnabled||a.requestFullscreen&&document.fullscreenEnabled)}function zd(){return!!(document.webkitIsFullScreen||document.mozFullScreen||document.msFullscreenElement||document.fullscreenElement)} -function Bd(a){a.requestFullscreen?a.requestFullscreen():a.msRequestFullscreen?a.msRequestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen&&a.webkitRequestFullscreen()}var Cd=function(){var a;return function(){if(!a){var b=document.body;b.webkitRequestFullscreen?a="webkitfullscreenchange":b.mozRequestFullScreen?a="mozfullscreenchange":b.msRequestFullscreen?a="MSFullscreenChange":b.requestFullscreen&&(a="fullscreenchange")}return a}}();function Dd(a){a=a?a:{};var b=document.createElement("DIV");b.className=void 0!==a.className?a.className:"ol-mouse-position";md.call(this,{element:b,render:a.render?a.render:Ed,target:a.target});y(this,Vc(Fd),this.Ym,this);a.coordinateFormat&&this.kj(a.coordinateFormat);a.projection&&this.$h(a.projection);this.u=void 0!==a.undefinedHTML?a.undefinedHTML:"";this.l=b.innerHTML;this.o=this.j=this.c=null}v(Dd,md); -function Ed(a){a=a.frameState;a?this.c!=a.viewState.projection&&(this.c=a.viewState.projection,this.j=null):this.c=null;Gd(this,this.o)}k=Dd.prototype;k.Ym=function(){this.j=null};k.xh=function(){return this.get(Hd)};k.Zh=function(){return this.get(Fd)};k.Ll=function(a){this.o=this.a.xe(a);Gd(this,this.o)};k.Ml=function(){Gd(this,null);this.o=null};k.setMap=function(a){md.prototype.setMap.call(this,a);a&&(a=a.a,this.v.push(y(a,"mousemove",this.Ll,this),y(a,"mouseout",this.Ml,this)))}; -k.kj=function(a){this.set(Hd,a)};k.$h=function(a){this.set(Fd,Tb(a))};function Gd(a,b){var c=a.u;if(b&&a.c){if(!a.j){var d=a.Zh();a.j=d?Vb(a.c,d):fc}if(b=a.a.Wa(b))a.j(b,b),c=(c=a.xh())?c(b):b.toString()}a.l&&c==a.l||(a.element.innerHTML=c,a.l=c)}var Fd="projection",Hd="coordinateFormat";function Id(a,b,c){Oc.call(this,a);this.map=b;this.frameState=void 0!==c?c:null}v(Id,Oc);function Jd(a,b,c,d,e){Id.call(this,a,b,e);this.originalEvent=c;this.pixel=b.xe(c);this.coordinate=b.Wa(this.pixel);this.dragging=void 0!==d?d:!1}v(Jd,Id);Jd.prototype.preventDefault=function(){Id.prototype.preventDefault.call(this);this.originalEvent.preventDefault()};Jd.prototype.stopPropagation=function(){Id.prototype.stopPropagation.call(this);this.originalEvent.stopPropagation()};var Kd=["experimental-webgl","webgl","webkit-3d","moz-webgl"];function Ld(a,b){var c,d,e=Kd.length;for(d=0;d<e;++d)try{if(c=a.getContext(Kd[d],b))return c}catch(f){}return null};var Md,Nd="undefined"!==typeof navigator?navigator.userAgent.toLowerCase():"",Od=-1!==Nd.indexOf("firefox"),Pd=-1!==Nd.indexOf("safari")&&-1==Nd.indexOf("chrom"),Qd=-1!==Nd.indexOf("webkit")&&-1==Nd.indexOf("edge"),Rd=-1!==Nd.indexOf("macintosh"),Sd=window.devicePixelRatio||1,Td=!1,Ud=function(){if(!("HTMLCanvasElement"in window))return!1;try{var a=document.createElement("CANVAS").getContext("2d");return a?(void 0!==a.setLineDash&&(Td=!0),!0):!1}catch(b){return!1}}(),Vd="DeviceOrientationEvent"in -window,Wd="geolocation"in navigator,Xd="ontouchstart"in window,Yd="PointerEvent"in window,Zd=!!navigator.msPointerEnabled,$d=!1,ae,be=[];if("WebGLRenderingContext"in window)try{var ce=Ld(document.createElement("CANVAS"),{failIfMajorPerformanceCaveat:!0});ce&&($d=!0,ae=ce.getParameter(ce.MAX_TEXTURE_SIZE),be=ce.getSupportedExtensions())}catch(a){}Md=$d;fa=be;ea=ae;var de={Iq:"singleclick",xq:"click",yq:"dblclick",Bq:"pointerdrag",Eq:"pointermove",Aq:"pointerdown",Hq:"pointerup",Gq:"pointerover",Fq:"pointerout",Cq:"pointerenter",Dq:"pointerleave",zq:"pointercancel"};function ee(a,b,c,d,e){Jd.call(this,a,b,c.b,d,e);this.b=c}v(ee,Jd);function fe(a,b){this.b=a;this.f=b};function ge(a){fe.call(this,a,{mousedown:this.fm,mousemove:this.gm,mouseup:this.jm,mouseover:this.im,mouseout:this.hm});this.a=a.i;this.i=[]}v(ge,fe);function he(a,b){a=a.i;var c=b.clientX;b=b.clientY;for(var d=0,e=a.length,f;d<e&&(f=a[d]);d++){var g=Math.abs(b-f[1]);if(25>=Math.abs(c-f[0])&&25>=g)return!0}return!1}function ie(a){var b=je(a,a),c=b.preventDefault;b.preventDefault=function(){a.preventDefault();c()};b.pointerId=1;b.isPrimary=!0;b.pointerType="mouse";return b}k=ge.prototype; -k.fm=function(a){if(!he(this,a)){(1).toString()in this.a&&this.cancel(a);var b=ie(a);this.a[(1).toString()]=a;ke(this.b,"pointerdown",b,a)}};k.gm=function(a){if(!he(this,a)){var b=ie(a);ke(this.b,"pointermove",b,a)}};k.jm=function(a){if(!he(this,a)){var b=this.a[(1).toString()];b&&b.button===a.button&&(b=ie(a),ke(this.b,"pointerup",b,a),delete this.a[(1).toString()])}};k.im=function(a){if(!he(this,a)){var b=ie(a);le(this.b,b,a)}};k.hm=function(a){if(!he(this,a)){var b=ie(a);me(this.b,b,a)}}; -k.cancel=function(a){var b=ie(a);this.b.cancel(b,a);delete this.a[(1).toString()]};function ne(a){fe.call(this,a,{MSPointerDown:this.om,MSPointerMove:this.pm,MSPointerUp:this.sm,MSPointerOut:this.qm,MSPointerOver:this.rm,MSPointerCancel:this.nm,MSGotPointerCapture:this.lm,MSLostPointerCapture:this.mm});this.a=a.i;this.i=["","unavailable","touch","pen","mouse"]}v(ne,fe);function oe(a,b){var c=b;"number"===typeof b.pointerType&&(c=je(b,b),c.pointerType=a.i[b.pointerType]);return c}k=ne.prototype; -k.om=function(a){this.a[a.pointerId.toString()]=a;var b=oe(this,a);ke(this.b,"pointerdown",b,a)};k.pm=function(a){var b=oe(this,a);ke(this.b,"pointermove",b,a)};k.sm=function(a){var b=oe(this,a);ke(this.b,"pointerup",b,a);delete this.a[a.pointerId.toString()]};k.qm=function(a){var b=oe(this,a);me(this.b,b,a)};k.rm=function(a){var b=oe(this,a);le(this.b,b,a)};k.nm=function(a){var b=oe(this,a);this.b.cancel(b,a);delete this.a[a.pointerId.toString()]}; -k.mm=function(a){this.b.b(new pe("lostpointercapture",a,a))};k.lm=function(a){this.b.b(new pe("gotpointercapture",a,a))};function qe(a){fe.call(this,a,{pointerdown:this.ip,pointermove:this.jp,pointerup:this.mp,pointerout:this.kp,pointerover:this.lp,pointercancel:this.hp,gotpointercapture:this.ul,lostpointercapture:this.em})}v(qe,fe);k=qe.prototype;k.ip=function(a){re(this.b,a)};k.jp=function(a){re(this.b,a)};k.mp=function(a){re(this.b,a)};k.kp=function(a){re(this.b,a)};k.lp=function(a){re(this.b,a)};k.hp=function(a){re(this.b,a)};k.em=function(a){re(this.b,a)};k.ul=function(a){re(this.b,a)};function pe(a,b,c){Oc.call(this,a);this.b=b;a=c?c:{};this.buttons=se(a);this.pressure=te(a,this.buttons);this.bubbles="bubbles"in a?a.bubbles:!1;this.cancelable="cancelable"in a?a.cancelable:!1;this.view="view"in a?a.view:null;this.detail="detail"in a?a.detail:null;this.screenX="screenX"in a?a.screenX:0;this.screenY="screenY"in a?a.screenY:0;this.clientX="clientX"in a?a.clientX:0;this.clientY="clientY"in a?a.clientY:0;this.ctrlKey="ctrlKey"in a?a.ctrlKey:!1;this.altKey="altKey"in a?a.altKey:!1;this.shiftKey= -"shiftKey"in a?a.shiftKey:!1;this.metaKey="metaKey"in a?a.metaKey:!1;this.button="button"in a?a.button:0;this.relatedTarget="relatedTarget"in a?a.relatedTarget:null;this.pointerId="pointerId"in a?a.pointerId:0;this.width="width"in a?a.width:0;this.height="height"in a?a.height:0;this.tiltX="tiltX"in a?a.tiltX:0;this.tiltY="tiltY"in a?a.tiltY:0;this.pointerType="pointerType"in a?a.pointerType:"";this.isPrimary="isPrimary"in a?a.isPrimary:!1;b.preventDefault&&(this.preventDefault=function(){b.preventDefault()})} -v(pe,Oc);function se(a){if(a.buttons||ue)a=a.buttons;else switch(a.which){case 1:a=1;break;case 2:a=4;break;case 3:a=2;break;default:a=0}return a}function te(a,b){var c=0;a.pressure?c=a.pressure:c=b?.5:0;return c}var ue=!1;try{ue=1===(new MouseEvent("click",{buttons:1})).buttons}catch(a){};function ve(a,b){fe.call(this,a,{touchstart:this.rq,touchmove:this.qq,touchend:this.pq,touchcancel:this.oq});this.a=a.i;this.j=b;this.i=void 0;this.g=0;this.c=void 0}v(ve,fe);k=ve.prototype;k.ij=function(){this.g=0;this.c=void 0}; -function we(a,b,c){b=je(b,c);b.pointerId=c.identifier+2;b.bubbles=!0;b.cancelable=!0;b.detail=a.g;b.button=0;b.buttons=1;b.width=c.webkitRadiusX||c.radiusX||0;b.height=c.webkitRadiusY||c.radiusY||0;b.pressure=c.webkitForce||c.force||.5;b.isPrimary=a.i===c.identifier;b.pointerType="touch";b.clientX=c.clientX;b.clientY=c.clientY;b.screenX=c.screenX;b.screenY=c.screenY;return b} -function xe(a,b,c){function d(){b.preventDefault()}var e=Array.prototype.slice.call(b.changedTouches),f=e.length,g;for(g=0;g<f;++g){var h=we(a,b,e[g]);h.preventDefault=d;c.call(a,b,h)}} -k.rq=function(a){var b=a.touches,c=Object.keys(this.a),d=c.length;if(d>=b.length){var e=[],f;for(f=0;f<d;++f){var g=c[f];var h=this.a[g];var l;if(!(l=1==g))a:{for(var m=b.length,n=0;n<m;n++)if(l=b[n],l.identifier===g-2){l=!0;break a}l=!1}l||e.push(h.out)}for(f=0;f<e.length;++f)this.Kf(a,e[f])}b=a.changedTouches[0];c=Object.keys(this.a).length;if(!c||1===c&&(1).toString()in this.a)this.i=b.identifier,void 0!==this.c&&clearTimeout(this.c);ye(this,a);this.g++;xe(this,a,this.cp)}; -k.cp=function(a,b){this.a[b.pointerId]={target:b.target,out:b,Wi:b.target};var c=this.b;b.bubbles=!0;ke(c,"pointerover",b,a);c=this.b;b.bubbles=!1;ke(c,"pointerenter",b,a);ke(this.b,"pointerdown",b,a)};k.qq=function(a){a.preventDefault();xe(this,a,this.km)}; -k.km=function(a,b){var c=this.a[b.pointerId];if(c){var d=c.out,e=c.Wi;ke(this.b,"pointermove",b,a);d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,d.target=e,b.target?(me(this.b,d,a),le(this.b,b,a)):(b.target=e,b.relatedTarget=null,this.Kf(a,b)));c.out=b;c.Wi=b.target}};k.pq=function(a){ye(this,a);xe(this,a,this.sq)}; -k.sq=function(a,b){ke(this.b,"pointerup",b,a);this.b.out(b,a);ze(this.b,b,a);delete this.a[b.pointerId];b.isPrimary&&(this.i=void 0,this.c=setTimeout(this.ij.bind(this),200))};k.oq=function(a){xe(this,a,this.Kf)};k.Kf=function(a,b){this.b.cancel(b,a);this.b.out(b,a);ze(this.b,b,a);delete this.a[b.pointerId];b.isPrimary&&(this.i=void 0,this.c=setTimeout(this.ij.bind(this),200))}; -function ye(a,b){var c=a.j.i;b=b.changedTouches[0];if(a.i===b.identifier){var d=[b.clientX,b.clientY];c.push(d);setTimeout(function(){ma(c,d)},2500)}};function Ae(a){Qc.call(this);this.g=a;this.i={};this.f={};this.a=[];Yd?Be(this,new qe(this)):Zd?Be(this,new ne(this)):(a=new ge(this),Be(this,a),Xd&&Be(this,new ve(this,a)));a=this.a.length;for(var b,c=0;c<a;c++)b=this.a[c],Ce(this,Object.keys(b.f))}v(Ae,Qc);function Be(a,b){var c=Object.keys(b.f);c&&(c.forEach(function(a){var c=b.f[a];c&&(this.f[a]=c.bind(b))},a),a.a.push(b))}Ae.prototype.c=function(a){var b=this.f[a.type];b&&b(a)}; -function Ce(a,b){b.forEach(function(a){y(this.g,a,this.c,this)},a)}function De(a,b){b.forEach(function(a){Kc(this.g,a,this.c,this)},a)}function je(a,b){for(var c={},d,e=0,f=Ee.length;e<f;e++)d=Ee[e][0],c[d]=a[d]||b[d]||Ee[e][1];return c}function ze(a,b,c){b.bubbles=!1;ke(a,"pointerleave",b,c)}Ae.prototype.out=function(a,b){a.bubbles=!0;ke(this,"pointerout",a,b)};Ae.prototype.cancel=function(a,b){ke(this,"pointercancel",a,b)}; -function me(a,b,c){a.out(b,c);var d=b.target,e=b.relatedTarget;d&&e&&d.contains(e)||ze(a,b,c)}function le(a,b,c){b.bubbles=!0;ke(a,"pointerover",b,c);var d=b.target,e=b.relatedTarget;d&&e&&d.contains(e)||(b.bubbles=!1,ke(a,"pointerenter",b,c))}function ke(a,b,c,d){a.b(new pe(b,d,c))}function re(a,b){a.b(new pe(b.type,b,b))}Ae.prototype.ka=function(){for(var a=this.a.length,b,c=0;c<a;c++)b=this.a[c],De(this,Object.keys(b.f));Qc.prototype.ka.call(this)}; -var Ee=[["bubbles",!1],["cancelable",!1],["view",null],["detail",null],["screenX",0],["screenY",0],["clientX",0],["clientY",0],["ctrlKey",!1],["altKey",!1],["shiftKey",!1],["metaKey",!1],["button",0],["relatedTarget",null],["buttons",0],["pointerId",0],["width",0],["height",0],["pressure",0],["tiltX",0],["tiltY",0],["pointerType",""],["hwTimestamp",0],["isPrimary",!1],["type",""],["target",null],["currentTarget",null],["which",0]];function Fe(a,b){Qc.call(this);this.i=a;this.j=0;this.o=!1;this.f=[];this.D=b?b*Sd:Sd;this.c=null;a=this.i.a;this.S=0;this.u={};this.g=new Ae(a);this.a=null;this.l=y(this.g,"pointerdown",this.Ol,this);this.v=y(this.g,"pointermove",this.Lp,this)}v(Fe,Qc);function Ge(a,b){var c=new ee("click",a.i,b);a.b(c);a.j?(clearTimeout(a.j),a.j=0,c=new ee("dblclick",a.i,b),a.b(c)):a.j=setTimeout(function(){this.j=0;var a=new ee("singleclick",this.i,b);this.b(a)}.bind(a),250)} -function He(a,b){"pointerup"==b.type||"pointercancel"==b.type?delete a.u[b.pointerId]:"pointerdown"==b.type&&(a.u[b.pointerId]=!0);a.S=Object.keys(a.u).length}k=Fe.prototype;k.Jh=function(a){He(this,a);var b=new ee("pointerup",this.i,a);this.b(b);this.o||a.button||Ge(this,this.c);this.S||(this.f.forEach(Ec),this.f.length=0,this.o=!1,this.c=null,Nc(this.a),this.a=null)}; -k.Ol=function(a){He(this,a);var b=new ee("pointerdown",this.i,a);this.b(b);this.c=a;this.f.length||(this.a=new Ae(document),this.f.push(y(this.a,"pointermove",this.Hm,this),y(this.a,"pointerup",this.Jh,this),y(this.g,"pointercancel",this.Jh,this)))};k.Hm=function(a){if(Ie(this,a)){this.o=!0;var b=new ee("pointerdrag",this.i,a,this.o);this.b(b)}a.preventDefault()};k.Lp=function(a){this.b(new ee(a.type,this.i,a,!(!this.c||!Ie(this,a))))}; -function Ie(a,b){return Math.abs(b.clientX-a.c.clientX)>a.D||Math.abs(b.clientY-a.c.clientY)>a.D}k.ka=function(){this.v&&(Ec(this.v),this.v=null);this.l&&(Ec(this.l),this.l=null);this.f.forEach(Ec);this.f.length=0;this.a&&(Nc(this.a),this.a=null);this.g&&(Nc(this.g),this.g=null);Qc.prototype.ka.call(this)};function Ke(a,b){this.l=a;this.c=b;this.b=[];this.i=[];this.a={}}Ke.prototype.clear=function(){this.b.length=0;this.i.length=0;ub(this.a)};function Le(a){var b=a.b,c=a.i,d=b[0];1==b.length?(b.length=0,c.length=0):(b[0]=b.pop(),c[0]=c.pop(),Me(a,0));b=a.c(d);delete a.a[b];return d}Ke.prototype.f=function(a){xa(!(this.c(a)in this.a),31);var b=this.l(a);return Infinity!=b?(this.b.push(a),this.i.push(b),this.a[this.c(a)]=!0,Ne(this,0,this.b.length-1),!0):!1}; -function Me(a,b){for(var c=a.b,d=a.i,e=c.length,f=c[b],g=d[b],h=b;b<e>>1;){var l=2*b+1,m=2*b+2,l=m<e&&d[m]<d[l]?m:l;c[b]=c[l];d[b]=d[l];b=l}c[b]=f;d[b]=g;Ne(a,h,b)}function Ne(a,b,c){var d=a.b;a=a.i;for(var e=d[c],f=a[c];c>b;){var g=c-1>>1;if(a[g]>f)d[c]=d[g],a[c]=a[g],c=g;else break}d[c]=e;a[c]=f} -function Oe(a){var b=a.l,c=a.b,d=a.i,e=0,f=c.length,g;for(g=0;g<f;++g){var h=c[g];var l=b(h);Infinity==l?delete a.a[a.c(h)]:(d[e]=l,c[e++]=h)}c.length=e;d.length=e;for(b=(a.b.length>>1)-1;0<=b;b--)Me(a,b)};function Pe(a,b){Ke.call(this,function(b){return a.apply(null,b)},function(a){return a[0].bb()});this.v=b;this.j=0;this.g={}}v(Pe,Ke);Pe.prototype.f=function(a){var b=Ke.prototype.f.call(this,a);b&&y(a[0],"change",this.o,this);return b};Pe.prototype.o=function(a){a=a.target;var b=a.getState();if(2===b||3===b||4===b||5===b)Kc(a,"change",this.o,this),a=a.bb(),a in this.g&&(delete this.g[a],--this.j),this.v()}; -function Qe(a,b,c){for(var d=0,e,f;a.j<b&&d<c&&0<a.b.length;)e=Le(a)[0],f=e.bb(),0!==e.getState()||f in a.g||(a.g[f]=!0,++a.j,++d,e.load())};function Re(a){return function(b,c,d){if(void 0!==b)return b=ka(a,b,d),b=Ca(b+c,0,a.length-1),c=Math.floor(b),b!=c&&c<a.length-1?a[c]/Math.pow(a[c]/a[c+1],b-c):a[c]}}function Se(a,b,c){return function(d,e,f){if(void 0!==d)return d=Math.max(Math.floor(Math.log(b/d)/Math.log(a)+(-f/2+.5))+e,0),void 0!==c&&(d=Math.min(d,c)),b/Math.pow(a,d)}};function Te(a){if(void 0!==a)return 0}function Ue(a,b){if(void 0!==a)return a+b}function Ve(a){var b=2*Math.PI/a;return function(a,d){if(void 0!==a)return a=Math.floor((a+d)/b+.5)*b}}function We(){var a=Ha(5);return function(b,c){if(void 0!==b)return Math.abs(b+c)<=a?0:b+c}};function Xe(a,b){a=void 0!==b?a.toFixed(b):""+a;b=a.indexOf(".");b=-1===b?a.length:b;return 2<b?a:Array(3-b).join("0")+a}function Ye(a){a=(""+a).split(".");for(var b=["1","3"],c=0;c<Math.max(a.length,b.length);c++){var d=parseInt(a[c]||"0",10),e=parseInt(b[c]||"0",10);if(d>e)return 1;if(e>d)return-1}return 0};function Ze(a,b){a[0]+=b[0];a[1]+=b[1];return a}function $e(a,b){var c=b.pd(),d=b.wa();b=d[0];var d=d[1],e=a[0]-b;a=a[1]-d;e||a||(e=1);var f=Math.sqrt(e*e+a*a);return[b+c*e/f,d+c*a/f]}function af(a,b){var c=a[0];a=a[1];var d=b[0],e=b[1];b=d[0];var d=d[1],f=e[0],e=e[1],g=f-b,h=e-d,c=g||h?(g*(c-b)+h*(a-d))/(g*g+h*h||0):0;0>=c?(a=b,c=d):1<=c?(a=f,c=e):(a=b+c*g,c=d+c*h);return[a,c]} -function bf(a,b,c){b=Ia(b+180,360)-180;var d=Math.abs(3600*b);c=c||0;var e=Math.pow(10,c),f=Math.floor(d/3600),g=Math.floor((d-3600*f)/60),d=Math.ceil((d-3600*f-60*g)*e)/e;60<=d&&(d=0,g+=1);60<=g&&(g=0,f+=1);return f+"\u00b0 "+Xe(g)+"\u2032 "+Xe(d,c)+"\u2033"+(b?" "+a.charAt(0>b?1:0):"")}function cf(a,b,c){return a?b.replace("{x}",a[0].toFixed(c)).replace("{y}",a[1].toFixed(c)):""}function df(a,b){for(var c=!0,d=a.length-1;0<=d;--d)if(a[d]!=b[d]){c=!1;break}return c} -function ef(a,b){var c=Math.cos(b);b=Math.sin(b);var d=a[1]*c+a[0]*b;a[0]=a[0]*c-a[1]*b;a[1]=d;return a}function gf(a,b){a[0]*=b;a[1]*=b}function hf(a,b){var c=a[0]-b[0];a=a[1]-b[1];return c*c+a*a}function jf(a,b){return Math.sqrt(hf(a,b))}function kf(a,b){return hf(a,af(a,b))}function lf(a,b){return cf(a,"{x}, {y}",b)};function mf(){return!0}function nf(){return!1};function of(){Tc.call(this);this.l=Oa();this.v=-1;this.f={};this.o=this.g=0}v(of,Tc);k=of.prototype;k.Ab=function(a,b){b=b?b:[NaN,NaN];this.Kb(a[0],a[1],b,Infinity);return b};k.sb=function(a){return this.Mc(a[0],a[1])};k.Mc=nf;k.G=function(a){this.v!=this.i&&(this.l=this.se(this.l),this.v=this.i);var b=this.l;a?(a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3]):a=b;return a};k.Rb=function(a){return this.Vd(a*a)};k.tb=function(a,b){this.Dc(ec(a,b));return this};function pf(a,b,c,d,e,f){for(var g=f?f:[],h=0;b<c;b+=d){var l=a[b],m=a[b+1];g[h++]=e[0]*l+e[2]*m+e[4];g[h++]=e[1]*l+e[3]*m+e[5]}f&&g.length!=h&&(g.length=h);return g}function qf(a,b,c,d,e,f,g){for(var h=g?g:[],l=0,m;b<c;b+=d)for(h[l++]=a[b]+e,h[l++]=a[b+1]+f,m=b+2;m<b+d;++m)h[l++]=a[m];g&&h.length!=l&&(h.length=l);return h};function rf(){of.call(this);this.ja="XY";this.a=2;this.A=null}v(rf,of);function sf(a){var b;"XY"==a?b=2:"XYZ"==a||"XYM"==a?b=3:"XYZM"==a&&(b=4);return b}k=rf.prototype;k.Mc=nf;k.se=function(a){return $a(this.A,0,this.A.length,this.a,a)};k.ac=function(){return this.A.slice(0,this.a)};k.ga=function(){return this.A};k.bc=function(){return this.A.slice(this.A.length-this.a)};k.cc=function(){return this.ja}; -k.Vd=function(a){this.o!=this.i&&(ub(this.f),this.g=0,this.o=this.i);if(0>a||this.g&&a<=this.g)return this;var b=a.toString();if(this.f.hasOwnProperty(b))return this.f[b];var c=this.hd(a);if(c.ga().length<this.A.length)return this.f[b]=c;this.g=a;return this};k.hd=function(){return this};k.qa=function(){return this.a};function tf(a,b,c){a.a=sf(b);a.ja=b;a.A=c} -function uf(a,b,c,d){if(b)c=sf(b);else{for(b=0;b<d;++b)if(c.length)c=c[0];else{a.ja="XY";a.a=2;return}c=c.length;var e;2==c?e="XY":3==c?e="XYZ":4==c&&(e="XYZM");b=e}a.ja=b;a.a=c}k.Dc=function(a){this.A&&(a(this.A,this.A,this.a),this.s())}; -k.rotate=function(a,b){var c=this.ga();if(c){var d=c.length,e=this.qa(),f=c?c:[],g=Math.cos(a);a=Math.sin(a);var h=b[0];b=b[1];for(var l=0,m=0;m<d;m+=e){var n=c[m]-h,p=c[m+1]-b;f[l++]=h+n*g-p*a;f[l++]=b+n*a+p*g;for(n=m+2;n<m+e;++n)f[l++]=c[n]}c&&f.length!=l&&(f.length=l);this.s()}}; -k.scale=function(a,b,c){var d=b;void 0===d&&(d=a);var e=c;e||(e=nb(this.G()));if(c=this.ga()){b=c.length;for(var f=this.qa(),g=c?c:[],h=e[0],e=e[1],l=0,m=0;m<b;m+=f){var n=c[m]-h,p=c[m+1]-e;g[l++]=h+a*n;g[l++]=e+d*p;for(n=m+2;n<m+f;++n)g[l++]=c[n]}c&&g.length!=l&&(g.length=l);this.s()}};k.translate=function(a,b){var c=this.ga();c&&(qf(c,0,c.length,this.qa(),a,b,c),this.s())};function vf(a,b,c,d){for(var e=0,f=a[c-d],g=a[c-d+1];b<c;b+=d)var h=a[b],l=a[b+1],e=e+(g*h-f*l),f=h,g=l;return e/2}function wf(a,b,c,d){var e=0,f;var g=0;for(f=c.length;g<f;++g){var h=c[g],e=e+vf(a,b,h,d);b=h}return e};function xf(a,b,c,d,e,f,g){var h=a[b],l=a[b+1],m=a[c]-h,n=a[c+1]-l;if(m||n)if(f=((e-h)*m+(f-l)*n)/(m*m+n*n),1<f)b=c;else if(0<f){for(e=0;e<d;++e)g[e]=Ja(a[b+e],a[c+e],f);g.length=d;return}for(e=0;e<d;++e)g[e]=a[b+e];g.length=d}function yf(a,b,c,d,e){var f=a[b],g=a[b+1];for(b+=d;b<c;b+=d){var h=a[b],l=a[b+1],f=Ga(f,g,h,l);f>e&&(e=f);f=h;g=l}return e}function zf(a,b,c,d,e){var f;var g=0;for(f=c.length;g<f;++g){var h=c[g];e=yf(a,b,h,d,e);b=h}return e} -function Af(a,b,c,d,e,f,g,h,l,m,n){if(b==c)return m;if(!e){var p=Ga(g,h,a[b],a[b+1]);if(p<m){for(n=0;n<d;++n)l[n]=a[b+n];l.length=d;return p}return m}for(var q=n?n:[NaN,NaN],r=b+d;r<c;)if(xf(a,r-d,r,d,g,h,q),p=Ga(g,h,q[0],q[1]),p<m){m=p;for(n=0;n<d;++n)l[n]=q[n];l.length=d;r+=d}else r+=d*Math.max((Math.sqrt(p)-Math.sqrt(m))/e|0,1);if(f&&(xf(a,c-d,b,d,g,h,q),p=Ga(g,h,q[0],q[1]),p<m)){m=p;for(n=0;n<d;++n)l[n]=q[n];l.length=d}return m} -function Bf(a,b,c,d,e,f,g,h,l,m,n){n=n?n:[NaN,NaN];var p;var q=0;for(p=c.length;q<p;++q){var r=c[q];m=Af(a,b,r,d,e,f,g,h,l,m,n);b=r}return m};function Cf(a,b){var c=0,d;var e=0;for(d=b.length;e<d;++e)a[c++]=b[e];return c}function Df(a,b,c,d){var e;var f=0;for(e=c.length;f<e;++f){var g=c[f],h;for(h=0;h<d;++h)a[b++]=g[h]}return b}function Ef(a,b,c,d,e){e=e?e:[];var f=0,g;var h=0;for(g=c.length;h<g;++h)b=Df(a,b,c[h],d),e[f++]=b;e.length=f;return e};function Ff(a,b,c,d,e){e=void 0!==e?e:[];for(var f=0;b<c;b+=d)e[f++]=a.slice(b,b+d);e.length=f;return e}function Gf(a,b,c,d,e){e=void 0!==e?e:[];var f=0,g;var h=0;for(g=c.length;h<g;++h){var l=c[h];e[f++]=Ff(a,b,l,d,e[f]);b=l}e.length=f;return e};function Hf(a,b,c,d,e,f,g){var h=(c-b)/d;if(3>h){for(;b<c;b+=d)f[g++]=a[b],f[g++]=a[b+1];return g}var l=Array(h);l[0]=1;l[h-1]=1;c=[b,c-d];for(var m=0,n;0<c.length;){var p=c.pop(),q=c.pop(),r=0,u=a[q],x=a[q+1],B=a[p],E=a[p+1];for(n=q+d;n<p;n+=d){var A=Fa(a[n],a[n+1],u,x,B,E);A>r&&(m=n,r=A)}r>e&&(l[(m-b)/d]=1,q+d<m&&c.push(q,m),m+d<p&&c.push(m,p))}for(n=0;n<h;++n)l[n]&&(f[g++]=a[b+n*d],f[g++]=a[b+n*d+1]);return g} -function If(a,b,c,d,e,f,g,h){var l;var m=0;for(l=c.length;m<l;++m){var n=c[m];a:{var p=a,q=n,r=d,u=e,x=f,B=g;if(b!=q){var E=u*Math.round(p[b]/u),A=u*Math.round(p[b+1]/u);b+=r;x[B++]=E;x[B++]=A;do{var L=u*Math.round(p[b]/u);g=u*Math.round(p[b+1]/u);b+=r;if(b==q){x[B++]=L;x[B++]=g;g=B;break a}}while(L==E&&g==A);for(;b<q;){var oa=u*Math.round(p[b]/u);var ha=u*Math.round(p[b+1]/u);b+=r;if(oa!=L||ha!=g){var ga=L-E,z=g-A,M=oa-E,ba=ha-A;ga*ba==z*M&&(0>ga&&M<ga||ga==M||0<ga&&M>ga)&&(0>z&&ba<z||z==ba||0<z&& -ba>z)||(x[B++]=L,x[B++]=g,E=L,A=g);L=oa;g=ha}}x[B++]=L;x[B++]=g}g=B}h.push(g);b=n}return g};function Jf(a,b){rf.call(this);this.c=this.j=-1;this.ma(a,b)}v(Jf,rf);k=Jf.prototype;k.clone=function(){var a=new Jf(null);Kf(a,this.ja,this.A.slice());return a};k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;this.c!=this.i&&(this.j=Math.sqrt(yf(this.A,0,this.A.length,this.a,0)),this.c=this.i);return Af(this.A,0,this.A.length,this.a,this.j,!0,a,b,c,d)};k.qn=function(){return vf(this.A,0,this.A.length,this.a)};k.X=function(){return Ff(this.A,0,this.A.length,this.a)}; -k.hd=function(a){var b=[];b.length=Hf(this.A,0,this.A.length,this.a,a,b,0);a=new Jf(null);Kf(a,"XY",b);return a};k.U=function(){return"LinearRing"};k.Xa=function(){};k.ma=function(a,b){a?(uf(this,b,a,1),this.A||(this.A=[]),this.A.length=Df(this.A,0,a,this.a),this.s()):Kf(this,"XY",null)};function Kf(a,b,c){tf(a,b,c);a.s()};function C(a,b){rf.call(this);this.ma(a,b)}v(C,rf);k=C.prototype;k.clone=function(){var a=new C(null);a.ba(this.ja,this.A.slice());return a};k.Kb=function(a,b,c,d){var e=this.A;a=Ga(a,b,e[0],e[1]);if(a<d){d=this.a;for(b=0;b<d;++b)c[b]=e[b];c.length=d;return a}return d};k.X=function(){return this.A?this.A.slice():[]};k.se=function(a){return Za(this.A,a)};k.U=function(){return"Point"};k.Xa=function(a){return Ua(a,this.A[0],this.A[1])}; -k.ma=function(a,b){a?(uf(this,b,a,0),this.A||(this.A=[]),this.A.length=Cf(this.A,a),this.s()):this.ba("XY",null)};k.ba=function(a,b){tf(this,a,b);this.s()};function Lf(a,b,c,d,e){return!db(e,function(e){return!Mf(a,b,c,d,e[0],e[1])})}function Mf(a,b,c,d,e,f){for(var g=0,h=a[c-d],l=a[c-d+1];b<c;b+=d){var m=a[b],n=a[b+1];l<=f?n>f&&0<(m-h)*(f-l)-(e-h)*(n-l)&&g++:n<=f&&0>(m-h)*(f-l)-(e-h)*(n-l)&&g--;h=m;l=n}return!!g}function Nf(a,b,c,d,e,f){if(!c.length||!Mf(a,b,c[0],d,e,f))return!1;var g;b=1;for(g=c.length;b<g;++b)if(Mf(a,c[b-1],c[b],d,e,f))return!1;return!0};function Of(a,b,c,d,e,f,g){var h,l=e[f+1],m=[],n=c[0];var p=a[n-d];var q=a[n-d+1];for(h=b;h<n;h+=d){var r=a[h];var u=a[h+1];if(l<=q&&u<=l||q<=l&&l<=u)p=(l-q)/(u-q)*(r-p)+p,m.push(p);p=r;q=u}n=NaN;q=-Infinity;m.sort(ia);p=m[0];h=1;for(u=m.length;h<u;++h){r=m[h];var x=Math.abs(r-p);x>q&&(p=(p+r)/2,Nf(a,b,c,d,p,l)&&(n=p,q=x));p=r}isNaN(n)&&(n=e[f]);return g?(g.push(n,l),g):[n,l]};function Pf(a,b,c,d,e,f){for(var g=[a[b],a[b+1]],h=[],l;b+d<c;b+=d){h[0]=a[b+d];h[1]=a[b+d+1];if(l=e.call(f,g,h))return l;g[0]=h[0];g[1]=h[1]}return!1};function Qf(a,b,c,d,e){var f=ab(Oa(),a,b,c,d);return qb(e,f)?Va(e,f)||f[0]>=e[0]&&f[2]<=e[2]||f[1]>=e[1]&&f[3]<=e[3]?!0:Pf(a,b,c,d,function(a,b){var c=!1,d=Wa(e,a),f=Wa(e,b);if(1===d||1===f)c=!0;else{var g=e[0],h=e[1],r=e[2],u=e[3],x=b[0];b=b[1];a=(b-a[1])/(x-a[0]);f&2&&!(d&2)&&(c=x-(b-u)/a,c=c>=g&&c<=r);c||!(f&4)||d&4||(c=b-(x-r)*a,c=c>=h&&c<=u);c||!(f&8)||d&8||(c=x-(b-h)/a,c=c>=g&&c<=r);c||!(f&16)||d&16||(c=b-(x-g)*a,c=c>=h&&c<=u)}return c}):!1} -function Rf(a,b,c,d,e){var f=c[0];if(!(Qf(a,b,f,d,e)||Mf(a,b,f,d,e[0],e[1])||Mf(a,b,f,d,e[0],e[3])||Mf(a,b,f,d,e[2],e[1])||Mf(a,b,f,d,e[2],e[3])))return!1;if(1===c.length)return!0;b=1;for(f=c.length;b<f;++b)if(Lf(a,c[b-1],c[b],d,e))return!1;return!0};function Sf(a,b,c,d){for(var e=0,f=a[c-d],g=a[c-d+1];b<c;b+=d)var h=a[b],l=a[b+1],e=e+(h-f)*(l+g),f=h,g=l;return 0<e}function Tf(a,b,c,d){var e=0;d=void 0!==d?d:!1;var f;var g=0;for(f=b.length;g<f;++g){var h=b[g],e=Sf(a,e,h,c);if(!g){if(d&&e||!d&&!e)return!1}else if(d&&!e||!d&&e)return!1;e=h}return!0} -function Uf(a,b,c,d,e){e=void 0!==e?e:!1;var f;var g=0;for(f=c.length;g<f;++g){var h=c[g],l=Sf(a,b,h,d);if(g?e&&!l||!e&&l:e&&l||!e&&!l)for(var l=a,m=h,n=d;b<m-n;){var p;for(p=0;p<n;++p){var q=l[b+p];l[b+p]=l[m-n+p];l[m-n+p]=q}b+=n;m-=n}b=h}return b}function Vf(a,b,c,d){var e=0,f;var g=0;for(f=b.length;g<f;++g)e=Uf(a,e,b[g],c,d);return e};function D(a,b){rf.call(this);this.c=[];this.u=-1;this.D=null;this.I=this.C=this.B=-1;this.j=null;this.ma(a,b)}v(D,rf);k=D.prototype;k.pk=function(a){this.A?la(this.A,a.ga()):this.A=a.ga().slice();this.c.push(this.A.length);this.s()};k.clone=function(){var a=new D(null);a.ba(this.ja,this.A.slice(),this.c.slice());return a}; -k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;this.C!=this.i&&(this.B=Math.sqrt(zf(this.A,0,this.c,this.a,0)),this.C=this.i);return Bf(this.A,0,this.c,this.a,this.B,!0,a,b,c,d)};k.Mc=function(a,b){return Nf(this.ec(),0,this.c,this.a,a,b)};k.tn=function(){return wf(this.ec(),0,this.c,this.a)};k.X=function(a){if(void 0!==a){var b=this.ec().slice();Uf(b,0,this.c,this.a,a)}else b=this.A;return Gf(b,0,this.c,this.a)};k.Bb=function(){return this.c}; -function Wf(a){if(a.u!=a.i){var b=nb(a.G());a.D=Of(a.ec(),0,a.c,a.a,b,0);a.u=a.i}return a.D}k.Tk=function(){return new C(Wf(this))};k.Zk=function(){return this.c.length};k.Ch=function(a){if(0>a||this.c.length<=a)return null;var b=new Jf(null);Kf(b,this.ja,this.A.slice(a?this.c[a-1]:0,this.c[a]));return b};k.Sd=function(){var a=this.ja,b=this.A,c=this.c,d=[],e=0,f;var g=0;for(f=c.length;g<f;++g){var h=c[g],l=new Jf(null);Kf(l,a,b.slice(e,h));d.push(l);e=h}return d}; -k.ec=function(){if(this.I!=this.i){var a=this.A;Tf(a,this.c,this.a)?this.j=a:(this.j=a.slice(),this.j.length=Uf(this.j,0,this.c,this.a));this.I=this.i}return this.j};k.hd=function(a){var b=[],c=[];b.length=If(this.A,0,this.c,this.a,Math.sqrt(a),b,0,c);a=new D(null);a.ba("XY",b,c);return a};k.U=function(){return"Polygon"};k.Xa=function(a){return Rf(this.ec(),0,this.c,this.a,a)}; -k.ma=function(a,b){a?(uf(this,b,a,2),this.A||(this.A=[]),a=Ef(this.A,0,a,this.a,this.c),this.A.length=a.length?a[a.length-1]:0,this.s()):this.ba("XY",null,this.c)};k.ba=function(a,b,c){tf(this,a,b);this.c=c;this.s()};function Xf(a,b,c,d){var e=d?d:32;d=[];var f;for(f=0;f<e;++f)la(d,a.offset(b,c,2*Math.PI*f/e));d.push(d[0],d[1]);a=new D(null);a.ba("XY",d,[d.length]);return a}function Yf(a){var b=a[0],c=a[1],d=a[2];a=a[3];b=[b,c,b,a,d,a,d,c,b,c];c=new D(null);c.ba("XY",b,[b.length]);return c} -function Zf(a,b,c){var d=b?b:32,e=a.qa();b=a.ja;for(var f=new D(null,b),d=e*(d+1),e=Array(d),g=0;g<d;g++)e[g]=0;f.ba(b,e,[e.length]);$f(f,a.wa(),a.pd(),c);return f}function $f(a,b,c,d){var e=a.ga(),f=a.ja,g=a.qa(),h=a.Bb(),l=e.length/g-1;d=d?d:0;for(var m,n,p=0;p<=l;++p)n=p*g,m=d+2*Ia(p,l)*Math.PI/l,e[n]=b[0]+c*Math.cos(m),e[n+1]=b[1]+c*Math.sin(m);a.ba(f,e,h)};function F(a){Tc.call(this);a=tb({},a);this.o=[0,0];this.c=[];this.wf=this.wf.bind(this);this.v=$b(a.projection);ag(this,a)}v(F,Tc); -function ag(a,b){var c={};c.center=void 0!==b.center?b.center:null;var d=void 0!==b.minZoom?b.minZoom:0;var e=void 0!==b.maxZoom?b.maxZoom:28;var f=void 0!==b.zoomFactor?b.zoomFactor:2;if(void 0!==b.resolutions){e=b.resolutions;var g=e[0];var h=e[e.length-1];e=Re(e)}else{g=$b(b.projection);h=g.G();var l=(h?Math.max(lb(h),mb(h)):360*zb.degrees/g.sc())/256/Math.pow(2,0),m=l/Math.pow(2,28);g=b.maxResolution;void 0!==g?d=0:g=l/Math.pow(f,d);h=b.minResolution;void 0===h&&(h=void 0!==b.maxZoom?void 0!== -b.maxResolution?g/Math.pow(f,e):l/Math.pow(f,e):m);e=d+Math.floor(Math.log(g/h)/Math.log(f));h=g/Math.pow(f,e-d);e=Se(f,g,e-d)}a.a=g;a.f=h;a.C=f;a.j=b.resolutions;a.l=d;(void 0!==b.enableRotation?b.enableRotation:1)?(d=b.constrainRotation,d=void 0===d||!0===d?We():!1===d?Ue:"number"===typeof d?Ve(d):Ue):d=Te;a.g={center:void 0!==b.extent?Bc(b.extent):Cc,resolution:e,rotation:d};void 0!==b.resolution?c.resolution=b.resolution:void 0!==b.zoom&&(c.resolution=a.constrainResolution(a.a,b.zoom-a.l));c.rotation= -void 0!==b.rotation?b.rotation:0;a.H(c);a.D=b}function bg(a,b){var c=tb({},a.D);void 0!==c.resolution?c.resolution=a.Pa():c.zoom=a.Hh();c.center=a.wa();c.rotation=a.Qa();return tb({},c,b)}k=F.prototype; -k.animate=function(a){var b=Date.now(),c=this.wa().slice(),d=this.Pa(),e=this.Qa(),f=arguments.length;if(1<f&&"function"===typeof arguments[f-1]){var g=arguments[f-1];--f}for(var h=[],l=0;l<f;++l){var m=arguments[l],n={start:b,complete:!1,anchor:m.anchor,duration:void 0!==m.duration?m.duration:1E3,easing:m.easing||sd};m.center&&(n.Rg=c,n.Tg=m.center,c=n.Tg);void 0!==m.zoom?(n.tf=d,n.zd=this.constrainResolution(this.a,m.zoom-this.l,0),d=n.zd):m.resolution&&(n.tf=d,n.zd=m.resolution,d=n.zd);void 0!== -m.rotation&&(n.Sg=e,n.uf=m.rotation,e=n.uf);n.callback=g;b+=n.duration;h.push(n)}this.c.push(h);cg(this,0,1);this.wf()};k.Ic=function(){return 0<dg(this)[0]};k.Rk=function(){return 0<dg(this)[1]};k.ed=function(){cg(this,0,-dg(this)[0]);for(var a=0,b=this.c.length;a<b;++a){var c=this.c[a];c[0].callback&&c[0].callback(!1)}this.c.length=0}; -k.wf=function(){void 0!==this.u&&(cancelAnimationFrame(this.u),this.u=void 0);if(this.Ic()){for(var a=Date.now(),b=!1,c=this.c.length-1;0<=c;--c){for(var d=this.c[c],e=!0,f=0,g=d.length;f<g;++f){var h=d[f];if(!h.complete){b=a-h.start;b=0<h.duration?b/h.duration:1;1<=b?(h.complete=!0,b=1):e=!1;b=h.easing(b);if(h.Rg){var l=h.Rg[0],m=h.Rg[1];this.set("center",[l+b*(h.Tg[0]-l),m+b*(h.Tg[1]-m)])}h.tf&&h.zd&&(l=1===b?h.zd:h.tf+b*(h.zd-h.tf),h.anchor&&this.set("center",eg(this,l,h.anchor)),this.set("resolution", -l));void 0!==h.Sg&&void 0!==h.uf&&(b=1===b?h.uf:h.Sg+b*(h.uf-h.Sg),h.anchor&&this.set("center",fg(this,b,h.anchor)),this.set("rotation",b));b=!0;if(!h.complete)break}}e&&(this.c[c]=null,cg(this,0,-1),(d=d[0].callback)&&d(!0))}this.c=this.c.filter(Boolean);b&&void 0===this.u&&(this.u=requestAnimationFrame(this.wf))}};function fg(a,b,c){var d=a.wa();if(void 0!==d){var e=[d[0]-c[0],d[1]-c[1]];ef(e,b-a.Qa());Ze(e,c)}return e} -function eg(a,b,c){var d,e=a.wa();a=a.Pa();void 0!==e&&void 0!==a&&(d=[c[0]-b*(c[0]-e[0])/a,c[1]-b*(c[1]-e[1])/a]);return d}function gg(a){var b=[100,100];a='.ol-viewport[data-view="'+w(a)+'"]';if(a=document.querySelector(a))a=getComputedStyle(a),b[0]=parseInt(a.width,10),b[1]=parseInt(a.height,10);return b}k.Ec=function(a){return this.g.center(a)};k.constrainResolution=function(a,b,c){return this.g.resolution(a,b||0,c||0)};k.constrainRotation=function(a,b){return this.g.rotation(a,b||0)};k.wa=function(){return this.get("center")}; -function dg(a,b){return void 0!==b?(b[0]=a.o[0],b[1]=a.o[1],b):a.o.slice()}k.dd=function(a){a=a||gg(this);var b=this.wa();xa(b,1);var c=this.Pa();xa(void 0!==c,2);var d=this.Qa();xa(void 0!==d,3);return ob(b,c,d,a)};k.Nm=function(){return this.a};k.Pm=function(){return this.f};k.Om=function(){return this.Ce(this.f)};k.eq=function(a){ag(this,bg(this,{maxZoom:a}))};k.Qm=function(){return this.Ce(this.a)};k.fq=function(a){ag(this,bg(this,{minZoom:a}))};k.Rm=function(){return this.v};k.Pa=function(){return this.get("resolution")}; -k.Sm=function(){return this.j};k.ze=function(a,b){b=b||gg(this);return Math.max(lb(a)/b[0],mb(a)/b[1])};function hg(a){var b=a.a,c=Math.log(b/a.f)/Math.log(2);return function(a){return b/Math.pow(2,a*c)}}k.Qa=function(){return this.get("rotation")};function ig(a){var b=a.a,c=Math.log(b/a.f)/Math.log(2);return function(a){return Math.log(b/a)/Math.log(2)/c}}k.getState=function(){var a=this.wa(),b=this.v,c=this.Pa(),d=this.Qa();return{center:a.slice(),projection:void 0!==b?b:null,resolution:c,rotation:d}}; -k.Hh=function(){var a,b=this.Pa();void 0!==b&&(a=this.Ce(b));return a};k.Ce=function(a){if(a>=this.f&&a<=this.a){var b=this.l||0;if(this.j){var c=ka(this.j,a,1);b+=c;if(c==this.j.length-1)return b;var d=this.j[c];c=d/this.j[c+1]}else d=this.a,c=this.C;b+=Math.log(d/a)/Math.log(c)}return b}; -k.Qf=function(a,b){b=b||{};var c=b.size;c||(c=gg(this));if(a instanceof rf)if("Circle"===a.U()){a=a.G();var d=Yf(a);d.rotate(this.Qa(),nb(a))}else d=a;else xa(Array.isArray(a),24),xa(!kb(a),25),d=Yf(a);var e=b.padding?b.padding:[0,0,0,0],f=void 0!==b.constrainResolution?b.constrainResolution:!0,g=void 0!==b.nearest?b.nearest:!1,h;void 0!==b.minResolution?h=b.minResolution:void 0!==b.maxZoom?h=this.constrainResolution(this.a,b.maxZoom-this.l,0):h=0;var l=d.ga(),m=this.Qa();a=Math.cos(-m);var m=Math.sin(-m), -n=Infinity,p=Infinity,q=-Infinity,r=-Infinity;d=d.qa();for(var u=0,x=l.length;u<x;u+=d)var B=l[u]*a-l[u+1]*m,E=l[u]*m+l[u+1]*a,n=Math.min(n,B),p=Math.min(p,E),q=Math.max(q,B),r=Math.max(r,E);c=this.ze([n,p,q,r],[c[0]-e[1]-e[3],c[1]-e[0]-e[2]]);c=isNaN(c)?h:Math.max(c,h);f&&(h=this.constrainResolution(c,0,0),!g&&h<c&&(h=this.constrainResolution(h,-1,0)),c=h);m=-m;h=(n+q)/2+(e[1]-e[3])/2*c;e=(p+r)/2+(e[0]-e[2])/2*c;a=[h*a-e*m,e*a+h*m];e=b.callback?b.callback:ua;void 0!==b.duration?this.animate({resolution:c, -center:a,duration:b.duration,easing:b.easing},e):(this.Vc(c),this.ob(a),setTimeout(e.bind(void 0,!0),0))};k.uk=function(a,b,c){var d=this.Qa(),e=Math.cos(-d),d=Math.sin(-d),f=a[0]*e-a[1]*d;a=a[1]*e+a[0]*d;var g=this.Pa(),f=f+(b[0]/2-c[0])*g;a+=(c[1]-b[1]/2)*g;d=-d;this.ob([f*e-a*d,a*e+f*d])};function jg(a){return!!a.wa()&&void 0!==a.Pa()}k.rotate=function(a,b){void 0!==b&&(b=fg(this,a,b),this.ob(b));this.Oe(a)};k.ob=function(a){this.set("center",a);this.Ic()&&this.ed()}; -function cg(a,b,c){a.o[b]+=c;a.s()}k.Vc=function(a){this.set("resolution",a);this.Ic()&&this.ed()};k.Oe=function(a){this.set("rotation",a);this.Ic()&&this.ed()};k.lq=function(a){a=this.constrainResolution(this.a,a-this.l,0);this.Vc(a)};function kg(a,b,c){this.f=a;this.c=b;this.g=c;this.b=[];this.a=this.i=0}function lg(a){a.b.length=0;a.i=0;a.a=0}function mg(a){if(6>a.b.length)return!1;var b=Date.now()-a.g,c=a.b.length-3;if(a.b[c+2]<b)return!1;for(var d=c-3;0<d&&a.b[d+2]>b;)d-=3;b=a.b[c+2]-a.b[d+2];if(b<1E3/60)return!1;var e=a.b[c]-a.b[d],c=a.b[c+1]-a.b[d+1];a.i=Math.atan2(c,e);a.a=Math.sqrt(e*e+c*c)/b;return a.a>a.c};function ng(a){Tc.call(this);this.v=null;this.Ha(!0);this.handleEvent=a.handleEvent}v(ng,Tc);ng.prototype.c=function(){return this.get("active")};ng.prototype.f=function(){return this.v};ng.prototype.Ha=function(a){this.set("active",a)};ng.prototype.setMap=function(a){this.v=a};function og(a,b,c,d){if(void 0!==b){var e=a.Qa(),f=a.wa();void 0!==e&&f&&0<d?a.animate({rotation:b,anchor:c,duration:d,easing:rd}):a.rotate(b,c)}} -function pg(a,b,c,d){var e=a.Pa();b=a.constrainResolution(e,b,0);if(c&&void 0!==b&&b!==e){var f=a.wa();c=eg(a,b,c);c=a.Ec(c);c=[(b*f[0]-e*c[0])/(b-e),(b*f[1]-e*c[1])/(b-e)]}qg(a,b,c,d)}function qg(a,b,c,d){if(b){var e=a.Pa(),f=a.wa();void 0!==e&&f&&b!==e&&d?a.animate({resolution:b,anchor:c,duration:d,easing:rd}):(c&&(c=eg(a,b,c),a.ob(c)),a.Vc(b))}};function rg(a){a=a?a:{};this.a=a.delta?a.delta:1;ng.call(this,{handleEvent:sg});this.g=void 0!==a.duration?a.duration:250}v(rg,ng);function sg(a){var b=!1,c=a.originalEvent;if("dblclick"==a.type){var b=a.coordinate,c=c.shiftKey?-this.a:this.a,d=a.map.Z();pg(d,c,b,this.g);a.preventDefault();b=!0}return!b};function tg(a){a=a.originalEvent;return a.altKey&&!(a.metaKey||a.ctrlKey)&&a.shiftKey}function ug(a){a=a.originalEvent;return!a.button&&!(Qd&&Rd&&a.ctrlKey)}function vg(a){return"pointermove"==a.type}function wg(a){return"singleclick"==a.type}function xg(a){a=a.originalEvent;return!a.altKey&&!(a.metaKey||a.ctrlKey)&&!a.shiftKey}function yg(a){a=a.originalEvent;return!a.altKey&&!(a.metaKey||a.ctrlKey)&&a.shiftKey} -function Ag(a){a=a.originalEvent.target.tagName;return"INPUT"!==a&&"SELECT"!==a&&"TEXTAREA"!==a}function Bg(a){xa(a.b,56);return"mouse"==a.b.pointerType}function Cg(a){a=a.b;return a.isPrimary&&0===a.button};function Dg(a){a=a?a:{};ng.call(this,{handleEvent:a.handleEvent?a.handleEvent:Eg});this.yf=a.handleDownEvent?a.handleDownEvent:nf;this.If=a.handleDragEvent?a.handleDragEvent:ua;this.Jf=a.handleMoveEvent?a.handleMoveEvent:ua;this.sk=a.handleUpEvent?a.handleUpEvent:nf;this.D=!1;this.na={};this.o=[]}v(Dg,ng);function Fg(a){for(var b=a.length,c=0,d=0,e=0;e<b;e++)c+=a[e].clientX,d+=a[e].clientY;return[c/b,d/b]} -function Eg(a){if(!(a instanceof ee))return!0;var b=!1,c=a.type;if("pointerdown"===c||"pointerdrag"===c||"pointerup"===c)c=a.b,"pointerup"==a.type?delete this.na[c.pointerId]:"pointerdown"==a.type?this.na[c.pointerId]=c:c.pointerId in this.na&&(this.na[c.pointerId]=c),this.o=vb(this.na);this.D?"pointerdrag"==a.type?this.If(a):"pointerup"==a.type&&(this.D=this.sk(a)&&0<this.o.length):"pointerdown"==a.type?(this.D=a=this.yf(a),b=this.Xc(a)):"pointermove"==a.type&&this.Jf(a);return!b} -Dg.prototype.Xc=function(a){return a};function Gg(a){Dg.call(this,{handleDownEvent:Hg,handleDragEvent:Ig,handleUpEvent:Jg});a=a?a:{};this.a=a.kinetic;this.g=null;this.u=a.condition?a.condition:xg;this.j=!1}v(Gg,Dg);function Ig(a){var b=this.o,c=Fg(b);if(b.length==this.l){if(this.a&&this.a.b.push(c[0],c[1],Date.now()),this.g){var d=this.g[0]-c[0],e=c[1]-this.g[1];a=a.map.Z();var f=a.getState(),d=[d,e];gf(d,f.resolution);ef(d,f.rotation);Ze(d,f.center);d=a.Ec(d);a.ob(d)}}else this.a&&lg(this.a);this.g=c;this.l=b.length} -function Jg(a){var b=a.map;a=b.Z();if(this.o.length)return this.a&&lg(this.a),this.g=null,!0;if(!this.j&&this.a&&mg(this.a)){var c=this.a;c=(c.c-c.a)/c.f;var d=this.a.i,e=a.wa(),e=b.Ja(e),b=b.Wa([e[0]-c*Math.cos(d),e[1]-c*Math.sin(d)]);a.animate({center:a.Ec(b),duration:500,easing:rd})}cg(a,1,-1);return!1} -function Hg(a){if(0<this.o.length&&this.u(a)){var b=a.map.Z();this.g=null;this.D||cg(b,1,1);dg(b)[0]&&b.ob(a.frameState.viewState.center);this.a&&lg(this.a);this.j=1<this.o.length;return!0}return!1}Gg.prototype.Xc=nf;function Kg(a){a=a?a:{};Dg.call(this,{handleDownEvent:Lg,handleDragEvent:Mg,handleUpEvent:Ng});this.g=a.condition?a.condition:tg;this.a=void 0;this.j=void 0!==a.duration?a.duration:250}v(Kg,Dg);function Mg(a){if(Bg(a)){var b=a.map,c=b.Z();if(c.g.rotation!==Te){b=b.Ob();a=a.pixel;a=Math.atan2(b[1]/2-a[1],a[0]-b[0]/2);if(void 0!==this.a){var b=a-this.a,d=c.Qa();og(c,d-b)}this.a=a}}} -function Ng(a){if(!Bg(a))return!0;a=a.map.Z();cg(a,1,-1);var b=a.Qa(),c=this.j,b=a.constrainRotation(b,0);og(a,b,void 0,c);return!1}function Lg(a){return Bg(a)&&ug(a)&&this.g(a)?(cg(a.map.Z(),1,1),this.a=void 0,!0):!1}Kg.prototype.Xc=nf;function Og(a){this.Gc=null;this.a=document.createElement("div");this.a.style.position="absolute";this.a.className="ol-box "+a;this.i=this.c=this.b=null}v(Og,Mc);Og.prototype.ka=function(){this.setMap(null)};function Pg(a){var b=a.c,c=a.i;a=a.a.style;a.left=Math.min(b[0],c[0])+"px";a.top=Math.min(b[1],c[1])+"px";a.width=Math.abs(c[0]-b[0])+"px";a.height=Math.abs(c[1]-b[1])+"px"} -Og.prototype.setMap=function(a){if(this.b){this.b.C.removeChild(this.a);var b=this.a.style;b.left=b.top=b.width=b.height="inherit"}(this.b=a)&&this.b.C.appendChild(this.a)};function Qg(a){var b=a.c,c=a.i,b=[b,[b[0],c[1]],c,[c[0],b[1]]].map(a.b.Wa,a.b);b[4]=b[0].slice();a.Gc?a.Gc.ma([b]):a.Gc=new D([b])}Og.prototype.V=function(){return this.Gc};function Rg(a){Dg.call(this,{handleDownEvent:Sg,handleDragEvent:Tg,handleUpEvent:Ug});a=a?a:{};this.a=new Og(a.className||"ol-dragbox");this.u=void 0!==a.minArea?a.minArea:64;this.g=null;this.C=a.condition?a.condition:mf;this.l=a.boxEndCondition?a.boxEndCondition:Vg}v(Rg,Dg);function Vg(a,b,c){a=c[0]-b[0];b=c[1]-b[1];return a*a+b*b>=this.u}function Tg(a){if(Bg(a)){var b=this.a,c=a.pixel;b.c=this.g;b.i=c;Qg(b);Pg(b);this.b(new Wg(Xg,a.coordinate,a))}}Rg.prototype.V=function(){return this.a.V()}; -Rg.prototype.j=ua;function Ug(a){if(!Bg(a))return!0;this.a.setMap(null);this.l(a,this.g,a.pixel)&&(this.j(a),this.b(new Wg(Yg,a.coordinate,a)));return!1}function Sg(a){if(Bg(a)&&ug(a)&&this.C(a)){this.g=a.pixel;this.a.setMap(a.map);var b=this.a,c=this.g;b.c=this.g;b.i=c;Qg(b);Pg(b);this.b(new Wg(Zg,a.coordinate,a));return!0}return!1}var Zg="boxstart",Xg="boxdrag",Yg="boxend";function Wg(a,b,c){Oc.call(this,a);this.coordinate=b;this.mapBrowserEvent=c}v(Wg,Oc);function $g(a){a=a?a:{};var b=a.condition?a.condition:yg;this.B=void 0!==a.duration?a.duration:200;this.I=void 0!==a.out?a.out:!1;Rg.call(this,{condition:b,className:a.className||"ol-dragzoom"})}v($g,Rg); -$g.prototype.j=function(){var a=this.v,b=a.Z(),c=a.Ob(),d=this.V().G();if(this.I){var e=b.dd(c),d=[a.Ja(eb(d)),a.Ja(hb(d))],a=Ya(void 0),f;var g=0;for(f=d.length;g<f;++g)Pa(a,d[g]);d=b.ze(a,c);rb(e,1/d);d=e}c=b.constrainResolution(b.ze(d,c));e=nb(d);e=b.Ec(e);b.animate({resolution:c,center:e,duration:this.B,easing:rd})};function ah(a){ng.call(this,{handleEvent:bh});a=a||{};this.a=function(a){return xg(a)&&Ag(a)};this.g=a.condition?a.condition:this.a;this.j=void 0!==a.duration?a.duration:100;this.o=void 0!==a.pixelDelta?a.pixelDelta:128}v(ah,ng); -function bh(a){var b=!1;if("keydown"==a.type){var c=a.originalEvent.keyCode;if(this.g(a)&&(40==c||37==c||39==c||38==c)){var b=a.map.Z(),d=b.Pa()*this.o,e=0,f=0;40==c?f=-d:37==c?e=-d:39==c?e=d:f=d;d=[e,f];ef(d,b.Qa());c=this.j;if(e=b.wa())d=b.Ec([e[0]+d[0],e[1]+d[1]]),c?b.animate({duration:c,easing:td,center:d}):b.ob(d);a.preventDefault();b=!0}}return!b};function ch(a){ng.call(this,{handleEvent:dh});a=a?a:{};this.g=a.condition?a.condition:Ag;this.a=a.delta?a.delta:1;this.j=void 0!==a.duration?a.duration:100}v(ch,ng);function dh(a){var b=!1;if("keydown"==a.type||"keypress"==a.type){var c=a.originalEvent.charCode;!this.g(a)||43!=c&&45!=c||(b=43==c?this.a:-this.a,c=a.map.Z(),pg(c,b,void 0,this.j),a.preventDefault(),b=!0)}return!b};function eh(a){ng.call(this,{handleEvent:fh});a=a||{};this.j=0;this.D=void 0!==a.duration?a.duration:250;this.na=void 0!==a.timeout?a.timeout:80;this.C=void 0!==a.useAnchor?a.useAnchor:!0;this.R=a.constrainResolution||!1;this.a=null;this.l=this.o=this.u=this.g=void 0}v(eh,ng); -function fh(a){var b=a.type;if("wheel"!==b&&"mousewheel"!==b)return!0;a.preventDefault();var b=a.map,c=a.originalEvent;this.C&&(this.a=a.coordinate);if("wheel"==a.type){var d=c.deltaY;Od&&c.deltaMode===WheelEvent.DOM_DELTA_PIXEL&&(d/=Sd);c.deltaMode===WheelEvent.DOM_DELTA_LINE&&(d*=40)}else"mousewheel"==a.type&&(d=-c.wheelDeltaY,Pd&&(d/=3));if(0===d)return!1;a=Date.now();void 0===this.g&&(this.g=a);if(!this.o||400<a-this.g)this.o=4>Math.abs(d)?gh:hh;if(this.o===gh){b=b.Z();this.l?clearTimeout(this.l): -cg(b,1,1);this.l=setTimeout(this.B.bind(this),400);var c=b.Pa()*Math.pow(2,d/300),e=b.f,f=b.a,g=0;c<e?(c=Math.max(c,e/1.5),g=1):c>f&&(c=Math.min(c,1.5*f),g=-1);if(this.a){var h=eg(b,c,this.a);b.ob(b.Ec(h))}b.Vc(c);!g&&this.R&&b.animate({resolution:b.constrainResolution(c,0<d?-1:1),easing:rd,anchor:this.a,duration:this.D});0<g?b.animate({resolution:e,easing:rd,anchor:this.a,duration:500}):0>g&&b.animate({resolution:f,easing:rd,anchor:this.a,duration:500});this.g=a;return!1}this.j+=d;d=Math.max(this.na- -(a-this.g),0);clearTimeout(this.u);this.u=setTimeout(this.I.bind(this,b),d);return!1}eh.prototype.B=function(){this.l=void 0;cg(this.v.Z(),1,-1)};eh.prototype.I=function(a){a=a.Z();a.Ic()&&a.ed();pg(a,-Ca(this.j,-1,1),this.a,this.D);this.o=void 0;this.j=0;this.a=null;this.u=this.g=void 0};eh.prototype.T=function(a){this.C=a;a||(this.a=null)};var gh="trackpad",hh="wheel";function ih(a){Dg.call(this,{handleDownEvent:jh,handleDragEvent:kh,handleUpEvent:lh});a=a||{};this.g=null;this.j=void 0;this.a=!1;this.l=0;this.C=void 0!==a.threshold?a.threshold:.3;this.u=void 0!==a.duration?a.duration:250}v(ih,Dg); -function kh(a){var b=0,c=this.o[0],d=this.o[1],c=Math.atan2(d.clientY-c.clientY,d.clientX-c.clientX);void 0!==this.j&&(b=c-this.j,this.l+=b,!this.a&&Math.abs(this.l)>this.C&&(this.a=!0));this.j=c;a=a.map;c=a.Z();if(c.g.rotation!==Te){var d=a.a.getBoundingClientRect(),e=Fg(this.o);e[0]-=d.left;e[1]-=d.top;this.g=a.Wa(e);this.a&&(d=c.Qa(),a.render(),og(c,d+b,this.g))}} -function lh(a){if(2>this.o.length){a=a.map.Z();cg(a,1,-1);if(this.a){var b=a.Qa(),c=this.g,d=this.u,b=a.constrainRotation(b,0);og(a,b,c,d)}return!1}return!0}function jh(a){return 2<=this.o.length?(a=a.map,this.g=null,this.j=void 0,this.a=!1,this.l=0,this.D||cg(a.Z(),1,1),!0):!1}ih.prototype.Xc=nf;function mh(a){Dg.call(this,{handleDownEvent:nh,handleDragEvent:oh,handleUpEvent:ph});a=a?a:{};this.l=a.constrainResolution||!1;this.g=null;this.u=void 0!==a.duration?a.duration:400;this.a=void 0;this.j=1}v(mh,Dg); -function oh(a){var b=1,c=this.o[0],d=this.o[1],e=c.clientX-d.clientX,c=c.clientY-d.clientY,e=Math.sqrt(e*e+c*c);void 0!==this.a&&(b=this.a/e);this.a=e;a=a.map;var e=a.Z(),d=e.Pa(),f=e.a,g=e.f,c=d*b;c>f?(b=f/d,c=f):c<g&&(b=g/d,c=g);1!=b&&(this.j=b);b=a.a.getBoundingClientRect();d=Fg(this.o);d[0]-=b.left;d[1]-=b.top;this.g=a.Wa(d);a.render();qg(e,c,this.g)} -function ph(a){if(2>this.o.length){a=a.map.Z();cg(a,1,-1);var b=a.Pa();if(this.l||b<a.f||b>a.a){var c=this.g,d=this.u,b=a.constrainResolution(b,0,this.j-1);qg(a,b,c,d)}return!1}return!0}function nh(a){return 2<=this.o.length?(a=a.map,this.g=null,this.a=void 0,this.j=1,this.D||cg(a.Z(),1,1),!0):!1}mh.prototype.Xc=nf;function qh(a){a=a?a:{};var b=new Yc,c=new kg(-.005,.05,100);(void 0!==a.altShiftDragRotate?a.altShiftDragRotate:1)&&b.push(new Kg);(void 0!==a.doubleClickZoom?a.doubleClickZoom:1)&&b.push(new rg({delta:a.zoomDelta,duration:a.zoomDuration}));(void 0!==a.dragPan?a.dragPan:1)&&b.push(new Gg({kinetic:c}));(void 0!==a.pinchRotate?a.pinchRotate:1)&&b.push(new ih);(void 0!==a.pinchZoom?a.pinchZoom:1)&&b.push(new mh({constrainResolution:a.constrainResolution,duration:a.zoomDuration}));if(void 0!==a.keyboard? -a.keyboard:1)b.push(new ah),b.push(new ch({delta:a.zoomDelta,duration:a.zoomDuration}));(void 0!==a.mouseWheelZoom?a.mouseWheelZoom:1)&&b.push(new eh({constrainResolution:a.constrainResolution,duration:a.zoomDuration}));(void 0!==a.shiftDragZoom?a.shiftDragZoom:1)&&b.push(new $g({duration:a.zoomDuration}));return b};function sh(a){Tc.call(this);var b=tb({},a);b.opacity=void 0!==a.opacity?a.opacity:1;b.visible=void 0!==a.visible?a.visible:!0;b.zIndex=void 0!==a.zIndex?a.zIndex:0;b.maxResolution=void 0!==a.maxResolution?a.maxResolution:Infinity;b.minResolution=void 0!==a.minResolution?a.minResolution:0;this.H(b);this.a={layer:this,Je:!0}}v(sh,Tc); -function th(a){a.a.opacity=Ca(a.hc(),0,1);a.a.yj=a.$f();a.a.visible=a.Mb();a.a.extent=a.G();a.a.zIndex=a.Ba();a.a.maxResolution=a.fc();a.a.minResolution=Math.max(a.gc(),0);return a.a}k=sh.prototype;k.G=function(){return this.get("extent")};k.fc=function(){return this.get("maxResolution")};k.gc=function(){return this.get("minResolution")};k.hc=function(){return this.get("opacity")};k.Mb=function(){return this.get("visible")};k.Ba=function(){return this.get("zIndex")}; -k.vc=function(a){this.set("extent",a)};k.Ac=function(a){this.set("maxResolution",a)};k.Bc=function(a){this.set("minResolution",a)};k.wc=function(a){this.set("opacity",a)};k.xc=function(a){this.set("visible",a)};k.Vb=function(a){this.set("zIndex",a)};function uh(a){var b=a||{};a=tb({},b);delete a.layers;b=b.layers;sh.call(this,a);this.f=[];this.c={};y(this,Vc(vh),this.Hl,this);b?Array.isArray(b)?b=new Yc(b.slice(),{unique:!0}):xa(b instanceof Yc,43):b=new Yc(void 0,{unique:!0});this.xi(b)}v(uh,sh);k=uh.prototype;k.Fd=function(){};k.Fe=function(){this.Mb()&&this.s()}; -k.Hl=function(){this.f.forEach(Ec);this.f.length=0;var a=this.qd();this.f.push(y(a,"add",this.Gl,this),y(a,"remove",this.Il,this));for(var b in this.c)this.c[b].forEach(Ec);ub(this.c);var a=a.a,c;b=0;for(c=a.length;b<c;b++){var d=a[b];this.c[w(d).toString()]=[y(d,"propertychange",this.Fe,this),y(d,"change",this.Fe,this)]}this.s()};k.Gl=function(a){a=a.element;var b=w(a).toString();this.c[b]=[y(a,"propertychange",this.Fe,this),y(a,"change",this.Fe,this)];this.s()}; -k.Il=function(a){a=w(a.element).toString();this.c[a].forEach(Ec);delete this.c[a];this.s()};k.qd=function(){return this.get(vh)};k.xi=function(a){this.set(vh,a)}; -k.Yf=function(a){var b=void 0!==a?a:[],c=b.length;this.qd().forEach(function(a){a.Yf(b)});a=th(this);var d;for(d=b.length;c<d;c++){var e=b[c];e.opacity*=a.opacity;e.visible=e.visible&&a.visible;e.maxResolution=Math.min(e.maxResolution,a.maxResolution);e.minResolution=Math.max(e.minResolution,a.minResolution);void 0!==a.extent&&(e.extent=void 0!==e.extent?pb(e.extent,a.extent):a.extent)}return b};k.$f=function(){return"ready"};var vh="layers";function wh(a){var b=tb({},a);delete b.source;sh.call(this,b);this.v=this.l=this.o=null;a.map&&this.setMap(a.map);y(this,Vc("source"),this.Ul,this);this.Wc(a.source?a.source:null)}v(wh,sh);function xh(a,b){return a.visible&&b>=a.minResolution&&b<a.maxResolution}k=wh.prototype;k.Yf=function(a){a=a?a:[];a.push(th(this));return a};k.ha=function(){return this.get("source")||null};k.$f=function(){var a=this.ha();return a?a.getState():"undefined"};k.Tn=function(){this.s()}; -k.Ul=function(){this.v&&(Ec(this.v),this.v=null);var a=this.ha();a&&(this.v=y(a,"change",this.Tn,this));this.s()};k.setMap=function(a){this.o&&(Ec(this.o),this.o=null);a||this.s();this.l&&(Ec(this.l),this.l=null);a&&(this.o=y(a,"precompose",function(a){var b=th(this);b.Je=!1;b.zIndex=Infinity;a.frameState.layerStatesArray.push(b);a.frameState.layerStates[w(this)]=b},this),this.l=y(this,"change",a.render,a),this.s())};k.Wc=function(a){this.set("source",a)};function yh(){this.b={};this.a=0}yh.prototype.clear=function(){this.b={};this.a=0};yh.prototype.get=function(a,b,c){a=b+":"+a+":"+(c?gd(c):"null");return a in this.b?this.b[a]:null};yh.prototype.set=function(a,b,c,d){this.b[b+":"+a+":"+(c?gd(c):"null")]=d;++this.a};var zh=new yh;var Ah=Array(6);function Bh(){return[1,0,0,1,0,0]}function Ch(a){return Dh(a,1,0,0,1,0,0)}function Eh(a,b){var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],l=b[0],m=b[1],n=b[2],p=b[3],q=b[4];b=b[5];a[0]=c*l+e*m;a[1]=d*l+f*m;a[2]=c*n+e*p;a[3]=d*n+f*p;a[4]=c*q+e*b+g;a[5]=d*q+f*b+h;return a}function Dh(a,b,c,d,e,f,g){a[0]=b;a[1]=c;a[2]=d;a[3]=e;a[4]=f;a[5]=g;return a}function Fh(a,b){a[0]=b[0];a[1]=b[1];a[2]=b[2];a[3]=b[3];a[4]=b[4];a[5]=b[5];return a} -function Gh(a,b){var c=b[0],d=b[1];b[0]=a[0]*c+a[2]*d+a[4];b[1]=a[1]*c+a[3]*d+a[5];return b}function Hh(a,b){var c=Math.cos(b);b=Math.sin(b);Eh(a,Dh(Ah,c,b,-b,c,0,0))}function Ih(a,b,c){return Eh(a,Dh(Ah,b,0,0,c,0,0))}function Jh(a,b,c){Eh(a,Dh(Ah,1,0,0,1,b,c))}function Kh(a,b,c,d,e,f,g,h){var l=Math.sin(f);f=Math.cos(f);a[0]=d*f;a[1]=e*l;a[2]=-d*l;a[3]=e*f;a[4]=g*d*f-h*d*l+b;a[5]=g*e*l+h*e*f+c;return a} -function Lh(a){var b=a[0]*a[3]-a[1]*a[2];xa(!!b,32);var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5];a[0]=f/b;a[1]=-d/b;a[2]=-e/b;a[3]=c/b;a[4]=(e*h-f*g)/b;a[5]=-(c*h-d*g)/b;return a};function Mh(a,b){this.o=b;this.c={};this.v={}}v(Mh,Mc);function Nh(a){var b=a.viewState,c=a.coordinateToPixelTransform,d=a.pixelToCoordinateTransform;Kh(c,a.size[0]/2,a.size[1]/2,1/b.resolution,-1/b.resolution,-b.rotation,-b.center[0],-b.center[1]);Lh(Fh(d,c))}k=Mh.prototype;k.ka=function(){for(var a in this.c)Nc(this.c[a])};function Oh(){if(32<zh.a){var a=0,b;for(b in zh.b){var c=zh.b[b];a++&3||Rc(c)||(delete zh.b[b],--zh.a)}}} -k.Ea=function(a,b,c,d,e,f,g){function h(a,c){var f=w(a).toString(),g=b.layerStates[w(c)].Je;if(!(f in b.skippedFeatureUids)||g)return d.call(e,a,g?c:null)}var l,m=b.viewState,n=m.resolution,p=m.projection,m=a;if(p.i){var p=p.G(),q=lb(p),r=a[0];if(r<p[0]||r>p[2])m=[r+q*Math.ceil((p[0]-r)/q),a[1]]}p=b.layerStatesArray;for(q=p.length-1;0<=q;--q){var u=p[q],r=u.layer;if(xh(u,n)&&f.call(g,r)&&(u=Ph(this,r),r.ha()&&(l=u.Ea(r.ha().u?m:a,b,c,h,e)),l))return l}}; -k.Ei=function(a,b,c,d,e){return void 0!==this.Ea(a,b,c,mf,this,d,e)};function Ph(a,b){var c=w(b).toString();if(c in a.c)return a.c[c];b=b.Fd(a);a.c[c]=b;a.v[c]=y(b,"change",a.Fl,a);return b}k.Fl=function(){this.o.render()};k.Jg=ua;k.Rp=function(a,b){for(var c in this.c)if(!(b&&c in b.layerStates)){a=c;var d=this.c[a];delete this.c[a];Ec(this.v[a]);delete this.v[a];Nc(d)}};function Qh(a,b){for(var c in a.c)if(!(c in b.layerStates)){b.postRenderFunctions.push(a.Rp.bind(a));break}} -function ra(a,b){return a.zIndex-b.zIndex};function Rh(a,b,c,d,e){Oc.call(this,a);this.vectorContext=b;this.frameState=c;this.context=d;this.glContext=e}v(Rh,Oc);var Sh=[0,0,0,1],Th=[],Uh=[0,0,0,1];function Vh(a,b,c,d){b&&(a.translate(c,d),a.rotate(b),a.translate(-c,-d))};function Wh(){}k=Wh.prototype;k.zb=function(){};k.rd=function(){};k.Zb=function(){};k.te=function(){};k.ue=function(){};k.mc=function(){};k.nc=function(){};k.oc=function(){};k.pc=function(){};k.qc=function(){};k.rc=function(){};k.yc=function(){};k.Ma=function(){};k.Ub=function(){};k.Cb=function(){};function Xh(a,b,c,d,e){this.i=a;this.u=b;this.c=c;this.S=d;this.Yb=e;this.M=this.b=this.a=this.Ua=this.R=this.I=null;this.na=this.T=this.l=this.B=this.C=this.D=0;this.fa=!1;this.f=this.fb=0;this.pa=!1;this.oa=0;this.Ia="";this.va=this.Jb=0;this.Sa=!1;this.j=this.$a=0;this.ra=this.o=this.g=null;this.v=[];this.xb=Bh()}v(Xh,Wh); -function Yh(a,b,c){if(a.M){b=pf(b,0,c,2,a.S,a.v);c=a.i;var d=a.xb,e=c.globalAlpha;1!=a.l&&(c.globalAlpha=e*a.l);var f=a.fb;a.fa&&(f+=a.Yb);var g;var h=0;for(g=b.length;h<g;h+=2){var l=b[h]-a.D,m=b[h+1]-a.C;a.pa&&(l=Math.round(l),m=Math.round(m));if(f||1!=a.f){var n=l+a.D,p=m+a.C;Kh(d,n,p,a.f,a.f,f,-n,-p);c.setTransform.apply(c,d)}c.drawImage(a.M,a.T,a.na,a.oa,a.B,l,m,a.oa,a.B)}(f||1!=a.f)&&c.setTransform(1,0,0,1,0,0);1!=a.l&&(c.globalAlpha=e)}} -function Zh(a,b,c,d){var e=0;if(a.ra&&""!==a.Ia){a.g&&$h(a,a.g);a.o&&ai(a,a.o);var f=a.ra,g=a.i,h=a.Ua;h?(h.font!=f.font&&(h.font=g.font=f.font),h.textAlign!=f.textAlign&&(h.textAlign=g.textAlign=f.textAlign),h.textBaseline!=f.textBaseline&&(h.textBaseline=g.textBaseline=f.textBaseline)):(g.font=f.font,g.textAlign=f.textAlign,g.textBaseline=f.textBaseline,a.Ua={font:f.font,textAlign:f.textAlign,textBaseline:f.textBaseline});b=pf(b,e,c,d,a.S,a.v);f=a.i;g=a.$a;for(a.Sa&&(g+=a.Yb);e<c;e+=d){var h=b[e]+ -a.Jb,l=b[e+1]+a.va;if(g||1!=a.j){var m=Kh(a.xb,h,l,a.j,a.j,g,-h,-l);f.setTransform.apply(f,m)}a.o&&f.strokeText(a.Ia,h,l);a.g&&f.fillText(a.Ia,h,l)}(g||1!=a.j)&&f.setTransform(1,0,0,1,0,0)}}function bi(a,b,c,d,e,f){var g=a.i;a=pf(b,c,d,e,a.S,a.v);g.moveTo(a[0],a[1]);b=a.length;f&&(b-=2);for(c=2;c<b;c+=2)g.lineTo(a[c],a[c+1]);f&&g.closePath();return d}function ci(a,b,c,d,e){var f;var g=0;for(f=d.length;g<f;++g)c=bi(a,b,c,d[g],e,!0);return c}k=Xh.prototype; -k.Zb=function(a){if(qb(this.c,a.G())){if(this.a||this.b){this.a&&$h(this,this.a);this.b&&ai(this,this.b);var b=this.S;var c=this.v,d=a.ga();b=d?pf(d,0,d.length,a.qa(),b,c):null;c=b[2]-b[0];d=b[3]-b[1];c=Math.sqrt(c*c+d*d);d=this.i;d.beginPath();d.arc(b[0],b[1],c,0,2*Math.PI);this.a&&d.fill();this.b&&d.stroke()}""!==this.Ia&&Zh(this,a.wa(),2,2)}};k.rd=function(a){this.Ma(a.Fa(),a.Ga());this.Ub(a.Y());this.Cb(a.Na())}; -k.zb=function(a){switch(a.U()){case "Point":this.qc(a);break;case "LineString":this.mc(a);break;case "Polygon":this.rc(a);break;case "MultiPoint":this.oc(a);break;case "MultiLineString":this.nc(a);break;case "MultiPolygon":this.pc(a);break;case "GeometryCollection":this.ue(a);break;case "Circle":this.Zb(a)}};k.te=function(a,b){(a=(0,b.Za)(a))&&qb(this.c,a.G())&&(this.rd(b),this.zb(a))};k.ue=function(a){a=a.a;var b;var c=0;for(b=a.length;c<b;++c)this.zb(a[c])}; -k.qc=function(a){var b=a.ga();a=a.qa();this.M&&Yh(this,b,b.length);""!==this.Ia&&Zh(this,b,b.length,a)};k.oc=function(a){var b=a.ga();a=a.qa();this.M&&Yh(this,b,b.length);""!==this.Ia&&Zh(this,b,b.length,a)};k.mc=function(a){if(qb(this.c,a.G())){if(this.b){ai(this,this.b);var b=this.i,c=a.ga();b.beginPath();bi(this,c,0,c.length,a.qa(),!1);b.stroke()}""!==this.Ia&&(a=di(a),Zh(this,a,2,2))}}; -k.nc=function(a){var b=a.G();if(qb(this.c,b)){if(this.b){ai(this,this.b);var b=this.i,c=a.ga(),d=0,e=a.Bb(),f=a.qa();b.beginPath();var g;var h=0;for(g=e.length;h<g;++h)d=bi(this,c,d,e[h],f,!1);b.stroke()}""!==this.Ia&&(a=ei(a),Zh(this,a,a.length,2))}};k.rc=function(a){if(qb(this.c,a.G())){if(this.b||this.a){this.a&&$h(this,this.a);this.b&&ai(this,this.b);var b=this.i;b.beginPath();ci(this,a.ec(),0,a.Bb(),a.qa());this.a&&b.fill();this.b&&b.stroke()}""!==this.Ia&&(a=Wf(a),Zh(this,a,2,2))}}; -k.pc=function(a){if(qb(this.c,a.G())){if(this.b||this.a){this.a&&$h(this,this.a);this.b&&ai(this,this.b);var b=this.i,c=fi(a),d=0,e=a.c,f=a.qa(),g;b.beginPath();var h=0;for(g=e.length;h<g;++h)d=ci(this,c,d,e[h],f);this.a&&b.fill();this.b&&b.stroke()}""!==this.Ia&&(a=gi(a),Zh(this,a,a.length,2))}};function $h(a,b){var c=a.i,d=a.I;d?d.fillStyle!=b.fillStyle&&(d.fillStyle=c.fillStyle=b.fillStyle):(c.fillStyle=b.fillStyle,a.I={fillStyle:b.fillStyle})} -function ai(a,b){var c=a.i,d=a.R;d?(d.lineCap!=b.lineCap&&(d.lineCap=c.lineCap=b.lineCap),Td&&!pa(d.lineDash,b.lineDash)&&c.setLineDash(d.lineDash=b.lineDash),d.lineJoin!=b.lineJoin&&(d.lineJoin=c.lineJoin=b.lineJoin),d.lineWidth!=b.lineWidth&&(d.lineWidth=c.lineWidth=b.lineWidth),d.miterLimit!=b.miterLimit&&(d.miterLimit=c.miterLimit=b.miterLimit),d.strokeStyle!=b.strokeStyle&&(d.strokeStyle=c.strokeStyle=b.strokeStyle)):(c.lineCap=b.lineCap,Td&&c.setLineDash(b.lineDash),c.lineJoin=b.lineJoin,c.lineWidth= -b.lineWidth,c.miterLimit=b.miterLimit,c.strokeStyle=b.strokeStyle,a.R={lineCap:b.lineCap,lineDash:b.lineDash,lineJoin:b.lineJoin,lineWidth:b.lineWidth,miterLimit:b.miterLimit,strokeStyle:b.strokeStyle})} -k.Ma=function(a,b){a?(a=a.b,this.a={fillStyle:id(a?a:Sh)}):this.a=null;if(b){a=b.a;var c=b.f,d=b.i,e=b.g,f=b.j,g=b.c;b=b.o;this.b={lineCap:void 0!==c?c:"round",lineDash:d?d:Th,lineDashOffset:e?e:0,lineJoin:void 0!==f?f:"round",lineWidth:this.u*(void 0!==g?g:1),miterLimit:void 0!==b?b:10,strokeStyle:id(a?a:Uh)}}else this.b=null}; -k.Ub=function(a){if(a){var b=a.Hc(),c=a.Y(1),d=a.Oc(),e=a.ic();this.D=b[0];this.C=b[1];this.B=e[1];this.M=c;this.l=a.f;this.T=d[0];this.na=d[1];this.fa=a.l;this.fb=a.g;this.f=a.a;this.pa=a.v;this.oa=e[0]}else this.M=null}; -k.Cb=function(a){if(a){var b=a.Fa();b?(b=b.b,this.g={fillStyle:id(b?b:Sh)}):this.g=null;var c=a.Ga();if(c){var b=c.a,d=c.f,e=c.i,f=c.g,g=c.j,h=c.c,c=c.o;this.o={lineCap:void 0!==d?d:"round",lineDash:e?e:Th,lineDashOffset:f?f:0,lineJoin:void 0!==g?g:"round",lineWidth:void 0!==h?h:1,miterLimit:void 0!==c?c:10,strokeStyle:id(b?b:Uh)}}else this.o=null;var b=a.a,d=a.i,e=a.c,f=a.o,g=a.f,h=a.b,c=a.Na(),l=a.g;a=a.j;this.ra={font:void 0!==b?b:"10px sans-serif",textAlign:void 0!==l?l:"center",textBaseline:void 0!== -a?a:"middle"};this.Ia=void 0!==c?c:"";this.Jb=void 0!==d?this.u*d:0;this.va=void 0!==e?this.u*e:0;this.Sa=void 0!==f?f:!1;this.$a=void 0!==g?g:0;this.j=this.u*(void 0!==h?h:1)}else this.Ia=""};function hi(a,b){Mh.call(this,0,b);this.i=jd();this.b=this.i.canvas;this.b.style.width="100%";this.b.style.height="100%";this.b.style.display="block";this.b.className="ol-unselectable";a.insertBefore(this.b,a.childNodes[0]||null);this.a=!0;this.f=Bh()}v(hi,Mh); -function ii(a,b,c){var d=a.o,e=a.i;if(Rc(d,b)){var f=c.extent,g=c.pixelRatio,h=c.viewState.rotation,l=c.viewState,m=c.pixelRatio/l.resolution;a=Kh(a.f,a.b.width/2,a.b.height/2,m,-m,-l.rotation,-l.center[0],-l.center[1]);d.b(new Rh(b,new Xh(e,g,f,a,h),c,e,null))}}hi.prototype.U=function(){return"canvas"}; -hi.prototype.Jg=function(a){if(a){var b=this.i,c=a.pixelRatio,d=Math.round(a.size[0]*c),e=Math.round(a.size[1]*c);this.b.width!=d||this.b.height!=e?(this.b.width=d,this.b.height=e):b.clearRect(0,0,d,e);c=a.viewState.rotation;Nh(a);ii(this,"precompose",a);var f=a.layerStatesArray;qa(f);c&&(b.save(),Vh(b,c,d/2,e/2));var d=a.viewState.resolution,g,e=0;for(g=f.length;e<g;++e){var h=f[e];var l=h.layer;l=Ph(this,l);xh(h,d)&&"ready"==h.yj&&l.sd(a,h)&&l.S(a,h,b)}c&&b.restore();ii(this,"postcompose",a);this.a|| -(this.b.style.display="",this.a=!0);Qh(this,a);a.postRenderFunctions.push(Oh)}else this.a&&(this.b.style.display="none",this.a=!1)};hi.prototype.Di=function(a,b,c,d,e,f){var g=b.viewState.resolution,h=b.layerStatesArray,l=h.length;a=Gh(b.pixelToCoordinateTransform,a.slice());for(--l;0<=l;--l){var m=h[l];var n=m.layer;if(xh(m,g)&&e.call(f,n)&&(m=Ph(this,n).u(a,b,c,d)))return m}};var ji=["Polygon","Circle","LineString","Image","Text"];function ki(){};function li(a){this.b=a};function mi(a){this.b=a}v(mi,li);mi.prototype.U=function(){return 35632};function ni(a){this.b=a}v(ni,li);ni.prototype.U=function(){return 35633};function oi(){this.b="precision mediump float;varying vec2 a;varying vec2 b;varying float c;varying float d;uniform float m;uniform vec4 n;uniform vec4 o;uniform vec2 p;void main(void){vec2 windowCenter=vec2((a.x+1.0)/2.0*p.x*d,(a.y+1.0)/2.0*p.y*d);vec2 windowOffset=vec2((b.x+1.0)/2.0*p.x*d,(b.y+1.0)/2.0*p.y*d);float radius=length(windowCenter-windowOffset);float dist=length(windowCenter-gl_FragCoord.xy);if(dist>radius+c){if(o.a==0.0){gl_FragColor=n;}else{gl_FragColor=o;}gl_FragColor.a=gl_FragColor.a-(dist-(radius+c));}else if(n.a==0.0){gl_FragColor=o;if(dist<radius-c){gl_FragColor.a=gl_FragColor.a-(radius-c-dist);}} else{gl_FragColor=n;float strokeDist=radius-c;float antialias=2.0*d;if(dist>strokeDist){gl_FragColor=o;}else if(dist>=strokeDist-antialias){float step=smoothstep(strokeDist-antialias,strokeDist,dist);gl_FragColor=mix(n,o,step);}} gl_FragColor.a=gl_FragColor.a*m;if(gl_FragColor.a<=0.0){discard;}}"} -v(oi,mi);var pi=new oi; -function qi(){this.b="varying vec2 a;varying vec2 b;varying float c;varying float d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;void main(void){mat4 offsetMatrix=i*j;a=vec4(h*vec4(e,0.0,1.0)).xy;d=l;float lineWidth=k*l;c=lineWidth/2.0;if(lineWidth==0.0){lineWidth=2.0*l;}vec2 offset;float radius=g+3.0*l;if(f==0.0){offset=vec2(-1.0,1.0);}else if(f==1.0){offset=vec2(-1.0,-1.0);}else if(f==2.0){offset=vec2(1.0,-1.0);}else{offset=vec2(1.0,1.0);}gl_Position=h*vec4(e+offset*radius,0.0,1.0)+offsetMatrix*vec4(offset*lineWidth,0.0,0.0);b=vec4(h*vec4(e.x+g,e.y,0.0,1.0)).xy;if(distance(a,b)>20000.0){gl_Position=vec4(a,0.0,1.0);}}"} -v(qi,ni);var ri=new qi;function si(a,b){this.B=a.getUniformLocation(b,"n");this.oa=a.getUniformLocation(b,"k");this.c=a.getUniformLocation(b,"j");this.f=a.getUniformLocation(b,"i");this.a=a.getUniformLocation(b,"m");this.ra=a.getUniformLocation(b,"l");this.i=a.getUniformLocation(b,"h");this.I=a.getUniformLocation(b,"p");this.R=a.getUniformLocation(b,"o");this.j=a.getAttribLocation(b,"f");this.b=a.getAttribLocation(b,"e");this.S=a.getAttribLocation(b,"g")};function ti(){return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}function ui(a,b){a[0]=b[0];a[1]=b[1];a[4]=b[2];a[5]=b[3];a[12]=b[4];a[13]=b[5];return a};function vi(a,b){this.origin=nb(b);this.xb=Bh();this.Sa=Bh();this.$a=Bh();this.Jb=ti();this.b=[];this.o=null;this.i=[];this.f=[];this.a=[];this.l=null;this.g=void 0}v(vi,Wh); -vi.prototype.La=function(a,b,c,d,e,f,g,h,l,m,n){var p=a.b;if(this.g){var q=p.isEnabled(p.STENCIL_TEST);var r=p.getParameter(p.STENCIL_FUNC);var u=p.getParameter(p.STENCIL_VALUE_MASK);var x=p.getParameter(p.STENCIL_REF);var B=p.getParameter(p.STENCIL_WRITEMASK);var E=p.getParameter(p.STENCIL_FAIL);var A=p.getParameter(p.STENCIL_PASS_DEPTH_PASS);var L=p.getParameter(p.STENCIL_PASS_DEPTH_FAIL);p.enable(p.STENCIL_TEST);p.clear(p.STENCIL_BUFFER_BIT);p.stencilMask(255);p.stencilFunc(p.ALWAYS,1,255);p.stencilOp(p.KEEP, -p.KEEP,p.REPLACE);this.g.La(a,b,c,d,e,f,g,h,l,m,n);p.stencilMask(0);p.stencilFunc(p.NOTEQUAL,1,255)}wi(a,34962,this.l);wi(a,34963,this.o);f=this.rf(p,a,e,f);var oa=Ch(this.xb);Ih(oa,2/(c*e[0]),2/(c*e[1]));Hh(oa,-d);Jh(oa,-(b[0]-this.origin[0]),-(b[1]-this.origin[1]));b=Ch(this.$a);Ih(b,2/e[0],2/e[1]);e=Ch(this.Sa);d&&Hh(e,-d);p.uniformMatrix4fv(f.i,!1,ui(this.Jb,oa));p.uniformMatrix4fv(f.f,!1,ui(this.Jb,b));p.uniformMatrix4fv(f.c,!1,ui(this.Jb,e));p.uniform1f(f.a,g);if(l){m?a=this.ve(p,a,h,l,n):(p.clear(p.COLOR_BUFFER_BIT| -p.DEPTH_BUFFER_BIT),this.Od(p,a,h,!0),a=(a=l(null))?a:void 0);var ha=a}else this.Od(p,a,h,!1);this.sf(p,f);this.g&&(q||p.disable(p.STENCIL_TEST),p.clear(p.STENCIL_BUFFER_BIT),p.stencilFunc(r,x,u),p.stencilMask(B),p.stencilOp(E,L,A));return ha};function xi(a,b,c,d){a.drawElements(4,d-c,b.g?5125:5123,c*(b.g?4:2))};var yi=[0,0,0,1],zi=[],Ai=[0,0,0,1];function Bi(a,b,c,d,e,f){a=(c-a)*(f-b)-(e-a)*(d-b);return a<=Ci&&a>=-Ci?void 0:0<a}var Ci=Number.EPSILON||2.220446049250313E-16;function Di(a){this.b=void 0!==a?a:[];this.a=Ei}var Ei=35044;function Fi(a,b){vi.call(this,0,b);this.v=null;this.j=[];this.u=[];this.S=0;this.c={fillColor:null,strokeColor:null,lineDash:null,lineDashOffset:void 0,lineWidth:void 0,s:!1}}v(Fi,vi);k=Fi.prototype; -k.Zb=function(a,b){var c=a.pd(),d=a.qa();if(c){this.i.push(this.b.length);this.f.push(b);this.c.s&&(this.u.push(this.b.length),this.c.s=!1);this.S=c;a=a.ga();a=qf(a,0,2,d,-this.origin[0],-this.origin[1]);b=this.a.length;var c=this.b.length,e=b/4,f;for(f=0;2>f;f+=d)this.a[b++]=a[f],this.a[b++]=a[f+1],this.a[b++]=0,this.a[b++]=this.S,this.a[b++]=a[f],this.a[b++]=a[f+1],this.a[b++]=1,this.a[b++]=this.S,this.a[b++]=a[f],this.a[b++]=a[f+1],this.a[b++]=2,this.a[b++]=this.S,this.a[b++]=a[f],this.a[b++]= -a[f+1],this.a[b++]=3,this.a[b++]=this.S,this.b[c++]=e,this.b[c++]=e+1,this.b[c++]=e+2,this.b[c++]=e+2,this.b[c++]=e+3,this.b[c++]=e,e+=4}else this.c.s&&(this.j.pop(),this.j.length&&(d=this.j[this.j.length-1],this.c.fillColor=d[0],this.c.strokeColor=d[1],this.c.lineWidth=d[2],this.c.s=!1))};k.Db=function(){this.l=new Di(this.a);this.o=new Di(this.b);this.i.push(this.b.length);!this.u.length&&0<this.j.length&&(this.j=[]);this.b=this.a=null}; -k.Eb=function(a){var b=this.l,c=this.o;return function(){Gi(a,b);Gi(a,c)}};k.rf=function(a,b,c,d){var e=Hi(b,pi,ri);if(this.v)var f=this.v;else this.v=f=new si(a,e);b.Qc(e);a.enableVertexAttribArray(f.b);a.vertexAttribPointer(f.b,2,5126,!1,16,0);a.enableVertexAttribArray(f.j);a.vertexAttribPointer(f.j,1,5126,!1,16,8);a.enableVertexAttribArray(f.S);a.vertexAttribPointer(f.S,1,5126,!1,16,12);a.uniform2fv(f.I,c);a.uniform1f(f.ra,d);return f}; -k.sf=function(a,b){a.disableVertexAttribArray(b.b);a.disableVertexAttribArray(b.j);a.disableVertexAttribArray(b.S)}; -k.Od=function(a,b,c){if(wb(c)){var d=this.i[this.i.length-1];for(c=this.u.length-1;0<=c;--c){var e=this.u[c];var f=this.j[c];a.uniform4fv(this.v.B,f[0]);Ii(this,a,f[1],f[2]);xi(a,b,e,d);d=e}}else{var g=this.i.length-2;f=d=this.i[g+1];for(e=this.u.length-1;0<=e;--e){var h=this.j[e];a.uniform4fv(this.v.B,h[0]);Ii(this,a,h[1],h[2]);for(h=this.u[e];0<=g&&this.i[g]>=h;){var l=this.i[g];var m=this.f[g];m=w(m).toString();c[m]&&(d!==f&&xi(a,b,d,f),f=l);g--;d=l}d!==f&&xi(a,b,d,f);d=f=h}}}; -k.ve=function(a,b,c,d,e){var f,g;var h=this.i.length-2;var l=this.i[h+1];for(f=this.u.length-1;0<=f;--f){var m=this.j[f];a.uniform4fv(this.v.B,m[0]);Ii(this,a,m[1],m[2]);for(g=this.u[f];0<=h&&this.i[h]>=g;){m=this.i[h];var n=this.f[h];var p=w(n).toString();if(void 0===c[p]&&n.V()&&(void 0===e||qb(e,n.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),xi(a,b,m,l),l=d(n)))return l;h--;l=m}}};function Ii(a,b,c,d){b.uniform4fv(a.v.R,c);b.uniform1f(a.v.oa,d)} -k.Ma=function(a,b){if(b){var c=b.i;this.c.lineDash=c?c:zi;c=b.g;this.c.lineDashOffset=c?c:0;c=b.a;c instanceof CanvasGradient||c instanceof CanvasPattern?c=Ai:c=ed(c).map(function(a,b){return 3!=b?a/255:a})||Ai;b=b.c;b=void 0!==b?b:1}else c=[0,0,0,0],b=0;a=a?a.b:[0,0,0,0];a instanceof CanvasGradient||a instanceof CanvasPattern?a=yi:a=ed(a).map(function(a,b){return 3!=b?a/255:a})||yi;this.c.strokeColor&&pa(this.c.strokeColor,c)&&this.c.fillColor&&pa(this.c.fillColor,a)&&this.c.lineWidth===b||(this.c.s= -!0,this.c.fillColor=a,this.c.strokeColor=c,this.c.lineWidth=b,this.j.push([a,c,b]))};function Ji(){this.b="precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}v(Ji,mi);var Ki=new Ji; -function Li(){this.b="varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.0,0.0);gl_Position=h*vec4(c,0.0,1.0)+offsets;a=d;b=f;}"}v(Li,ni);var Mi=new Li; -function Ni(a,b){this.c=a.getUniformLocation(b,"j");this.f=a.getUniformLocation(b,"i");this.a=a.getUniformLocation(b,"k");this.i=a.getUniformLocation(b,"h");this.v=a.getAttribLocation(b,"e");this.u=a.getAttribLocation(b,"f");this.b=a.getAttribLocation(b,"c");this.D=a.getAttribLocation(b,"g");this.C=a.getAttribLocation(b,"d")};function Oi(a,b){this.j=a;this.b=b;this.a={};this.c={};this.i={};this.l=this.v=this.f=this.o=null;(this.g=ja(fa,"OES_element_index_uint"))&&b.getExtension("OES_element_index_uint");y(this.j,"webglcontextlost",this.Xo,this);y(this.j,"webglcontextrestored",this.Yo,this)}v(Oi,Mc); -function wi(a,b,c){var d=a.b,e=c.b,f=String(w(c));if(f in a.a)d.bindBuffer(b,a.a[f].buffer);else{var g=d.createBuffer();d.bindBuffer(b,g);var h;34962==b?h=new Float32Array(e):34963==b&&(h=a.g?new Uint32Array(e):new Uint16Array(e));d.bufferData(b,h,c.a);a.a[f]={lc:c,buffer:g}}}function Gi(a,b){var c=a.b;b=String(w(b));var d=a.a[b];c.isContextLost()||c.deleteBuffer(d.buffer);delete a.a[b]}k=Oi.prototype; -k.ka=function(){Lc(this.j);var a=this.b;if(!a.isContextLost()){for(var b in this.a)a.deleteBuffer(this.a[b].buffer);for(b in this.i)a.deleteProgram(this.i[b]);for(b in this.c)a.deleteShader(this.c[b]);a.deleteFramebuffer(this.f);a.deleteRenderbuffer(this.l);a.deleteTexture(this.v)}};k.Wo=function(){return this.b}; -function Pi(a){if(!a.f){var b=a.b,c=b.createFramebuffer();b.bindFramebuffer(b.FRAMEBUFFER,c);var d=Qi(b,1,1),e=b.createRenderbuffer();b.bindRenderbuffer(b.RENDERBUFFER,e);b.renderbufferStorage(b.RENDERBUFFER,b.DEPTH_COMPONENT16,1,1);b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,d,0);b.framebufferRenderbuffer(b.FRAMEBUFFER,b.DEPTH_ATTACHMENT,b.RENDERBUFFER,e);b.bindTexture(b.TEXTURE_2D,null);b.bindRenderbuffer(b.RENDERBUFFER,null);b.bindFramebuffer(b.FRAMEBUFFER,null);a.f=c; -a.v=d;a.l=e}return a.f}function Ri(a,b){var c=String(w(b));if(c in a.c)return a.c[c];var d=a.b,e=d.createShader(b.U());d.shaderSource(e,b.b);d.compileShader(e);return a.c[c]=e}function Hi(a,b,c){var d=w(b)+"/"+w(c);if(d in a.i)return a.i[d];var e=a.b,f=e.createProgram();e.attachShader(f,Ri(a,b));e.attachShader(f,Ri(a,c));e.linkProgram(f);return a.i[d]=f}k.Xo=function(){ub(this.a);ub(this.c);ub(this.i);this.l=this.v=this.f=this.o=null};k.Yo=function(){}; -k.Qc=function(a){if(a==this.o)return!1;this.b.useProgram(a);this.o=a;return!0};function Si(a,b,c){var d=a.createTexture();a.bindTexture(a.TEXTURE_2D,d);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR);void 0!==b&&a.texParameteri(3553,10242,b);void 0!==c&&a.texParameteri(3553,10243,c);return d}function Qi(a,b,c){var d=Si(a,void 0,void 0);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,b,c,0,a.RGBA,a.UNSIGNED_BYTE,null);return d} -function Ti(a,b){var c=Si(a,33071,33071);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,b);return c};function Ui(a,b){vi.call(this,0,b);this.C=this.D=void 0;this.S=[];this.v=[];this.oa=void 0;this.j=[];this.c=[];this.I=this.ra=void 0;this.B=null;this.fb=this.fa=this.na=this.T=this.Ua=this.R=void 0;this.va=[];this.u=[];this.pa=void 0}v(Ui,vi);k=Ui.prototype;k.Eb=function(a){var b=this.l,c=this.o,d=this.va,e=this.u,f=a.b;return function(){if(!f.isContextLost()){var g;var h=0;for(g=d.length;h<g;++h)f.deleteTexture(d[h]);h=0;for(g=e.length;h<g;++h)f.deleteTexture(e[h])}Gi(a,b);Gi(a,c)}}; -function Vi(a,b,c,d){var e=a.D,f=a.C,g=a.oa,h=a.ra,l=a.I,m=a.R,n=a.Ua,p=a.T,q=a.na?1:0,r=-a.fa,u=a.fb,x=a.pa,B=Math.cos(r),r=Math.sin(r),E=a.b.length,A=a.a.length,L;for(L=0;L<c;L+=d){var oa=b[L]-a.origin[0];var ha=b[L+1]-a.origin[1];var ga=A/8;var z=-u*e;var M=-u*(g-f);a.a[A++]=oa;a.a[A++]=ha;a.a[A++]=z*B-M*r;a.a[A++]=z*r+M*B;a.a[A++]=n/l;a.a[A++]=(p+g)/h;a.a[A++]=m;a.a[A++]=q;z=u*(x-e);M=-u*(g-f);a.a[A++]=oa;a.a[A++]=ha;a.a[A++]=z*B-M*r;a.a[A++]=z*r+M*B;a.a[A++]=(n+x)/l;a.a[A++]=(p+g)/h;a.a[A++]= -m;a.a[A++]=q;z=u*(x-e);M=u*f;a.a[A++]=oa;a.a[A++]=ha;a.a[A++]=z*B-M*r;a.a[A++]=z*r+M*B;a.a[A++]=(n+x)/l;a.a[A++]=p/h;a.a[A++]=m;a.a[A++]=q;z=-u*e;M=u*f;a.a[A++]=oa;a.a[A++]=ha;a.a[A++]=z*B-M*r;a.a[A++]=z*r+M*B;a.a[A++]=n/l;a.a[A++]=p/h;a.a[A++]=m;a.a[A++]=q;a.b[E++]=ga;a.b[E++]=ga+1;a.b[E++]=ga+2;a.b[E++]=ga;a.b[E++]=ga+2;a.b[E++]=ga+3}}k.oc=function(a,b){this.i.push(this.b.length);this.f.push(b);b=a.ga();Vi(this,b,b.length,a.qa())}; -k.qc=function(a,b){this.i.push(this.b.length);this.f.push(b);b=a.ga();Vi(this,b,b.length,a.qa())};k.Db=function(a){a=a.b;this.S.push(this.b.length);this.v.push(this.b.length);this.l=new Di(this.a);this.o=new Di(this.b);var b={};Wi(this.va,this.j,b,a);Wi(this.u,this.c,b,a);this.oa=this.C=this.D=void 0;this.c=this.j=null;this.I=this.ra=void 0;this.b=null;this.fb=this.fa=this.na=this.T=this.Ua=this.R=void 0;this.a=null;this.pa=void 0}; -function Wi(a,b,c,d){var e,f=b.length;for(e=0;e<f;++e){var g=b[e];var h=w(g).toString();h in c?g=c[h]:(g=Ti(d,g),c[h]=g);a[e]=g}} -k.rf=function(a,b){var c=Hi(b,Ki,Mi);if(this.B)var d=this.B;else this.B=d=new Ni(a,c);b.Qc(c);a.enableVertexAttribArray(d.b);a.vertexAttribPointer(d.b,2,5126,!1,32,0);a.enableVertexAttribArray(d.v);a.vertexAttribPointer(d.v,2,5126,!1,32,8);a.enableVertexAttribArray(d.C);a.vertexAttribPointer(d.C,2,5126,!1,32,16);a.enableVertexAttribArray(d.u);a.vertexAttribPointer(d.u,1,5126,!1,32,24);a.enableVertexAttribArray(d.D);a.vertexAttribPointer(d.D,1,5126,!1,32,28);return d}; -k.sf=function(a,b){a.disableVertexAttribArray(b.b);a.disableVertexAttribArray(b.v);a.disableVertexAttribArray(b.C);a.disableVertexAttribArray(b.u);a.disableVertexAttribArray(b.D)}; -k.Od=function(a,b,c,d){var e=d?this.u:this.va;d=d?this.v:this.S;if(wb(c)){var f;c=0;var g=e.length;for(f=0;c<g;++c){a.bindTexture(3553,e[c]);var h=d[c];xi(a,b,f,h);f=h}}else for(f=g=0,h=e.length;f<h;++f){a.bindTexture(3553,e[f]);for(var l=0<f?d[f-1]:0,m=d[f],n=l;g<this.i.length&&this.i[g]<=m;){var p=w(this.f[g]).toString();void 0!==c[p]?(n!==l&&xi(a,b,n,l),l=n=g===this.i.length-1?m:this.i[g+1]):l=g===this.i.length-1?m:this.i[g+1];g++}n!==l&&xi(a,b,n,l)}}; -k.ve=function(a,b,c,d,e){var f,g,h=this.i.length-1;for(f=this.u.length-1;0<=f;--f){a.bindTexture(3553,this.u[f]);var l=0<f?this.v[f-1]:0;for(g=this.v[f];0<=h&&this.i[h]>=l;){var m=this.i[h];var n=this.f[h];var p=w(n).toString();if(void 0===c[p]&&n.V()&&(void 0===e||qb(e,n.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),xi(a,b,m,g),g=d(n)))return g;g=m;h--}}}; -k.Ub=function(a){var b=a.Hc(),c=a.Y(1),d=a.ye(),e=a.qg(1),f=a.f,g=a.Oc(),h=a.l,l=a.g,m=a.ic();a=a.a;if(this.j.length){var n=this.j[this.j.length-1];w(n)!=w(c)&&(this.S.push(this.b.length),this.j.push(c))}else this.j.push(c);this.c.length?(n=this.c[this.c.length-1],w(n)!=w(e)&&(this.v.push(this.b.length),this.c.push(e))):this.c.push(e);this.D=b[0];this.C=b[1];this.oa=m[1];this.ra=d[1];this.I=d[0];this.R=f;this.Ua=g[0];this.T=g[1];this.fa=l;this.na=h;this.fb=a;this.pa=m[0]};function Xi(a,b,c){var d=b-c;return a[0]===a[d]&&a[1]===a[d+1]&&3<(b-0)/c?!!vf(a,0,b,c):!1};function Yi(){this.b="precision mediump float;varying float a;varying vec2 b;varying float c;uniform float m;uniform vec4 n;uniform vec2 o;uniform float p;void main(void){if(a>0.0){vec2 windowCoords=vec2((b.x+1.0)/2.0*o.x*p,(b.y+1.0)/2.0*o.y*p);if(length(windowCoords-gl_FragCoord.xy)>c*p){discard;}} gl_FragColor=n;float alpha=n.a*m;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}v(Yi,mi);var Zi=new Yi; -function $i(){this.b="varying float a;varying vec2 b;varying float c;attribute vec2 d;attribute vec2 e;attribute vec2 f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;uniform float k;uniform float l;bool nearlyEquals(in float value,in float ref){float epsilon=0.000000000001;return value>=ref-epsilon&&value<=ref+epsilon;}void alongNormal(out vec2 offset,in vec2 nextP,in float turnDir,in float direction){vec2 dirVect=nextP-e;vec2 normal=normalize(vec2(-turnDir*dirVect.y,turnDir*dirVect.x));offset=k/2.0*normal*direction;}void miterUp(out vec2 offset,out float round,in bool isRound,in float direction){float halfWidth=k/2.0;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=f-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;round=0.0;if(isRound){round=1.0;}else if(miterLength>l+k){offset=halfWidth*tmpNormal*direction;}} bool miterDown(out vec2 offset,in vec4 projPos,in mat4 offsetMatrix,in float direction){bool degenerate=false;vec2 tangent=normalize(normalize(f-e)+normalize(e-d));vec2 normal=vec2(-tangent.y,tangent.x);vec2 dirVect=d-e;vec2 tmpNormal=normalize(vec2(-dirVect.y,dirVect.x));vec2 longOffset,shortOffset,longVertex;vec4 shortProjVertex;float halfWidth=k/2.0;if(length(f-e)>length(d-e)){longOffset=tmpNormal*direction*halfWidth;shortOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=f;shortProjVertex=h*vec4(d,0.0,1.0);}else{shortOffset=tmpNormal*direction*halfWidth;longOffset=normalize(vec2(dirVect.y,-dirVect.x))*direction*halfWidth;longVertex=d;shortProjVertex=h*vec4(f,0.0,1.0);}vec4 p1=h*vec4(longVertex,0.0,1.0)+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p2=projPos+offsetMatrix*vec4(longOffset,0.0,0.0);vec4 p3=shortProjVertex+offsetMatrix*vec4(-shortOffset,0.0,0.0);vec4 p4=shortProjVertex+offsetMatrix*vec4(shortOffset,0.0,0.0);float denom=(p4.y-p3.y)*(p2.x-p1.x)-(p4.x-p3.x)*(p2.y-p1.y);float firstU=((p4.x-p3.x)*(p1.y-p3.y)-(p4.y-p3.y)*(p1.x-p3.x))/denom;float secondU=((p2.x-p1.x)*(p1.y-p3.y)-(p2.y-p1.y)*(p1.x-p3.x))/denom;float epsilon=0.000000000001;if(firstU>epsilon&&firstU<1.0-epsilon&&secondU>epsilon&&secondU<1.0-epsilon){shortProjVertex.x=p1.x+firstU*(p2.x-p1.x);shortProjVertex.y=p1.y+firstU*(p2.y-p1.y);offset=shortProjVertex.xy;degenerate=true;}else{float miterLength=abs(halfWidth/dot(normal,tmpNormal));offset=normal*direction*miterLength;}return degenerate;}void squareCap(out vec2 offset,out float round,in bool isRound,in vec2 nextP,in float turnDir,in float direction){round=0.0;vec2 dirVect=e-nextP;vec2 firstNormal=normalize(dirVect);vec2 secondNormal=vec2(turnDir*firstNormal.y*direction,-turnDir*firstNormal.x*direction);vec2 hypotenuse=normalize(firstNormal-secondNormal);vec2 normal=vec2(turnDir*hypotenuse.y*direction,-turnDir*hypotenuse.x*direction);float length=sqrt(c*c*2.0);offset=normal*length;if(isRound){round=1.0;}} void main(void){bool degenerate=false;float direction=float(sign(g));mat4 offsetMatrix=i*j;vec2 offset;vec4 projPos=h*vec4(e,0.0,1.0);bool round=nearlyEquals(mod(g,2.0),0.0);a=0.0;c=k/2.0;b=projPos.xy;if(nearlyEquals(mod(g,3.0),0.0)||nearlyEquals(mod(g,17.0),0.0)){alongNormal(offset,f,1.0,direction);}else if(nearlyEquals(mod(g,5.0),0.0)||nearlyEquals(mod(g,13.0),0.0)){alongNormal(offset,d,-1.0,direction);}else if(nearlyEquals(mod(g,23.0),0.0)){miterUp(offset,a,round,direction);}else if(nearlyEquals(mod(g,19.0),0.0)){degenerate=miterDown(offset,projPos,offsetMatrix,direction);}else if(nearlyEquals(mod(g,7.0),0.0)){squareCap(offset,a,round,f,1.0,direction);}else if(nearlyEquals(mod(g,11.0),0.0)){squareCap(offset,a,round,d,-1.0,direction);}if(!degenerate){vec4 offsets=offsetMatrix*vec4(offset,0.0,0.0);gl_Position=projPos+offsets;}else{gl_Position=vec4(offset,0.0,1.0);}}"} -v($i,ni);var aj=new $i;function bj(a,b){this.B=a.getUniformLocation(b,"n");this.oa=a.getUniformLocation(b,"k");this.R=a.getUniformLocation(b,"l");this.c=a.getUniformLocation(b,"j");this.f=a.getUniformLocation(b,"i");this.a=a.getUniformLocation(b,"m");this.ra=a.getUniformLocation(b,"p");this.i=a.getUniformLocation(b,"h");this.I=a.getUniformLocation(b,"o");this.g=a.getAttribLocation(b,"g");this.o=a.getAttribLocation(b,"d");this.l=a.getAttribLocation(b,"f");this.b=a.getAttribLocation(b,"e")};function cj(a,b){vi.call(this,0,b);this.v=null;this.u=[];this.j=[];this.c={strokeColor:null,lineCap:void 0,lineDash:null,lineDashOffset:void 0,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0,s:!1}}v(cj,vi); -function dj(a,b,c,d){var e,f=a.a.length,g=a.b.length,h="bevel"===a.c.lineJoin?0:"miter"===a.c.lineJoin?1:2,l="butt"===a.c.lineCap?0:"square"===a.c.lineCap?1:2,m=Xi(b,c,d),n=g,p=1;for(e=0;e<c;e+=d){var q=f/7;var r=u;var u=x||[b[e],b[e+1]];if(e)if(e===c-d){if(m)var x=B;else r=r||[0,0],f=ej(a,r,u,[0,0],p*fj*(l||1),f),f=ej(a,r,u,[0,0],-p*fj*(l||1),f),a.b[g++]=q,a.b[g++]=n-1,a.b[g++]=n,a.b[g++]=n,a.b[g++]=q+1,a.b[g++]=q,l&&(f=ej(a,r,u,[0,0],p*gj*l,f),f=ej(a,r,u,[0,0],-p*gj*l,f),a.b[g++]=q+2,a.b[g++]=q, -a.b[g++]=q+1,a.b[g++]=q+1,a.b[g++]=q+3,a.b[g++]=q+2);break}else x=[b[e+d],b[e+d+1]];else{x=[b[e+d],b[e+d+1]];if(c-0===2*d&&pa(u,x))break;if(m){r=[b[c-2*d],b[c-2*d+1]];var B=x}else{l&&(f=ej(a,[0,0],u,x,p*hj*l,f),f=ej(a,[0,0],u,x,-p*hj*l,f),a.b[g++]=q+2,a.b[g++]=q,a.b[g++]=q+1,a.b[g++]=q+1,a.b[g++]=q+3,a.b[g++]=q+2);f=ej(a,[0,0],u,x,p*ij*(l||1),f);f=ej(a,[0,0],u,x,-p*ij*(l||1),f);n=f/7-1;continue}}var E=Bi(r[0],r[1],u[0],u[1],x[0],x[1])?-1:1;f=ej(a,r,u,x,E*jj*(h||1),f);f=ej(a,r,u,x,E*kj*(h||1),f);f= -ej(a,r,u,x,-E*lj*(h||1),f);0<e&&(a.b[g++]=q,a.b[g++]=n-1,a.b[g++]=n,a.b[g++]=q+2,a.b[g++]=q,a.b[g++]=0<p*E?n:n-1);a.b[g++]=q;a.b[g++]=q+2;a.b[g++]=q+1;n=q+2;p=E;h&&(f=ej(a,r,u,x,E*mj*h,f),a.b[g++]=q+1,a.b[g++]=q+3,a.b[g++]=q)}m&&(q=q||f/7,E=Sf([r[0],r[1],u[0],u[1],x[0],x[1]],0,6,2)?1:-1,f=ej(a,r,u,x,E*jj*(h||1),f),ej(a,r,u,x,-E*lj*(h||1),f),a.b[g++]=q,a.b[g++]=n-1,a.b[g++]=n,a.b[g++]=q+1,a.b[g++]=q,a.b[g++]=0<p*E?n:n-1)} -function ej(a,b,c,d,e,f){a.a[f++]=b[0];a.a[f++]=b[1];a.a[f++]=c[0];a.a[f++]=c[1];a.a[f++]=d[0];a.a[f++]=d[1];a.a[f++]=e;return f}function nj(a,b,c,d){c-=b;return c<2*d?!1:c===2*d?!pa([a[b],a[b+1]],[a[b+d],a[b+d+1]]):!0}k=cj.prototype;k.mc=function(a,b){var c=a.ga();a=a.qa();nj(c,0,c.length,a)&&(c=qf(c,0,c.length,a,-this.origin[0],-this.origin[1]),this.c.s&&(this.j.push(this.b.length),this.c.s=!1),this.i.push(this.b.length),this.f.push(b),dj(this,c,c.length,a))}; -k.nc=function(a,b){var c=this.b.length,d=a.Bb();d.unshift(0);var e=a.ga();a=a.qa();var f;if(1<d.length){var g=1;for(f=d.length;g<f;++g)if(nj(e,d[g-1],d[g],a)){var h=qf(e,d[g-1],d[g],a,-this.origin[0],-this.origin[1]);dj(this,h,h.length,a)}}this.b.length>c&&(this.i.push(c),this.f.push(b),this.c.s&&(this.j.push(c),this.c.s=!1))}; -function oj(a,b,c,d){Xi(b,b.length,d)||(b.push(b[0]),b.push(b[1]));dj(a,b,b.length,d);if(c.length){var e;b=0;for(e=c.length;b<e;++b)Xi(c[b],c[b].length,d)||(c[b].push(c[b][0]),c[b].push(c[b][1])),dj(a,c[b],c[b].length,d)}}function pj(a,b,c){c=void 0===c?a.b.length:c;a.i.push(c);a.f.push(b);a.c.s&&(a.j.push(c),a.c.s=!1)}k.Db=function(){this.l=new Di(this.a);this.o=new Di(this.b);this.i.push(this.b.length);!this.j.length&&0<this.u.length&&(this.u=[]);this.b=this.a=null}; -k.Eb=function(a){var b=this.l,c=this.o;return function(){Gi(a,b);Gi(a,c)}}; -k.rf=function(a,b,c,d){var e=Hi(b,Zi,aj);if(this.v)var f=this.v;else this.v=f=new bj(a,e);b.Qc(e);a.enableVertexAttribArray(f.o);a.vertexAttribPointer(f.o,2,5126,!1,28,0);a.enableVertexAttribArray(f.b);a.vertexAttribPointer(f.b,2,5126,!1,28,8);a.enableVertexAttribArray(f.l);a.vertexAttribPointer(f.l,2,5126,!1,28,16);a.enableVertexAttribArray(f.g);a.vertexAttribPointer(f.g,1,5126,!1,28,24);a.uniform2fv(f.I,c);a.uniform1f(f.ra,d);return f}; -k.sf=function(a,b){a.disableVertexAttribArray(b.o);a.disableVertexAttribArray(b.b);a.disableVertexAttribArray(b.l);a.disableVertexAttribArray(b.g)}; -k.Od=function(a,b,c,d){var e=a.getParameter(a.DEPTH_FUNC),f=a.getParameter(a.DEPTH_WRITEMASK);d||(a.enable(a.DEPTH_TEST),a.depthMask(!0),a.depthFunc(a.NOTEQUAL));if(wb(c)){var g=this.i[this.i.length-1];for(c=this.j.length-1;0<=c;--c){var h=this.j[c];var l=this.u[c];qj(this,a,l[0],l[1],l[2]);xi(a,b,h,g);a.clear(a.DEPTH_BUFFER_BIT);g=h}}else{var m=this.i.length-2;l=g=this.i[m+1];for(h=this.j.length-1;0<=h;--h){var n=this.u[h];qj(this,a,n[0],n[1],n[2]);for(n=this.j[h];0<=m&&this.i[m]>=n;){var p=this.i[m]; -var q=this.f[m];q=w(q).toString();c[q]&&(g!==l&&(xi(a,b,g,l),a.clear(a.DEPTH_BUFFER_BIT)),l=p);m--;g=p}g!==l&&(xi(a,b,g,l),a.clear(a.DEPTH_BUFFER_BIT));g=l=n}}d||(a.disable(a.DEPTH_TEST),a.clear(a.DEPTH_BUFFER_BIT),a.depthMask(f),a.depthFunc(e))}; -k.ve=function(a,b,c,d,e){var f,g;var h=this.i.length-2;var l=this.i[h+1];for(f=this.j.length-1;0<=f;--f){var m=this.u[f];qj(this,a,m[0],m[1],m[2]);for(g=this.j[f];0<=h&&this.i[h]>=g;){m=this.i[h];var n=this.f[h];var p=w(n).toString();if(void 0===c[p]&&n.V()&&(void 0===e||qb(e,n.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),xi(a,b,m,l),l=d(n)))return l;h--;l=m}}};function qj(a,b,c,d,e){b.uniform4fv(a.v.B,c);b.uniform1f(a.v.oa,d);b.uniform1f(a.v.R,e)} -k.Ma=function(a,b){a=b.f;this.c.lineCap=void 0!==a?a:"round";a=b.i;this.c.lineDash=a?a:zi;a=b.g;this.c.lineDashOffset=a?a:0;a=b.j;this.c.lineJoin=void 0!==a?a:"round";a=b.a;a instanceof CanvasGradient||a instanceof CanvasPattern?a=Ai:a=ed(a).map(function(a,b){return 3!=b?a/255:a})||Ai;var c=b.c,c=void 0!==c?c:1;b=b.o;b=void 0!==b?b:10;this.c.strokeColor&&pa(this.c.strokeColor,a)&&this.c.lineWidth===c&&this.c.miterLimit===b||(this.c.s=!0,this.c.strokeColor=a,this.c.lineWidth=c,this.c.miterLimit=b, -this.u.push([a,c,b]))};var ij=3,fj=5,hj=7,gj=11,jj=13,kj=17,lj=19,mj=23;function rj(){this.b="precision mediump float;uniform vec4 e;uniform float f;void main(void){gl_FragColor=e;float alpha=e.a*f;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}v(rj,mi);var sj=new rj;function tj(){this.b="attribute vec2 a;uniform mat4 b;uniform mat4 c;uniform mat4 d;void main(void){gl_Position=b*vec4(a,0.0,1.0);}"}v(tj,ni);var uj=new tj; -function vj(a,b){this.B=a.getUniformLocation(b,"e");this.c=a.getUniformLocation(b,"d");this.f=a.getUniformLocation(b,"c");this.a=a.getUniformLocation(b,"f");this.i=a.getUniformLocation(b,"b");this.b=a.getAttribLocation(b,"a")};function wj(a){a=a||{};this.a=void 0!==a.color?a.color:null;this.f=a.lineCap;this.i=void 0!==a.lineDash?a.lineDash:null;this.g=a.lineDashOffset;this.j=a.lineJoin;this.o=a.miterLimit;this.c=a.width;this.b=void 0}k=wj.prototype;k.clone=function(){var a=this.a;return new wj({color:a&&a.slice?a.slice():a||void 0,lineCap:this.f,lineDash:this.i?this.i.slice():void 0,lineDashOffset:this.g,lineJoin:this.j,miterLimit:this.o,width:this.c})};k.No=function(){return this.a};k.Vk=function(){return this.f}; -k.Oo=function(){return this.i};k.Wk=function(){return this.g};k.Xk=function(){return this.j};k.bl=function(){return this.o};k.Po=function(){return this.c};k.Qo=function(a){this.a=a;this.b=void 0};k.aq=function(a){this.f=a;this.b=void 0};k.setLineDash=function(a){this.i=a;this.b=void 0};k.bq=function(a){this.g=a;this.b=void 0};k.cq=function(a){this.j=a;this.b=void 0};k.gq=function(a){this.o=a;this.b=void 0};k.jq=function(a){this.c=a;this.b=void 0};function xj(a){this.b=this.a=this.i=void 0;this.f=void 0===a?!0:a;this.c=0}function yj(a){var b=a.b;if(b){var c=b.next,d=b.ub;c&&(c.ub=d);d&&(d.next=c);a.b=c||d;a.i===a.a?(a.b=void 0,a.i=void 0,a.a=void 0):a.i===b?a.i=a.b:a.a===b&&(a.a=d?a.b.ub:a.b);a.c--}}function zj(a){a.b=a.i;if(a.b)return a.b.data}function Aj(a){if(a.b&&a.b.next)return a.b=a.b.next,a.b.data}function Bj(a){if(a.b&&a.b.next)return a.b.next.data}function Cj(a){if(a.b&&a.b.ub)return a.b=a.b.ub,a.b.data} -function Dj(a){if(a.b&&a.b.ub)return a.b.ub.data}function Ej(a){if(a.b)return a.b.data}xj.prototype.concat=function(a){if(a.b){if(this.b){var b=this.b.next;this.b.next=a.i;a.i.ub=this.b;b.ub=a.a;a.a.next=b;this.c+=a.c}else this.b=a.b,this.i=a.i,this.a=a.a,this.c=a.c;a.b=void 0;a.i=void 0;a.a=void 0;a.c=0}};var Fj={$d:function(){}}; -(function(a){function b(a,e,f,g,h){f=f||0;g=g||a.length-1;for(h=h||d;g>f;){if(600<g-f){var l=g-f+1,m=e-f+1,n=Math.log(l),p=.5*Math.exp(2*n/3),n=.5*Math.sqrt(n*p*(l-p)/l)*(0>m-l/2?-1:1);b(a,e,Math.max(f,Math.floor(e-m*p/l+n)),Math.min(g,Math.floor(e+(l-m)*p/l+n)),h)}l=a[e];m=f;p=g;c(a,f,e);for(0<h(a[g],l)&&c(a,f,g);m<p;){c(a,m,p);m++;for(p--;0>h(a[m],l);)m++;for(;0<h(a[p],l);)p--}0===h(a[f],l)?c(a,f,p):(p++,c(a,p,g));p<=e&&(f=p+1);e<=p&&(g=p-1)}}function c(a,b,c){var d=a[b];a[b]=a[c];a[c]=d}function d(a, -b){return a<b?-1:a>b?1:0}function e(a,b){if(!(this instanceof e))return new e(a,b);this.Hf=Math.max(4,a||9);this.fh=Math.max(2,Math.ceil(.4*this.Hf));b&&this.ek(b);this.clear()}function f(a,b){g(a,0,a.children.length,b,a)}function g(a,b,c,d,e){e||(e=u(null));e.ca=Infinity;e.da=Infinity;e.$=-Infinity;e.ia=-Infinity;for(var f;b<c;b++)f=a.children[b],h(e,a.ib?d(f):f);return e}function h(a,b){a.ca=Math.min(a.ca,b.ca);a.da=Math.min(a.da,b.da);a.$=Math.max(a.$,b.$);a.ia=Math.max(a.ia,b.ia)}function l(a, -b){return a.ca-b.ca}function m(a,b){return a.da-b.da}function n(a){return(a.$-a.ca)*(a.ia-a.da)}function p(a){return a.$-a.ca+(a.ia-a.da)}function q(a,b){return a.ca<=b.ca&&a.da<=b.da&&b.$<=a.$&&b.ia<=a.ia}function r(a,b){return b.ca<=a.$&&b.da<=a.ia&&b.$>=a.ca&&b.ia>=a.da}function u(a){return{children:a,height:1,ib:!0,ca:Infinity,da:Infinity,$:-Infinity,ia:-Infinity}}function x(a,b,c,d,e){for(var f=[b,c],g;f.length;)c=f.pop(),b=f.pop(),c-b<=d||(g=b+Math.ceil((c-b)/d/2)*d,B(a,g,b,c,e),f.push(b,g, -g,c))}var B=b;e.prototype={all:function(){return this.$g(this.data,[])},search:function(a){var b=this.data,c=[],d=this.wb;if(!r(a,b))return c;for(var e=[],f,g,h,l;b;){f=0;for(g=b.children.length;f<g;f++)h=b.children[f],l=b.ib?d(h):h,r(a,l)&&(b.ib?c.push(h):q(a,l)?this.$g(h,c):e.push(h));b=e.pop()}return c},load:function(a){if(!a||!a.length)return this;if(a.length<this.fh){for(var b=0,c=a.length;b<c;b++)this.Ca(a[b]);return this}a=this.bh(a.slice(),0,a.length-1,0);this.data.children.length?this.data.height=== -a.height?this.hh(this.data,a):(this.data.height<a.height&&(b=this.data,this.data=a,a=b),this.eh(a,this.data.height-a.height-1,!0)):this.data=a;return this},Ca:function(a){a&&this.eh(a,this.data.height-1);return this},clear:function(){this.data=u([]);return this},remove:function(a,b){if(!a)return this;for(var c=this.data,d=this.wb(a),e=[],f=[],g,h,l,m;c||e.length;){c||(c=e.pop(),h=e[e.length-1],g=f.pop(),m=!0);if(c.ib){a:{l=a;var n=c.children,p=b;if(p){for(var r=0;r<n.length;r++)if(p(l,n[r])){l=r; -break a}l=-1}else l=n.indexOf(l)}if(-1!==l){c.children.splice(l,1);e.push(c);this.ck(e);break}}m||c.ib||!q(c,d)?h?(g++,c=h.children[g],m=!1):c=null:(e.push(c),f.push(g),g=0,h=c,c=c.children[0])}return this},wb:function(a){return a},Lf:l,Mf:m,toJSON:function(){return this.data},$g:function(a,b){for(var c=[];a;)a.ib?b.push.apply(b,a.children):c.push.apply(c,a.children),a=c.pop();return b},bh:function(a,b,c,d){var e=c-b+1,g=this.Hf;if(e<=g){var h=u(a.slice(b,c+1));f(h,this.wb);return h}d||(d=Math.ceil(Math.log(e)/ -Math.log(g)),g=Math.ceil(e/Math.pow(g,d-1)));h=u([]);h.ib=!1;h.height=d;var e=Math.ceil(e/g),g=e*Math.ceil(Math.sqrt(g)),l;for(x(a,b,c,g,this.Lf);b<=c;b+=g){var m=Math.min(b+g-1,c);x(a,b,m,e,this.Mf);for(l=b;l<=m;l+=e){var n=Math.min(l+e-1,m);h.children.push(this.bh(a,l,n,d-1))}}f(h,this.wb);return h},bk:function(a,b,c,d){for(var e,f,g,h,l,m,p,q;;){d.push(b);if(b.ib||d.length-1===c)break;p=q=Infinity;e=0;for(f=b.children.length;e<f;e++)g=b.children[e],l=n(g),m=(Math.max(g.$,a.$)-Math.min(g.ca,a.ca))* -(Math.max(g.ia,a.ia)-Math.min(g.da,a.da))-l,m<q?(q=m,p=l<p?l:p,h=g):m===q&&l<p&&(p=l,h=g);b=h||b.children[0]}return b},eh:function(a,b,c){var d=this.wb;c=c?a:d(a);var d=[],e=this.bk(c,this.data,b,d);e.children.push(a);for(h(e,c);0<=b;)if(d[b].children.length>this.Hf)this.jk(d,b),b--;else break;this.Zj(c,d,b)},jk:function(a,b){var c=a[b],d=c.children.length,e=this.fh;this.$j(c,e,d);d=this.ak(c,e,d);d=u(c.children.splice(d,c.children.length-d));d.height=c.height;d.ib=c.ib;f(c,this.wb);f(d,this.wb); -b?a[b-1].children.push(d):this.hh(c,d)},hh:function(a,b){this.data=u([a,b]);this.data.height=a.height+1;this.data.ib=!1;f(this.data,this.wb)},ak:function(a,b,c){var d,e;var f=e=Infinity;for(d=b;d<=c-b;d++){var h=g(a,0,d,this.wb);var l=g(a,d,c,this.wb);var m=Math.max(0,Math.min(h.$,l.$)-Math.max(h.ca,l.ca))*Math.max(0,Math.min(h.ia,l.ia)-Math.max(h.da,l.da));h=n(h)+n(l);if(m<f){f=m;var p=d;e=h<e?h:e}else m===f&&h<e&&(e=h,p=d)}return p},$j:function(a,b,c){var d=a.ib?this.Lf:l,e=a.ib?this.Mf:m,f=this.ah(a, -b,c,d);b=this.ah(a,b,c,e);f<b&&a.children.sort(d)},ah:function(a,b,c,d){a.children.sort(d);d=this.wb;var e=g(a,0,b,d),f=g(a,c-b,c,d),l=p(e)+p(f),m;for(m=b;m<c-b;m++){var n=a.children[m];h(e,a.ib?d(n):n);l+=p(e)}for(m=c-b-1;m>=b;m--)n=a.children[m],h(f,a.ib?d(n):n),l+=p(f);return l},Zj:function(a,b,c){for(;0<=c;c--)h(b[c],a)},ck:function(a){for(var b=a.length-1,c;0<=b;b--)0===a[b].children.length?0<b?(c=a[b-1].children,c.splice(c.indexOf(a[b]),1)):this.clear():f(a[b],this.wb)},ek:function(a){var b= -["return a"," - b",";"];this.Lf=new Function("a","b",b.join(a[0]));this.Mf=new Function("a","b",b.join(a[1]));this.wb=new Function("a","return {minX: a"+a[0]+", minY: a"+a[1]+", maxX: a"+a[2]+", maxY: a"+a[3]+"};")}};a["default"]=e})(Fj.$d=Fj.$d||{});Fj.$d=Fj.$d.default;function Gj(a){this.a=Fj.$d(a);this.b={}}k=Gj.prototype;k.Ca=function(a,b){a={ca:a[0],da:a[1],$:a[2],ia:a[3],value:b};this.a.Ca(a);this.b[w(b)]=a};k.load=function(a,b){for(var c=Array(b.length),d=0,e=b.length;d<e;d++){var f=a[d],g=b[d],f={ca:f[0],da:f[1],$:f[2],ia:f[3],value:g};c[d]=f;this.b[w(g)]=f}this.a.load(c)};k.remove=function(a){a=w(a);var b=this.b[a];delete this.b[a];return null!==this.a.remove(b)};function Hj(a,b,c){var d=a.b[w(c)];bb([d.ca,d.da,d.$,d.ia],b)||(a.remove(c),a.Ca(b,c))} -function Ij(a){return a.a.all().map(function(a){return a.value})}function Jj(a,b){return a.a.search({ca:b[0],da:b[1],$:b[2],ia:b[3]}).map(function(a){return a.value})}k.forEach=function(a,b){return Kj(Ij(this),a,b)};function Lj(a,b,c,d){return Kj(Jj(a,b),c,d)}function Kj(a,b,c){for(var d,e=0,f=a.length;e<f&&!(d=b.call(c,a[e]));e++);return d}k.clear=function(){this.a.clear();this.b={}};k.G=function(a){var b=this.a.data;return Xa(b.ca,b.da,b.$,b.ia,a)}; -k.concat=function(a){this.a.load(a.a.all());for(var b in a.b)this.b[b|0]=a.b[b|0]};function Mj(a,b){vi.call(this,0,b);this.g=new cj(0,b);this.v=null;this.u=[];this.c=[];this.j={fillColor:null,s:!1}}v(Mj,vi); -function Nj(a,b,c,d){var e=new xj,f=new Gj;b=Oj(a,b,d,e,f,!0);if(c.length){var g,h=[];var l=0;for(g=c.length;l<g;++l){var m={list:new xj,$:void 0,Mg:new Gj};h.push(m);m.$=Oj(a,c[l],d,m.list,m.Mg,!1)}h.sort(function(a,b){return b.$[0]===a.$[0]?a.$[1]-b.$[1]:b.$[0]-a.$[0]});for(l=0;l<h.length;++l){c=h[l].list;g=d=zj(c);do{if(Pj(g,f).length){var n=!0;break}g=Aj(c)}while(d!==g);n||(Qj(c,h[l].Mg,!0),Rj(c,h[l].$[0],e,b[0],f)&&(f.concat(h[l].Mg),Qj(e,f,!1)))}}else Qj(e,f,!1);Sj(a,e,f)} -function Oj(a,b,c,d,e,f){var g,h=a.a.length/2,l,m=[],n=[];if(f===Sf(b,0,b.length,c)){var p=l=Tj(a,b[0],b[1],h++);f=b[0];var q=b[1];var r=c;for(g=b.length;r<g;r+=c){var u=Tj(a,b[r],b[r+1],h++);n.push(Uj(p,u,d));m.push([Math.min(p.x,u.x),Math.min(p.y,u.y),Math.max(p.x,u.x),Math.max(p.y,u.y)]);b[r]>f&&(f=b[r],q=b[r+1]);p=u}}else for(r=b.length-c,p=l=Tj(a,b[r],b[r+1],h++),f=b[r],q=b[r+1],r-=c,g=0;r>=g;r-=c)u=Tj(a,b[r],b[r+1],h++),n.push(Uj(p,u,d)),m.push([Math.min(p.x,u.x),Math.min(p.y,u.y),Math.max(p.x, -u.x),Math.max(p.y,u.y)]),b[r]>f&&(f=b[r],q=b[r+1]),p=u;n.push(Uj(u,l,d));m.push([Math.min(p.x,u.x),Math.min(p.y,u.y),Math.max(p.x,u.x),Math.max(p.y,u.y)]);e.load(m,n);return[f,q]}function Qj(a,b,c){var d=zj(a),e=d,f=Aj(a),g=!1;do{var h=c?Bi(f.W.x,f.W.y,e.W.x,e.W.y,e.aa.x,e.aa.y):Bi(e.aa.x,e.aa.y,e.W.x,e.W.y,f.W.x,f.W.y);void 0===h?(Vj(e,f,a,b),g=!0,f===d&&(d=Bj(a)),f=e,Cj(a)):e.W.Fb!==h&&(e.W.Fb=h,g=!0);e=f;f=Aj(a)}while(e!==d);return g} -function Rj(a,b,c,d,e){for(var f=zj(a);f.W.x!==b;)f=Aj(a);b=f.W;d={x:d,y:b.y,hb:-1};var g=Infinity,h;var l=Pj({aa:b,W:d},e,!0);var m=0;for(h=l.length;m<h;++m){var n=l[m],p=Wj(b,d,n.aa,n.W,!0),q=Math.abs(b.x-p[0]);if(q<g&&void 0!==Bi(b.x,b.y,n.aa.x,n.aa.y,n.W.x,n.W.y)){g=q;var r={x:p[0],y:p[1],hb:-1};f=n}}if(Infinity===g)return!1;l=f.W;if(0<g&&(f=Xj(b,r,f.W,e),f.length))for(r=Infinity,m=0,h=f.length;m<h;++m)if(g=f[m],n=Math.atan2(b.y-g.y,d.x-g.x),n<r||n===r&&g.x<l.x)r=n,l=g;for(f=zj(c);f.W.x!==l.x|| -f.W.y!==l.y;)f=Aj(c);d={x:b.x,y:b.y,hb:b.hb,Fb:void 0};m={x:f.W.x,y:f.W.y,hb:f.W.hb,Fb:void 0};Bj(a).aa=d;Uj(b,f.W,a,e);Uj(m,d,a,e);f.W=m;a.f&&a.b&&(a.i=a.b,a.a=a.b.ub);c.concat(a);return!0} -function Sj(a,b,c){for(var d=!1,e=Yj(b,c);3<b.c;)if(e){if(!Zj(a,b,c,e,d)&&!Qj(b,c,d)&&!ak(a,b,c,!0))break}else if(!Zj(a,b,c,e,d)&&!Qj(b,c,d)&&!ak(a,b,c))if(e=Yj(b,c)){var d=b,f=2*d.c,g=Array(f),h=zj(d),l=h,m=0;do g[m++]=l.aa.x,g[m++]=l.aa.y,l=Aj(d);while(l!==h);d=!Sf(g,0,f,2);Qj(b,c,d)}else{e=a;d=b;f=g=zj(d);do{h=Pj(f,c);if(h.length){g=h[0];h=Wj(f.aa,f.W,g.aa,g.W);h=Tj(e,h[0],h[1],e.a.length/2);l=new xj;m=new Gj;Uj(h,f.W,l,m);f.W=h;Hj(c,[Math.min(f.aa.x,h.x),Math.min(f.aa.y,h.y),Math.max(f.aa.x,h.x), -Math.max(f.aa.y,h.y)],f);for(f=Aj(d);f!==g;)Uj(f.aa,f.W,l,m),c.remove(f),yj(d),f=Ej(d);Uj(g.aa,h,l,m);g.aa=h;Hj(c,[Math.min(g.W.x,h.x),Math.min(g.W.y,h.y),Math.max(g.W.x,h.x),Math.max(g.W.y,h.y)],g);Qj(d,c,!1);Sj(e,d,c);Qj(l,m,!1);Sj(e,l,m);break}f=Aj(d)}while(f!==g);break}3===b.c&&(e=a.b.length,a.b[e++]=Dj(b).aa.hb,a.b[e++]=Ej(b).aa.hb,a.b[e++]=Bj(b).aa.hb)} -function Zj(a,b,c,d,e){var f=a.b.length,g=zj(b),h=Dj(b),l=g,m=Aj(b),n=Bj(b),p=!1;do{var q=l.aa;var r=l.W;var u=m.W;if(!1===r.Fb){var x=e?bk(n.W,u,r,q,h.aa):bk(h.aa,q,r,u,n.W);!d&&Pj({aa:q,W:u},c).length||!x||Xj(q,r,u,c,!0).length||!d&&!1!==q.Fb&&!1!==u.Fb&&Sf([h.aa.x,h.aa.y,q.x,q.y,r.x,r.y,u.x,u.y,n.W.x,n.W.y],0,10,2)!==!e||(a.b[f++]=q.hb,a.b[f++]=r.hb,a.b[f++]=u.hb,Vj(l,m,b,c),m===g&&(g=n),p=!0)}h=Dj(b);l=Ej(b);m=Aj(b);n=Bj(b)}while(l!==g&&3<b.c);return p} -function ak(a,b,c,d){var e=zj(b);Aj(b);var f=e,g=Aj(b),h=!1;do{var l=Wj(f.aa,f.W,g.aa,g.W,d);if(l){var h=a.b.length,m=a.a.length/2,n=Cj(b);yj(b);c.remove(n);var p=n===e;d?(l[0]===f.aa.x&&l[1]===f.aa.y?(Cj(b),l=f.aa,g.aa=l,c.remove(f),p=p||f===e):(l=g.W,f.W=l,c.remove(g),p=p||g===e),yj(b)):(l=Tj(a,l[0],l[1],m),f.W=l,g.aa=l,Hj(c,[Math.min(f.aa.x,f.W.x),Math.min(f.aa.y,f.W.y),Math.max(f.aa.x,f.W.x),Math.max(f.aa.y,f.W.y)],f),Hj(c,[Math.min(g.aa.x,g.W.x),Math.min(g.aa.y,g.W.y),Math.max(g.aa.x,g.W.x), -Math.max(g.aa.y,g.W.y)],g));a.b[h++]=n.aa.hb;a.b[h++]=n.W.hb;a.b[h++]=l.hb;h=!0;if(p)break}f=Dj(b);g=Aj(b)}while(f!==e);return h}function Yj(a,b){var c=zj(a),d=c;do{if(Pj(d,b).length)return!1;d=Aj(a)}while(d!==c);return!0}function Tj(a,b,c,d){var e=a.a.length;a.a[e++]=b;a.a[e++]=c;return{x:b,y:c,hb:d,Fb:void 0}} -function Uj(a,b,c,d){var e={aa:a,W:b},f={ub:void 0,next:void 0,data:e},g=c.b;if(g){var h=g.next;f.ub=g;f.next=h;g.next=f;h&&(h.ub=f);g===c.a&&(c.a=f)}else c.i=f,c.a=f,c.f&&(f.next=f,f.ub=f);c.b=f;c.c++;d&&d.Ca([Math.min(a.x,b.x),Math.min(a.y,b.y),Math.max(a.x,b.x),Math.max(a.y,b.y)],e);return e}function Vj(a,b,c,d){Ej(c)===b&&(yj(c),a.W=b.W,d.remove(b),Hj(d,[Math.min(a.aa.x,a.W.x),Math.min(a.aa.y,a.W.y),Math.max(a.aa.x,a.W.x),Math.max(a.aa.y,a.W.y)],a))} -function Xj(a,b,c,d,e){var f,g,h=[],l=Jj(d,[Math.min(a.x,b.x,c.x),Math.min(a.y,b.y,c.y),Math.max(a.x,b.x,c.x),Math.max(a.y,b.y,c.y)]);d=0;for(f=l.length;d<f;++d)for(g in l[d]){var m=l[d][g];"object"!==typeof m||e&&!m.Fb||m.x===a.x&&m.y===a.y||m.x===b.x&&m.y===b.y||m.x===c.x&&m.y===c.y||-1!==h.indexOf(m)||!Mf([a.x,a.y,b.x,b.y,c.x,c.y],0,6,2,m.x,m.y)||h.push(m)}return h} -function Pj(a,b,c){var d=a.aa,e=a.W;b=Jj(b,[Math.min(d.x,e.x),Math.min(d.y,e.y),Math.max(d.x,e.x),Math.max(d.y,e.y)]);var f=[],g;var h=0;for(g=b.length;h<g;++h){var l=b[h];a!==l&&(c||l.aa!==e||l.W!==d)&&Wj(d,e,l.aa,l.W,c)&&f.push(l)}return f} -function Wj(a,b,c,d,e){var f=(d.y-c.y)*(b.x-a.x)-(d.x-c.x)*(b.y-a.y);if(f&&(d=((d.x-c.x)*(a.y-c.y)-(d.y-c.y)*(a.x-c.x))/f,c=((b.x-a.x)*(a.y-c.y)-(b.y-a.y)*(a.x-c.x))/f,!e&&d>Ci&&d<1-Ci&&c>Ci&&c<1-Ci||e&&0<=d&&1>=d&&0<=c&&1>=c))return[a.x+d*(b.x-a.x),a.y+d*(b.y-a.y)]} -function bk(a,b,c,d,e){if(void 0===b.Fb||void 0===d.Fb)return!1;var f=(c.x-d.x)*(b.y-d.y)>(c.y-d.y)*(b.x-d.x);e=(e.x-d.x)*(b.y-d.y)<(e.y-d.y)*(b.x-d.x);a=(a.x-b.x)*(d.y-b.y)>(a.y-b.y)*(d.x-b.x);c=(c.x-b.x)*(d.y-b.y)<(c.y-b.y)*(d.x-b.x);b=b.Fb?c||a:c&&a;return(d.Fb?e||f:e&&f)&&b}k=Mj.prototype; -k.pc=function(a,b){var c=a.c,d=a.qa(),e=this.b.length,f=this.g.b.length;a=a.ga();var g,h,l;var m=h=0;for(g=c.length;m<g;++m){var n=c[m];if(0<n.length){var p=qf(a,h,n[0],d,-this.origin[0],-this.origin[1]);if(p.length){var q=[];h=1;for(l=n.length;h<l;++h)if(n[h]!==n[h-1]){var r=qf(a,n[h-1],n[h],d,-this.origin[0],-this.origin[1]);q.push(r)}oj(this.g,p,q,d);Nj(this,p,q,d)}}h=n[n.length-1]}this.b.length>e&&(this.i.push(e),this.f.push(b),this.j.s&&(this.c.push(e),this.j.s=!1));this.g.b.length>f&&pj(this.g, -b,f)};k.rc=function(a,b){var c=a.Bb(),d=a.qa();if(0<c.length){a=a.ga().map(Number);var e=qf(a,0,c[0],d,-this.origin[0],-this.origin[1]);if(e.length){var f=[],g;var h=1;for(g=c.length;h<g;++h)if(c[h]!==c[h-1]){var l=qf(a,c[h-1],c[h],d,-this.origin[0],-this.origin[1]);f.push(l)}this.i.push(this.b.length);this.f.push(b);this.j.s&&(this.c.push(this.b.length),this.j.s=!1);pj(this.g,b);oj(this.g,e,f,d);Nj(this,e,f,d)}}}; -k.Db=function(a){this.l=new Di(this.a);this.o=new Di(this.b);this.i.push(this.b.length);this.g.Db(a);!this.c.length&&0<this.u.length&&(this.u=[]);this.b=this.a=null};k.Eb=function(a){var b=this.l,c=this.o,d=this.g.Eb(a);return function(){Gi(a,b);Gi(a,c);d()}};k.rf=function(a,b){var c=Hi(b,sj,uj);if(this.v)var d=this.v;else this.v=d=new vj(a,c);b.Qc(c);a.enableVertexAttribArray(d.b);a.vertexAttribPointer(d.b,2,5126,!1,8,0);return d};k.sf=function(a,b){a.disableVertexAttribArray(b.b)}; -k.Od=function(a,b,c,d){var e=a.getParameter(a.DEPTH_FUNC),f=a.getParameter(a.DEPTH_WRITEMASK);d||(a.enable(a.DEPTH_TEST),a.depthMask(!0),a.depthFunc(a.NOTEQUAL));if(wb(c)){var g=this.i[this.i.length-1];for(c=this.c.length-1;0<=c;--c){var h=this.c[c];var l=this.u[c];a.uniform4fv(this.v.B,l);xi(a,b,h,g);g=h}}else{var m=this.i.length-2;l=g=this.i[m+1];for(h=this.c.length-1;0<=h;--h){var n=this.u[h];a.uniform4fv(this.v.B,n);for(n=this.c[h];0<=m&&this.i[m]>=n;){var p=this.i[m];var q=this.f[m];q=w(q).toString(); -c[q]&&(g!==l&&(xi(a,b,g,l),a.clear(a.DEPTH_BUFFER_BIT)),l=p);m--;g=p}g!==l&&(xi(a,b,g,l),a.clear(a.DEPTH_BUFFER_BIT));g=l=n}}d||(a.disable(a.DEPTH_TEST),a.clear(a.DEPTH_BUFFER_BIT),a.depthMask(f),a.depthFunc(e))}; -k.ve=function(a,b,c,d,e){var f,g;var h=this.i.length-2;var l=this.i[h+1];for(f=this.c.length-1;0<=f;--f){var m=this.u[f];a.uniform4fv(this.v.B,m);for(g=this.c[f];0<=h&&this.i[h]>=g;){m=this.i[h];var n=this.f[h];var p=w(n).toString();if(void 0===c[p]&&n.V()&&(void 0===e||qb(e,n.V().G()))&&(a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT),xi(a,b,m,l),l=d(n)))return l;h--;l=m}}}; -k.Ma=function(a,b){a=a?a.b:[0,0,0,0];a instanceof CanvasGradient||a instanceof CanvasPattern?a=yi:a=ed(a).map(function(a,b){return 3!=b?a/255:a})||yi;this.j.fillColor&&pa(a,this.j.fillColor)||(this.j.fillColor=a,this.j.s=!0,this.u.push(a));b?this.g.Ma(null,b):this.g.Ma(null,new wj({color:[0,0,0,0],lineWidth:0}))};function ck(){}ck.prototype.La=function(){};function dk(a,b,c){this.f=b;this.g=a;this.c=c;this.a={}}v(dk,ki);function ek(a,b){var c=[],d;for(d in a.a){var e=a.a[d],f;for(f in e)c.push(e[f].Eb(b))}return function(){for(var a=c.length,b,d=0;d<a;d++)b=c[d].apply(this,arguments);return b}}function fk(a,b){for(var c in a.a){var d=a.a[c],e;for(e in d)d[e].Db(b)}}dk.prototype.b=function(a,b){var c=void 0!==a?a.toString():"0";a=this.a[c];void 0===a&&(a={},this.a[c]=a);c=a[b];void 0===c&&(c=new gk[b](this.g,this.f),a[b]=c);return c}; -dk.prototype.i=function(){return wb(this.a)};dk.prototype.La=function(a,b,c,d,e,f,g,h){var l=Object.keys(this.a).map(Number);l.sort(ia);var m,n;var p=0;for(m=l.length;p<m;++p){var q=this.a[l[p].toString()];var r=0;for(n=ji.length;r<n;++r){var u=q[ji[r]];void 0!==u&&u.La(a,b,c,d,e,f,g,h,void 0,!1)}}}; -function hk(a,b,c,d,e,f,g,h,l,m,n){var p=ik,q=Object.keys(a.a).map(Number);q.sort(function(a,b){return b-a});var r,u;var x=0;for(r=q.length;x<r;++x){var B=a.a[q[x].toString()];for(u=ji.length-1;0<=u;--u){var E=B[ji[u]];if(void 0!==E&&(E=E.La(b,c,d,e,p,f,g,h,l,m,n)))return E}}} -dk.prototype.Ea=function(a,b,c,d,e,f,g,h,l,m){var n=b.b;n.bindFramebuffer(n.FRAMEBUFFER,Pi(b));var p;void 0!==this.c&&(p=Qa(Za(a),d*this.c));return hk(this,b,a,d,e,g,h,l,function(a){var b=new Uint8Array(4);n.readPixels(0,0,1,1,n.RGBA,n.UNSIGNED_BYTE,b);if(0<b[3]&&(a=m(a)))return a},!0,p)}; -function jk(a,b,c,d,e,f,g,h){var l=c.b;l.bindFramebuffer(l.FRAMEBUFFER,Pi(c));return void 0!==hk(a,c,b,d,e,f,g,h,function(){var a=new Uint8Array(4);l.readPixels(0,0,1,1,l.RGBA,l.UNSIGNED_BYTE,a);return 0<a[3]},!1)}var ik=[1,1],gk={Circle:Fi,Image:Ui,LineString:cj,Polygon:Mj,Text:ck};function kk(a,b,c,d,e,f,g){this.b=a;this.i=b;this.a=f;this.c=g;this.j=e;this.g=d;this.f=c;this.o=this.l=this.v=null}v(kk,Wh);k=kk.prototype;k.rd=function(a){this.Ma(a.Fa(),a.Ga());this.Ub(a.Y())}; -k.zb=function(a){switch(a.U()){case "Point":this.qc(a,null);break;case "LineString":this.mc(a,null);break;case "Polygon":this.rc(a,null);break;case "MultiPoint":this.oc(a,null);break;case "MultiLineString":this.nc(a,null);break;case "MultiPolygon":this.pc(a,null);break;case "GeometryCollection":this.ue(a,null);break;case "Circle":this.Zb(a,null)}};k.te=function(a,b){(a=(0,b.Za)(a))&&qb(this.a,a.G())&&(this.rd(b),this.zb(a))};k.ue=function(a){a=a.a;var b;var c=0;for(b=a.length;c<b;++c)this.zb(a[c])}; -k.qc=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"Image");d.Ub(this.v);d.qc(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()};k.oc=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"Image");d.Ub(this.v);d.oc(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()}; -k.mc=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"LineString");d.Ma(null,this.o);d.mc(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()};k.nc=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"LineString");d.Ma(null,this.o);d.nc(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()}; -k.rc=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"Polygon");d.Ma(this.l,this.o);d.rc(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()};k.pc=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"Polygon");d.Ma(this.l,this.o);d.pc(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()}; -k.Zb=function(a,b){var c=this.b,d=(new dk(1,this.a)).b(0,"Circle");d.Ma(this.l,this.o);d.Zb(a,b);d.Db(c);d.La(this.b,this.i,this.f,this.g,this.j,this.c,1,{},void 0,!1);d.Eb(c)()};k.Ub=function(a){this.v=a};k.Ma=function(a,b){this.l=a;this.o=b};function lk(){this.c=0;this.b={};this.i=this.a=null}k=lk.prototype;k.clear=function(){this.c=0;this.b={};this.i=this.a=null};k.forEach=function(a,b){for(var c=this.a;c;)a.call(b,c.Yc,c.uc,this),c=c.Nb};k.get=function(a){a=this.b[a];xa(!!a,15);if(a===this.i)return a.Yc;a===this.a?(this.a=this.a.Nb,this.a.vd=null):(a.Nb.vd=a.vd,a.vd.Nb=a.Nb);a.Nb=null;a.vd=this.i;this.i=this.i.Nb=a;return a.Yc}; -k.pop=function(){var a=this.a;delete this.b[a.uc];a.Nb&&(a.Nb.vd=null);this.a=a.Nb;this.a||(this.i=null);--this.c;return a.Yc};k.replace=function(a,b){this.get(a);this.b[a].Yc=b};k.set=function(a,b){xa(!(a in this.b),16);b={uc:a,Nb:null,vd:this.i,Yc:b};this.i?this.i.Nb=b:this.a=b;this.i=b;this.b[a]=b;++this.c};function mk(a,b){Mh.call(this,0,b);this.b=document.createElement("CANVAS");this.b.style.width="100%";this.b.style.height="100%";this.b.style.display="block";this.b.className="ol-unselectable";a.insertBefore(this.b,a.childNodes[0]||null);this.S=this.D=0;this.C=jd();this.l=!0;this.i=Ld(this.b,{antialias:!0,depth:!0,failIfMajorPerformanceCaveat:!0,preserveDrawingBuffer:!1,stencil:!0});this.f=new Oi(this.b,this.i);y(this.b,"webglcontextlost",this.Yn,this);y(this.b,"webglcontextrestored",this.Zn,this); -this.a=new lk;this.u=null;this.j=new Ke(function(a){var b=a[1];a=a[2];var c=b[0]-this.u[0],b=b[1]-this.u[1];return 65536*Math.log(a)+Math.sqrt(c*c+b*b)/a}.bind(this),function(a){return a[0].bb()});this.B=function(){if(this.j.b.length){Oe(this.j);var a=Le(this.j);nk(this,a[0],a[3],a[4])}return!1}.bind(this);this.g=0;ok(this)}v(mk,Mh); -function nk(a,b,c,d){var e=a.i,f=b.bb();if(a.a.b.hasOwnProperty(f))a=a.a.get(f),e.bindTexture(3553,a.Ib),9729!=a.Ph&&(e.texParameteri(3553,10240,9729),a.Ph=9729),9729!=a.Rh&&(e.texParameteri(3553,10241,9729),a.Rh=9729);else{var g=e.createTexture();e.bindTexture(3553,g);if(0<d){var h=a.C.canvas,l=a.C;a.D!==c[0]||a.S!==c[1]?(h.width=c[0],h.height=c[1],a.D=c[0],a.S=c[1]):l.clearRect(0,0,c[0],c[1]);l.drawImage(b.Y(),d,d,c[0],c[1],0,0,c[0],c[1]);e.texImage2D(3553,0,6408,6408,5121,h)}else e.texImage2D(3553, -0,6408,6408,5121,b.Y());e.texParameteri(3553,10240,9729);e.texParameteri(3553,10241,9729);e.texParameteri(3553,10242,33071);e.texParameteri(3553,10243,33071);a.a.set(f,{Ib:g,Ph:9729,Rh:9729})}}function pk(a,b,c){var d=a.o;if(Rc(d,b)){a=a.f;var e=c.viewState;d.b(new Rh(b,new kk(a,e.center,e.resolution,e.rotation,c.size,c.extent,c.pixelRatio),c,null,a))}}k=mk.prototype;k.ka=function(){var a=this.i;a.isContextLost()||this.a.forEach(function(b){b&&a.deleteTexture(b.Ib)});Nc(this.f);Mh.prototype.ka.call(this)}; -k.xk=function(a,b){a=this.i;for(var c;1024<this.a.c-this.g;){if(c=this.a.a.Yc)a.deleteTexture(c.Ib);else if(+this.a.a.uc==b.index)break;else--this.g;this.a.pop()}};k.U=function(){return"webgl"};k.Yn=function(a){a.preventDefault();this.a.clear();this.g=0;a=this.c;for(var b in a)a[b].mg()};k.Zn=function(){ok(this);this.o.render()};function ok(a){a=a.i;a.activeTexture(33984);a.blendFuncSeparate(770,771,1,771);a.disable(2884);a.disable(2929);a.disable(3089);a.disable(2960)} -k.Jg=function(a){var b=this.f,c=this.i;if(c.isContextLost())return!1;if(!a)return this.l&&(this.b.style.display="none",this.l=!1),!1;this.u=a.focus;this.a.set((-a.index).toString(),null);++this.g;pk(this,"precompose",a);var d=[],e=a.layerStatesArray;qa(e);var f=a.viewState.resolution,g;var h=0;for(g=e.length;h<g;++h){var l=e[h];if(xh(l,f)&&"ready"==l.yj){var m=Ph(this,l.layer);m.ng(a,l,b)&&d.push(l)}}e=a.size[0]*a.pixelRatio;f=a.size[1]*a.pixelRatio;if(this.b.width!=e||this.b.height!=f)this.b.width= -e,this.b.height=f;c.bindFramebuffer(36160,null);c.clearColor(0,0,0,0);c.clear(16384);c.enable(3042);c.viewport(0,0,this.b.width,this.b.height);h=0;for(g=d.length;h<g;++h)l=d[h],m=Ph(this,l.layer),m.Gi(a,l,b);this.l||(this.b.style.display="",this.l=!0);Nh(a);1024<this.a.c-this.g&&a.postRenderFunctions.push(this.xk.bind(this));this.j.b.length&&(a.postRenderFunctions.push(this.B),a.animate=!0);pk(this,"postcompose",a);Qh(this,a);a.postRenderFunctions.push(Oh)}; -k.Ea=function(a,b,c,d,e,f,g){if(this.i.isContextLost())return!1;var h=b.viewState,l=b.layerStatesArray,m;for(m=l.length-1;0<=m;--m){var n=l[m];var p=n.layer;if(xh(n,h.resolution)&&f.call(g,p)&&(n=Ph(this,p).Ea(a,b,c,d,e)))return n}};k.Ei=function(a,b,c,d,e){c=!1;if(this.i.isContextLost())return!1;var f=b.viewState,g=b.layerStatesArray,h;for(h=g.length-1;0<=h;--h){var l=g[h],m=l.layer;if(xh(l,f.resolution)&&d.call(e,m)&&(c=Ph(this,m).Ue(a,b)))return!0}return c}; -k.Di=function(a,b,c,d,e){if(this.i.isContextLost())return!1;var f=b.viewState,g=b.layerStatesArray,h;for(h=g.length-1;0<=h;--h){var l=g[h];var m=l.layer;if(xh(l,f.resolution)&&e.call(d,m)&&(l=Ph(this,m).lg(a,b,c,d)))return l}};var qk=["canvas","webgl"]; -function G(a){Tc.call(this);var b=rk(a);this.Cf=void 0!==a.loadTilesWhileAnimating?a.loadTilesWhileAnimating:!1;this.Df=void 0!==a.loadTilesWhileInteracting?a.loadTilesWhileInteracting:!1;this.If=void 0!==a.pixelRatio?a.pixelRatio:Sd;this.yf=b.logos;this.pa=function(){this.j=void 0;this.Sp.call(this,Date.now())}.bind(this);this.Yb=Bh();this.Jf=Bh();this.ad=0;this.I=this.R=this.T=this.g=this.c=null;this.a=document.createElement("DIV");this.a.className="ol-viewport"+(Xd?" ol-touch":"");this.a.style.position= -"relative";this.a.style.overflow="hidden";this.a.style.width="100%";this.a.style.height="100%";this.a.style.msTouchAction="none";this.a.style.touchAction="none";this.C=document.createElement("DIV");this.C.className="ol-overlaycontainer";this.a.appendChild(this.C);this.D=document.createElement("DIV");this.D.className="ol-overlaycontainer-stopevent";for(var c="click dblclick mousedown touchstart MSPointerDown pointerdown mousewheel wheel".split(" "),d=0,e=c.length;d<e;++d)y(this.D,c[d],Pc);this.a.appendChild(this.D); -this.Sa=new Fe(this,a.moveTolerance);for(var f in de)y(this.Sa,de[f],this.Ih,this);this.va=b.keyboardEventTarget;this.u=null;y(this.a,"wheel",this.ld,this);y(this.a,"mousewheel",this.ld,this);this.l=b.controls;this.o=b.interactions;this.v=b.overlays;this.rg={};this.B=new b.Up(this.a,this);this.na=null;this.xb=[];this.$a=new Pe(this.ql.bind(this),this.Wl.bind(this));this.fa={};y(this,Vc("layergroup"),this.El,this);y(this,Vc("view"),this.Xl,this);y(this,Vc("size"),this.Tl,this);y(this,Vc("target"), -this.Vl,this);this.H(b.values);this.l.forEach(function(a){a.setMap(this)},this);y(this.l,"add",function(a){a.element.setMap(this)},this);y(this.l,"remove",function(a){a.element.setMap(null)},this);this.o.forEach(function(a){a.setMap(this)},this);y(this.o,"add",function(a){a.element.setMap(this)},this);y(this.o,"remove",function(a){a.element.setMap(null)},this);this.v.forEach(this.kh,this);y(this.v,"add",function(a){this.kh(a.element)},this);y(this.v,"remove",function(a){var b=a.element.g;void 0!== -b&&delete this.rg[b.toString()];a.element.setMap(null)},this)}v(G,Tc);k=G.prototype;k.kk=function(a){this.l.push(a)};k.lk=function(a){this.o.push(a)};k.ih=function(a){this.Kc().qd().push(a)};k.jh=function(a){this.v.push(a)};k.kh=function(a){var b=a.g;void 0!==b&&(this.rg[b.toString()]=a);a.setMap(this)}; -k.ka=function(){Nc(this.Sa);Nc(this.B);Kc(this.a,"wheel",this.ld,this);Kc(this.a,"mousewheel",this.ld,this);this.f&&(window.removeEventListener("resize",this.f,!1),this.f=void 0);this.j&&(cancelAnimationFrame(this.j),this.j=void 0);this.Le(null);Tc.prototype.ka.call(this)};k.we=function(a,b,c){if(this.c)return a=this.Wa(a),c=c?c:{},this.B.Ea(a,this.c,void 0!==c.hitTolerance?c.hitTolerance*this.c.pixelRatio:0,b,null,c.layerFilter?c.layerFilter:mf,null)}; -k.Im=function(a,b,c,d,e){if(this.c)return this.B.Di(a,this.c,b,void 0!==c?c:null,d?d:mf,void 0!==e?e:null)};k.Yl=function(a,b){if(!this.c)return!1;a=this.Wa(a);b=b?b:{};return this.B.Ei(a,this.c,void 0!==b.hitTolerance?b.hitTolerance*this.c.pixelRatio:0,b.layerFilter?b.layerFilter:mf,null)};k.Tf=function(a){return this.Wa(this.xe(a))};k.xe=function(a){var b=this.a.getBoundingClientRect();a=a.changedTouches?a.changedTouches[0]:a;return[a.clientX-b.left,a.clientY-b.top]};k.ag=function(){return this.get("target")}; -k.jd=function(){var a=this.ag();return void 0!==a?"string"===typeof a?document.getElementById(a):a:null};k.Wa=function(a){var b=this.c;return b?Gh(b.pixelToCoordinateTransform,a.slice()):null};k.Lk=function(){return this.l};k.fl=function(){return this.v};k.el=function(a){a=this.rg[a.toString()];return void 0!==a?a:null};k.Sk=function(){return this.o};k.Kc=function(){return this.get("layergroup")};k.Xh=function(){return this.Kc().qd()}; -k.Ja=function(a){var b=this.c;return b?Gh(b.coordinateToPixelTransform,a.slice(0,2)):null};k.Ob=function(){return this.get("size")};k.Z=function(){return this.get("view")};k.sl=function(){return this.a};k.ql=function(a,b,c,d){var e=this.c;if(!(e&&b in e.wantedTiles&&e.wantedTiles[b][a.bb()]))return Infinity;a=c[0]-e.focus[0];c=c[1]-e.focus[1];return 65536*Math.log(d)+Math.sqrt(a*a+c*c)/d};k.ld=function(a,b){a=new Jd(b||a.type,this,a);this.Ih(a)}; -k.Ih=function(a){if(this.c){this.na=a.coordinate;a.frameState=this.c;var b=this.o.a,c;if(!1!==this.b(a))for(c=b.length-1;0<=c;c--){var d=b[c];if(d.c()&&!d.handleEvent(a))break}}};k.Rl=function(){var a=this.c,b=this.$a;if(b.b.length){var c=16,d=c;if(a){var e=a.viewHints;e[0]&&(c=this.Cf?8:0,d=2);e[1]&&(c=this.Df?8:0,d=2)}b.j<c&&(Oe(b),Qe(b,c,d))}b=this.xb;c=0;for(d=b.length;c<d;++c)b[c](this,a);b.length=0};k.Tl=function(){this.render()}; -k.Vl=function(){var a;this.ag()&&(a=this.jd());if(this.u){for(var b=0,c=this.u.length;b<c;++b)Ec(this.u[b]);this.u=null}a?(a.appendChild(this.a),a=this.va?this.va:a,this.u=[y(a,"keydown",this.ld,this),y(a,"keypress",this.ld,this)],this.f||(this.f=this.Ad.bind(this),window.addEventListener("resize",this.f,!1))):(ld(this.a),this.f&&(window.removeEventListener("resize",this.f,!1),this.f=void 0));this.Ad()};k.Wl=function(){this.render()};k.Lh=function(){this.render()}; -k.Xl=function(){this.T&&(Ec(this.T),this.T=null);this.R&&(Ec(this.R),this.R=null);var a=this.Z();a&&(this.a.setAttribute("data-view",w(a)),this.T=y(a,"propertychange",this.Lh,this),this.R=y(a,"change",this.Lh,this));this.render()};k.El=function(){this.I&&(this.I.forEach(Ec),this.I=null);var a=this.Kc();a&&(this.I=[y(a,"propertychange",this.render,this),y(a,"change",this.render,this)]);this.render()};k.Tp=function(){this.j&&cancelAnimationFrame(this.j);this.pa()}; -k.render=function(){void 0===this.j&&(this.j=requestAnimationFrame(this.pa))};k.Mp=function(a){return this.l.remove(a)};k.Np=function(a){return this.o.remove(a)};k.Pp=function(a){return this.Kc().qd().remove(a)};k.Qp=function(a){return this.v.remove(a)}; -k.Sp=function(a){var b,c=this.Ob(),d=this.Z(),e=Oa(),f=this.c,g=null;if(void 0!==c&&0<c[0]&&0<c[1]&&d&&jg(d)){var g=dg(d,this.c?this.c.viewHints:void 0),h=this.Kc().Yf(),l={};var m=0;for(b=h.length;m<b;++m)l[w(h[m].layer)]=h[m];m=d.getState();g={animate:!1,attributions:{},coordinateToPixelTransform:this.Yb,extent:e,focus:this.na?this.na:m.center,index:this.ad++,layerStates:l,layerStatesArray:h,logos:tb({},this.yf),pixelRatio:this.If,pixelToCoordinateTransform:this.Jf,postRenderFunctions:[],size:c, -skippedFeatureUids:this.fa,tileQueue:this.$a,time:a,usedTiles:{},viewState:m,viewHints:g,wantedTiles:{}}}g&&(g.extent=ob(m.center,m.resolution,m.rotation,g.size,e));this.c=g;this.B.Jg(g);g&&(g.animate&&this.render(),Array.prototype.push.apply(this.xb,g.postRenderFunctions),!f||this.g&&(kb(this.g)||bb(g.extent,this.g))||(this.b(new Id("movestart",this,f)),this.g=Ya(this.g)),!this.g||g.viewHints[0]||g.viewHints[1]||bb(g.extent,this.g)||(this.b(new Id("moveend",this,g)),Ra(g.extent,this.g)));this.b(new Id("postrender", -this,g));setTimeout(this.Rl.bind(this),0)};k.qj=function(a){this.set("layergroup",a)};k.Qg=function(a){this.set("size",a)};k.Le=function(a){this.set("target",a)};k.iq=function(a){this.set("view",a)};k.xj=function(a){a=w(a).toString();this.fa[a]=!0;this.render()}; -k.Ad=function(){var a=this.jd();if(a){var b=getComputedStyle(a);this.Qg([a.offsetWidth-parseFloat(b.borderLeftWidth)-parseFloat(b.paddingLeft)-parseFloat(b.paddingRight)-parseFloat(b.borderRightWidth),a.offsetHeight-parseFloat(b.borderTopWidth)-parseFloat(b.paddingTop)-parseFloat(b.paddingBottom)-parseFloat(b.borderBottomWidth)])}else this.Qg(void 0)};k.Cj=function(a){a=w(a).toString();delete this.fa[a];this.render()}; -function rk(a){var b=null;void 0!==a.keyboardEventTarget&&(b="string"===typeof a.keyboardEventTarget?document.getElementById(a.keyboardEventTarget):a.keyboardEventTarget);var c={},d={};if(void 0===a.logo||"boolean"===typeof a.logo&&a.logo)d["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszWWMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvYasvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvXH1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1VkbMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLPVcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqTacrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaarldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+HizeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDnBAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSFhYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJREFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxCBrb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7ahgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCnB3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDgq82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC"]= -"https://openlayers.org/";else{var e=a.logo;"string"===typeof e?d[e]="":e instanceof HTMLElement?d[w(e).toString()]=e:e&&(xa("string"==typeof e.href,44),xa("string"==typeof e.src,45),d[e.src]=e.href)}e=a.layers instanceof uh?a.layers:new uh({layers:a.layers});c.layergroup=e;c.target=a.target;c.view=void 0!==a.view?a.view:new F;var e=Mh,f;void 0!==a.renderer?(Array.isArray(a.renderer)?f=a.renderer:"string"===typeof a.renderer?f=[a.renderer]:xa(!1,46),0<=f.indexOf("dom")&&(f=f.concat(qk))):f=qk;var g; -var h=0;for(g=f.length;h<g;++h){var l=f[h];if("canvas"==l){if(Ud){e=hi;break}}else if("webgl"==l&&Md){e=mk;break}}void 0!==a.controls?Array.isArray(a.controls)?f=new Yc(a.controls.slice()):(xa(a.controls instanceof Yc,47),f=a.controls):f=xd();void 0!==a.interactions?Array.isArray(a.interactions)?h=new Yc(a.interactions.slice()):(xa(a.interactions instanceof Yc,48),h=a.interactions):h=qh();void 0!==a.overlays?Array.isArray(a.overlays)?a=new Yc(a.overlays.slice()):(xa(a.overlays instanceof Yc,49),a= -a.overlays):a=new Yc;return{controls:f,interactions:h,keyboardEventTarget:b,logos:d,overlays:a,Up:e,values:c}};function sk(a){Tc.call(this);this.g=a.id;this.l=void 0!==a.insertFirst?a.insertFirst:!0;this.v=void 0!==a.stopEvent?a.stopEvent:!0;this.c=document.createElement("DIV");this.c.className="ol-overlay-container ol-selectable";this.c.style.position="absolute";this.autoPan=void 0!==a.autoPan?a.autoPan:!1;this.j=a.autoPanAnimation||{};this.o=void 0!==a.autoPanMargin?a.autoPanMargin:20;this.a={re:"",Ie:"",nf:"",vf:"",visible:!0};this.f=null;y(this,Vc(tk),this.zl,this);y(this,Vc(uk),this.Jl,this);y(this,Vc(vk), -this.Nl,this);y(this,Vc(wk),this.Pl,this);y(this,Vc(xk),this.Ql,this);void 0!==a.element&&this.lj(a.element);this.rj(void 0!==a.offset?a.offset:[0,0]);this.uj(void 0!==a.positioning?a.positioning:"top-left");void 0!==a.position&&this.Ne(a.position)}v(sk,Tc);k=sk.prototype;k.Rd=function(){return this.get(tk)};k.Jm=function(){return this.g};k.Me=function(){return this.get(uk)};k.Dh=function(){return this.get(vk)};k.Yh=function(){return this.get(wk)};k.Eh=function(){return this.get(xk)}; -k.zl=function(){for(var a=this.c;a.lastChild;)a.removeChild(a.lastChild);(a=this.Rd())&&this.c.appendChild(a)};k.Jl=function(){this.f&&(ld(this.c),Ec(this.f),this.f=null);var a=this.Me();a&&(this.f=y(a,"postrender",this.render,this),yk(this),a=this.v?a.D:a.C,this.l?a.insertBefore(this.c,a.childNodes[0]||null):a.appendChild(this.c))};k.render=function(){yk(this)};k.Nl=function(){yk(this)}; -k.Pl=function(){yk(this);if(this.get(wk)&&this.autoPan){var a=this.Me();if(a&&a.jd()){var b=zk(a.jd(),a.Ob()),c=this.Rd(),d=c.offsetWidth,e=getComputedStyle(c),d=d+(parseInt(e.marginLeft,10)+parseInt(e.marginRight,10)),e=c.offsetHeight,f=getComputedStyle(c),e=e+(parseInt(f.marginTop,10)+parseInt(f.marginBottom,10)),g=zk(c,[d,e]),c=this.o;Va(b,g)||(d=g[0]-b[0],e=b[2]-g[2],f=g[1]-b[1],g=b[3]-g[3],b=[0,0],0>d?b[0]=d-c:0>e&&(b[0]=Math.abs(e)+c),0>f?b[1]=f-c:0>g&&(b[1]=Math.abs(g)+c),0===b[0]&&0===b[1])|| -(c=a.Z().wa(),c=a.Ja(c),b=[c[0]+b[0],c[1]+b[1]],a.Z().animate({center:a.Wa(b),duration:this.j.duration,easing:this.j.easing}))}}};k.Ql=function(){yk(this)};k.lj=function(a){this.set(tk,a)};k.setMap=function(a){this.set(uk,a)};k.rj=function(a){this.set(vk,a)};k.Ne=function(a){this.set(wk,a)};function zk(a,b){var c=a.getBoundingClientRect();a=c.left+window.pageXOffset;c=c.top+window.pageYOffset;return[a,c,a+b[0],c+b[1]]}k.uj=function(a){this.set(xk,a)}; -function Ak(a,b){a.a.visible!==b&&(a.c.style.display=b?"":"none",a.a.visible=b)} -function yk(a){var b=a.Me(),c=a.Yh();if(b&&b.c&&c){var c=b.Ja(c),d=b.Ob(),b=a.c.style,e=a.Dh(),f=a.Eh();Ak(a,!0);var g=e[0],e=e[1];if("bottom-right"==f||"center-right"==f||"top-right"==f)""!==a.a.Ie&&(a.a.Ie=b.left=""),g=Math.round(d[0]-c[0]-g)+"px",a.a.nf!=g&&(a.a.nf=b.right=g);else{""!==a.a.nf&&(a.a.nf=b.right="");if("bottom-center"==f||"center-center"==f||"top-center"==f)g-=a.c.offsetWidth/2;g=Math.round(c[0]+g)+"px";a.a.Ie!=g&&(a.a.Ie=b.left=g)}if("bottom-left"==f||"bottom-center"==f||"bottom-right"== -f)""!==a.a.vf&&(a.a.vf=b.top=""),c=Math.round(d[1]-c[1]-e)+"px",a.a.re!=c&&(a.a.re=b.bottom=c);else{""!==a.a.re&&(a.a.re=b.bottom="");if("center-left"==f||"center-center"==f||"center-right"==f)e-=a.c.offsetHeight/2;c=Math.round(c[1]+e)+"px";a.a.vf!=c&&(a.a.vf=b.top=c)}}else Ak(a,!1)}var tk="element",uk="map",vk="offset",wk="position",xk="positioning";function Bk(a){function b(a){a=h.Tf(a);l.a.Z().ob(a);window.removeEventListener("mousemove",c);window.removeEventListener("mouseup",b)}function c(a){a=h.Tf({clientX:a.clientX-n.offsetWidth/2,clientY:a.clientY+n.offsetHeight/2});m.Ne(a)}a=a?a:{};this.j=void 0!==a.collapsed?a.collapsed:!0;this.o=void 0!==a.collapsible?a.collapsible:!0;this.o||(this.j=!1);var d=void 0!==a.className?a.className:"ol-overviewmap",e=void 0!==a.tipLabel?a.tipLabel:"Overview map",f=void 0!==a.collapseLabel?a.collapseLabel: -"\u00ab";"string"===typeof f?(this.u=document.createElement("span"),this.u.textContent=f):this.u=f;f=void 0!==a.label?a.label:"\u00bb";"string"===typeof f?(this.D=document.createElement("span"),this.D.textContent=f):this.D=f;var g=this.o&&!this.j?this.u:this.D,f=document.createElement("button");f.setAttribute("type","button");f.title=e;f.appendChild(g);y(f,"click",this.an,this);this.C=document.createElement("DIV");this.C.className="ol-overviewmap-map";var h=this.c=new G({controls:new Yc,interactions:new Yc, -view:a.view});a.layers&&a.layers.forEach(function(a){h.ih(a)},this);e=document.createElement("DIV");e.className="ol-overviewmap-box";e.style.boxSizing="border-box";this.l=new sk({position:[0,0],positioning:"bottom-left",element:e});this.c.jh(this.l);e=document.createElement("div");e.className=d+" ol-unselectable ol-control"+(this.j&&this.o?" ol-collapsed":"")+(this.o?"":" ol-uncollapsible");e.appendChild(this.C);e.appendChild(f);md.call(this,{element:e,render:a.render?a.render:Ck,target:a.target}); -var l=this,m=this.l,n=this.l.Rd();n.addEventListener("mousedown",function(){window.addEventListener("mousemove",c);window.addEventListener("mouseup",b)})}v(Bk,md);k=Bk.prototype;k.setMap=function(a){var b=this.a;a!==b&&(b&&((b=b.Z())&&Kc(b,Vc("rotation"),this.Ge,this),this.c.Le(null)),md.prototype.setMap.call(this,a),a&&(this.c.Le(this.C),this.v.push(y(a,"propertychange",this.Kl,this)),this.c.Xh().dc()||this.c.qj(a.Kc()),a=a.Z()))&&(y(a,Vc("rotation"),this.Ge,this),jg(a)&&(this.c.Ad(),Dk(this)))}; -k.Kl=function(a){"view"===a.key&&((a=a.oldValue)&&Kc(a,Vc("rotation"),this.Ge,this),a=this.a.Z(),y(a,Vc("rotation"),this.Ge,this))};k.Ge=function(){this.c.Z().Oe(this.a.Z().Qa())};function Ck(){var a=this.a,b=this.c;if(a.c&&b.c){var c=a.Ob(),a=a.Z().dd(c),d=b.Ob(),c=b.Z().dd(d),e=b.Ja(ib(a)),f=b.Ja(gb(a)),b=Math.abs(e[0]-f[0]),e=Math.abs(e[1]-f[1]),f=d[0],d=d[1];b<.1*f||e<.1*d||b>.75*f||e>.75*d?Dk(this):Va(c,a)||(a=this.c,c=this.a.Z(),a.Z().ob(c.wa()))}Ek(this)} -function Dk(a){var b=a.a;a=a.c;var c=b.Ob(),b=b.Z().dd(c);a=a.Z();rb(b,1/(.1*Math.pow(2,Math.log(7.5)/Math.LN2/2)));a.Qf(b)}function Ek(a){var b=a.a,c=a.c;if(b.c&&c.c){var d=b.Ob(),e=b.Z(),f=c.Z(),c=e.Qa(),b=a.l,g=a.l.Rd(),h=e.dd(d),d=f.Pa(),e=eb(h),f=hb(h);if(a=a.a.Z().wa()){var l=[e[0]-a[0],e[1]-a[1]];ef(l,c);Ze(l,a)}b.Ne(l);g&&(g.style.width=Math.abs((e[0]-f[0])/d)+"px",g.style.height=Math.abs((f[1]-e[1])/d)+"px")}}k.an=function(a){a.preventDefault();Fk(this)}; -function Fk(a){a.element.classList.toggle("ol-collapsed");a.j?kd(a.u,a.D):kd(a.D,a.u);a.j=!a.j;var b=a.c;a.j||b.c||(b.Ad(),Dk(a),Jc(b,"postrender",function(){Ek(this)},a))}k.$m=function(){return this.o};k.cn=function(a){this.o!==a&&(this.o=a,this.element.classList.toggle("ol-uncollapsible"),!a&&this.j&&Fk(this))};k.bn=function(a){this.o&&this.j!==a&&Fk(this)};k.Zm=function(){return this.j};k.gl=function(){return this.c};function Gk(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-scale-line";this.o=document.createElement("DIV");this.o.className=b+"-inner";this.c=document.createElement("DIV");this.c.className=b+" ol-unselectable";this.c.appendChild(this.o);this.u=null;this.l=void 0!==a.minWidth?a.minWidth:64;this.j=!1;this.B=void 0;this.D="";md.call(this,{element:this.c,render:a.render?a.render:Hk,target:a.target});y(this,Vc(Ik),this.T,this);this.I(a.units||"metric")}v(Gk,md);var Jk=[1,2,5];Gk.prototype.C=function(){return this.get(Ik)}; -function Hk(a){(a=a.frameState)?this.u=a.viewState:this.u=null;Kk(this)}Gk.prototype.T=function(){Kk(this)};Gk.prototype.I=function(a){this.set(Ik,a)}; -function Kk(a){var b=a.u;if(b){var c=b.projection,d=c.sc(),b=Sb(c,b.resolution,b.center)*d,d=a.l*b,c="",e=a.C();"degrees"==e?(c=zb.degrees,b/=c,d<c/60?(c="\u2033",b*=3600):d<c?(c="\u2032",b*=60):c="\u00b0"):"imperial"==e?.9144>d?(c="in",b/=.0254):1609.344>d?(c="ft",b/=.3048):(c="mi",b/=1609.344):"nautical"==e?(b/=1852,c="nm"):"metric"==e?.001>d?(c="\u03bcm",b*=1E6):1>d?(c="mm",b*=1E3):1E3>d?c="m":(c="km",b/=1E3):"us"==e?.9144>d?(c="in",b*=39.37):1609.344>d?(c="ft",b/=.30480061):(c="mi",b/=1609.3472): -xa(!1,33);for(var e=3*Math.floor(Math.log(a.l*b)/Math.log(10)),f;;){f=Jk[(e%3+3)%3]*Math.pow(10,Math.floor(e/3));d=Math.round(f/b);if(isNaN(d)){a.c.style.display="none";a.j=!1;return}if(d>=a.l)break;++e}b=f+" "+c;a.D!=b&&(a.o.innerHTML=b,a.D=b);a.B!=d&&(a.o.style.width=d+"px",a.B=d);a.j||(a.c.style.display="",a.j=!0)}else a.j&&(a.c.style.display="none",a.j=!1)}var Ik="units";function Lk(a){a=a?a:{};this.c=void 0;this.j=Mk;this.D=this.l=0;this.I=null;this.na=!1;this.T=void 0!==a.duration?a.duration:200;var b=void 0!==a.className?a.className:"ol-zoomslider",c=document.createElement("button");c.setAttribute("type","button");c.className=b+"-thumb ol-unselectable";var d=document.createElement("div");d.className=b+" ol-unselectable ol-control";d.appendChild(c);this.o=new Ae(d);y(this.o,"pointerdown",this.yl,this);y(this.o,"pointermove",this.wl,this);y(this.o,"pointerup",this.xl, -this);y(d,"click",this.vl,this);y(c,"click",Pc);md.call(this,{element:d,render:a.render?a.render:Nk})}v(Lk,md);Lk.prototype.ka=function(){Nc(this.o);md.prototype.ka.call(this)};var Mk=0;k=Lk.prototype;k.setMap=function(a){md.prototype.setMap.call(this,a);a&&a.render()}; -function Nk(a){if(a.frameState){if(!this.na){var b=this.element,c=b.offsetWidth,d=b.offsetHeight,e=b.firstElementChild,f=getComputedStyle(e),b=e.offsetWidth+parseFloat(f.marginRight)+parseFloat(f.marginLeft),e=e.offsetHeight+parseFloat(f.marginTop)+parseFloat(f.marginBottom);this.I=[b,e];c>d?(this.j=1,this.D=c-b):(this.j=Mk,this.l=d-e);this.na=!0}a=a.frameState.viewState.resolution;a!==this.c&&(this.c=a,Ok(this,a))}} -k.vl=function(a){var b=this.a.Z();a=Pk(this,Ca(1===this.j?(a.offsetX-this.I[0]/2)/this.D:(a.offsetY-this.I[1]/2)/this.l,0,1));b.animate({resolution:b.constrainResolution(a),duration:this.T,easing:rd})};k.yl=function(a){this.u||a.b.target!==this.element.firstElementChild||(cg(this.a.Z(),1,1),this.C=a.clientX,this.B=a.clientY,this.u=!0)}; -k.wl=function(a){if(this.u){var b=this.element.firstElementChild;this.c=Pk(this,Ca(1===this.j?(a.clientX-this.C+parseInt(b.style.left,10))/this.D:(a.clientY-this.B+parseInt(b.style.top,10))/this.l,0,1));this.a.Z().Vc(this.c);Ok(this,this.c);this.C=a.clientX;this.B=a.clientY}};k.xl=function(){if(this.u){var a=this.a.Z();cg(a,1,-1);a.animate({resolution:a.constrainResolution(this.c),duration:this.T,easing:rd});this.u=!1;this.B=this.C=void 0}}; -function Ok(a,b){b=1-ig(a.a.Z())(b);var c=a.element.firstElementChild;1==a.j?c.style.left=a.D*b+"px":c.style.top=a.l*b+"px"}function Pk(a,b){return hg(a.a.Z())(1-b)};function Qk(a){a=a?a:{};this.c=a.extent?a.extent:null;var b=void 0!==a.className?a.className:"ol-zoom-extent",c=void 0!==a.label?a.label:"E",d=void 0!==a.tipLabel?a.tipLabel:"Fit to extent",e=document.createElement("button");e.setAttribute("type","button");e.title=d;e.appendChild("string"===typeof c?document.createTextNode(c):c);y(e,"click",this.j,this);c=document.createElement("div");c.className=b+" ol-unselectable ol-control";c.appendChild(e);md.call(this,{element:c,target:a.target})}v(Qk,md); -Qk.prototype.j=function(a){a.preventDefault();a=this.a.Z();var b=this.c?this.c:a.v.G();a.Qf(b)};function Rk(a){Tc.call(this);a=a?a:{};this.a=null;y(this,Vc(Sk),this.vm,this);this.gg(void 0!==a.tracking?a.tracking:!1)}v(Rk,Tc);k=Rk.prototype;k.ka=function(){this.gg(!1);Tc.prototype.ka.call(this)}; -k.ap=function(a){if(null!==a.alpha){var b=Ha(a.alpha);this.set(Tk,b);"boolean"===typeof a.absolute&&a.absolute?this.set(Uk,b):"number"===typeof a.webkitCompassHeading&&-1!=a.webkitCompassAccuracy&&this.set(Uk,Ha(a.webkitCompassHeading))}null!==a.beta&&this.set(Vk,Ha(a.beta));null!==a.gamma&&this.set(Wk,Ha(a.gamma));this.s()};k.Fk=function(){return this.get(Tk)};k.Ik=function(){return this.get(Vk)};k.Ok=function(){return this.get(Wk)};k.um=function(){return this.get(Uk)};k.Th=function(){return this.get(Sk)}; -k.vm=function(){if(Vd){var a=this.Th();a&&!this.a?this.a=y(window,"deviceorientation",this.ap,this):a||null===this.a||(Ec(this.a),this.a=null)}};k.gg=function(a){this.set(Sk,a)};var Tk="alpha",Vk="beta",Wk="gamma",Uk="heading",Sk="tracking";function Xk(a){this.f=a.opacity;this.l=a.rotateWithView;this.g=a.rotation;this.a=a.scale;this.v=a.snapToPixel}k=Xk.prototype;k.Ze=function(){return this.f};k.$e=function(){return this.l};k.af=function(){return this.g};k.bf=function(){return this.a};k.Ae=function(){return this.v};k.td=function(a){this.f=a};k.cf=function(a){this.g=a};k.ud=function(a){this.a=a};function Yk(a){this.D=this.u=this.c=null;this.Va=void 0!==a.fill?a.fill:null;this.oa=[0,0];this.o=a.points;this.b=void 0!==a.radius?a.radius:a.radius1;this.i=a.radius2;this.j=void 0!==a.angle?a.angle:0;this.Ya=void 0!==a.stroke?a.stroke:null;this.B=this.ra=this.C=null;this.S=a.atlasManager;Zk(this,this.S);Xk.call(this,{opacity:1,rotateWithView:void 0!==a.rotateWithView?a.rotateWithView:!1,rotation:void 0!==a.rotation?a.rotation:0,scale:1,snapToPixel:void 0!==a.snapToPixel?a.snapToPixel:!0})} -v(Yk,Xk);k=Yk.prototype;k.clone=function(){var a=new Yk({fill:this.Fa()?this.Fa().clone():void 0,points:this.o,radius:this.b,radius2:this.i,angle:this.j,snapToPixel:this.v,stroke:this.Ga()?this.Ga().clone():void 0,rotation:this.g,rotateWithView:this.l,atlasManager:this.S});a.td(this.f);a.ud(this.a);return a};k.Hc=function(){return this.C};k.Pi=function(){return this.j};k.Fa=function(){return this.Va};k.qg=function(){return this.D};k.Y=function(){return this.u};k.ye=function(){return this.B}; -k.Ye=function(){return 2};k.Oc=function(){return this.oa};k.Qi=function(){return this.o};k.Ri=function(){return this.b};k.Fh=function(){return this.i};k.ic=function(){return this.ra};k.Ga=function(){return this.Ya};k.Nh=function(){};k.load=function(){};k.Bj=function(){}; -function Zk(a,b){var c="",d="",e=0,f=null,g=0;if(a.Ya){var h=a.Ya.a;null===h&&(h=Uh);h=id(h);g=a.Ya.c;void 0===g&&(g=1);f=a.Ya.i;Td||(f=null);d=a.Ya.j;void 0===d&&(d="round");c=a.Ya.f;void 0===c&&(c="round");e=a.Ya.o;void 0===e&&(e=10)}var l=2*(a.b+g)+1,c={strokeStyle:h,zj:g,size:l,lineCap:c,lineDash:f,lineJoin:d,miterLimit:e};if(void 0===b){var m=jd(l,l);a.u=m.canvas;b=l=a.u.width;a.rh(c,m,0,0);a.Va?a.D=a.u:(m=jd(c.size,c.size),a.D=m.canvas,a.qh(c,m,0,0))}else l=Math.round(l),(d=!a.Va)&&(m=a.qh.bind(a, -c)),a.Ya?(e=a.Ya,void 0===e.b&&(e.b="s",e.b=e.a?"string"===typeof e.a?e.b+e.a:e.b+w(e.a).toString():e.b+"-",e.b+=","+(void 0!==e.f?e.f.toString():"-")+","+(e.i?e.i.toString():"-")+","+(void 0!==e.g?e.g:"-")+","+(void 0!==e.j?e.j:"-")+","+(void 0!==e.o?e.o.toString():"-")+","+(void 0!==e.c?e.c.toString():"-")),e=e.b):e="-",a.Va?(f=a.Va,void 0===f.a&&(f.a=f.b instanceof CanvasPattern||f.b instanceof CanvasGradient?w(f.b).toString():"f"+(f.b?gd(f.b):"-")),f=f.a):f="-",a.c&&e==a.c[1]&&f==a.c[2]&&a.b== -a.c[3]&&a.i==a.c[4]&&a.j==a.c[5]&&a.o==a.c[6]||(a.c=["r"+e+f+(void 0!==a.b?a.b.toString():"-")+(void 0!==a.i?a.i.toString():"-")+(void 0!==a.j?a.j.toString():"-")+(void 0!==a.o?a.o.toString():"-"),e,f,a.b,a.i,a.j,a.o]),m=b.add(a.c[0],l,l,a.rh.bind(a,c),m),a.u=m.image,a.oa=[m.offsetX,m.offsetY],b=m.image.width,a.D=d?m.Zl:a.u;a.C=[l/2,l/2];a.ra=[l,l];a.B=[b,b]} -k.rh=function(a,b,c,d){b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();var e=this.o;if(Infinity===e)b.arc(a.size/2,a.size/2,this.b,0,2*Math.PI,!0);else{var f=void 0!==this.i?this.i:this.b;f!==this.b&&(e*=2);for(c=0;c<=e;c++){d=2*c*Math.PI/e-Math.PI/2+this.j;var g=c%2?f:this.b;b.lineTo(a.size/2+g*Math.cos(d),a.size/2+g*Math.sin(d))}}this.Va&&(c=this.Va.b,null===c&&(c=Sh),b.fillStyle=id(c),b.fill());this.Ya&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.zj,a.lineDash&&b.setLineDash(a.lineDash), -b.lineCap=a.lineCap,b.lineJoin=a.lineJoin,b.miterLimit=a.miterLimit,b.stroke());b.closePath()}; -k.qh=function(a,b,c,d){b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();c=this.o;if(Infinity===c)b.arc(a.size/2,a.size/2,this.b,0,2*Math.PI,!0);else{d=void 0!==this.i?this.i:this.b;d!==this.b&&(c*=2);var e;for(e=0;e<=c;e++){var f=2*e*Math.PI/c-Math.PI/2+this.j;var g=e%2?d:this.b;b.lineTo(a.size/2+g*Math.cos(f),a.size/2+g*Math.sin(f))}}b.fillStyle=Sh;b.fill();this.Ya&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.zj,a.lineDash&&b.setLineDash(a.lineDash),b.stroke());b.closePath()};function $k(a){a=a||{};Yk.call(this,{points:Infinity,fill:a.fill,radius:a.radius,snapToPixel:a.snapToPixel,stroke:a.stroke,atlasManager:a.atlasManager})}v($k,Yk);$k.prototype.clone=function(){var a=new $k({fill:this.Fa()?this.Fa().clone():void 0,stroke:this.Ga()?this.Ga().clone():void 0,radius:this.b,snapToPixel:this.v,atlasManager:this.S});a.td(this.f);a.ud(this.a);return a};$k.prototype.Uc=function(a){this.b=a;Zk(this,this.S)};function al(a){a=a||{};this.b=void 0!==a.color?a.color:null;this.a=void 0}al.prototype.clone=function(){var a=this.b;return new al({color:a&&a.slice?a.slice():a||void 0})};al.prototype.i=function(){return this.b};al.prototype.c=function(a){this.b=a;this.a=void 0};function bl(a){a=a||{};this.Gc=null;this.Za=cl;void 0!==a.geometry&&this.Ra(a.geometry);this.Va=void 0!==a.fill?a.fill:null;this.M=void 0!==a.image?a.image:null;this.Ya=void 0!==a.stroke?a.stroke:null;this.Ia=void 0!==a.text?a.text:null;this.Fj=a.zIndex}k=bl.prototype; -k.clone=function(){var a=this.V();a&&a.clone&&(a=a.clone());return new bl({geometry:a,fill:this.Fa()?this.Fa().clone():void 0,image:this.Y()?this.Y().clone():void 0,stroke:this.Ga()?this.Ga().clone():void 0,text:this.Na()?this.Na().clone():void 0,zIndex:this.Ba()})};k.V=function(){return this.Gc};k.Pk=function(){return this.Za};k.Fa=function(){return this.Va};k.pf=function(a){this.Va=a};k.Y=function(){return this.M};k.Og=function(a){this.M=a};k.Ga=function(){return this.Ya}; -k.qf=function(a){this.Ya=a};k.Na=function(){return this.Ia};k.xd=function(a){this.Ia=a};k.Ba=function(){return this.Fj};k.Ra=function(a){"function"===typeof a?this.Za=a:"string"===typeof a?this.Za=function(b){return b.get(a)}:a?a&&(this.Za=function(){return a}):this.Za=cl;this.Gc=a};k.Vb=function(a){this.Fj=a};function dl(a){if("function"!==typeof a){if(Array.isArray(a))var b=a;else xa(a instanceof bl,41),b=[a];a=function(){return b}}return a}var el=null; -function fl(){if(!el){var a=new al({color:"rgba(255,255,255,0.4)"}),b=new wj({color:"#3399CC",width:1.25});el=[new bl({image:new $k({fill:a,stroke:b,radius:5}),fill:a,stroke:b})]}return el} -function gl(){var a={},b=[255,255,255,1],c=[0,153,255,1];a.Polygon=[new bl({fill:new al({color:[255,255,255,.5]})})];a.MultiPolygon=a.Polygon;a.LineString=[new bl({stroke:new wj({color:b,width:5})}),new bl({stroke:new wj({color:c,width:3})})];a.MultiLineString=a.LineString;a.Circle=a.Polygon.concat(a.LineString);a.Point=[new bl({image:new $k({radius:6,fill:new al({color:c}),stroke:new wj({color:b,width:1.5})}),zIndex:Infinity})];a.MultiPoint=a.Point;a.GeometryCollection=a.Polygon.concat(a.LineString, -a.Point);return a}function cl(a){return a.V()};function H(a){Tc.call(this);this.a=void 0;this.c="geometry";this.g=null;this.j=void 0;this.f=null;y(this,Vc(this.c),this.Ee,this);void 0!==a&&(a instanceof of||!a?this.Ra(a):this.H(a))}v(H,Tc);k=H.prototype;k.clone=function(){var a=new H(this.N());a.Tc(this.c);var b=this.V();b&&a.Ra(b.clone());(b=this.g)&&a.hg(b);return a};k.V=function(){return this.get(this.c)};k.wm=function(){return this.a};k.Qk=function(){return this.c};k.xm=function(){return this.g};k.Lc=function(){return this.j};k.Al=function(){this.s()}; -k.Ee=function(){this.f&&(Ec(this.f),this.f=null);var a=this.V();a&&(this.f=y(a,"change",this.Al,this));this.s()};k.Ra=function(a){this.set(this.c,a)};k.hg=function(a){this.j=(this.g=a)?hl(a):void 0;this.s()};k.jc=function(a){this.a=a;this.s()};k.Tc=function(a){Kc(this,Vc(this.c),this.Ee,this);this.c=a;y(this,Vc(this.c),this.Ee,this);this.Ee()}; -function hl(a){var b;if("function"===typeof a)2==a.length?b=function(b){return a(this,b)}:b=a;else{if(Array.isArray(a))var c=a;else xa(a instanceof bl,41),c=[a];b=function(){return c}}return b};var il=document.implementation.createDocument("","",null);function jl(a,b){return il.createElementNS(a,b)}function kl(a,b){return ll(a,b,[]).join("")}function ll(a,b,c){if(a.nodeType==Node.CDATA_SECTION_NODE||a.nodeType==Node.TEXT_NODE)b?c.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):c.push(a.nodeValue);else for(a=a.firstChild;a;a=a.nextSibling)ll(a,b,c);return c}function ml(a){return a instanceof Document}function nl(a){return a instanceof Node} -function pl(a){return(new DOMParser).parseFromString(a,"application/xml")}function ql(a,b){return function(c,d){c=a.call(b,c,d);void 0!==c&&la(d[d.length-1],c)}}function rl(a,b){return function(c,d){c=a.call(void 0!==b?b:this,c,d);void 0!==c&&d[d.length-1].push(c)}}function sl(a,b){return function(c,d){c=a.call(void 0!==b?b:this,c,d);void 0!==c&&(d[d.length-1]=c)}} -function tl(a){return function(b,c){var d=a.call(this,b,c);if(void 0!==d){c=c[c.length-1];b=b.localName;var e;b in c?e=c[b]:e=c[b]=[];e.push(d)}}}function I(a,b){return function(c,d){var e=a.call(this,c,d);void 0!==e&&(d[d.length-1][void 0!==b?b:c.localName]=e)}}function J(a,b){return function(c,d,e){a.call(void 0!==b?b:this,c,d,e);e[e.length-1].node.appendChild(c)}} -function vl(a){var b,c;return function(d,e,f){if(!b){b={};var g={};g[d.localName]=a;b[d.namespaceURI]=g;c=wl(d.localName)}xl(b,c,e,f)}}function wl(a,b){return function(c,d,e){c=d[d.length-1].node;d=a;void 0===d&&(d=e);e=b;void 0===b&&(e=c.namespaceURI);return jl(e,d)}}var yl=wl();function zl(a,b){for(var c=b.length,d=Array(c),e=0;e<c;++e)d[e]=a[b[e]];return d}function K(a,b,c){c=void 0!==c?c:{};var d;var e=0;for(d=a.length;e<d;++e)c[a[e]]=b;return c} -function Al(a,b,c,d){for(b=b.firstElementChild;b;b=b.nextElementSibling){var e=a[b.namespaceURI];void 0!==e&&(e=e[b.localName])&&e.call(d,b,c)}}function N(a,b,c,d,e){d.push(a);Al(b,c,d,e);return d.pop()}function xl(a,b,c,d,e,f){for(var g=(void 0!==e?e:c).length,h,l,m=0;m<g;++m)h=c[m],void 0!==h&&(l=b.call(f,h,d,void 0!==e?e[m]:void 0),void 0!==l&&a[l.namespaceURI][l.localName].call(f,l,h,d))}function Bl(a,b,c,d,e,f,g){e.push(a);xl(b,c,d,e,f,g);e.pop()};function Cl(a,b,c,d){return function(e,f,g){var h=new XMLHttpRequest;h.open("GET","function"===typeof a?a(e,f,g):a,!0);"arraybuffer"==b.U()&&(h.responseType="arraybuffer");h.onload=function(){if(!h.status||200<=h.status&&300>h.status){var a=b.U();if("json"==a||"text"==a)var e=h.responseText;else"xml"==a?(e=h.responseXML)||(e=pl(h.responseText)):"arraybuffer"==a&&(e=h.response);e?c.call(this,b.Oa(e,{featureProjection:g}),b.kb(e)):d.call(this)}else d.call(this)}.bind(this);h.onerror=function(){d.call(this)}.bind(this); -h.send()}}function Dl(a,b){return Cl(a,b,function(a){this.cd(a)},ua)};function El(){this.f=this.defaultDataProjection=null}function Fl(a,b,c){var d;c&&(d={dataProjection:c.dataProjection?c.dataProjection:a.kb(b),featureProjection:c.featureProjection});return Gl(a,d)}function Gl(a,b){return tb({dataProjection:a.defaultDataProjection,featureProjection:a.f},b)} -function Hl(a,b,c){var d=c?Tb(c.featureProjection):null,e=c?Tb(c.dataProjection):null,f;d&&e&&!dc(d,e)?a instanceof of?f=(b?a.clone():a).tb(b?d:e,b?e:d):f=hc(a,e,d):f=a;if(b&&c&&void 0!==c.decimals){var g=Math.pow(10,c.decimals);f===a&&(f=f.clone());f.Dc(function(a){for(var b=0,c=a.length;b<c;++b)a[b]=Math.round(a[b]*g)/g;return a})}return f};function Il(){El.call(this)}v(Il,El);function Jl(a){return"string"===typeof a?(a=JSON.parse(a))?a:null:null!==a?a:null}k=Il.prototype;k.U=function(){return"json"};k.Tb=function(a,b){return this.Rc(Jl(a),Fl(this,a,b))};k.Oa=function(a,b){return this.yg(Jl(a),Fl(this,a,b))};k.Sc=function(a,b){return this.Cg(Jl(a),Fl(this,a,b))};k.kb=function(a){return this.Fg(Jl(a))};k.Bd=function(a,b){return JSON.stringify(this.Zc(a,b))};k.Wb=function(a,b){return JSON.stringify(this.he(a,b))}; -k.$c=function(a,b){return JSON.stringify(this.je(a,b))};function Kl(a,b,c,d,e,f){var g=NaN,h=NaN,l=(c-b)/d;if(1===l)g=a[b],h=a[b+1];else if(2==l)g=(1-e)*a[b]+e*a[b+d],h=(1-e)*a[b+1]+e*a[b+d+1];else if(l){var h=a[b],l=a[b+1],m=0,g=[0],n;for(n=b+d;n<c;n+=d){var p=a[n],q=a[n+1],m=m+Math.sqrt((p-h)*(p-h)+(q-l)*(q-l));g.push(m);h=p;l=q}c=e*m;l=0;m=g.length;for(n=!1;l<m;)e=l+(m-l>>1),h=+ia(g[e],c),0>h?l=e+1:(m=e,n=!h);e=n?l:~l;0>e?(c=(c-g[-e-2])/(g[-e-1]-g[-e-2]),b+=(-e-2)*d,g=Ja(a[b],a[b+d],c),h=Ja(a[b+1],a[b+d+1],c)):(g=a[b+e*d],h=a[b+e*d+1])}return f?(f[0]= -g,f[1]=h,f):[g,h]}function Ll(a,b,c,d,e,f){if(c==b)return null;if(e<a[b+d-1])return f?(c=a.slice(b,b+d),c[d-1]=e,c):null;if(a[c-1]<e)return f?(c=a.slice(c-d,c),c[d-1]=e,c):null;if(e==a[b+d-1])return a.slice(b,b+d);b/=d;for(c/=d;b<c;)f=b+c>>1,e<a[(f+1)*d-1]?c=f:b=f+1;c=a[b*d-1];if(e==c)return a.slice((b-1)*d,(b-1)*d+d);f=(e-c)/(a[(b+1)*d-1]-c);c=[];var g;for(g=0;g<d-1;++g)c.push(Ja(a[(b-1)*d+g],a[b*d+g],f));c.push(e);return c} -function Ml(a,b,c,d,e,f){var g=0;if(f)return Ll(a,g,b[b.length-1],c,d,e);if(d<a[c-1])return e?(a=a.slice(0,c),a[c-1]=d,a):null;if(a[a.length-1]<d)return e?(a=a.slice(a.length-c),a[c-1]=d,a):null;e=0;for(f=b.length;e<f;++e){var h=b[e];if(g!=h){if(d<a[g+c-1])break;else if(d<=a[h-1])return Ll(a,g,h,c,d,!1);g=h}}return null};function O(a,b){rf.call(this);this.c=null;this.u=this.D=this.j=-1;this.ma(a,b)}v(O,rf);k=O.prototype;k.mk=function(a){this.A?la(this.A,a):this.A=a.slice();this.s()};k.clone=function(){var a=new O(null);a.ba(this.ja,this.A.slice());return a};k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;this.u!=this.i&&(this.D=Math.sqrt(yf(this.A,0,this.A.length,this.a,0)),this.u=this.i);return Af(this.A,0,this.A.length,this.a,this.D,!1,a,b,c,d)}; -k.Ck=function(a,b){return Pf(this.A,0,this.A.length,this.a,a,b)};k.nn=function(a,b){return"XYM"!=this.ja&&"XYZM"!=this.ja?null:Ll(this.A,0,this.A.length,this.a,a,void 0!==b?b:!1)};k.X=function(){return Ff(this.A,0,this.A.length,this.a)};k.wh=function(a,b){return Kl(this.A,0,this.A.length,this.a,a,b)};k.pn=function(){var a=this.A,b=this.a,c=a[0],d=a[1],e=0,f;for(f=0+b;f<this.A.length;f+=b)var g=a[f],h=a[f+1],e=e+Math.sqrt((g-c)*(g-c)+(h-d)*(h-d)),c=g,d=h;return e}; -function di(a){a.j!=a.i&&(a.c=a.wh(.5,a.c),a.j=a.i);return a.c}k.hd=function(a){var b=[];b.length=Hf(this.A,0,this.A.length,this.a,a,b,0);a=new O(null);a.ba("XY",b);return a};k.U=function(){return"LineString"};k.Xa=function(a){return Qf(this.A,0,this.A.length,this.a,a)};k.ma=function(a,b){a?(uf(this,b,a,1),this.A||(this.A=[]),this.A.length=Df(this.A,0,a,this.a),this.s()):this.ba("XY",null)};k.ba=function(a,b){tf(this,a,b);this.s()};function P(a,b){rf.call(this);this.c=[];this.j=this.u=-1;this.ma(a,b)}v(P,rf);k=P.prototype;k.nk=function(a){this.A?la(this.A,a.ga().slice()):this.A=a.ga().slice();this.c.push(this.A.length);this.s()};k.clone=function(){var a=new P(null);a.ba(this.ja,this.A.slice(),this.c.slice());return a};k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;this.j!=this.i&&(this.u=Math.sqrt(zf(this.A,0,this.c,this.a,0)),this.j=this.i);return Bf(this.A,0,this.c,this.a,this.u,!1,a,b,c,d)}; -k.rn=function(a,b,c){return"XYM"!=this.ja&&"XYZM"!=this.ja||!this.A.length?null:Ml(this.A,this.c,this.a,a,void 0!==b?b:!1,void 0!==c?c:!1)};k.X=function(){return Gf(this.A,0,this.c,this.a)};k.Bb=function(){return this.c};k.Yk=function(a){if(0>a||this.c.length<=a)return null;var b=new O(null);b.ba(this.ja,this.A.slice(a?this.c[a-1]:0,this.c[a]));return b}; -k.gd=function(){var a=this.A,b=this.c,c=this.ja,d=[],e=0,f;var g=0;for(f=b.length;g<f;++g){var h=b[g],l=new O(null);l.ba(c,a.slice(e,h));d.push(l);e=h}return d};function ei(a){var b=[],c=a.A,d=0,e=a.c;a=a.a;var f;var g=0;for(f=e.length;g<f;++g){var h=e[g],d=Kl(c,d,h,a,.5);la(b,d);d=h}return b}k.hd=function(a){var b=[],c=[],d=this.A,e=this.c,f=this.a,g=0,h=0,l;var m=0;for(l=e.length;m<l;++m){var n=e[m],h=Hf(d,g,n,f,a,b,h);c.push(h);g=n}b.length=h;a=new P(null);a.ba("XY",b,c);return a};k.U=function(){return"MultiLineString"}; -k.Xa=function(a){a:{var b=this.A,c=this.c,d=this.a,e=0,f;var g=0;for(f=c.length;g<f;++g){if(Qf(b,e,c[g],d,a)){a=!0;break a}e=c[g]}a=!1}return a};k.ma=function(a,b){a?(uf(this,b,a,2),this.A||(this.A=[]),a=Ef(this.A,0,a,this.a,this.c),this.A.length=a.length?a[a.length-1]:0,this.s()):this.ba("XY",null,this.c)};k.ba=function(a,b,c){tf(this,a,b);this.c=c;this.s()};function Nl(a,b){var c=a.ja,d=[],e=[],f;var g=0;for(f=b.length;g<f;++g){var h=b[g];g||(c=h.ja);la(d,h.ga());e.push(d.length)}a.ba(c,d,e)};function Q(a,b){rf.call(this);this.ma(a,b)}v(Q,rf);k=Q.prototype;k.qk=function(a){this.A?la(this.A,a.ga()):this.A=a.ga().slice();this.s()};k.clone=function(){var a=new Q(null);a.ba(this.ja,this.A.slice());return a};k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;var e=this.A,f=this.a,g;var h=0;for(g=e.length;h<g;h+=f){var l=Ga(a,b,e[h],e[h+1]);if(l<d){d=l;for(l=0;l<f;++l)c[l]=e[h+l];c.length=f}}return d};k.X=function(){return Ff(this.A,0,this.A.length,this.a)}; -k.il=function(a){var b=this.A?this.A.length/this.a:0;if(0>a||b<=a)return null;b=new C(null);b.ba(this.ja,this.A.slice(a*this.a,(a+1)*this.a));return b};k.Zd=function(){var a=this.A,b=this.ja,c=this.a,d=[],e;var f=0;for(e=a.length;f<e;f+=c){var g=new C(null);g.ba(b,a.slice(f,f+c));d.push(g)}return d};k.U=function(){return"MultiPoint"};k.Xa=function(a){var b=this.A,c=this.a,d;var e=0;for(d=b.length;e<d;e+=c){var f=b[e];var g=b[e+1];if(Ua(a,f,g))return!0}return!1}; -k.ma=function(a,b){a?(uf(this,b,a,1),this.A||(this.A=[]),this.A.length=Df(this.A,0,a,this.a),this.s()):this.ba("XY",null)};k.ba=function(a,b){tf(this,a,b);this.s()};function R(a,b){rf.call(this);this.c=[];this.u=-1;this.D=null;this.I=this.C=this.B=-1;this.j=null;this.ma(a,b)}v(R,rf);k=R.prototype;k.rk=function(a){if(this.A){var b=this.A.length;la(this.A,a.ga());a=a.Bb().slice();var c;var d=0;for(c=a.length;d<c;++d)a[d]+=b}else this.A=a.ga().slice(),a=a.Bb().slice(),this.c.push();this.c.push(a);this.s()};k.clone=function(){for(var a=new R(null),b=this.c.length,c=Array(b),d=0;d<b;++d)c[d]=this.c[d].slice();Ol(a,this.ja,this.A.slice(),c);return a}; -k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;if(this.C!=this.i){var e=this.c,f=0,g=0,h;var l=0;for(h=e.length;l<h;++l)var m=e[l],g=zf(this.A,f,m,this.a,g),f=m[m.length-1];this.B=Math.sqrt(g);this.C=this.i}e=fi(this);f=this.c;g=this.a;l=this.B;h=0;var m=[NaN,NaN],n;var p=0;for(n=f.length;p<n;++p){var q=f[p];d=Bf(e,h,q,g,l,!0,a,b,c,d,m);h=q[q.length-1]}return d}; -k.Mc=function(a,b){a:{var c=fi(this),d=this.c,e=0;if(d.length){var f;var g=0;for(f=d.length;g<f;++g){var h=d[g];if(Nf(c,e,h,this.a,a,b)){a=!0;break a}e=h[h.length-1]}}a=!1}return a};k.sn=function(){var a=fi(this),b=this.c,c=0,d=0,e;var f=0;for(e=b.length;f<e;++f)var g=b[f],d=d+wf(a,c,g,this.a),c=g[g.length-1];return d}; -k.X=function(a){if(void 0!==a){var b=fi(this).slice();Vf(b,this.c,this.a,a)}else b=this.A;a=b;b=this.c;var c=this.a,d=0,e=[],f=0,g;var h=0;for(g=b.length;h<g;++h){var l=b[h];e[f++]=Gf(a,d,l,c,e[f]);d=l[l.length-1]}e.length=f;return e}; -function gi(a){if(a.u!=a.i){var b=a.A,c=a.c,d=a.a,e=0,f=[],g;var h=0;for(g=c.length;h<g;++h){var l=c[h],e=$a(b,e,l[0],d);f.push((e[0]+e[2])/2,(e[1]+e[3])/2);e=l[l.length-1]}b=fi(a);c=a.c;d=a.a;h=0;g=[];l=0;for(e=c.length;l<e;++l){var m=c[l];g=Of(b,h,m,d,f,2*l,g);h=m[m.length-1]}a.D=g;a.u=a.i}return a.D}k.Uk=function(){var a=new Q(null);a.ba("XY",gi(this).slice());return a}; -function fi(a){if(a.I!=a.i){var b=a.A;a:{var c=a.c;var d;var e=0;for(d=c.length;e<d;++e)if(!Tf(b,c[e],a.a,void 0)){c=!1;break a}c=!0}c?a.j=b:(a.j=b.slice(),a.j.length=Vf(a.j,a.c,a.a));a.I=a.i}return a.j}k.hd=function(a){var b=[],c=[],d=this.A,e=this.c,f=this.a;a=Math.sqrt(a);var g=0,h=0,l;var m=0;for(l=e.length;m<l;++m){var n=e[m],p=[],h=If(d,g,n,f,a,b,h,p);c.push(p);g=n[n.length-1]}b.length=h;d=new R(null);Ol(d,"XY",b,c);return d}; -k.jl=function(a){if(0>a||this.c.length<=a)return null;if(a){var b=this.c[a-1];b=b[b.length-1]}else b=0;a=this.c[a].slice();var c=a[a.length-1];if(b){var d;var e=0;for(d=a.length;e<d;++e)a[e]-=b}e=new D(null);e.ba(this.ja,this.A.slice(b,c),a);return e};k.Td=function(){var a=this.ja,b=this.A,c=this.c,d=[],e=0,f,g;var h=0;for(f=c.length;h<f;++h){var l=c[h].slice(),m=l[l.length-1];if(e){var n=0;for(g=l.length;n<g;++n)l[n]-=e}n=new D(null);n.ba(a,b.slice(e,m),l);d.push(n);e=m}return d};k.U=function(){return"MultiPolygon"}; -k.Xa=function(a){a:{var b=fi(this),c=this.c,d=this.a,e=0,f;var g=0;for(f=c.length;g<f;++g){var h=c[g];if(Rf(b,e,h,d,a)){a=!0;break a}e=h[h.length-1]}a=!1}return a};k.ma=function(a,b){if(a){uf(this,b,a,3);this.A||(this.A=[]);b=this.A;var c=this.a,d=this.c,e=0,d=d?d:[],f=0,g;var h=0;for(g=a.length;h<g;++h)e=Ef(b,e,a[h],c,d[f]),d[f++]=e,e=e[e.length-1];d.length=f;d.length?(a=d[d.length-1],this.A.length=a.length?a[a.length-1]:0):this.A.length=0;this.s()}else Ol(this,"XY",null,this.c)}; -function Ol(a,b,c,d){tf(a,b,c);a.c=d;a.s()}function Pl(a,b){var c=a.ja,d=[],e=[],f;var g=0;for(f=b.length;g<f;++g){var h=b[g];g||(c=h.ja);var l=d.length;var m=h.Bb();var n;var p=0;for(n=m.length;p<n;++p)m[p]+=l;la(d,h.ga());e.push(m)}Ol(a,c,d,e)};function Ql(a){a=a?a:{};El.call(this);this.b=a.geometryName}v(Ql,Il); -function Rl(a,b){if(!a)return null;if("number"===typeof a.x&&"number"===typeof a.y)var c="Point";else if(a.points)c="MultiPoint";else if(a.paths)c=1===a.paths.length?"LineString":"MultiLineString";else if(a.rings){var d=a.rings,e=Sl(a),f=[],g=[];c=[];var h;var l=0;for(h=d.length;l<h;++l)f.length=0,Df(f,0,d[l],e.length),Sf(f,0,f.length,e.length)?g.push([d[l]]):c.push(d[l]);for(;c.length;){d=c.shift();e=!1;for(l=g.length-1;0<=l;l--)if(Va((new Jf(g[l][0])).G(),(new Jf(d)).G())){g[l].push(d);e=!0;break}e|| -g.push([d.reverse()])}a=tb({},a);1===g.length?(c="Polygon",a.rings=g[0]):(c="MultiPolygon",a.rings=g)}return Hl((0,Tl[c])(a),!1,b)}function Sl(a){var b="XY";!0===a.hasZ&&!0===a.hasM?b="XYZM":!0===a.hasZ?b="XYZ":!0===a.hasM&&(b="XYM");return b}function Ul(a){a=a.ja;return{hasZ:"XYZ"===a||"XYZM"===a,hasM:"XYM"===a||"XYZM"===a}} -var Tl={Point:function(a){return void 0!==a.m&&void 0!==a.z?new C([a.x,a.y,a.z,a.m],"XYZM"):void 0!==a.z?new C([a.x,a.y,a.z],"XYZ"):void 0!==a.m?new C([a.x,a.y,a.m],"XYM"):new C([a.x,a.y])},LineString:function(a){return new O(a.paths[0],Sl(a))},Polygon:function(a){return new D(a.rings,Sl(a))},MultiPoint:function(a){return new Q(a.points,Sl(a))},MultiLineString:function(a){return new P(a.paths,Sl(a))},MultiPolygon:function(a){return new R(a.rings,Sl(a))}},Vl={Point:function(a){var b=a.X(),c;a=a.ja; -"XYZ"===a?c={x:b[0],y:b[1],z:b[2]}:"XYM"===a?c={x:b[0],y:b[1],m:b[2]}:"XYZM"===a?c={x:b[0],y:b[1],z:b[2],m:b[3]}:"XY"===a?c={x:b[0],y:b[1]}:xa(!1,34);return c},LineString:function(a){var b=Ul(a);return{hasZ:b.hasZ,hasM:b.hasM,paths:[a.X()]}},Polygon:function(a){var b=Ul(a);return{hasZ:b.hasZ,hasM:b.hasM,rings:a.X(!1)}},MultiPoint:function(a){var b=Ul(a);return{hasZ:b.hasZ,hasM:b.hasM,points:a.X()}},MultiLineString:function(a){var b=Ul(a);return{hasZ:b.hasZ,hasM:b.hasM,paths:a.X()}},MultiPolygon:function(a){var b= -Ul(a);a=a.X(!1);for(var c=[],d=0;d<a.length;d++)for(var e=a[d].length-1;0<=e;e--)c.push(a[d][e]);return{hasZ:b.hasZ,hasM:b.hasM,rings:c}}};k=Ql.prototype;k.Rc=function(a,b){var c=Rl(a.geometry,b),d=new H;this.b&&d.Tc(this.b);d.Ra(c);b&&b.dg&&a.attributes[b.dg]&&d.jc(a.attributes[b.dg]);a.attributes&&d.H(a.attributes);return d};k.yg=function(a,b){b=b?b:{};if(a.features){var c=[],d=a.features,e;b.dg=a.objectIdFieldName;a=0;for(e=d.length;a<e;++a)c.push(this.Rc(d[a],b));return c}return[this.Rc(a,b)]}; -k.Cg=function(a,b){return Rl(a,b)};k.Fg=function(a){return a.spatialReference&&a.spatialReference.wkid?Tb("EPSG:"+a.spatialReference.wkid):null};function Wl(a,b){return(0,Vl[a.U()])(Hl(a,!0,b),b)}k.je=function(a,b){return Wl(a,Gl(this,b))};k.Zc=function(a,b){b=Gl(this,b);var c={},d=a.V();d&&(c.geometry=Wl(d,b));d=a.N();delete d[a.c];c.attributes=wb(d)?{}:d;b&&b.featureProjection&&(c.spatialReference={wkid:Tb(b.featureProjection).mb.split(":").pop()});return c}; -k.he=function(a,b){b=Gl(this,b);var c=[],d;var e=0;for(d=a.length;e<d;++e)c.push(this.Zc(a[e],b));return{features:c}};function Xl(a){this.kc=a};function Yl(a,b){this.kc=a;this.b=Array.prototype.slice.call(arguments,1);xa(2<=this.b.length,57)}v(Yl,Xl);function Zl(a){var b=["And"].concat(Array.prototype.slice.call(arguments));Yl.apply(this,b)}v(Zl,Yl);function $l(a,b,c){this.kc="BBOX";this.geometryName=a;this.extent=b;this.srsName=c}v($l,Xl);function am(a,b){this.kc=a;this.b=b}v(am,Xl);function bm(a,b,c){am.call(this,"During",a);this.a=b;this.i=c}v(bm,am);function cm(a,b,c,d){am.call(this,a,b);this.i=c;this.a=d}v(cm,am);function dm(a,b,c){cm.call(this,"PropertyIsEqualTo",a,b,c)}v(dm,cm);function em(a,b){cm.call(this,"PropertyIsGreaterThan",a,b)}v(em,cm);function fm(a,b){cm.call(this,"PropertyIsGreaterThanOrEqualTo",a,b)}v(fm,cm);function gm(a,b,c,d){this.kc=a;this.geometryName=b||"the_geom";this.geometry=c;this.srsName=d}v(gm,Xl);function hm(a,b,c){gm.call(this,"Intersects",a,b,c)}v(hm,gm);function im(a,b,c){am.call(this,"PropertyIsBetween",a);this.a=b;this.i=c}v(im,am);function jm(a,b,c,d,e,f){am.call(this,"PropertyIsLike",a);this.c=b;this.g=void 0!==c?c:"*";this.f=void 0!==d?d:".";this.i=void 0!==e?e:"!";this.a=f}v(jm,am);function km(a){am.call(this,"PropertyIsNull",a)}v(km,am);function lm(a,b){cm.call(this,"PropertyIsLessThan",a,b)}v(lm,cm);function mm(a,b){cm.call(this,"PropertyIsLessThanOrEqualTo",a,b)}v(mm,cm);function nm(a){this.kc="Not";this.condition=a}v(nm,Xl);function om(a,b,c){cm.call(this,"PropertyIsNotEqualTo",a,b,c)}v(om,cm);function pm(a){var b=["Or"].concat(Array.prototype.slice.call(arguments));Yl.apply(this,b)}v(pm,Yl);function qm(a,b,c){gm.call(this,"Within",a,b,c)}v(qm,gm);function rm(a){var b=[null].concat(Array.prototype.slice.call(arguments));return new (Function.prototype.bind.apply(Zl,b))}function sm(a,b,c){return new $l(a,b,c)};function tm(a){of.call(this);this.a=a?a:null;um(this)}v(tm,of);function vm(a){var b=[],c;var d=0;for(c=a.length;d<c;++d)b.push(a[d].clone());return b}function wm(a){var b;if(a.a){var c=0;for(b=a.a.length;c<b;++c)Kc(a.a[c],"change",a.s,a)}}function um(a){var b;if(a.a){var c=0;for(b=a.a.length;c<b;++c)y(a.a[c],"change",a.s,a)}}k=tm.prototype;k.clone=function(){var a=new tm(null);a.oj(this.a);return a}; -k.Kb=function(a,b,c,d){if(d<Sa(this.G(),a,b))return d;var e=this.a,f;var g=0;for(f=e.length;g<f;++g)d=e[g].Kb(a,b,c,d);return d};k.Mc=function(a,b){var c=this.a,d;var e=0;for(d=c.length;e<d;++e)if(c[e].Mc(a,b))return!0;return!1};k.se=function(a){Ya(a);for(var b=this.a,c=0,d=b.length;c<d;++c)cb(a,b[c].G());return a};k.Vf=function(){return vm(this.a)}; -k.Vd=function(a){this.o!=this.i&&(ub(this.f),this.g=0,this.o=this.i);if(0>a||this.g&&a<this.g)return this;var b=a.toString();if(this.f.hasOwnProperty(b))return this.f[b];var c=[],d=this.a,e=!1,f;var g=0;for(f=d.length;g<f;++g){var h=d[g],l=h.Vd(a);c.push(l);l!==h&&(e=!0)}if(e)return a=new tm(null),wm(a),a.a=c,um(a),a.s(),this.f[b]=a;this.g=a;return this};k.U=function(){return"GeometryCollection"};k.Xa=function(a){var b=this.a,c;var d=0;for(c=b.length;d<c;++d)if(b[d].Xa(a))return!0;return!1}; -k.rotate=function(a,b){for(var c=this.a,d=0,e=c.length;d<e;++d)c[d].rotate(a,b);this.s()};k.scale=function(a,b,c){c||(c=nb(this.G()));for(var d=this.a,e=0,f=d.length;e<f;++e)d[e].scale(a,b,c);this.s()};k.oj=function(a){a=vm(a);wm(this);this.a=a;um(this);this.s()};k.Dc=function(a){var b=this.a,c;var d=0;for(c=b.length;d<c;++d)b[d].Dc(a);this.s()};k.translate=function(a,b){var c=this.a,d;var e=0;for(d=c.length;e<d;++e)c[e].translate(a,b);this.s()};k.ka=function(){wm(this);of.prototype.ka.call(this)};function xm(a){a=a?a:{};El.call(this);this.defaultDataProjection=Tb(a.defaultDataProjection?a.defaultDataProjection:"EPSG:4326");a.featureProjection&&(this.f=Tb(a.featureProjection));this.b=a.geometryName}v(xm,Il);function ym(a,b){return a?Hl((0,zm[a.type])(a),!1,b):null}function Am(a,b){return(0,Bm[a.U()])(Hl(a,!0,b),b)} -var zm={Point:function(a){return new C(a.coordinates)},LineString:function(a){return new O(a.coordinates)},Polygon:function(a){return new D(a.coordinates)},MultiPoint:function(a){return new Q(a.coordinates)},MultiLineString:function(a){return new P(a.coordinates)},MultiPolygon:function(a){return new R(a.coordinates)},GeometryCollection:function(a,b){a=a.geometries.map(function(a){return ym(a,b)});return new tm(a)}},Bm={Point:function(a){return{type:"Point",coordinates:a.X()}},LineString:function(a){return{type:"LineString", -coordinates:a.X()}},Polygon:function(a,b){if(b)var c=b.rightHanded;return{type:"Polygon",coordinates:a.X(c)}},MultiPoint:function(a){return{type:"MultiPoint",coordinates:a.X()}},MultiLineString:function(a){return{type:"MultiLineString",coordinates:a.X()}},MultiPolygon:function(a,b){if(b)var c=b.rightHanded;return{type:"MultiPolygon",coordinates:a.X(c)}},GeometryCollection:function(a,b){return{type:"GeometryCollection",geometries:a.a.map(function(a){var c=tb({},b);delete c.featureProjection;return Am(a, -c)})}},Circle:function(){return{type:"GeometryCollection",geometries:[]}}};k=xm.prototype;k.Rc=function(a,b){a="Feature"===a.type?a:{type:"Feature",geometry:a};b=ym(a.geometry,b);var c=new H;this.b&&c.Tc(this.b);c.Ra(b);void 0!==a.id&&c.jc(a.id);a.properties&&c.H(a.properties);return c};k.yg=function(a,b){if("FeatureCollection"===a.type){var c=[];a=a.features;var d;var e=0;for(d=a.length;e<d;++e)c.push(this.Rc(a[e],b))}else c=[this.Rc(a,b)];return c};k.Cg=function(a,b){return ym(a,b)}; -k.Fg=function(a){a=a.crs;var b;a?"name"==a.type?b=Tb(a.properties.name):"EPSG"==a.type?b=Tb("EPSG:"+a.properties.code):xa(!1,36):b=this.defaultDataProjection;return b};k.Zc=function(a,b){b=Gl(this,b);var c={type:"Feature"},d=a.a;void 0!==d&&(c.id=d);(d=a.V())?c.geometry=Am(d,b):c.geometry=null;b=a.N();delete b[a.c];wb(b)?c.properties=null:c.properties=b;return c};k.he=function(a,b){b=Gl(this,b);var c=[],d;var e=0;for(d=a.length;e<d;++e)c.push(this.Zc(a[e],b));return{type:"FeatureCollection",features:c}}; -k.je=function(a,b){return Am(a,Gl(this,b))};function Cm(){this.i=new XMLSerializer;El.call(this)}v(Cm,El);k=Cm.prototype;k.U=function(){return"xml"};k.Tb=function(a,b){return ml(a)?Dm(this,a,b):nl(a)?this.xg(a,b):"string"===typeof a?(a=pl(a),Dm(this,a,b)):null};function Dm(a,b,c){a=Em(a,b,c);return 0<a.length?a[0]:null}k.xg=function(){return null};k.Oa=function(a,b){return ml(a)?Em(this,a,b):nl(a)?this.zc(a,b):"string"===typeof a?(a=pl(a),Em(this,a,b)):[]}; -function Em(a,b,c){var d=[];for(b=b.firstChild;b;b=b.nextSibling)b.nodeType==Node.ELEMENT_NODE&&la(d,a.zc(b,c));return d}k.Sc=function(a,b){if(ml(a))return null;if(nl(a))return this.aj(a,b);"string"===typeof a&&pl(a);return null};k.aj=function(){return null};k.kb=function(a){return ml(a)?this.Eg(a):nl(a)?this.kf(a):"string"===typeof a?(a=pl(a),this.Eg(a)):null};k.Eg=function(){return this.defaultDataProjection};k.kf=function(){return this.defaultDataProjection}; -k.Bd=function(a,b){return this.i.serializeToString(this.Vg(a,b))};k.Vg=function(){return null};k.Wb=function(a,b){a=this.Xb(a,b);return this.i.serializeToString(a)};k.Xb=function(){return null};k.$c=function(a,b){a=this.ie(a,b);return this.i.serializeToString(a)};k.ie=function(){return null};function Fm(a){a=a?a:{};this.featureType=a.featureType;this.featureNS=a.featureNS;this.srsName=a.srsName;this.schemaLocation="";this.b={};this.b["http://www.opengis.net/gml"]={featureMember:sl(Fm.prototype.be),featureMembers:sl(Fm.prototype.be)};Cm.call(this)}v(Fm,Cm);var Gm=/^[\s\xa0]*$/;k=Fm.prototype; -k.be=function(a,b){var c=a.localName,d=null;if("FeatureCollection"==c)"http://www.opengis.net/wfs"===a.namespaceURI?d=N([],this.b,a,b,this):d=N(null,this.b,a,b,this);else if("featureMembers"==c||"featureMember"==c){var e=b[0],f=e.featureType,g=e.featureNS,h;if(!f&&a.childNodes){f=[];g={};var l=0;for(h=a.childNodes.length;l<h;++l){var m=a.childNodes[l];if(1===m.nodeType){var n=m.nodeName.split(":").pop();if(-1===f.indexOf(n)){var p="",q=0,m=m.namespaceURI,r;for(r in g){if(g[r]===m){p=r;break}++q}p|| -(p="p"+q,g[p]=m);f.push(p+":"+n)}}}"featureMember"!=c&&(e.featureType=f,e.featureNS=g)}"string"===typeof g&&(l=g,g={},g.p0=l);var e={},f=Array.isArray(f)?f:[f],u;for(u in g){n={};l=0;for(h=f.length;l<h;++l)(-1===f[l].indexOf(":")?"p0":f[l].split(":")[0])===u&&(n[f[l].split(":").pop()]="featureMembers"==c?rl(this.wg,this):sl(this.wg,this));e[g[u]]=n}"featureMember"==c?d=N(void 0,e,a,b):d=N([],e,a,b)}null===d&&(d=[]);return d}; -k.gf=function(a,b){var c=b[0];c.srsName=a.firstElementChild.getAttribute("srsName");if(a=N(null,this.Zg,a,b,this))return Hl(a,!1,c)}; -k.wg=function(a,b){var c;(c=a.getAttribute("fid"))||(c=a.getAttributeNS("http://www.opengis.net/gml","id")||"");var d={},e;for(a=a.firstElementChild;a;a=a.nextElementSibling){var f=a.localName;if(0===a.childNodes.length||1===a.childNodes.length&&(3===a.firstChild.nodeType||4===a.firstChild.nodeType)){var g=kl(a,!1);Gm.test(g)&&(g=void 0);d[f]=g}else"boundedBy"!==f&&(e=f),d[f]=this.gf(a,b)}b=new H(d);e&&b.Tc(e);c&&b.jc(c);return b}; -k.fj=function(a,b){if(a=this.ff(a,b))return b=new C(null),b.ba("XYZ",a),b};k.dj=function(a,b){if(a=N([],this.Nj,a,b,this))return new Q(a)};k.cj=function(a,b){if(a=N([],this.Mj,a,b,this))return b=new P(null),Nl(b,a),b};k.ej=function(a,b){if(a=N([],this.Oj,a,b,this))return b=new R(null),Pl(b,a),b};k.Xi=function(a,b){Al(this.Rj,a,b,this)};k.Mh=function(a,b){Al(this.Kj,a,b,this)};k.Yi=function(a,b){Al(this.Sj,a,b,this)};k.hf=function(a,b){if(a=this.ff(a,b))return b=new O(null),b.ba("XYZ",a),b}; -k.wp=function(a,b){if(a=N(null,this.ke,a,b,this))return a};k.bj=function(a,b){if(a=this.ff(a,b))return b=new Jf(null),Kf(b,"XYZ",a),b};k.jf=function(a,b){if((a=N([null],this.zf,a,b,this))&&a[0]){b=new D(null);var c=a[0],d=[c.length],e;var f=1;for(e=a.length;f<e;++f)la(c,a[f]),d.push(c.length);b.ba("XYZ",c,d);return b}};k.ff=function(a,b){return N(null,this.ke,a,b,this)};k.Nj={"http://www.opengis.net/gml":{pointMember:rl(Fm.prototype.Xi),pointMembers:rl(Fm.prototype.Xi)}}; -k.Mj={"http://www.opengis.net/gml":{lineStringMember:rl(Fm.prototype.Mh),lineStringMembers:rl(Fm.prototype.Mh)}};k.Oj={"http://www.opengis.net/gml":{polygonMember:rl(Fm.prototype.Yi),polygonMembers:rl(Fm.prototype.Yi)}};k.Rj={"http://www.opengis.net/gml":{Point:rl(Fm.prototype.ff)}};k.Kj={"http://www.opengis.net/gml":{LineString:rl(Fm.prototype.hf)}};k.Sj={"http://www.opengis.net/gml":{Polygon:rl(Fm.prototype.jf)}};k.le={"http://www.opengis.net/gml":{LinearRing:sl(Fm.prototype.wp)}}; -k.aj=function(a,b){return(a=this.gf(a,[Fl(this,a,b?b:{})]))?a:null};k.zc=function(a,b){var c={featureType:this.featureType,featureNS:this.featureNS};b&&tb(c,Fl(this,a,b));return this.be(a,[c])||[]};k.kf=function(a){return Tb(this.srsName?this.srsName:a.firstElementChild.getAttribute("srsName"))};function Hm(a){a=kl(a,!1);return Im(a)}function Im(a){if(a=/^\s*(true|1)|(false|0)\s*$/.exec(a))return void 0!==a[1]||!1}function Jm(a){a=kl(a,!1);a=Date.parse(a);return isNaN(a)?void 0:a/1E3}function Km(a){a=kl(a,!1);return Lm(a)}function Lm(a){if(a=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(a))return parseFloat(a[1])}function Mm(a){a=kl(a,!1);return Nm(a)}function Nm(a){if(a=/^\s*(\d+)\s*$/.exec(a))return parseInt(a[1],10)}function S(a){return kl(a,!1).trim()} -function Om(a,b){Pm(a,b?"1":"0")}function Qm(a,b){a.appendChild(il.createTextNode(b.toPrecision()))}function Rm(a,b){a.appendChild(il.createTextNode(b.toString()))}function Pm(a,b){a.appendChild(il.createTextNode(b))};function Sm(a){a=a?a:{};Fm.call(this,a);this.l=void 0!==a.surface?a.surface:!1;this.c=void 0!==a.curve?a.curve:!1;this.g=void 0!==a.multiCurve?a.multiCurve:!0;this.j=void 0!==a.multiSurface?a.multiSurface:!0;this.schemaLocation=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd"}v(Sm,Fm);k=Sm.prototype;k.Ap=function(a,b){if(a=N([],this.Lj,a,b,this))return b=new P(null),Nl(b,a),b}; -k.Bp=function(a,b){if(a=N([],this.Pj,a,b,this))return b=new R(null),Pl(b,a),b};k.ph=function(a,b){Al(this.Hj,a,b,this)};k.Aj=function(a,b){Al(this.Uj,a,b,this)};k.Ep=function(a,b){return N([null],this.Qj,a,b,this)};k.Hp=function(a,b){return N([null],this.Tj,a,b,this)};k.Fp=function(a,b){return N([null],this.zf,a,b,this)};k.zp=function(a,b){return N([null],this.ke,a,b,this)};k.cm=function(a,b){(a=N(void 0,this.le,a,b,this))&&b[b.length-1].push(a)}; -k.yk=function(a,b){(a=N(void 0,this.le,a,b,this))&&(b[b.length-1][0]=a)};k.gj=function(a,b){if((a=N([null],this.Vj,a,b,this))&&a[0]){b=new D(null);var c=a[0],d=[c.length],e;var f=1;for(e=a.length;f<e;++f)la(c,a[f]),d.push(c.length);b.ba("XYZ",c,d);return b}};k.Zi=function(a,b){if(a=N([null],this.Ij,a,b,this))return b=new O(null),b.ba("XYZ",a),b};k.vp=function(a,b){a=N([null],this.Jj,a,b,this);return Xa(a[1][0],a[1][1],a[2][0],a[2][1])}; -k.xp=function(a,b){var c=kl(a,!1),d=/^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/;a=[];for(var e;e=d.exec(c);)a.push(parseFloat(e[1])),c=c.substr(e[0].length);if(""===c){b=b[0].srsName;c="enu";b&&(c=Tb(b).b);if("neu"===c)for(b=0,c=a.length;b<c;b+=3)d=a[b],a[b]=a[b+1],a[b+1]=d;b=a.length;2==b&&a.push(0);if(b)return a}}; -k.Bg=function(a,b){var c=kl(a,!1).replace(/^\s*|\s*$/g,""),d=b[0].srsName,e=a.parentNode.getAttribute("srsDimension");b="enu";d&&(b=Tb(d).b);c=c.split(/\s+/);d=2;a.getAttribute("srsDimension")?d=Nm(a.getAttribute("srsDimension")):a.getAttribute("dimension")?d=Nm(a.getAttribute("dimension")):e&&(d=Nm(e));for(var f,g=[],h=0,l=c.length;h<l;h+=d)a=parseFloat(c[h]),e=parseFloat(c[h+1]),f=3===d?parseFloat(c[h+2]):0,"en"===b.substr(0,2)?g.push(a,e,f):g.push(e,a,f);return g}; -k.ke={"http://www.opengis.net/gml":{pos:sl(Sm.prototype.xp),posList:sl(Sm.prototype.Bg)}};k.zf={"http://www.opengis.net/gml":{interior:Sm.prototype.cm,exterior:Sm.prototype.yk}}; -k.Zg={"http://www.opengis.net/gml":{Point:sl(Fm.prototype.fj),MultiPoint:sl(Fm.prototype.dj),LineString:sl(Fm.prototype.hf),MultiLineString:sl(Fm.prototype.cj),LinearRing:sl(Fm.prototype.bj),Polygon:sl(Fm.prototype.jf),MultiPolygon:sl(Fm.prototype.ej),Surface:sl(Sm.prototype.gj),MultiSurface:sl(Sm.prototype.Bp),Curve:sl(Sm.prototype.Zi),MultiCurve:sl(Sm.prototype.Ap),Envelope:sl(Sm.prototype.vp)}};k.Lj={"http://www.opengis.net/gml":{curveMember:rl(Sm.prototype.ph),curveMembers:rl(Sm.prototype.ph)}}; -k.Pj={"http://www.opengis.net/gml":{surfaceMember:rl(Sm.prototype.Aj),surfaceMembers:rl(Sm.prototype.Aj)}};k.Hj={"http://www.opengis.net/gml":{LineString:rl(Fm.prototype.hf),Curve:rl(Sm.prototype.Zi)}};k.Uj={"http://www.opengis.net/gml":{Polygon:rl(Fm.prototype.jf),Surface:rl(Sm.prototype.gj)}};k.Vj={"http://www.opengis.net/gml":{patches:sl(Sm.prototype.Ep)}};k.Ij={"http://www.opengis.net/gml":{segments:sl(Sm.prototype.Hp)}};k.Jj={"http://www.opengis.net/gml":{lowerCorner:rl(Sm.prototype.Bg),upperCorner:rl(Sm.prototype.Bg)}}; -k.Qj={"http://www.opengis.net/gml":{PolygonPatch:sl(Sm.prototype.Fp)}};k.Tj={"http://www.opengis.net/gml":{LineStringSegment:sl(Sm.prototype.zp)}};function Tm(a,b,c){var d=c[c.length-1];c=d.hasZ;d=d.srsName;b=b.X();for(var e=b.length,f=Array(e),g,h=0;h<e;++h){g=b[h];var l=h,m=c,n="enu";d&&(n=Tb(d).b);n="en"===n.substr(0,2)?g[0]+" "+g[1]:g[1]+" "+g[0];m&&(n+=" "+(g[2]||0));f[l]=n}Pm(a,f.join(" "))} -k.ni=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=jl(a.namespaceURI,"pos");a.appendChild(d);c=c[c.length-1];a=c.hasZ;var e=c.srsName;c="enu";e&&(c=Tb(e).b);b=b.X();c="en"===c.substr(0,2)?b[0]+" "+b[1]:b[1]+" "+b[0];a&&(c+=" "+(b[2]||0));Pm(d,c)};var Um={"http://www.opengis.net/gml":{lowerCorner:J(Pm),upperCorner:J(Pm)}};k=Sm.prototype; -k.jn=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);Bl({node:a},Um,yl,[b[0]+" "+b[1],b[2]+" "+b[3]],c,["lowerCorner","upperCorner"],this)};k.ki=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=jl(a.namespaceURI,"posList");a.appendChild(d);Tm(d,b,c)};k.hn=function(a,b){a=b[b.length-1];b=a.node;var c=a.exteriorWritten;void 0===c&&(a.exteriorWritten=!0);return jl(b.namespaceURI,void 0!==c?"interior":"exterior")}; -k.Se=function(a,b,c){var d=c[c.length-1],e=d.hasZ,d=d.srsName;"PolygonPatch"!==a.nodeName&&d&&a.setAttribute("srsName",d);"Polygon"===a.nodeName||"PolygonPatch"===a.nodeName?(b=b.Sd(),Bl({node:a,hasZ:e,srsName:d},Vm,this.hn,b,c,void 0,this)):"Surface"===a.nodeName&&(e=jl(a.namespaceURI,"patches"),a.appendChild(e),a=jl(e.namespaceURI,"PolygonPatch"),e.appendChild(a),this.Se(a,b,c))}; -k.Re=function(a,b,c){var d=c[c.length-1].srsName;"LineStringSegment"!==a.nodeName&&d&&a.setAttribute("srsName",d);"LineString"===a.nodeName||"LineStringSegment"===a.nodeName?(d=jl(a.namespaceURI,"posList"),a.appendChild(d),Tm(d,b,c)):"Curve"===a.nodeName&&(d=jl(a.namespaceURI,"segments"),a.appendChild(d),a=jl(d.namespaceURI,"LineStringSegment"),d.appendChild(a),this.Re(a,b,c))}; -k.mi=function(a,b,c){var d=c[c.length-1],e=d.hasZ,f=d.srsName,d=d.surface;f&&a.setAttribute("srsName",f);b=b.Td();Bl({node:a,hasZ:e,srsName:f,surface:d},Wm,this.o,b,c,void 0,this)};k.kn=function(a,b,c){var d=c[c.length-1],e=d.srsName,d=d.hasZ;e&&a.setAttribute("srsName",e);b=b.Zd();Bl({node:a,hasZ:d,srsName:e},Xm,wl("pointMember"),b,c,void 0,this)}; -k.li=function(a,b,c){var d=c[c.length-1],e=d.hasZ,f=d.srsName,d=d.curve;f&&a.setAttribute("srsName",f);b=b.gd();Bl({node:a,hasZ:e,srsName:f,curve:d},Ym,this.o,b,c,void 0,this)};k.oi=function(a,b,c){var d=jl(a.namespaceURI,"LinearRing");a.appendChild(d);this.ki(d,b,c)};k.pi=function(a,b,c){var d=this.a(b,c);d&&(a.appendChild(d),this.Se(d,b,c))};k.ln=function(a,b,c){var d=jl(a.namespaceURI,"Point");a.appendChild(d);this.ni(d,b,c)}; -k.ji=function(a,b,c){var d=this.a(b,c);d&&(a.appendChild(d),this.Re(d,b,c))};k.od=function(a,b,c){var d=c[c.length-1],e=tb({},d);e.node=a;var f;Array.isArray(b)?d.dataProjection?f=hc(b,d.featureProjection,d.dataProjection):f=b:f=Hl(b,!0,d);Bl(e,Zm,this.a,[f],c,void 0,this)}; -k.ii=function(a,b,c){var d=b.a;d&&a.setAttribute("fid",d);var d=c[c.length-1],e=d.featureNS,f=b.c;d.lb||(d.lb={},d.lb[e]={});var g=b.N();b=[];var h=[];for(m in g){var l=g[m];null!==l&&(b.push(m),h.push(l),m==f||l instanceof of?m in d.lb[e]||(d.lb[e][m]=J(this.od,this)):m in d.lb[e]||(d.lb[e][m]=J(Pm)))}var m=tb({},d);m.node=a;Bl(m,d.lb,wl(void 0,e),h,c,b)}; -var Wm={"http://www.opengis.net/gml":{surfaceMember:J(Sm.prototype.pi),polygonMember:J(Sm.prototype.pi)}},Xm={"http://www.opengis.net/gml":{pointMember:J(Sm.prototype.ln)}},Ym={"http://www.opengis.net/gml":{lineStringMember:J(Sm.prototype.ji),curveMember:J(Sm.prototype.ji)}},Vm={"http://www.opengis.net/gml":{exterior:J(Sm.prototype.oi),interior:J(Sm.prototype.oi)}},Zm={"http://www.opengis.net/gml":{Curve:J(Sm.prototype.Re),MultiCurve:J(Sm.prototype.li),Point:J(Sm.prototype.ni),MultiPoint:J(Sm.prototype.kn), -LineString:J(Sm.prototype.Re),MultiLineString:J(Sm.prototype.li),LinearRing:J(Sm.prototype.ki),Polygon:J(Sm.prototype.Se),MultiPolygon:J(Sm.prototype.mi),Surface:J(Sm.prototype.Se),MultiSurface:J(Sm.prototype.mi),Envelope:J(Sm.prototype.jn)}},$m={MultiLineString:"lineStringMember",MultiCurve:"curveMember",MultiPolygon:"polygonMember",MultiSurface:"surfaceMember"};Sm.prototype.o=function(a,b){return jl("http://www.opengis.net/gml",$m[b[b.length-1].node.nodeName])}; -Sm.prototype.a=function(a,b){var c=b[b.length-1];b=c.multiSurface;var d=c.surface,e=c.curve,c=c.multiCurve;Array.isArray(a)?a="Envelope":(a=a.U(),"MultiPolygon"===a&&!0===b?a="MultiSurface":"Polygon"===a&&!0===d?a="Surface":"LineString"===a&&!0===e?a="Curve":"MultiLineString"===a&&!0===c&&(a="MultiCurve"));return jl("http://www.opengis.net/gml",a)}; -Sm.prototype.ie=function(a,b){b=Gl(this,b);var c=jl("http://www.opengis.net/gml","geom"),d={node:c,hasZ:this.hasZ,srsName:this.srsName,curve:this.c,surface:this.l,multiSurface:this.j,multiCurve:this.g};b&&tb(d,b);this.od(c,a,[d]);return c}; -Sm.prototype.Xb=function(a,b){b=Gl(this,b);var c=jl("http://www.opengis.net/gml","featureMembers");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.schemaLocation);var d={srsName:this.srsName,hasZ:this.hasZ,curve:this.c,surface:this.l,multiSurface:this.j,multiCurve:this.g,featureNS:this.featureNS,featureType:this.featureType};b&&tb(d,b);b=[d];var e=b[b.length-1],d=e.featureType,f=e.featureNS,g={};g[f]={};g[f][d]=J(this.ii,this);e=tb({},e);e.node=c;Bl(e,g,wl(d, -f),a,b);return c};function an(a){a=a?a:{};Fm.call(this,a);this.b["http://www.opengis.net/gml"].featureMember=rl(Fm.prototype.be);this.schemaLocation=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd"}v(an,Fm);k=an.prototype; -k.$i=function(a,b){a=kl(a,!1).replace(/^\s*|\s*$/g,"");var c=b[0].srsName;b="enu";c&&(c=Tb(c))&&(b=c.b);a=a.trim().split(/\s+/);for(var d,e,f=[],g=0,h=a.length;g<h;g++)e=a[g].split(/,+/),c=parseFloat(e[0]),d=parseFloat(e[1]),e=3===e.length?parseFloat(e[2]):0,"en"===b.substr(0,2)?f.push(c,d,e):f.push(d,c,e);return f};k.tp=function(a,b){a=N([null],this.Gj,a,b,this);return Xa(a[1][0],a[1][1],a[1][3],a[1][4])};k.am=function(a,b){(a=N(void 0,this.le,a,b,this))&&b[b.length-1].push(a)}; -k.bp=function(a,b){(a=N(void 0,this.le,a,b,this))&&(b[b.length-1][0]=a)};k.ke={"http://www.opengis.net/gml":{coordinates:sl(an.prototype.$i)}};k.zf={"http://www.opengis.net/gml":{innerBoundaryIs:an.prototype.am,outerBoundaryIs:an.prototype.bp}};k.Gj={"http://www.opengis.net/gml":{coordinates:rl(an.prototype.$i)}}; -k.Zg={"http://www.opengis.net/gml":{Point:sl(Fm.prototype.fj),MultiPoint:sl(Fm.prototype.dj),LineString:sl(Fm.prototype.hf),MultiLineString:sl(Fm.prototype.cj),LinearRing:sl(Fm.prototype.bj),Polygon:sl(Fm.prototype.jf),MultiPolygon:sl(Fm.prototype.ej),Box:sl(an.prototype.tp)}}; -k.jg=function(a,b){var c=b[b.length-1];b=c.multiSurface;var d=c.surface,c=c.multiCurve;Array.isArray(a)?a="Envelope":(a=a.U(),"MultiPolygon"===a&&!0===b?a="MultiSurface":"Polygon"===a&&!0===d?a="Surface":"MultiLineString"===a&&!0===c&&(a="MultiCurve"));return jl("http://www.opengis.net/gml",a)};k.ai=function(a,b,c){var d=c[c.length-1],e=tb({},d);e.node=a;var f;Array.isArray(b)?d.dataProjection?f=hc(b,d.featureProjection,d.dataProjection):f=b:f=Hl(b,!0,d);Bl(e,bn,this.jg,[f],c,void 0,this)}; -k.Pe=function(a,b,c){var d=c[c.length-1].srsName;"LineStringSegment"!==a.nodeName&&d&&a.setAttribute("srsName",d);"LineString"===a.nodeName||"LineStringSegment"===a.nodeName?(d=cn(a.namespaceURI),a.appendChild(d),dn(d,b,c)):"Curve"===a.nodeName&&(d=jl(a.namespaceURI,"segments"),a.appendChild(d),a=jl(d.namespaceURI,"LineStringSegment"),d.appendChild(a),this.Pe(a,b,c))};function cn(a){a=jl(a,"coordinates");a.setAttribute("decimal",".");a.setAttribute("cs",",");a.setAttribute("ts"," ");return a} -function dn(a,b,c){var d=c[c.length-1];c=d.hasZ;d=d.srsName;b=b.X();for(var e=b.length,f=Array(e),g,h=0;h<e;++h)g=b[h],f[h]=en(g,d,c);Pm(a,f.join(" "))} -k.Qe=function(a,b,c){var d=c[c.length-1],e=d.hasZ,d=d.srsName;"PolygonPatch"!==a.nodeName&&d&&a.setAttribute("srsName",d);"Polygon"===a.nodeName||"PolygonPatch"===a.nodeName?(b=b.Sd(),Bl({node:a,hasZ:e,srsName:d},fn,this.dn,b,c,void 0,this)):"Surface"===a.nodeName&&(e=jl(a.namespaceURI,"patches"),a.appendChild(e),a=jl(e.namespaceURI,"PolygonPatch"),e.appendChild(a),this.Qe(a,b,c))}; -k.dn=function(a,b){a=b[b.length-1];b=a.node;var c=a.exteriorWritten;void 0===c&&(a.exteriorWritten=!0);return jl(b.namespaceURI,void 0!==c?"innerBoundaryIs":"outerBoundaryIs")};k.gi=function(a,b,c){var d=jl(a.namespaceURI,"LinearRing");a.appendChild(d);this.ci(d,b,c)};function en(a,b,c){var d="enu";b&&(d=Tb(b).b);b="en"===d.substr(0,2)?a[0]+","+a[1]:a[1]+","+a[0];c&&(b+=","+(a[2]||0));return b} -k.di=function(a,b,c){var d=c[c.length-1],e=d.hasZ,f=d.srsName,d=d.curve;f&&a.setAttribute("srsName",f);b=b.gd();Bl({node:a,hasZ:e,srsName:f,curve:d},gn,this.a,b,c,void 0,this)};k.fi=function(a,b,c){var d=c[c.length-1];c=d.hasZ;var e=d.srsName;e&&a.setAttribute("srsName",e);d=cn(a.namespaceURI);a.appendChild(d);a=b.X();a=en(a,e,c);Pm(d,a)}; -k.fn=function(a,b,c){var d=c[c.length-1],e=d.hasZ;(d=d.srsName)&&a.setAttribute("srsName",d);b=b.Zd();Bl({node:a,hasZ:e,srsName:d},hn,wl("pointMember"),b,c,void 0,this)};k.gn=function(a,b,c){var d=jl(a.namespaceURI,"Point");a.appendChild(d);this.fi(d,b,c)};k.bi=function(a,b,c){var d=this.jg(b,c);d&&(a.appendChild(d),this.Pe(d,b,c))};k.ci=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=cn(a.namespaceURI);a.appendChild(d);dn(d,b,c)}; -k.ei=function(a,b,c){var d=c[c.length-1],e=d.hasZ,f=d.srsName,d=d.surface;f&&a.setAttribute("srsName",f);b=b.Td();Bl({node:a,hasZ:e,srsName:f,surface:d},jn,this.a,b,c,void 0,this)};k.hi=function(a,b,c){var d=this.jg(b,c);d&&(a.appendChild(d),this.Qe(d,b,c))};k.en=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);Bl({node:a},kn,yl,[b[0]+" "+b[1],b[2]+" "+b[3]],c,["lowerCorner","upperCorner"],this)}; -var bn={"http://www.opengis.net/gml":{Curve:J(an.prototype.Pe),MultiCurve:J(an.prototype.di),Point:J(an.prototype.fi),MultiPoint:J(an.prototype.fn),LineString:J(an.prototype.Pe),MultiLineString:J(an.prototype.di),LinearRing:J(an.prototype.ci),Polygon:J(an.prototype.Qe),MultiPolygon:J(an.prototype.ei),Surface:J(an.prototype.Qe),MultiSurface:J(an.prototype.ei),Envelope:J(an.prototype.en)}},fn={"http://www.opengis.net/gml":{outerBoundaryIs:J(an.prototype.gi),innerBoundaryIs:J(an.prototype.gi)}},hn={"http://www.opengis.net/gml":{pointMember:J(an.prototype.gn)}}, -gn={"http://www.opengis.net/gml":{lineStringMember:J(an.prototype.bi),curveMember:J(an.prototype.bi)}};an.prototype.a=function(a,b){return jl("http://www.opengis.net/gml",ln[b[b.length-1].node.nodeName])};var ln={MultiLineString:"lineStringMember",MultiCurve:"curveMember",MultiPolygon:"polygonMember",MultiSurface:"surfaceMember"},jn={"http://www.opengis.net/gml":{surfaceMember:J(an.prototype.hi),polygonMember:J(an.prototype.hi)}},kn={"http://www.opengis.net/gml":{lowerCorner:J(Pm),upperCorner:J(Pm)}};function mn(a){a=a?a:{};Cm.call(this);this.defaultDataProjection=Tb("EPSG:4326");this.b=a.readExtensions}v(mn,Cm);var nn=[null,"http://www.topografix.com/GPX/1/0","http://www.topografix.com/GPX/1/1"];function on(a,b,c,d){a.push(parseFloat(c.getAttribute("lon")),parseFloat(c.getAttribute("lat")));"ele"in d?(a.push(d.ele),delete d.ele,b.hasZ=!0):a.push(0);"time"in d?(a.push(d.time),delete d.time,b.hasM=!0):a.push(0);return a} -function pn(a,b,c){var d="XY",e=2;a.hasZ&&a.hasM?(d="XYZM",e=4):a.hasZ?(d="XYZ",e=3):a.hasM&&(d="XYM",e=3);if(4!==e){var f;var g=0;for(f=b.length/4;g<f;g++)b[g*e]=b[4*g],b[g*e+1]=b[4*g+1],a.hasZ&&(b[g*e+2]=b[4*g+2]),a.hasM&&(b[g*e+2]=b[4*g+3]);b.length=b.length/4*e;if(c)for(g=0,f=c.length;g<f;g++)c[g]=c[g]/4*e}return d}function qn(a,b){var c=b[b.length-1],d=a.getAttribute("href");null!==d&&(c.link=d);Al(rn,a,b)}function sn(a,b){b[b.length-1].extensionsNode_=a} -function tn(a,b){var c=b[0];if(a=N({flatCoordinates:[],layoutOptions:{}},un,a,b)){b=a.flatCoordinates;delete a.flatCoordinates;var d=a.layoutOptions;delete a.layoutOptions;var d=pn(d,b),e=new O(null);e.ba(d,b);Hl(e,!1,c);c=new H(e);c.H(a);return c}} -function vn(a,b){var c=b[0];if(a=N({flatCoordinates:[],ends:[],layoutOptions:{}},wn,a,b)){b=a.flatCoordinates;delete a.flatCoordinates;var d=a.ends;delete a.ends;var e=a.layoutOptions;delete a.layoutOptions;var e=pn(e,b,d),f=new P(null);f.ba(e,b,d);Hl(f,!1,c);c=new H(f);c.H(a);return c}}function xn(a,b){var c=b[0];if(b=N({},yn,a,b)){var d={};a=on([],d,a,b);d=pn(d,a);a=new C(a,d);Hl(a,!1,c);c=new H(a);c.H(b);return c}} -var zn={rte:tn,trk:vn,wpt:xn},An=K(nn,{rte:rl(tn),trk:rl(vn),wpt:rl(xn)}),rn=K(nn,{text:I(S,"linkText"),type:I(S,"linkType")}),un=K(nn,{name:I(S),cmt:I(S),desc:I(S),src:I(S),link:qn,number:I(Mm),extensions:sn,type:I(S),rtept:function(a,b){var c=N({},Bn,a,b);c&&(b=b[b.length-1],on(b.flatCoordinates,b.layoutOptions,a,c))}}),Bn=K(nn,{ele:I(Km),time:I(Jm)}),wn=K(nn,{name:I(S),cmt:I(S),desc:I(S),src:I(S),link:qn,number:I(Mm),type:I(S),extensions:sn,trkseg:function(a,b){var c=b[b.length-1];Al(Cn,a,b);c.ends.push(c.flatCoordinates.length)}}), -Cn=K(nn,{trkpt:function(a,b){var c=N({},Dn,a,b);c&&(b=b[b.length-1],on(b.flatCoordinates,b.layoutOptions,a,c))}}),Dn=K(nn,{ele:I(Km),time:I(Jm)}),yn=K(nn,{ele:I(Km),time:I(Jm),magvar:I(Km),geoidheight:I(Km),name:I(S),cmt:I(S),desc:I(S),src:I(S),link:qn,sym:I(S),type:I(S),fix:I(S),sat:I(Mm),hdop:I(Km),vdop:I(Km),pdop:I(Km),ageofdgpsdata:I(Km),dgpsid:I(Mm),extensions:sn}); -function En(a,b){b||(b=[]);for(var c=0,d=b.length;c<d;++c){var e=b[c];if(a.b){var f=e.get("extensionsNode_")||null;a.b(e,f)}e.set("extensionsNode_",void 0)}}mn.prototype.xg=function(a,b){if(!ja(nn,a.namespaceURI))return null;var c=zn[a.localName];if(!c)return null;a=c(a,[Fl(this,a,b)]);if(!a)return null;En(this,[a]);return a};mn.prototype.zc=function(a,b){return ja(nn,a.namespaceURI)?"gpx"==a.localName&&(a=N([],An,a,[Fl(this,a,b)]))?(En(this,a),a):[]:[]}; -function Fn(a,b,c){a.setAttribute("href",b);b=c[c.length-1].properties;Bl({node:a},Gn,yl,[b.linkText,b.linkType],c,Hn)}function In(a,b,c){var d=c[c.length-1],e=d.node.namespaceURI,f=d.properties;a.setAttributeNS(null,"lat",b[1]);a.setAttributeNS(null,"lon",b[0]);switch(d.geometryLayout){case "XYZM":b[3]&&(f.time=b[3]);case "XYZ":b[2]&&(f.ele=b[2]);break;case "XYM":b[2]&&(f.time=b[2])}b="rtept"==a.nodeName?Jn[e]:Kn[e];d=zl(f,b);Bl({node:a,properties:f},Ln,yl,d,c,b)} -var Hn=["text","type"],Gn=K(nn,{text:J(Pm),type:J(Pm)}),Mn=K(nn,"name cmt desc src link number type rtept".split(" ")),Nn=K(nn,{name:J(Pm),cmt:J(Pm),desc:J(Pm),src:J(Pm),link:J(Fn),number:J(Rm),type:J(Pm),rtept:vl(J(In))}),Jn=K(nn,["ele","time"]),On=K(nn,"name cmt desc src link number type trkseg".split(" ")),Rn=K(nn,{name:J(Pm),cmt:J(Pm),desc:J(Pm),src:J(Pm),link:J(Fn),number:J(Rm),type:J(Pm),trkseg:vl(J(function(a,b,c){Bl({node:a,geometryLayout:b.ja,properties:{}},Pn,Qn,b.X(),c)}))}),Qn=wl("trkpt"), -Pn=K(nn,{trkpt:J(In)}),Kn=K(nn,"ele time magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid".split(" ")),Ln=K(nn,{ele:J(Qm),time:J(function(a,b){b=new Date(1E3*b);a.appendChild(il.createTextNode(b.getUTCFullYear()+"-"+Xe(b.getUTCMonth()+1)+"-"+Xe(b.getUTCDate())+"T"+Xe(b.getUTCHours())+":"+Xe(b.getUTCMinutes())+":"+Xe(b.getUTCSeconds())+"Z"))}),magvar:J(Qm),geoidheight:J(Qm),name:J(Pm),cmt:J(Pm),desc:J(Pm),src:J(Pm),link:J(Fn),sym:J(Pm),type:J(Pm),fix:J(Pm), -sat:J(Rm),hdop:J(Qm),vdop:J(Qm),pdop:J(Qm),ageofdgpsdata:J(Qm),dgpsid:J(Rm)}),Sn={Point:"wpt",LineString:"rte",MultiLineString:"trk"};function Tn(a,b){if(a=a.V())if(a=Sn[a.U()])return jl(b[b.length-1].node.namespaceURI,a)} -var Un=K(nn,{rte:J(function(a,b,c){var d=c[0],e=b.N();a={node:a,properties:e};if(b=b.V())b=Hl(b,!0,d),a.geometryLayout=b.ja,e.rtept=b.X();d=Mn[c[c.length-1].node.namespaceURI];e=zl(e,d);Bl(a,Nn,yl,e,c,d)}),trk:J(function(a,b,c){var d=c[0],e=b.N();a={node:a,properties:e};if(b=b.V())b=Hl(b,!0,d),e.trkseg=b.gd();d=On[c[c.length-1].node.namespaceURI];e=zl(e,d);Bl(a,Rn,yl,e,c,d)}),wpt:J(function(a,b,c){var d=c[0],e=c[c.length-1];e.properties=b.N();if(b=b.V())b=Hl(b,!0,d),e.geometryLayout=b.ja,In(a,b.X(), -c)})});mn.prototype.Xb=function(a,b){b=Gl(this,b);var c=jl("http://www.topografix.com/GPX/1/1","gpx");c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation","http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd");c.setAttribute("version","1.1");c.setAttribute("creator","OpenLayers");Bl({node:c},Un,Tn,a,[b]);return c};function Vn(){El.call(this)}v(Vn,El);function Wn(a){return"string"===typeof a?a:""}k=Vn.prototype;k.U=function(){return"text"};k.Tb=function(a,b){return this.ae(Wn(a),Gl(this,b))};k.Oa=function(a,b){return this.zg(Wn(a),Gl(this,b))};k.Sc=function(a,b){return this.wd(Wn(a),Gl(this,b))};k.kb=function(){return this.defaultDataProjection};k.Bd=function(a,b){return this.ge(a,Gl(this,b))};k.Wb=function(a,b){return this.Wg(a,Gl(this,b))};k.$c=function(a,b){return this.Cd(a,Gl(this,b))};function Xn(a){a=a?a:{};El.call(this);this.defaultDataProjection=Tb("EPSG:4326");this.b=a.altitudeMode?a.altitudeMode:"none"}v(Xn,Vn);var Yn=/^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/,Zn=/^H.([A-Z]{3}).*?:(.*)/,$n=/^HFDTE(\d{2})(\d{2})(\d{2})/,ao=/\r\n|\r|\n/;k=Xn.prototype; -k.ae=function(a,b){var c=this.b,d=a.split(ao);a={};var e=[],f=2E3,g=0,h=1,l=-1,m;var n=0;for(m=d.length;n<m;++n){var p=d[n],q;if("B"==p.charAt(0)){if(q=Yn.exec(p)){var p=parseInt(q[1],10),r=parseInt(q[2],10),u=parseInt(q[3],10),x=parseInt(q[4],10)+parseInt(q[5],10)/6E4;"S"==q[6]&&(x=-x);var B=parseInt(q[7],10)+parseInt(q[8],10)/6E4;"W"==q[9]&&(B=-B);e.push(B,x);"none"!=c&&e.push("gps"==c?parseInt(q[11],10):"barometric"==c?parseInt(q[12],10):0);q=Date.UTC(f,g,h,p,r,u);q<l&&(q=Date.UTC(f,g,h+1,p,r, -u));e.push(q/1E3);l=q}}else"H"==p.charAt(0)&&((q=$n.exec(p))?(h=parseInt(q[1],10),g=parseInt(q[2],10)-1,f=2E3+parseInt(q[3],10)):(q=Zn.exec(p))&&(a[q[1]]=q[2].trim()))}if(!e.length)return null;d=new O(null);d.ba("none"==c?"XYM":"XYZM",e);b=new H(Hl(d,!1,b));b.H(a);return b};k.zg=function(a,b){return(a=this.ae(a,b))?[a]:[]};k.ge=function(){};k.Wg=function(){};k.Cd=function(){};k.wd=function(){};function bo(a,b,c,d,e,f){Qc.call(this);this.j=null;this.M=a?a:new Image;null!==d&&(this.M.crossOrigin=d);this.c=f?document.createElement("CANVAS"):null;this.g=f;this.f=null;this.i=e;this.a=c;this.o=b;this.l=!1;2==this.i&&co(this)}v(bo,Qc);function co(a){var b=jd(1,1);try{b.drawImage(a.M,0,0),b.getImageData(0,0,1,1)}catch(c){a.l=!0}}bo.prototype.v=function(){this.i=3;this.f.forEach(Ec);this.f=null;this.b("change")}; -bo.prototype.u=function(){this.i=2;this.a&&(this.M.width=this.a[0],this.M.height=this.a[1]);this.a=[this.M.width,this.M.height];this.f.forEach(Ec);this.f=null;co(this);if(!this.l&&null!==this.g){this.c.width=this.M.width;this.c.height=this.M.height;var a=this.c.getContext("2d");a.drawImage(this.M,0,0);for(var b=a.getImageData(0,0,this.M.width,this.M.height),c=b.data,d=this.g[0]/255,e=this.g[1]/255,f=this.g[2]/255,g=0,h=c.length;g<h;g+=4)c[g]*=d,c[g+1]*=e,c[g+2]*=f;a.putImageData(b,0,0)}this.b("change")}; -bo.prototype.Y=function(){return this.c?this.c:this.M};bo.prototype.load=function(){if(0==this.i){this.i=1;this.f=[Jc(this.M,"error",this.v,this),Jc(this.M,"load",this.u,this)];try{this.M.src=this.o}catch(a){this.v()}}};function eo(a){a=a||{};this.o=void 0!==a.anchor?a.anchor:[.5,.5];this.u=null;this.i=void 0!==a.anchorOrigin?a.anchorOrigin:"top-left";this.C=void 0!==a.anchorXUnits?a.anchorXUnits:"fraction";this.B=void 0!==a.anchorYUnits?a.anchorYUnits:"fraction";this.ra=void 0!==a.crossOrigin?a.crossOrigin:null;var b=void 0!==a.img?a.img:null,c=void 0!==a.imgSize?a.imgSize:null,d=a.src;xa(!(void 0!==d&&b),4);xa(!b||b&&c,5);void 0!==d&&d.length||!b||(d=b.src||w(b).toString());xa(void 0!==d&&0<d.length,6);var e=void 0!== -a.src?0:2;this.j=void 0!==a.color?ed(a.color):null;var f=this.ra,g=this.j,h=zh.get(d,f,g);h||(h=new bo(b,d,c,f,e,g),zh.set(d,f,g,h));this.b=h;this.oa=void 0!==a.offset?a.offset:[0,0];this.c=void 0!==a.offsetOrigin?a.offsetOrigin:"top-left";this.S=null;this.D=void 0!==a.size?a.size:null;Xk.call(this,{opacity:void 0!==a.opacity?a.opacity:1,rotation:void 0!==a.rotation?a.rotation:0,scale:void 0!==a.scale?a.scale:1,snapToPixel:void 0!==a.snapToPixel?a.snapToPixel:!0,rotateWithView:void 0!==a.rotateWithView? -a.rotateWithView:!1})}v(eo,Xk);k=eo.prototype; -k.clone=function(){var a=this.Y(1);if(2===this.b.i)if("IMG"===a.tagName.toUpperCase())var b=a.cloneNode(!0);else{b=document.createElement("canvas");var c=b.getContext("2d");b.width=a.width;b.height=a.height;c.drawImage(a,0,0)}return new eo({anchor:this.o.slice(),anchorOrigin:this.i,anchorXUnits:this.C,anchorYUnits:this.B,crossOrigin:this.ra,color:this.j&&this.j.slice?this.j.slice():this.j||void 0,img:b?b:void 0,imgSize:b?this.b.a.slice():void 0,src:b?void 0:this.b.o,offset:this.oa.slice(),offsetOrigin:this.c, -size:null!==this.D?this.D.slice():void 0,opacity:this.f,scale:this.a,snapToPixel:this.v,rotation:this.g,rotateWithView:this.l})}; -k.Hc=function(){if(this.u)return this.u;var a=this.o,b=this.ic();if("fraction"==this.C||"fraction"==this.B){if(!b)return null;a=this.o.slice();"fraction"==this.C&&(a[0]*=b[0]);"fraction"==this.B&&(a[1]*=b[1])}if("top-left"!=this.i){if(!b)return null;a===this.o&&(a=this.o.slice());if("top-right"==this.i||"bottom-right"==this.i)a[0]=-a[0]+b[0];if("bottom-left"==this.i||"bottom-right"==this.i)a[1]=-a[1]+b[1]}return this.u=a};k.Lo=function(){return this.j};k.Y=function(a){return this.b.Y(a)};k.ye=function(){return this.b.a}; -k.Ye=function(){return this.b.i};k.qg=function(){var a=this.b;if(!a.j)if(a.l){var b=a.a[0],c=a.a[1],d=jd(b,c);d.fillRect(0,0,b,c);a.j=d.canvas}else a.j=a.M;return a.j};k.Oc=function(){if(this.S)return this.S;var a=this.oa;if("top-left"!=this.c){var b=this.ic(),c=this.b.a;if(!b||!c)return null;a=a.slice();if("top-right"==this.c||"bottom-right"==this.c)a[0]=c[0]-b[0]-a[0];if("bottom-left"==this.c||"bottom-right"==this.c)a[1]=c[1]-b[1]-a[1]}return this.S=a};k.Mo=function(){return this.b.o}; -k.ic=function(){return this.D?this.D:this.b.a};k.Nh=function(a,b){return y(this.b,"change",a,b)};k.load=function(){this.b.load()};k.Bj=function(a,b){Kc(this.b,"change",a,b)};function fo(a){a=a||{};this.a=a.font;this.f=a.rotation;this.o=a.rotateWithView;this.b=a.scale;this.Ia=a.text;this.g=a.textAlign;this.j=a.textBaseline;this.Va=void 0!==a.fill?a.fill:new al({color:"#333"});this.Ya=void 0!==a.stroke?a.stroke:null;this.i=void 0!==a.offsetX?a.offsetX:0;this.c=void 0!==a.offsetY?a.offsetY:0}k=fo.prototype; -k.clone=function(){return new fo({font:this.a,rotation:this.f,rotateWithView:this.o,scale:this.b,text:this.Na(),textAlign:this.g,textBaseline:this.j,fill:this.Fa()?this.Fa().clone():void 0,stroke:this.Ga()?this.Ga().clone():void 0,offsetX:this.i,offsetY:this.c})};k.Nk=function(){return this.a};k.cl=function(){return this.i};k.dl=function(){return this.c};k.Fa=function(){return this.Va};k.Ro=function(){return this.o};k.So=function(){return this.f};k.To=function(){return this.b};k.Ga=function(){return this.Ya}; -k.Na=function(){return this.Ia};k.nl=function(){return this.g};k.ol=function(){return this.j};k.nj=function(a){this.a=a};k.sj=function(a){this.i=a};k.tj=function(a){this.c=a};k.pf=function(a){this.Va=a};k.Uo=function(a){this.f=a};k.Si=function(a){this.b=a};k.qf=function(a){this.Ya=a};k.xd=function(a){this.Ia=a};k.vj=function(a){this.g=a};k.hq=function(a){this.j=a};function go(a){a=a?a:{};Cm.call(this);ho||(io=[255,255,255,1],jo=new al({color:io}),ko=[20,2],lo=mo="pixels",no=[64,64],oo="https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png",po=.5,qo=new eo({anchor:ko,anchorOrigin:"bottom-left",anchorXUnits:mo,anchorYUnits:lo,crossOrigin:"anonymous",rotation:0,scale:po,size:no,src:oo}),ro="NO_IMAGE",so=new wj({color:io,width:1}),to=new wj({color:[51,51,51,1],width:2}),uo=new fo({font:"bold 16px Helvetica",fill:jo,stroke:to,scale:.8}),vo=new bl({fill:jo, -image:qo,text:uo,stroke:so,zIndex:0}),ho=[vo]);this.defaultDataProjection=Tb("EPSG:4326");this.a=a.defaultStyle?a.defaultStyle:ho;this.c=void 0!==a.extractStyles?a.extractStyles:!0;this.j=void 0!==a.writeStyles?a.writeStyles:!0;this.b={};this.g=void 0!==a.showPointNames?a.showPointNames:!0}var ho,io,jo,ko,mo,lo,no,oo,po,qo,ro,so,to,uo,vo;v(go,Cm); -var wo=["http://www.google.com/kml/ext/2.2"],xo=[null,"http://earth.google.com/kml/2.0","http://earth.google.com/kml/2.1","http://earth.google.com/kml/2.2","http://www.opengis.net/kml/2.2"],yo={fraction:"fraction",pixels:"pixels",insetPixels:"pixels"}; -function zo(a,b){var c=[0,0],d="start";if(a.Y()){var e=a.Y().ye();null===e&&(e=no);2==e.length&&(d=a.Y().a,c[0]=d*e[0]/2,c[1]=-d*e[1]/2,d="left")}null!==a.Na()?(e=a.Na(),a=e.clone(),a.nj(e.a||uo.a),a.Si(e.b||uo.b),a.pf(e.Fa()||uo.Fa()),a.qf(e.Ga()||to)):a=uo.clone();a.xd(b);a.sj(c[0]);a.tj(c[1]);a.vj(d);return new bl({text:a})} -function Ao(a,b,c,d,e){return function(){var f=e,g="";f&&this.V()&&(f="Point"===this.V().U());f&&(g=this.get("name"),f=f&&g);if(a)return f?(f=zo(a[0],g),a.concat(f)):a;if(b){var h=Bo(b,c,d);return f?(f=zo(h[0],g),h.concat(f)):h}return f?(f=zo(c[0],g),c.concat(f)):c}}function Bo(a,b,c){return Array.isArray(a)?a:"string"===typeof a?(!(a in c)&&"#"+a in c&&(a="#"+a),Bo(c[a],b,c)):b} -function Co(a){a=kl(a,!1);if(a=/^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(a))return a=a[1],[parseInt(a.substr(6,2),16),parseInt(a.substr(4,2),16),parseInt(a.substr(2,2),16),parseInt(a.substr(0,2),16)/255]}function Do(a){a=kl(a,!1);for(var b=[],c=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i,d;d=c.exec(a);)b.push(parseFloat(d[1]),parseFloat(d[2]),d[3]?parseFloat(d[3]):0),a=a.substr(d[0].length);if(""===a)return b} -function Eo(a){var b=kl(a,!1).trim();return a.baseURI&&"about:blank"!==a.baseURI?(new URL(b,a.baseURI)).href:b}function Fo(a){return Km(a)}function Go(a,b){return N(null,Ho,a,b)}function Io(a,b){if(b=N({A:[],Ej:[]},Jo,a,b)){a=b.A;b=b.Ej;var c;var d=0;for(c=Math.min(a.length,b.length);d<c;++d)a[4*d+3]=b[d];b=new O(null);b.ba("XYZM",a);return b}}function Ko(a,b){var c=N({},Lo,a,b);if(a=N(null,Mo,a,b))return b=new O(null),b.ba("XYZ",a),b.H(c),b} -function No(a,b){var c=N({},Lo,a,b);if(a=N(null,Mo,a,b))return b=new D(null),b.ba("XYZ",a,[a.length]),b.H(c),b} -function Oo(a,b){a=N([],Po,a,b);if(!a)return null;if(!a.length)return new tm(a);var c=!0,d=a[0].U(),e;var f=1;for(e=a.length;f<e;++f)if(b=a[f],b.U()!=d){c=!1;break}if(c)if("Point"==d){var g=a[0];c=g.ja;d=g.ga();f=1;for(e=a.length;f<e;++f)b=a[f],la(d,b.ga());g=new Q(null);g.ba(c,d);Qo(g,a)}else"LineString"==d?(g=new P(null),Nl(g,a),Qo(g,a)):"Polygon"==d?(g=new R(null),Pl(g,a),Qo(g,a)):"GeometryCollection"==d?g=new tm(a):xa(!1,37);else g=new tm(a);return g} -function Ro(a,b){var c=N({},Lo,a,b);if(a=N(null,Mo,a,b))return b=new C(null),b.ba("XYZ",a),b.H(c),b}function So(a,b){var c=N({},Lo,a,b);if((a=N([null],To,a,b))&&a[0]){b=new D(null);var d=a[0],e=[d.length],f;var g=1;for(f=a.length;g<f;++g)la(d,a[g]),e.push(d.length);b.ba("XYZ",d,e);b.H(c);return b}} -function Uo(a,b){b=N({},Vo,a,b);if(!b)return null;a="fillStyle"in b?b.fillStyle:jo;var c=b.fill;void 0===c||c||(a=null);c="imageStyle"in b?b.imageStyle:qo;c==ro&&(c=void 0);var d="textStyle"in b?b.textStyle:uo,e="strokeStyle"in b?b.strokeStyle:so;b=b.outline;void 0===b||b||(e=null);return[new bl({fill:a,image:c,stroke:e,text:d,zIndex:void 0})]} -function Qo(a,b){var c=b.length,d=Array(b.length),e=Array(b.length),f,g;var h=g=!1;for(f=0;f<c;++f){var l=b[f];d[f]=l.get("extrude");e[f]=l.get("altitudeMode");h=h||void 0!==d[f];g=g||e[f]}h&&a.set("extrude",d);g&&a.set("altitudeMode",e)}function Wo(a,b){Al(Xo,a,b)}function Yo(a,b){Al(Zo,a,b)} -var $o=K(xo,{displayName:I(S),value:I(S)}),Xo=K(xo,{Data:function(a,b){var c=a.getAttribute("name");Al($o,a,b);a=b[b.length-1];null!==c?a[c]=a.value:null!==a.displayName&&(a[a.displayName]=a.value)},SchemaData:function(a,b){Al(ap,a,b)}}),Zo=K(xo,{LatLonAltBox:function(a,b){if(a=N({},bp,a,b))b=b[b.length-1],b.extent=[parseFloat(a.west),parseFloat(a.south),parseFloat(a.east),parseFloat(a.north)],b.altitudeMode=a.altitudeMode,b.minAltitude=parseFloat(a.minAltitude),b.maxAltitude=parseFloat(a.maxAltitude)}, -Lod:function(a,b){if(a=N({},cp,a,b))b=b[b.length-1],b.minLodPixels=parseFloat(a.minLodPixels),b.maxLodPixels=parseFloat(a.maxLodPixels),b.minFadeExtent=parseFloat(a.minFadeExtent),b.maxFadeExtent=parseFloat(a.maxFadeExtent)}}),bp=K(xo,{altitudeMode:I(S),minAltitude:I(Km),maxAltitude:I(Km),north:I(Km),south:I(Km),east:I(Km),west:I(Km)}),cp=K(xo,{minLodPixels:I(Km),maxLodPixels:I(Km),minFadeExtent:I(Km),maxFadeExtent:I(Km)}),Lo=K(xo,{extrude:I(Hm),altitudeMode:I(S)}),Ho=K(xo,{coordinates:sl(Do)}),To= -K(xo,{innerBoundaryIs:function(a,b){(a=N(void 0,dp,a,b))&&b[b.length-1].push(a)},outerBoundaryIs:function(a,b){(a=N(void 0,ep,a,b))&&(b[b.length-1][0]=a)}}),Jo=K(xo,{when:function(a,b){b=b[b.length-1].Ej;a=kl(a,!1);a=Date.parse(a);b.push(isNaN(a)?0:a)}},K(wo,{coord:function(a,b){b=b[b.length-1].A;a=kl(a,!1);(a=/^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i.exec(a))?b.push(parseFloat(a[1]),parseFloat(a[2]),parseFloat(a[3]), -0):b.push(0,0,0,0)}})),Mo=K(xo,{coordinates:sl(Do)}),fp=K(xo,{href:I(Eo)},K(wo,{x:I(Km),y:I(Km),w:I(Km),h:I(Km)})),gp=K(xo,{Icon:I(function(a,b){return(a=N({},fp,a,b))?a:null}),heading:I(Km),hotSpot:I(function(a){var b=a.getAttribute("xunits"),c=a.getAttribute("yunits");var d="insetPixels"!==b?"insetPixels"!==c?"bottom-left":"top-left":"insetPixels"!==c?"bottom-right":"top-right";return{x:parseFloat(a.getAttribute("x")),Xg:yo[b],y:parseFloat(a.getAttribute("y")),Yg:yo[c],origin:d}}),scale:I(Fo)}), -dp=K(xo,{LinearRing:sl(Go)}),hp=K(xo,{color:I(Co),scale:I(Fo)}),ip=K(xo,{color:I(Co),width:I(Km)}),Po=K(xo,{LineString:rl(Ko),LinearRing:rl(No),MultiGeometry:rl(Oo),Point:rl(Ro),Polygon:rl(So)}),jp=K(wo,{Track:rl(Io)}),lp=K(xo,{ExtendedData:Wo,Region:Yo,Link:function(a,b){Al(kp,a,b)},address:I(S),description:I(S),name:I(S),open:I(Hm),phoneNumber:I(S),visibility:I(Hm)}),kp=K(xo,{href:I(Eo)}),ep=K(xo,{LinearRing:sl(Go)}),mp=K(xo,{Style:I(Uo),key:I(S),styleUrl:I(Eo)}),op=K(xo,{ExtendedData:Wo,Region:Yo, -MultiGeometry:I(Oo,"geometry"),LineString:I(Ko,"geometry"),LinearRing:I(No,"geometry"),Point:I(Ro,"geometry"),Polygon:I(So,"geometry"),Style:I(Uo),StyleMap:function(a,b){if(a=N(void 0,np,a,b))b=b[b.length-1],Array.isArray(a)?b.Style=a:"string"===typeof a?b.styleUrl=a:xa(!1,38)},address:I(S),description:I(S),name:I(S),open:I(Hm),phoneNumber:I(S),styleUrl:I(Eo),visibility:I(Hm)},K(wo,{MultiTrack:I(function(a,b){if(a=N([],jp,a,b))return b=new P(null),Nl(b,a),b},"geometry"),Track:I(Io,"geometry")})), -pp=K(xo,{color:I(Co),fill:I(Hm),outline:I(Hm)}),ap=K(xo,{SimpleData:function(a,b){var c=a.getAttribute("name");null!==c&&(a=S(a),b[b.length-1][c]=a)}}),Vo=K(xo,{IconStyle:function(a,b){if(a=N({},gp,a,b)){b=b[b.length-1];var c="Icon"in a?a.Icon:{},d=!("Icon"in a)||0<Object.keys(c).length,e,f=c.href;f?e=f:d&&(e=oo);var f="bottom-left",g=a.hotSpot;if(g){var h=[g.x,g.y];var l=g.Xg;var m=g.Yg;f=g.origin}else e===oo?(h=ko,l=mo,m=lo):/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(e)&&(h=[.5,0],m=l="fraction"); -var n,g=c.x,p=c.y;void 0!==g&&void 0!==p&&(n=[g,p]);var q,g=c.w,c=c.h;void 0!==g&&void 0!==c&&(q=[g,c]);var r,c=a.heading;void 0!==c&&(r=Ha(c));a=a.scale;d?(e==oo&&(q=no,void 0===a&&(a=po)),e=new eo({anchor:h,anchorOrigin:f,anchorXUnits:l,anchorYUnits:m,crossOrigin:"anonymous",offset:n,offsetOrigin:"bottom-left",rotation:r,scale:a,size:q,src:e}),b.imageStyle=e):b.imageStyle=ro}},LabelStyle:function(a,b){(a=N({},hp,a,b))&&(b[b.length-1].textStyle=new fo({fill:new al({color:"color"in a?a.color:io}), -scale:a.scale}))},LineStyle:function(a,b){(a=N({},ip,a,b))&&(b[b.length-1].strokeStyle=new wj({color:"color"in a?a.color:io,width:"width"in a?a.width:1}))},PolyStyle:function(a,b){if(a=N({},pp,a,b)){b=b[b.length-1];b.fillStyle=new al({color:"color"in a?a.color:io});var c=a.fill;void 0!==c&&(b.fill=c);a=a.outline;void 0!==a&&(b.outline=a)}}}),np=K(xo,{Pair:function(a,b){if(a=N({},mp,a,b)){var c=a.key;c&&"normal"==c&&((c=a.styleUrl)&&(b[b.length-1]=c),(a=a.Style)&&(b[b.length-1]=a))}}});k=go.prototype; -k.vg=function(a,b){var c=K(xo,{Document:ql(this.vg,this),Folder:ql(this.vg,this),Placemark:rl(this.Dg,this),Style:this.Jp.bind(this),StyleMap:this.Ip.bind(this)});if(a=N([],c,a,b,this))return a};k.Dg=function(a,b){var c=N({geometry:null},op,a,b);if(c){var d=new H;a=a.getAttribute("id");null!==a&&d.jc(a);b=b[0];(a=c.geometry)&&Hl(a,!1,b);d.Ra(a);delete c.geometry;this.c&&d.hg(Ao(c.Style,c.styleUrl,this.a,this.b,this.g));delete c.Style;d.H(c);return d}}; -k.Jp=function(a,b){var c=a.getAttribute("id");null!==c&&(b=Uo(a,b))&&(a=a.baseURI&&"about:blank"!==a.baseURI?(new URL("#"+c,a.baseURI)).href:"#"+c,this.b[a]=b)};k.Ip=function(a,b){var c=a.getAttribute("id");null!==c&&(b=N(void 0,np,a,b))&&(a=a.baseURI&&"about:blank"!==a.baseURI?(new URL("#"+c,a.baseURI)).href:"#"+c,this.b[a]=b)};k.xg=function(a,b){return ja(xo,a.namespaceURI)?(a=this.Dg(a,[Fl(this,a,b)]))?a:null:null}; -k.zc=function(a,b){if(!ja(xo,a.namespaceURI))return[];var c=a.localName;if("Document"==c||"Folder"==c)return(c=this.vg(a,[Fl(this,a,b)]))?c:[];if("Placemark"==c)return(b=this.Dg(a,[Fl(this,a,b)]))?[b]:[];if("kml"==c){c=[];for(a=a.firstElementChild;a;a=a.nextElementSibling){var d=this.zc(a,b);d&&la(c,d)}return c}return[]};k.Cp=function(a){if(ml(a))return qp(this,a);if(nl(a))return rp(this,a);if("string"===typeof a)return a=pl(a),qp(this,a)}; -function qp(a,b){for(b=b.firstChild;b;b=b.nextSibling)if(b.nodeType==Node.ELEMENT_NODE){var c=rp(a,b);if(c)return c}}function rp(a,b){var c;for(c=b.firstElementChild;c;c=c.nextElementSibling)if(ja(xo,c.namespaceURI)&&"name"==c.localName)return S(c);for(c=b.firstElementChild;c;c=c.nextElementSibling)if(b=c.localName,ja(xo,c.namespaceURI)&&("Document"==b||"Folder"==b||"Placemark"==b||"kml"==b)&&(b=rp(a,c)))return b} -k.Dp=function(a){var b=[];ml(a)?la(b,sp(this,a)):nl(a)?la(b,tp(this,a)):"string"===typeof a&&(a=pl(a),la(b,sp(this,a)));return b};function sp(a,b){var c=[];for(b=b.firstChild;b;b=b.nextSibling)b.nodeType==Node.ELEMENT_NODE&&la(c,tp(a,b));return c} -function tp(a,b){var c,d=[];for(c=b.firstElementChild;c;c=c.nextElementSibling)if(ja(xo,c.namespaceURI)&&"NetworkLink"==c.localName){var e=N({},lp,c,[]);d.push(e)}for(c=b.firstElementChild;c;c=c.nextElementSibling)b=c.localName,!ja(xo,c.namespaceURI)||"Document"!=b&&"Folder"!=b&&"kml"!=b||la(d,tp(a,c));return d}k.Gp=function(a){var b=[];ml(a)?la(b,up(this,a)):nl(a)?la(b,this.lf(a)):"string"===typeof a&&(a=pl(a),la(b,up(this,a)));return b}; -function up(a,b){var c=[];for(b=b.firstChild;b;b=b.nextSibling)b.nodeType==Node.ELEMENT_NODE&&la(c,a.lf(b));return c}k.lf=function(a){var b,c=[];for(b=a.firstElementChild;b;b=b.nextElementSibling)if(ja(xo,b.namespaceURI)&&"Region"==b.localName){var d=N({},Zo,b,[]);c.push(d)}for(b=a.firstElementChild;b;b=b.nextElementSibling)a=b.localName,!ja(xo,b.namespaceURI)||"Document"!=a&&"Folder"!=a&&"kml"!=a||la(c,this.lf(b));return c}; -function vp(a,b){b=ed(b);b=[255*(4==b.length?b[3]:1),b[2],b[1],b[0]];var c;for(c=0;4>c;++c){var d=parseInt(b[c],10).toString(16);b[c]=1==d.length?"0"+d:d}Pm(a,b.join(""))}function wp(a,b,c){a={node:a};var d=b.U();if("GeometryCollection"==d){var e=b.Vf();var f=xp}else"MultiPoint"==d?(e=b.Zd(),f=yp):"MultiLineString"==d?(e=b.gd(),f=zp):"MultiPolygon"==d?(e=b.Td(),f=Ap):xa(!1,39);Bl(a,Bp,f,e,c)}function Cp(a,b,c){Bl({node:a},Dp,Ep,[b],c)} -function Fp(a,b,c){var d={node:a};b.a&&a.setAttribute("id",b.a);a=b.N();var e={address:1,description:1,name:1,open:1,phoneNumber:1,styleUrl:1,visibility:1};e[b.c]=1;var f=Object.keys(a||{}).sort().filter(function(a){return!e[a]});if(0<f.length){var g=zl(a,f);Bl(d,Gp,Hp,[{names:f,values:g}],c)}if(f=b.Lc())if(f=f.call(b,0))f=Array.isArray(f)?f[0]:f,this.j&&(a.Style=f),(f=f.Na())&&(a.name=f.Na());f=Ip[c[c.length-1].node.namespaceURI];a=zl(a,f);Bl(d,Gp,yl,a,c,f);a=c[0];(b=b.V())&&(b=Hl(b,!0,a));Bl(d, -Gp,xp,[b],c)}function Jp(a,b,c){var d=b.ga();a={node:a};a.layout=b.ja;a.stride=b.qa();Bl(a,Kp,Lp,[d],c)}function Mp(a,b,c){b=b.Sd();var d=b.shift();a={node:a};Bl(a,Np,Op,b,c);Bl(a,Np,Pp,[d],c)}function Qp(a,b){Qm(a,Math.round(1E6*b)/1E6)} -var Rp=K(xo,["Document","Placemark"]),Up=K(xo,{Document:J(function(a,b,c){Bl({node:a},Sp,Tp,b,c,void 0,this)}),Placemark:J(Fp)}),Sp=K(xo,{Placemark:J(Fp)}),Vp=K(xo,{Data:J(function(a,b,c){a.setAttribute("name",b.name);a={node:a};b=b.value;"object"==typeof b?(null!==b&&b.displayName&&Bl(a,Vp,yl,[b.displayName],c,["displayName"]),null!==b&&b.value&&Bl(a,Vp,yl,[b.value],c,["value"])):Bl(a,Vp,yl,[b],c,["value"])}),value:J(function(a,b){Pm(a,b)}),displayName:J(function(a,b){a.appendChild(il.createCDATASection(b))})}), -Wp={Point:"Point",LineString:"LineString",LinearRing:"LinearRing",Polygon:"Polygon",MultiPoint:"MultiGeometry",MultiLineString:"MultiGeometry",MultiPolygon:"MultiGeometry",GeometryCollection:"MultiGeometry"},Xp=K(xo,["href"],K(wo,["x","y","w","h"])),Yp=K(xo,{href:J(Pm)},K(wo,{x:J(Qm),y:J(Qm),w:J(Qm),h:J(Qm)})),Zp=K(xo,["scale","heading","Icon","hotSpot"]),aq=K(xo,{Icon:J(function(a,b,c){a={node:a};var d=Xp[c[c.length-1].node.namespaceURI],e=zl(b,d);Bl(a,Yp,yl,e,c,d);d=Xp[wo[0]];e=zl(b,d);Bl(a,Yp, -$p,e,c,d)}),heading:J(Qm),hotSpot:J(function(a,b){a.setAttribute("x",b.x);a.setAttribute("y",b.y);a.setAttribute("xunits",b.Xg);a.setAttribute("yunits",b.Yg)}),scale:J(Qp)}),bq=K(xo,["color","scale"]),cq=K(xo,{color:J(vp),scale:J(Qp)}),dq=K(xo,["color","width"]),eq=K(xo,{color:J(vp),width:J(Qm)}),Dp=K(xo,{LinearRing:J(Jp)}),Bp=K(xo,{LineString:J(Jp),Point:J(Jp),Polygon:J(Mp),GeometryCollection:J(wp)}),Ip=K(xo,"name open visibility address phoneNumber description styleUrl Style".split(" ")),Gp=K(xo, -{ExtendedData:J(function(a,b,c){a={node:a};var d=b.names;b=b.values;for(var e=d.length,f=0;f<e;f++)Bl(a,Vp,fq,[{name:d[f],value:b[f]}],c)}),MultiGeometry:J(wp),LineString:J(Jp),LinearRing:J(Jp),Point:J(Jp),Polygon:J(Mp),Style:J(function(a,b,c){a={node:a};var d={},e=b.Fa(),f=b.Ga(),g=b.Y();b=b.Na();g instanceof eo&&(d.IconStyle=g);b&&(d.LabelStyle=b);f&&(d.LineStyle=f);e&&(d.PolyStyle=e);b=gq[c[c.length-1].node.namespaceURI];d=zl(d,b);Bl(a,hq,yl,d,c,b)}),address:J(Pm),description:J(Pm),name:J(Pm), -open:J(Om),phoneNumber:J(Pm),styleUrl:J(Pm),visibility:J(Om)}),Kp=K(xo,{coordinates:J(function(a,b,c){c=c[c.length-1];var d=c.layout;c=c.stride;var e;"XY"==d||"XYM"==d?e=2:"XYZ"==d||"XYZM"==d?e=3:xa(!1,34);var f,g=b.length,h="";if(0<g){h+=b[0];for(d=1;d<e;++d)h+=","+b[d];for(f=c;f<g;f+=c)for(h+=" "+b[f],d=1;d<e;++d)h+=","+b[f+d]}Pm(a,h)})}),Np=K(xo,{outerBoundaryIs:J(Cp),innerBoundaryIs:J(Cp)}),iq=K(xo,{color:J(vp)}),gq=K(xo,["IconStyle","LabelStyle","LineStyle","PolyStyle"]),hq=K(xo,{IconStyle:J(function(a, -b,c){a={node:a};var d={},e=b.ic(),f=b.ye(),g={href:b.b.o};if(e){g.w=e[0];g.h=e[1];var h=b.Hc(),l=b.Oc();l&&f&&l[0]&&l[1]!==e[1]&&(g.x=l[0],g.y=f[1]-(l[1]+e[1]));h&&h[0]&&h[1]!==e[1]&&(d.hotSpot={x:h[0],Xg:"pixels",y:e[1]-h[1],Yg:"pixels"})}d.Icon=g;e=b.a;1!==e&&(d.scale=e);(b=b.g)&&(d.heading=b);b=Zp[c[c.length-1].node.namespaceURI];d=zl(d,b);Bl(a,aq,yl,d,c,b)}),LabelStyle:J(function(a,b,c){a={node:a};var d={},e=b.Fa();e&&(d.color=e.b);(b=b.b)&&1!==b&&(d.scale=b);b=bq[c[c.length-1].node.namespaceURI]; -d=zl(d,b);Bl(a,cq,yl,d,c,b)}),LineStyle:J(function(a,b,c){a={node:a};var d=dq[c[c.length-1].node.namespaceURI];b=zl({color:b.a,width:b.c},d);Bl(a,eq,yl,b,c,d)}),PolyStyle:J(function(a,b,c){Bl({node:a},iq,jq,[b.b],c)})});function $p(a,b,c){return jl(wo[0],"gx:"+c)}function Tp(a,b){return jl(b[b.length-1].node.namespaceURI,"Placemark")}function xp(a,b){if(a)return jl(b[b.length-1].node.namespaceURI,Wp[a.U()])} -var jq=wl("color"),Lp=wl("coordinates"),fq=wl("Data"),Hp=wl("ExtendedData"),Op=wl("innerBoundaryIs"),yp=wl("Point"),zp=wl("LineString"),Ep=wl("LinearRing"),Ap=wl("Polygon"),Pp=wl("outerBoundaryIs"); -go.prototype.Xb=function(a,b){b=Gl(this,b);var c=jl(xo[4],"kml");c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:gx",wo[0]);c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation","http://www.opengis.net/kml/2.2 https://developers.google.com/kml/schema/kml22gx.xsd");var d={node:c},e={};1<a.length?e.Document=a:1==a.length&&(e.Placemark=a[0]);a=Rp[c.namespaceURI]; -e=zl(e,a);Bl(d,Up,yl,e,[b],a,this);return c};Fj.Dd=function(){}; -(function(a){function b(a){this.lc=ArrayBuffer.isView&&ArrayBuffer.isView(a)?a:new Uint8Array(a||0);this.type=this.ea=0;this.length=this.lc.length}function c(a,b,c){var e=c.lc;var f=e[c.ea++];var g=(f&112)>>4;if(128>f)return d(a,g,b);f=e[c.ea++];g|=(f&127)<<3;if(128>f)return d(a,g,b);f=e[c.ea++];g|=(f&127)<<10;if(128>f)return d(a,g,b);f=e[c.ea++];g|=(f&127)<<17;if(128>f)return d(a,g,b);f=e[c.ea++];g|=(f&127)<<24;if(128>f)return d(a,g,b);f=e[c.ea++];if(128>f)return d(a,g|(f&1)<<31,b);throw Error("Expected varint not more than 10 bytes"); -}function d(a,b,c){return c?4294967296*b+(a>>>0):4294967296*(b>>>0)+(a>>>0)}var e={read:function(a,b,c,d,e){var f=8*e-d-1;var g=(1<<f)-1,h=g>>1,l=-7;e=c?e-1:0;var m=c?-1:1,x=a[b+e];e+=m;c=x&(1<<-l)-1;x>>=-l;for(l+=f;0<l;c=256*c+a[b+e],e+=m,l-=8);f=c&(1<<-l)-1;c>>=-l;for(l+=d;0<l;f=256*f+a[b+e],e+=m,l-=8);if(0===c)c=1-h;else{if(c===g)return f?NaN:Infinity*(x?-1:1);f+=Math.pow(2,d);c-=h}return(x?-1:1)*f*Math.pow(2,c-d)},write:function(a,b,c,d,e,n){var f,g=8*n-e-1,h=(1<<g)-1,l=h>>1,m=23===e?Math.pow(2, --24)-Math.pow(2,-77):0;n=d?0:n-1;var B=d?1:-1,E=0>b||0===b&&0>1/b?1:0;b=Math.abs(b);isNaN(b)||Infinity===b?(b=isNaN(b)?1:0,d=h):(d=Math.floor(Math.log(b)/Math.LN2),1>b*(f=Math.pow(2,-d))&&(d--,f*=2),b=1<=d+l?b+m/f:b+m*Math.pow(2,1-l),2<=b*f&&(d++,f/=2),d+l>=h?(b=0,d=h):1<=d+l?(b=(b*f-1)*Math.pow(2,e),d+=l):(b=b*Math.pow(2,l-1)*Math.pow(2,e),d=0));for(;8<=e;a[c+n]=b&255,n+=B,b/=256,e-=8);d=d<<e|b;for(g+=e;0<g;a[c+n]=d&255,n+=B,d/=256,g-=8);a[c+n-B]|=128*E}};b.c=0;b.i=1;b.b=2;b.a=5;b.prototype={Ag:function(a, -b,c){for(c=c||this.length;this.ea<c;){var d=this.Ka(),e=d>>3,f=this.ea;this.type=d&7;a(e,b,this);this.ea===f&&this.mq(d)}return b},yp:function(){var a=e.read(this.lc,this.ea,!0,23,4);this.ea+=4;return a},up:function(){var a=e.read(this.lc,this.ea,!0,52,8);this.ea+=8;return a},Ka:function(a){var b=this.lc;var d=b[this.ea++];var e=d&127;if(128>d)return e;d=b[this.ea++];e|=(d&127)<<7;if(128>d)return e;d=b[this.ea++];e|=(d&127)<<14;if(128>d)return e;d=b[this.ea++];e|=(d&127)<<21;if(128>d)return e;d=b[this.ea]; -return c(e|(d&15)<<28,a,this)},Kp:function(){return this.Ka(!0)},ce:function(){var a=this.Ka();return 1===a%2?(a+1)/-2:a/2},sp:function(){return!!this.Ka()},Gg:function(){for(var a=this.Ka()+this.ea,b=this.lc,c="",d=this.ea;d<a;){var e=b[d],n=null,p=239<e?4:223<e?3:191<e?2:1;if(d+p>a)break;if(1===p)128>e&&(n=e);else if(2===p){var q=b[d+1];128===(q&192)&&(n=(e&31)<<6|q&63,127>=n&&(n=null))}else if(3===p){q=b[d+1];var r=b[d+2];128===(q&192)&&128===(r&192)&&(n=(e&15)<<12|(q&63)<<6|r&63,2047>=n||55296<= -n&&57343>=n)&&(n=null)}else if(4===p){q=b[d+1];r=b[d+2];var u=b[d+3];128===(q&192)&&128===(r&192)&&128===(u&192)&&(n=(e&15)<<18|(q&63)<<12|(r&63)<<6|u&63,65535>=n||1114112<=n)&&(n=null)}null===n?(n=65533,p=1):65535<n&&(n-=65536,c+=String.fromCharCode(n>>>10&1023|55296),n=56320|n&1023);c+=String.fromCharCode(n);d+=p}this.ea=a;return c},mq:function(a){a&=7;if(a===b.c)for(;127<this.lc[this.ea++];);else if(a===b.b)this.ea=this.Ka()+this.ea;else if(a===b.a)this.ea+=4;else if(a===b.i)this.ea+=8;else throw Error("Unimplemented type: "+ -a);}};a["default"]=b})(Fj.Dd=Fj.Dd||{});Fj.Dd=Fj.Dd.default;Fj.xf={};Fj.xf.Bf=function(){}; -(function(a){function b(a,b){this.layers=a.Ag(l,{},b)}function c(a,b){this.x=a;this.y=b}function d(a,b,c,d,f){this.properties={};this.extent=c;this.type=0;this.Cc=a;this.Ef=-1;this.ne=d;this.pe=f;a.Ag(e,this,b)}function e(a,b,c){if(1==a)b.id=c.Ka();else if(2==a)for(a=c.Ka()+c.ea;c.ea<a;){var d=b.ne[c.Ka()],e=b.pe[c.Ka()];b.properties[d]=e}else 3==a?b.type=c.Ka():4==a&&(b.Ef=c.ea)}function f(a,b){this.version=1;this.name=null;this.extent=4096;this.length=0;this.Cc=a;this.ne=[];this.pe=[];this.me=[]; -a.Ag(g,this,b);this.length=this.me.length}function g(a,b,c){15===a?b.version=c.Ka():1===a?b.name=c.Gg():5===a?b.extent=c.Ka():2===a?b.me.push(c.ea):3===a?b.ne.push(c.Gg()):4===a&&b.pe.push(h(c))}function h(a){for(var b=null,c=a.Ka()+a.ea;a.ea<c;)b=a.Ka()>>3,b=1===b?a.Gg():2===b?a.yp():3===b?a.up():4===b?a.Kp():5===b?a.Ka():6===b?a.ce():7===b?a.sp():null;return b}function l(a,b,c){3===a&&(a=new m(c,c.Ka()+c.ea),a.length&&(b[a.name]=a))}c.prototype={clone:function(){return new c(this.x,this.y)},add:function(a){return this.clone().Yj(a)}, -rotate:function(a){return this.clone().hk(a)},round:function(){return this.clone().ik()},angle:function(){return Math.atan2(this.y,this.x)},Yj:function(a){this.x+=a.x;this.y+=a.y;return this},hk:function(a){var b=Math.cos(a);a=Math.sin(a);var c=a*this.x+b*this.y;this.x=b*this.x-a*this.y;this.y=c;return this},ik:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this}};c.Kq=function(a){return a instanceof c?a:Array.isArray(a)?new c(a[0],a[1]):a};d.b=["Unknown","Point","LineString", -"Polygon"];d.prototype.Oh=function(){var a=this.Cc;a.ea=this.Ef;for(var b=a.Ka()+a.ea,d=1,e=0,f=0,g=0,h=[],l;a.ea<b;)if(e||(e=a.Ka(),d=e&7,e>>=3),e--,1===d||2===d)f+=a.ce(),g+=a.ce(),1===d&&(l&&h.push(l),l=[]),l.push(new c(f,g));else if(7===d)l&&l.push(l[0].clone());else throw Error("unknown command "+d);l&&h.push(l);return h};d.prototype.bbox=function(){var a=this.Cc;a.ea=this.Ef;for(var b=a.Ka()+a.ea,c=1,d=0,e=0,f=0,g=Infinity,h=-Infinity,l=Infinity,m=-Infinity;a.ea<b;)if(d||(d=a.Ka(),c=d&7,d>>= -3),d--,1===c||2===c)e+=a.ce(),f+=a.ce(),e<g&&(g=e),e>h&&(h=e),f<l&&(l=f),f>m&&(m=f);else if(7!==c)throw Error("unknown command "+c);return[g,l,h,m]};var m=f;f.prototype.feature=function(a){if(0>a||a>=this.me.length)throw Error("feature index out of bounds");this.Cc.ea=this.me[a];a=this.Cc.Ka()+this.Cc.ea;return new d(this.Cc,a,this.extent,this.ne,this.pe)};var n=m;a["default"]={Bf:b,Wj:d,Xj:n};a.Bf=b;a.Wj=d;a.Xj=n})(Fj.xf=Fj.xf||{});function kq(a,b,c,d,e){this.g=e;this.i=a;this.b=b;this.f=c;this.c=d}k=kq.prototype;k.get=function(a){return this.c[a]};k.Bb=function(){return this.f};k.G=function(){this.a||(this.a="Point"===this.i?Za(this.b):$a(this.b,0,this.b.length,2));return this.a};k.Wn=function(){return this.g};k.ec=function(){return this.b};k.ga=kq.prototype.ec;k.V=function(){return this};k.Xn=function(){return this.c};k.Vd=kq.prototype.V;k.qa=function(){return 2};k.Lc=ua;k.U=function(){return this.i};function lq(a){El.call(this);a=a?a:{};this.defaultDataProjection=new Bb({code:"",units:"tile-pixels"});this.b=a.featureClass?a.featureClass:kq;this.a=a.geometryName;this.i=a.layerName?a.layerName:"layer";this.c=a.layers?a.layers:null}v(lq,El);k=lq.prototype;k.U=function(){return"arraybuffer"}; -k.Oa=function(a,b){var c=this.c;a=new Fj.Dd(a);a=new Fj.xf.Bf(a);var d=[],e=this.b,f;for(f in a.layers)if(!c||-1!=c.indexOf(f)){var g=a.layers[f];for(var h=0,l=g.length;h<l;++h){if(e===kq){var m=void 0;var n=g.feature(h),p=f,q=n.Oh(),r=[],u=[];mq(q,u,r);var x=n.type;1===x?m=1===q.length?"Point":"MultiPoint":2===x?m=1===q.length?"LineString":"MultiLineString":3===x&&(m="Polygon");q=n.properties;q[this.i]=p;m=new this.b(m,u,r,q,n.id)}else{x=g.feature(h);u=f;r=b;m=new this.b;n=x.id;p=x.properties;p[this.i]= -u;this.a&&m.Tc(this.a);u=void 0;q=x.type;if(0===q)u=null;else{var x=x.Oh(),B=[],E=[];mq(x,E,B);1===q?u=1===x.length?new C(null):new Q(null):2===q?1===x.length?u=new O(null):u=new P(null):3===q&&(u=new D(null));u.ba("XY",E,B)}r=Hl(u,!1,Gl(this,r));m.Ra(r);m.jc(n);m.H(p)}d.push(m)}}return d};k.kb=function(){return this.defaultDataProjection};k.mn=function(a){this.c=a}; -function mq(a,b,c){for(var d=0,e=0,f=a.length;e<f;++e){var g=a[e],h;var l=0;for(h=g.length;l<h;++l){var m=g[l];b.push(m.x,m.y)}d+=2*l;c.push(d)}}k.Tb=function(){};k.Sc=function(){};k.Bd=function(){};k.$c=function(){};k.Wb=function(){};function nq(){Cm.call(this);this.defaultDataProjection=Tb("EPSG:4326")}v(nq,Cm);function oq(a,b){b[b.length-1].fe[a.getAttribute("k")]=a.getAttribute("v")} -var pq=[null],qq=K(pq,{nd:function(a,b){b[b.length-1].md.push(a.getAttribute("ref"))},tag:oq}),sq=K(pq,{node:function(a,b){var c=b[0],d=b[b.length-1],e=a.getAttribute("id"),f=[parseFloat(a.getAttribute("lon")),parseFloat(a.getAttribute("lat"))];d.Sh[e]=f;a=N({fe:{}},rq,a,b);wb(a.fe)||(f=new C(f),Hl(f,!1,c),c=new H(f),c.jc(e),c.H(a.fe),d.features.push(c))},way:function(a,b){var c=b[0],d=a.getAttribute("id");a=N({md:[],fe:{}},qq,a,b);b=b[b.length-1];for(var e=[],f=0,g=a.md.length;f<g;f++)la(e,b.Sh[a.md[f]]); -a.md[0]==a.md[a.md.length-1]?(f=new D(null),f.ba("XY",e,[e.length])):(f=new O(null),f.ba("XY",e));Hl(f,!1,c);c=new H(f);c.jc(d);c.H(a.fe);b.features.push(c)}}),rq=K(pq,{tag:oq});nq.prototype.zc=function(a,b){b=Fl(this,a,b);return"osm"==a.localName&&(a=N({Sh:{},features:[]},sq,a,[b]),a.features)?a.features:[]};nq.prototype.Vg=function(){};nq.prototype.Xb=function(){};nq.prototype.ie=function(){};function tq(a){return a.getAttributeNS("http://www.w3.org/1999/xlink","href")};function uq(){}uq.prototype.read=function(a){return ml(a)?this.a(a):nl(a)?this.b(a):"string"===typeof a?(a=pl(a),this.a(a)):null};function vq(){}v(vq,uq);vq.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};vq.prototype.b=function(a){return(a=N({},wq,a,[]))?a:null}; -var xq=[null,"http://www.opengis.net/ows/1.1"],wq=K(xq,{ServiceIdentification:I(function(a,b){return N({},yq,a,b)}),ServiceProvider:I(function(a,b){return N({},zq,a,b)}),OperationsMetadata:I(function(a,b){return N({},Aq,a,b)})}),Cq=K(xq,{DeliveryPoint:I(S),City:I(S),AdministrativeArea:I(S),PostalCode:I(S),Country:I(S),ElectronicMailAddress:I(S)}),Dq=K(xq,{Value:tl(function(a){return S(a)})}),Eq=K(xq,{AllowedValues:I(function(a,b){return N({},Dq,a,b)})}),Gq=K(xq,{Phone:I(function(a,b){return N({}, -Fq,a,b)}),Address:I(function(a,b){return N({},Cq,a,b)})}),Iq=K(xq,{HTTP:I(function(a,b){return N({},Hq,a,b)})}),Hq=K(xq,{Get:tl(function(a,b){var c=tq(a);if(c)return N({href:c},Jq,a,b)}),Post:void 0}),Kq=K(xq,{DCP:I(function(a,b){return N({},Iq,a,b)})}),Aq=K(xq,{Operation:function(a,b){var c=a.getAttribute("name");(a=N({},Kq,a,b))&&(b[b.length-1][c]=a)}}),Fq=K(xq,{Voice:I(S),Facsimile:I(S)}),Jq=K(xq,{Constraint:tl(function(a,b){var c=a.getAttribute("name");if(c)return N({name:c},Eq,a,b)})}),Lq=K(xq, -{IndividualName:I(S),PositionName:I(S),ContactInfo:I(function(a,b){return N({},Gq,a,b)})}),yq=K(xq,{Title:I(S),ServiceTypeVersion:I(S),ServiceType:I(S)}),zq=K(xq,{ProviderName:I(S),ProviderSite:I(tq),ServiceContact:I(function(a,b){return N({},Lq,a,b)})});function Mq(a,b,c,d){var e;void 0!==d?e=d:e=[];for(var f=d=0;f<b;){var g=a[f++];e[d++]=a[f++];e[d++]=g;for(g=2;g<c;++g)e[d++]=a[f++]}e.length=d};function Nq(a){a=a?a:{};El.call(this);this.defaultDataProjection=Tb("EPSG:4326");this.b=a.factor?a.factor:1E5;this.a=a.geometryLayout?a.geometryLayout:"XY"}v(Nq,Vn);function Oq(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;var f;var g=0;for(f=a.length;g<f;)for(d=0;d<b;++d,++g){var h=a[g],l=h-e[d];e[d]=h;a[g]=l}return Pq(a,c?c:1E5)}function Qq(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;a=Rq(a,c?c:1E5);var f;c=0;for(f=a.length;c<f;)for(d=0;d<b;++d,++c)e[d]+=a[c],a[c]=e[d];return a} -function Pq(a,b){b=b?b:1E5;var c;var d=0;for(c=a.length;d<c;++d)a[d]=Math.round(a[d]*b);b=0;for(d=a.length;b<d;++b)c=a[b],a[b]=0>c?~(c<<1):c<<1;b="";d=0;for(c=a.length;d<c;++d){for(var e,f=a[d],g="";32<=f;)e=(32|f&31)+63,g+=String.fromCharCode(e),f>>=5;g+=String.fromCharCode(f+63);b+=g}return b} -function Rq(a,b){b=b?b:1E5;var c=[],d=0,e=0,f;var g=0;for(f=a.length;g<f;++g){var h=a.charCodeAt(g)-63,d=d|(h&31)<<e;32>h?(c.push(d),e=d=0):e+=5}a=0;for(d=c.length;a<d;++a)e=c[a],c[a]=e&1?~(e>>1):e>>1;a=0;for(d=c.length;a<d;++a)c[a]/=b;return c}k=Nq.prototype;k.ae=function(a,b){a=this.wd(a,b);return new H(a)};k.zg=function(a,b){return[this.ae(a,b)]};k.wd=function(a,b){var c=sf(this.a);a=Qq(a,c,this.b);Mq(a,a.length,c,a);c=Ff(a,0,a.length,c);return Hl(new O(c,this.a),!1,Gl(this,b))}; -k.ge=function(a,b){if(a=a.V())return this.Cd(a,b);xa(!1,40);return""};k.Wg=function(a,b){return this.ge(a[0],b)};k.Cd=function(a,b){a=Hl(a,!0,Gl(this,b));b=a.ga();a=a.qa();Mq(b,b.length,a,b);return Oq(b,a,this.b)};function Sq(a){a=a?a:{};El.call(this);this.a=a.layerName;this.b=a.layers?a.layers:null;this.defaultDataProjection=Tb(a.defaultDataProjection?a.defaultDataProjection:"EPSG:4326")}v(Sq,Il);function Tq(a,b){var c=[],d,e;var f=0;for(e=a.length;f<e;++f){var g=a[f];0<f&&c.pop();0<=g?d=b[g]:d=b[~g].slice().reverse();c.push.apply(c,d)}a=0;for(b=c.length;a<b;++a)c[a]=c[a].slice();return c} -function Uq(a,b,c,d,e,f,g){a=a.geometries;var h=[],l;var m=0;for(l=a.length;m<l;++m)h[m]=Vq(a[m],b,c,d,e,f,g);return h}function Vq(a,b,c,d,e,f,g){var h=a.type,l=Wq[h];c="Point"===h||"MultiPoint"===h?l(a,c,d):l(a,b);b=new H;b.Ra(Hl(c,!1,g));void 0!==a.id&&b.jc(a.id);a=a.properties;e&&(a||(a={}),a[e]=f);a&&b.H(a);return b} -Sq.prototype.yg=function(a,b){if("Topology"==a.type){var c=null,d=null;if(a.transform){var e=a.transform;c=e.scale;d=e.translate}var f=a.arcs;if(e){e=c;var g=d,h;var l=0;for(h=f.length;l<h;++l){var m,n=f[l],p=e,q=g,r=0,u=0;var x=0;for(m=n.length;x<m;++x){var B=n[x];r+=B[0];u+=B[1];B[0]=r;B[1]=u;Xq(B,p,q)}}}e=[];a=a.objects;var g=this.a,E;for(E in a)this.b&&-1==this.b.indexOf(E)||("GeometryCollection"===a[E].type?(l=a[E],e.push.apply(e,Uq(l,f,c,d,g,E,b))):(l=a[E],e.push(Vq(l,f,c,d,g,E,b))));return e}return[]}; -function Xq(a,b,c){a[0]=a[0]*b[0]+c[0];a[1]=a[1]*b[1]+c[1]}Sq.prototype.Fg=function(){return this.defaultDataProjection}; -var Wq={Point:function(a,b,c){a=a.coordinates;b&&c&&Xq(a,b,c);return new C(a)},LineString:function(a,b){a=Tq(a.arcs,b);return new O(a)},Polygon:function(a,b){var c=[],d;var e=0;for(d=a.arcs.length;e<d;++e)c[e]=Tq(a.arcs[e],b);return new D(c)},MultiPoint:function(a,b,c){a=a.coordinates;var d;if(b&&c){var e=0;for(d=a.length;e<d;++e)Xq(a[e],b,c)}return new Q(a)},MultiLineString:function(a,b){var c=[],d;var e=0;for(d=a.arcs.length;e<d;++e)c[e]=Tq(a.arcs[e],b);return new P(c)},MultiPolygon:function(a, -b){var c=[],d,e;var f=0;for(e=a.arcs.length;f<e;++f){var g=a.arcs[f];var h=[];var l=0;for(d=g.length;l<d;++l)h[l]=Tq(g[l],b);c[f]=h}return new R(c)}};k=Sq.prototype;k.Zc=function(){};k.he=function(){};k.je=function(){};k.Cg=function(){};k.Rc=function(){};function Yq(a){a=a?a:{};this.c=a.featureType;this.a=a.featureNS;this.b=a.gmlFormat?a.gmlFormat:new Sm;this.o=a.schemaLocation?a.schemaLocation:Zq["1.1.0"];Cm.call(this)}v(Yq,Cm);var Zq={"1.1.0":"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd","1.0.0":"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd"}; -Yq.prototype.zc=function(a,b){var c={featureType:this.c,featureNS:this.a};tb(c,Fl(this,a,b?b:{}));b=[c];this.b.b["http://www.opengis.net/gml"].featureMember=rl(Fm.prototype.be);(a=N([],this.b.b,a,b,this.b))||(a=[]);return a};Yq.prototype.j=function(a){if(ml(a))return $q(a);if(nl(a))return N({},ar,a,[]);if("string"===typeof a)return a=pl(a),$q(a)};Yq.prototype.g=function(a){if(ml(a))return br(this,a);if(nl(a))return cr(this,a);if("string"===typeof a)return a=pl(a),br(this,a)}; -function br(a,b){for(b=b.firstChild;b;b=b.nextSibling)if(b.nodeType==Node.ELEMENT_NODE)return cr(a,b)}var dr={"http://www.opengis.net/gml":{boundedBy:I(Fm.prototype.gf,"bounds")}};function cr(a,b){var c={},d=Nm(b.getAttribute("numberOfFeatures"));c.numberOfFeatures=d;return N(c,dr,b,[],a.b)} -var er={"http://www.opengis.net/wfs":{totalInserted:I(Mm),totalUpdated:I(Mm),totalDeleted:I(Mm)}},fr={"http://www.opengis.net/ogc":{FeatureId:rl(function(a){return a.getAttribute("fid")})}},gr={"http://www.opengis.net/wfs":{Feature:function(a,b){Al(fr,a,b)}}},ar={"http://www.opengis.net/wfs":{TransactionSummary:I(function(a,b){return N({},er,a,b)},"transactionSummary"),InsertResults:I(function(a,b){return N([],gr,a,b)},"insertIds")}}; -function $q(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return N({},ar,a,[])}var hr={"http://www.opengis.net/wfs":{PropertyName:J(Pm)}};function ir(a,b){var c=jl("http://www.opengis.net/ogc","Filter"),d=jl("http://www.opengis.net/ogc","FeatureId");c.appendChild(d);d.setAttribute("fid",b);a.appendChild(c)}function jr(a,b){a=(a?a:"feature")+":";return b.indexOf(a)?a+b:b} -var kr={"http://www.opengis.net/wfs":{Insert:J(function(a,b,c){var d=c[c.length-1],e=d.gmlVersion,d=jl(d.featureNS,d.featureType);a.appendChild(d);if(2===e){a=an.prototype;(e=b.a)&&d.setAttribute("fid",e);var e=c[c.length-1],f=e.featureNS,g=b.c;e.lb||(e.lb={},e.lb[f]={});var h=b.N();b=[];var l=[];for(n in h){var m=h[n];null!==m&&(b.push(n),l.push(m),n==g||m instanceof of?n in e.lb[f]||(e.lb[f][n]=J(a.ai,a)):n in e.lb[f]||(e.lb[f][n]=J(Pm)))}var n=tb({},e);n.node=d;Bl(n,e.lb,wl(void 0,f),l,c,b)}else Sm.prototype.ii(d, -b,c)}),Update:J(function(a,b,c){var d=c[c.length-1];xa(void 0!==b.a,27);var e=d.featurePrefix,f=d.featureNS;a.setAttribute("typeName",jr(e,d.featureType));a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+e,f);e=b.a;if(void 0!==e){for(var f=b.O(),g=[],h=0,l=f.length;h<l;h++){var m=b.get(f[h]);void 0!==m&&g.push({name:f[h],value:m})}Bl({gmlVersion:d.gmlVersion,node:a,hasZ:d.hasZ,srsName:d.srsName},kr,wl("Property"),g,c);ir(a,e)}}),Delete:J(function(a,b,c){c=c[c.length-1];xa(void 0!==b.a,26); -var d=c.featurePrefix,e=c.featureNS;a.setAttribute("typeName",jr(d,c.featureType));a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+d,e);b=b.a;void 0!==b&&ir(a,b)}),Property:J(function(a,b,c){var d=jl("http://www.opengis.net/wfs","Name"),e=c[c.length-1].gmlVersion;a.appendChild(d);Pm(d,b.name);void 0!==b.value&&null!==b.value&&(d=jl("http://www.opengis.net/wfs","Value"),a.appendChild(d),b.value instanceof of?2===e?an.prototype.ai(d,b.value,c):Sm.prototype.od(d,b.value,c):Pm(d,b.value))}), -Native:J(function(a,b){b.vq&&a.setAttribute("vendorId",b.vq);void 0!==b.Vp&&a.setAttribute("safeToIgnore",b.Vp);void 0!==b.value&&Pm(a,b.value)})}};function lr(a,b,c){var d={node:a};b.b.forEach(function(a){Bl(d,mr,wl(a.kc),[a],c)})}function nr(a,b){void 0!==b.a&&a.setAttribute("matchCase",b.a.toString());or(a,b.b);pr(a,""+b.i)}function qr(a,b,c){a=jl("http://www.opengis.net/ogc",a);Pm(a,c);b.appendChild(a)}function or(a,b){qr("PropertyName",a,b)}function pr(a,b){qr("Literal",a,b)} -function rr(a,b){var c=jl("http://www.opengis.net/gml","TimeInstant");a.appendChild(c);a=jl("http://www.opengis.net/gml","timePosition");c.appendChild(a);Pm(a,b)} -var mr={"http://www.opengis.net/wfs":{Query:J(function(a,b,c){var d=c[c.length-1],e=d.featurePrefix,f=d.featureNS,g=d.propertyNames,h=d.srsName;a.setAttribute("typeName",e?jr(e,b):b);h&&a.setAttribute("srsName",h);f&&a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+e,f);b=tb({},d);b.node=a;Bl(b,hr,wl("PropertyName"),g,c);if(d=d.filter)g=jl("http://www.opengis.net/ogc","Filter"),a.appendChild(g),Bl({node:g},mr,wl(d.kc),[d],c)})},"http://www.opengis.net/ogc":{During:J(function(a,b){var c=jl("http://www.opengis.net/fes", -"ValueReference");Pm(c,b.b);a.appendChild(c);c=jl("http://www.opengis.net/gml","TimePeriod");a.appendChild(c);a=jl("http://www.opengis.net/gml","begin");c.appendChild(a);rr(a,b.a);a=jl("http://www.opengis.net/gml","end");c.appendChild(a);rr(a,b.i)}),And:J(lr),Or:J(lr),Not:J(function(a,b,c){b=b.condition;Bl({node:a},mr,wl(b.kc),[b],c)}),BBOX:J(function(a,b,c){c[c.length-1].srsName=b.srsName;or(a,b.geometryName);Sm.prototype.od(a,b.extent,c)}),Intersects:J(function(a,b,c){c[c.length-1].srsName=b.srsName; -or(a,b.geometryName);Sm.prototype.od(a,b.geometry,c)}),Within:J(function(a,b,c){c[c.length-1].srsName=b.srsName;or(a,b.geometryName);Sm.prototype.od(a,b.geometry,c)}),PropertyIsEqualTo:J(nr),PropertyIsNotEqualTo:J(nr),PropertyIsLessThan:J(nr),PropertyIsLessThanOrEqualTo:J(nr),PropertyIsGreaterThan:J(nr),PropertyIsGreaterThanOrEqualTo:J(nr),PropertyIsNull:J(function(a,b){or(a,b.b)}),PropertyIsBetween:J(function(a,b){or(a,b.b);var c=jl("http://www.opengis.net/ogc","LowerBoundary");a.appendChild(c); -pr(c,""+b.a);c=jl("http://www.opengis.net/ogc","UpperBoundary");a.appendChild(c);pr(c,""+b.i)}),PropertyIsLike:J(function(a,b){a.setAttribute("wildCard",b.g);a.setAttribute("singleChar",b.f);a.setAttribute("escapeChar",b.i);void 0!==b.a&&a.setAttribute("matchCase",b.a.toString());or(a,b.b);pr(a,""+b.c)})}}; -Yq.prototype.l=function(a){var b=jl("http://www.opengis.net/wfs","GetFeature");b.setAttribute("service","WFS");b.setAttribute("version","1.1.0");if(a){a.handle&&b.setAttribute("handle",a.handle);a.outputFormat&&b.setAttribute("outputFormat",a.outputFormat);void 0!==a.maxFeatures&&b.setAttribute("maxFeatures",a.maxFeatures);a.resultType&&b.setAttribute("resultType",a.resultType);void 0!==a.startIndex&&b.setAttribute("startIndex",a.startIndex);void 0!==a.count&&b.setAttribute("count",a.count);var c= -a.filter;if(a.bbox){xa(a.geometryName,12);var d=sm(a.geometryName,a.bbox,a.srsName);c?c=rm(c,d):c=d}}b.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.o);c={node:b,srsName:a.srsName,featureNS:a.featureNS?a.featureNS:this.a,featurePrefix:a.featurePrefix,geometryName:a.geometryName,filter:c,propertyNames:a.propertyNames?a.propertyNames:[]};xa(Array.isArray(a.featureTypes),11);a=a.featureTypes;c=[c];d=tb({},c[c.length-1]);d.node=b;Bl(d,mr,wl("Query"),a,c);return b}; -Yq.prototype.v=function(a,b,c,d){var e=[],f=jl("http://www.opengis.net/wfs","Transaction"),g=d.version?d.version:"1.1.0",h="1.0.0"===g?2:3;f.setAttribute("service","WFS");f.setAttribute("version",g);if(d){var l=d.gmlOptions?d.gmlOptions:{};d.handle&&f.setAttribute("handle",d.handle)}f.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",Zq[g]);a&&(g={node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,gmlVersion:h,hasZ:d.hasZ,srsName:d.srsName}, -tb(g,l),Bl(g,kr,wl("Insert"),a,e));b&&(g={node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,gmlVersion:h,hasZ:d.hasZ,srsName:d.srsName},tb(g,l),Bl(g,kr,wl("Update"),b,e));c&&Bl({node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,gmlVersion:h,srsName:d.srsName},kr,wl("Delete"),c,e);d.nativeElements&&Bl({node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,gmlVersion:h,srsName:d.srsName},kr,wl("Native"), -d.nativeElements,e);return f};Yq.prototype.Eg=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.kf(a);return null};Yq.prototype.kf=function(a){if(a.firstElementChild&&a.firstElementChild.firstElementChild)for(a=a.firstElementChild.firstElementChild,a=a.firstElementChild;a;a=a.nextElementSibling)if(0!==a.childNodes.length&&(1!==a.childNodes.length||3!==a.firstChild.nodeType)){var b=[{}];this.b.gf(a,b);return Tb(b.pop().srsName)}return null};function sr(a){a=a?a:{};El.call(this);this.b=void 0!==a.splitCollection?a.splitCollection:!1}v(sr,Vn);function tr(a){a=a.X();return a.length?a.join(" "):""}function ur(a){a=a.X();for(var b=[],c=0,d=a.length;c<d;++c)b.push(a[c].join(" "));return b.join(",")}function vr(a){var b=[];a=a.Sd();for(var c=0,d=a.length;c<d;++c)b.push("("+ur(a[c])+")");return b.join(",")} -function wr(a){var b=a.U(),c=(0,xr[b])(a),b=b.toUpperCase();if(a instanceof rf){a=a.ja;var d="";if("XYZ"===a||"XYZM"===a)d+="Z";if("XYM"===a||"XYZM"===a)d+="M";a=d;0<a.length&&(b+=" "+a)}return c.length?b+"("+c+")":b+" EMPTY"} -var xr={Point:tr,LineString:ur,Polygon:vr,MultiPoint:function(a){var b=[];a=a.Zd();for(var c=0,d=a.length;c<d;++c)b.push("("+tr(a[c])+")");return b.join(",")},MultiLineString:function(a){var b=[];a=a.gd();for(var c=0,d=a.length;c<d;++c)b.push("("+ur(a[c])+")");return b.join(",")},MultiPolygon:function(a){var b=[];a=a.Td();for(var c=0,d=a.length;c<d;++c)b.push("("+vr(a[c])+")");return b.join(",")},GeometryCollection:function(a){var b=[];a=a.Vf();for(var c=0,d=a.length;c<d;++c)b.push(wr(a[c]));return b.join(",")}}; -k=sr.prototype;k.ae=function(a,b){return(a=this.wd(a,b))?(b=new H,b.Ra(a),b):null};k.zg=function(a,b){var c=[];a=this.wd(a,b);this.b&&"GeometryCollection"==a.U()?c=a.a:c=[a];b=[];for(var d=0,e=c.length;d<e;++d)a=new H,a.Ra(c[d]),b.push(a);return b};k.wd=function(a,b){a=new yr(new zr(a));Ar(a);return(a=Br(a))?Hl(a,!1,b):null};k.ge=function(a,b){return(a=a.V())?this.Cd(a,b):""}; -k.Wg=function(a,b){if(1==a.length)return this.ge(a[0],b);for(var c=[],d=0,e=a.length;d<e;++d)c.push(a[d].V());a=new tm(c);return this.Cd(a,b)};k.Cd=function(a,b){return wr(Hl(a,!0,b))};function zr(a){this.a=a;this.b=-1} -function Cr(a){var b=a.a.charAt(++a.b),c={position:a.b,value:b};if("("==b)c.type=2;else if(","==b)c.type=5;else if(")"==b)c.type=3;else if("0"<=b&&"9">=b||"."==b||"-"==b){c.type=4;var b=a.b,d=!1,e=!1;do{if("."==f)d=!0;else if("e"==f||"E"==f)e=!0;var f=a.a.charAt(++a.b)}while("0"<=f&&"9">=f||"."==f&&(void 0===d||!d)||!e&&("e"==f||"E"==f)||e&&("-"==f||"+"==f));a=parseFloat(a.a.substring(b,a.b--));c.value=a}else if("a"<=b&&"z">=b||"A"<=b&&"Z">=b){c.type=1;b=a.b;do f=a.a.charAt(++a.b);while("a"<=f&&"z">= -f||"A"<=f&&"Z">=f);a=a.a.substring(b,a.b--).toUpperCase();c.value=a}else{if(" "==b||"\t"==b||"\r"==b||"\n"==b)return Cr(a);if(""===b)c.type=6;else throw Error("Unexpected character: "+b);}return c}function yr(a){this.i=a;this.a="XY"}function Ar(a){a.b=Cr(a.i)}function Dr(a,b){(b=a.b.type==b)&&Ar(a);return b} -function Br(a){var b=a.b;if(Dr(a,1)){var b=b.value,c="XY",d=a.b;1==a.b.type&&(d=d.value,"Z"===d?c="XYZ":"M"===d?c="XYM":"ZM"===d&&(c="XYZM"),"XY"!==c&&Ar(a));a.a=c;if("GEOMETRYCOLLECTION"==b){a:{if(Dr(a,2)){b=[];do b.push(Br(a));while(Dr(a,5));if(Dr(a,3)){a=b;break a}}else if(Er(a)){a=[];break a}throw Error(Fr(a));}return new tm(a)}d=Gr[b];c=Hr[b];if(!d||!c)throw Error("Invalid geometry type: "+b);b=d.call(a);return new c(b,a.a)}throw Error(Fr(a));}k=yr.prototype; -k.tg=function(){if(Dr(this,2)){var a=Ir(this);if(Dr(this,3))return a}else if(Er(this))return null;throw Error(Fr(this));};k.sg=function(){if(Dr(this,2)){var a=Jr(this);if(Dr(this,3))return a}else if(Er(this))return[];throw Error(Fr(this));};k.ug=function(){if(Dr(this,2)){var a=Kr(this);if(Dr(this,3))return a}else if(Er(this))return[];throw Error(Fr(this));}; -k.fp=function(){if(Dr(this,2)){var a;if(2==this.b.type)for(a=[this.tg()];Dr(this,5);)a.push(this.tg());else a=Jr(this);if(Dr(this,3))return a}else if(Er(this))return[];throw Error(Fr(this));};k.ep=function(){if(Dr(this,2)){var a=Kr(this);if(Dr(this,3))return a}else if(Er(this))return[];throw Error(Fr(this));};k.gp=function(){if(Dr(this,2)){for(var a=[this.ug()];Dr(this,5);)a.push(this.ug());if(Dr(this,3))return a}else if(Er(this))return[];throw Error(Fr(this));}; -function Ir(a){for(var b=[],c=a.a.length,d=0;d<c;++d){var e=a.b;if(Dr(a,4))b.push(e.value);else break}if(b.length==c)return b;throw Error(Fr(a));}function Jr(a){for(var b=[Ir(a)];Dr(a,5);)b.push(Ir(a));return b}function Kr(a){for(var b=[a.sg()];Dr(a,5);)b.push(a.sg());return b}function Er(a){var b=1==a.b.type&&"EMPTY"==a.b.value;b&&Ar(a);return b}function Fr(a){return"Unexpected `"+a.b.value+"` at position "+a.b.position+" in `"+a.i.a+"`"} -var Hr={POINT:C,LINESTRING:O,POLYGON:D,MULTIPOINT:Q,MULTILINESTRING:P,MULTIPOLYGON:R},Gr={POINT:yr.prototype.tg,LINESTRING:yr.prototype.sg,POLYGON:yr.prototype.ug,MULTIPOINT:yr.prototype.fp,MULTILINESTRING:yr.prototype.ep,MULTIPOLYGON:yr.prototype.gp};function Lr(){this.version=void 0}v(Lr,uq);Lr.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};Lr.prototype.b=function(a){this.version=a.getAttribute("version").trim();return(a=N({version:this.version},Mr,a,[]))?a:null};function Nr(a,b){return N({},Or,a,b)}function Pr(a,b){return N({},Qr,a,b)}function Rr(a,b){if(b=Nr(a,b))return a=[Nm(a.getAttribute("width")),Nm(a.getAttribute("height"))],b.size=a,b} -function Sr(a,b){return N([],Tr,a,b)} -var Ur=[null,"http://www.opengis.net/wms"],Mr=K(Ur,{Service:I(function(a,b){return N({},Vr,a,b)}),Capability:I(function(a,b){return N({},Wr,a,b)})}),Wr=K(Ur,{Request:I(function(a,b){return N({},Xr,a,b)}),Exception:I(function(a,b){return N([],Yr,a,b)}),Layer:I(function(a,b){return N({},Zr,a,b)})}),Vr=K(Ur,{Name:I(S),Title:I(S),Abstract:I(S),KeywordList:I(Sr),OnlineResource:I(tq),ContactInformation:I(function(a,b){return N({},$r,a,b)}),Fees:I(S),AccessConstraints:I(S),LayerLimit:I(Mm),MaxWidth:I(Mm), -MaxHeight:I(Mm)}),$r=K(Ur,{ContactPersonPrimary:I(function(a,b){return N({},as,a,b)}),ContactPosition:I(S),ContactAddress:I(function(a,b){return N({},bs,a,b)}),ContactVoiceTelephone:I(S),ContactFacsimileTelephone:I(S),ContactElectronicMailAddress:I(S)}),as=K(Ur,{ContactPerson:I(S),ContactOrganization:I(S)}),bs=K(Ur,{AddressType:I(S),Address:I(S),City:I(S),StateOrProvince:I(S),PostCode:I(S),Country:I(S)}),Yr=K(Ur,{Format:rl(S)}),Zr=K(Ur,{Name:I(S),Title:I(S),Abstract:I(S),KeywordList:I(Sr),CRS:tl(S), -EX_GeographicBoundingBox:I(function(a,b){var c=N({},cs,a,b);if(c){a=c.westBoundLongitude;b=c.southBoundLatitude;var d=c.eastBoundLongitude,c=c.northBoundLatitude;if(void 0!==a&&void 0!==b&&void 0!==d&&void 0!==c)return[a,b,d,c]}}),BoundingBox:tl(function(a){var b=[Lm(a.getAttribute("minx")),Lm(a.getAttribute("miny")),Lm(a.getAttribute("maxx")),Lm(a.getAttribute("maxy"))],c=[Lm(a.getAttribute("resx")),Lm(a.getAttribute("resy"))];return{crs:a.getAttribute("CRS"),extent:b,res:c}}),Dimension:tl(function(a){return{name:a.getAttribute("name"), -units:a.getAttribute("units"),unitSymbol:a.getAttribute("unitSymbol"),"default":a.getAttribute("default"),multipleValues:Im(a.getAttribute("multipleValues")),nearestValue:Im(a.getAttribute("nearestValue")),current:Im(a.getAttribute("current")),values:S(a)}}),Attribution:I(function(a,b){return N({},ds,a,b)}),AuthorityURL:tl(function(a,b){if(b=Nr(a,b))return b.name=a.getAttribute("name"),b}),Identifier:tl(S),MetadataURL:tl(function(a,b){if(b=Nr(a,b))return b.type=a.getAttribute("type"),b}),DataURL:tl(Nr), -FeatureListURL:tl(Nr),Style:tl(function(a,b){return N({},es,a,b)}),MinScaleDenominator:I(Km),MaxScaleDenominator:I(Km),Layer:tl(function(a,b){var c=b[b.length-1],d=N({},Zr,a,b);if(d)return b=Im(a.getAttribute("queryable")),void 0===b&&(b=c.queryable),d.queryable=void 0!==b?b:!1,b=Nm(a.getAttribute("cascaded")),void 0===b&&(b=c.cascaded),d.cascaded=b,b=Im(a.getAttribute("opaque")),void 0===b&&(b=c.opaque),d.opaque=void 0!==b?b:!1,b=Im(a.getAttribute("noSubsets")),void 0===b&&(b=c.noSubsets),d.noSubsets= -void 0!==b?b:!1,(b=Lm(a.getAttribute("fixedWidth")))||(b=c.fixedWidth),d.fixedWidth=b,(a=Lm(a.getAttribute("fixedHeight")))||(a=c.fixedHeight),d.fixedHeight=a,["Style","CRS","AuthorityURL"].forEach(function(a){a in c&&(d[a]=(d[a]||[]).concat(c[a]))}),"EX_GeographicBoundingBox BoundingBox Dimension Attribution MinScaleDenominator MaxScaleDenominator".split(" ").forEach(function(a){a in d||(d[a]=c[a])}),d})}),ds=K(Ur,{Title:I(S),OnlineResource:I(tq),LogoURL:I(Rr)}),cs=K(Ur,{westBoundLongitude:I(Km), -eastBoundLongitude:I(Km),southBoundLatitude:I(Km),northBoundLatitude:I(Km)}),Xr=K(Ur,{GetCapabilities:I(Pr),GetMap:I(Pr),GetFeatureInfo:I(Pr)}),Qr=K(Ur,{Format:tl(S),DCPType:tl(function(a,b){return N({},fs,a,b)})}),fs=K(Ur,{HTTP:I(function(a,b){return N({},gs,a,b)})}),gs=K(Ur,{Get:I(Nr),Post:I(Nr)}),es=K(Ur,{Name:I(S),Title:I(S),Abstract:I(S),LegendURL:tl(Rr),StyleSheetURL:I(Nr),StyleURL:I(Nr)}),Or=K(Ur,{Format:I(S),OnlineResource:I(tq)}),Tr=K(Ur,{Keyword:rl(S)});function hs(a){a=a?a:{};this.a="http://mapserver.gis.umn.edu/mapserver";this.b=new an;this.c=a.layers?a.layers:null;Cm.call(this)}v(hs,Cm); -hs.prototype.zc=function(a,b){var c={};b&&tb(c,Fl(this,a,b));c=[c];a.setAttribute("namespaceURI",this.a);var d=a.localName;b=[];if(a.childNodes.length){if("msGMLOutput"==d)for(var e=0,f=a.childNodes.length;e<f;e++){var g=a.childNodes[e];if(g.nodeType===Node.ELEMENT_NODE){var h=c[0],l=g.localName.replace("_layer","");if(!this.c||ja(this.c,l)){l+="_feature";h.featureType=l;h.featureNS=this.a;var m={};m[l]=rl(this.b.wg,this.b);h=K([h.featureNS,null],m);g.setAttribute("namespaceURI",this.a);(g=N([],h, -g,c,this.b))&&la(b,g)}}}"FeatureCollection"==d&&(a=N([],this.b.b,a,[{}],this.b))&&(b=a)}return b};hs.prototype.Vg=function(){};hs.prototype.Xb=function(){};hs.prototype.ie=function(){};function is(){this.i=new vq}v(is,uq);is.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};is.prototype.b=function(a){var b=a.getAttribute("version").trim(),c=this.i.b(a);if(!c)return null;c.version=b;return(c=N(c,js,a,[]))?c:null};function ks(a){var b=S(a).split(" ");if(b&&2==b.length&&(a=+b[0],b=+b[1],!isNaN(a)&&!isNaN(b)))return[a,b]} -var ls=[null,"http://www.opengis.net/wmts/1.0"],ms=[null,"http://www.opengis.net/ows/1.1"],js=K(ls,{Contents:I(function(a,b){return N({},ns,a,b)})}),ns=K(ls,{Layer:tl(function(a,b){return N({},os,a,b)}),TileMatrixSet:tl(function(a,b){return N({},ps,a,b)})}),os=K(ls,{Style:tl(function(a,b){if(b=N({},qs,a,b))return a="true"===a.getAttribute("isDefault"),b.isDefault=a,b}),Format:tl(S),TileMatrixSetLink:tl(function(a,b){return N({},rs,a,b)}),Dimension:tl(function(a,b){return N({},ss,a,b)}),ResourceURL:tl(function(a){var b= -a.getAttribute("format"),c=a.getAttribute("template");a=a.getAttribute("resourceType");var d={};b&&(d.format=b);c&&(d.template=c);a&&(d.resourceType=a);return d})},K(ms,{Title:I(S),Abstract:I(S),WGS84BoundingBox:I(function(a,b){a=N([],ts,a,b);if(2==a.length)return Na(a)}),Identifier:I(S)})),qs=K(ls,{LegendURL:tl(function(a){var b={};b.format=a.getAttribute("format");b.href=tq(a);return b})},K(ms,{Title:I(S),Identifier:I(S)})),rs=K(ls,{TileMatrixSet:I(S),TileMatrixSetLimits:I(function(a,b){return N([], -us,a,b)})}),us=K(ls,{TileMatrixLimits:rl(function(a,b){return N({},vs,a,b)})}),vs=K(ls,{TileMatrix:I(S),MinTileRow:I(Mm),MaxTileRow:I(Mm),MinTileCol:I(Mm),MaxTileCol:I(Mm)}),ss=K(ls,{Default:I(S),Value:tl(S)},K(ms,{Identifier:I(S)})),ts=K(ms,{LowerCorner:rl(ks),UpperCorner:rl(ks)}),ps=K(ls,{WellKnownScaleSet:I(S),TileMatrix:tl(function(a,b){return N({},ws,a,b)})},K(ms,{SupportedCRS:I(S),Identifier:I(S)})),ws=K(ls,{TopLeftCorner:I(ks),ScaleDenominator:I(Km),TileWidth:I(Mm),TileHeight:I(Mm),MatrixWidth:I(Mm), -MatrixHeight:I(Mm)},K(ms,{Identifier:I(S)}));function xs(a){Tc.call(this);a=a||{};this.a=null;this.f=fc;this.c=void 0;y(this,Vc("projection"),this.Am,this);y(this,Vc("tracking"),this.Bm,this);void 0!==a.projection&&this.Wh(a.projection);void 0!==a.trackingOptions&&this.wj(a.trackingOptions);this.Ke(void 0!==a.tracking?a.tracking:!1)}v(xs,Tc);k=xs.prototype;k.ka=function(){this.Ke(!1);Tc.prototype.ka.call(this)};k.Am=function(){var a=this.Uh();a&&(this.f=Vb(Tb("EPSG:4326"),a),this.a&&this.set("position",this.f(this.a)))}; -k.Bm=function(){if(Wd){var a=this.Vh();a&&void 0===this.c?this.c=navigator.geolocation.watchPosition(this.np.bind(this),this.op.bind(this),this.Gh()):a||void 0===this.c||(navigator.geolocation.clearWatch(this.c),this.c=void 0)}}; -k.np=function(a){a=a.coords;this.set("accuracy",a.accuracy);this.set("altitude",null===a.altitude?void 0:a.altitude);this.set("altitudeAccuracy",null===a.altitudeAccuracy?void 0:a.altitudeAccuracy);this.set("heading",null===a.heading?void 0:Ha(a.heading));this.a?(this.a[0]=a.longitude,this.a[1]=a.latitude):this.a=[a.longitude,a.latitude];var b=this.f(this.a);this.set("position",b);this.set("speed",null===a.speed?void 0:a.speed);a=Xf(Jb,this.a,a.accuracy);a.Dc(this.f);this.set("accuracyGeometry",a); -this.s()};k.op=function(a){a.type="error";this.Ke(!1);this.b(a)};k.Dk=function(){return this.get("accuracy")};k.Ek=function(){return this.get("accuracyGeometry")||null};k.Gk=function(){return this.get("altitude")};k.Hk=function(){return this.get("altitudeAccuracy")};k.ym=function(){return this.get("heading")};k.zm=function(){return this.get("position")};k.Uh=function(){return this.get("projection")};k.ll=function(){return this.get("speed")};k.Vh=function(){return this.get("tracking")};k.Gh=function(){return this.get("trackingOptions")}; -k.Wh=function(a){this.set("projection",Tb(a))};k.Ke=function(a){this.set("tracking",a)};k.wj=function(a){this.set("trackingOptions",a)};function ys(a,b,c){rf.call(this);this.Ng(a,b?b:0,c)}v(ys,rf);k=ys.prototype;k.clone=function(){var a=new ys(null);tf(a,this.ja,this.A.slice());a.s();return a};k.Kb=function(a,b,c,d){var e=this.A;a-=e[0];var f=b-e[1];b=a*a+f*f;if(b<d){if(b)for(d=this.pd()/Math.sqrt(b),c[0]=e[0]+d*a,c[1]=e[1]+d*f,d=2;d<this.a;++d)c[d]=e[d];else for(d=0;d<this.a;++d)c[d]=e[d];c.length=this.a;return b}return d};k.Mc=function(a,b){var c=this.A;a-=c[0];b-=c[1];return a*a+b*b<=zs(this)}; -k.wa=function(){return this.A.slice(0,this.a)};k.se=function(a){var b=this.A,c=b[this.a]-b[0];return Xa(b[0]-c,b[1]-c,b[0]+c,b[1]+c,a)};k.pd=function(){return Math.sqrt(zs(this))};function zs(a){var b=a.A[a.a]-a.A[0];a=a.A[a.a+1]-a.A[1];return b*b+a*a}k.U=function(){return"Circle"};k.Xa=function(a){var b=this.G();return qb(a,b)?(b=this.wa(),a[0]<=b[0]&&a[2]>=b[0]||a[1]<=b[1]&&a[3]>=b[1]?!0:db(a,this.sb,this)):!1}; -k.ob=function(a){var b=this.a,c=a.slice();c[b]=c[0]+(this.A[b]-this.A[0]);var d;for(d=1;d<b;++d)c[b+d]=a[d];tf(this,this.ja,c);this.s()};k.Ng=function(a,b,c){if(a){uf(this,c,a,0);this.A||(this.A=[]);c=this.A;a=Cf(c,a);c[a++]=c[0]+b;var d;b=1;for(d=this.a;b<d;++b)c[a++]=c[b];c.length=a}else tf(this,"XY",null);this.s()};k.X=function(){};k.ma=function(){};k.Uc=function(a){this.A[this.a]=this.A[0]+a;this.s()};function As(a,b,c){for(var d=[],e=a(0),f=a(1),g=b(e),h=b(f),l=[f,e],m=[h,g],n=[1,0],p={},q=1E5,r,u,x,B,E;0<--q&&0<n.length;)x=n.pop(),e=l.pop(),g=m.pop(),f=x.toString(),f in p||(d.push(g[0],g[1]),p[f]=!0),B=n.pop(),f=l.pop(),h=m.pop(),E=(x+B)/2,r=a(E),u=b(r),Fa(u[0],u[1],g[0],g[1],h[0],h[1])<c?(d.push(h[0],h[1]),f=B.toString(),p[f]=!0):(n.push(B,E,E,x),m.push(h,u,u,g),l.push(f,r,r,e));return d}function Bs(a,b,c,d,e){var f=Tb("EPSG:4326");return As(function(d){return[a,b+(c-b)*d]},ec(f,d),e)} -function Cs(a,b,c,d,e){var f=Tb("EPSG:4326");return As(function(d){return[b+(c-b)*d,a]},ec(f,d),e)};function Ds(a){a=a||{};this.j=this.v=null;this.f=this.o=Infinity;this.g=this.l=-Infinity;this.ra=this.oa=Infinity;this.R=this.I=-Infinity;this.Jb=void 0!==a.targetSize?a.targetSize:100;this.fb=void 0!==a.maxLines?a.maxLines:100;this.i=[];this.c=[];this.pa=void 0!==a.strokeStyle?a.strokeStyle:Es;this.D=this.u=void 0;this.a=this.b=this.S=null;1==a.showLabels&&(this.na=a.lonLabelFormatter?a.lonLabelFormatter:bf.bind(this,"EW"),this.Ua=a.latLabelFormatter?a.latLabelFormatter:bf.bind(this,"NS"),this.fa= -void 0==a.lonLabelPosition?0:a.lonLabelPosition,this.T=void 0==a.latLabelPosition?1:a.latLabelPosition,this.B=void 0!==a.lonLabelStyle?a.lonLabelStyle:new fo({font:"12px Calibri,sans-serif",textBaseline:"bottom",fill:new al({color:"rgba(0,0,0,1)"}),stroke:new wj({color:"rgba(255,255,255,1)",width:3})}),this.C=void 0!==a.latLabelStyle?a.latLabelStyle:new fo({font:"12px Calibri,sans-serif",textAlign:"end",fill:new al({color:"rgba(0,0,0,1)"}),stroke:new wj({color:"rgba(255,255,255,1)",width:3})}),this.b= -[],this.a=[]);this.setMap(void 0!==a.map?a.map:null)}var Es=new wj({color:"rgba(0,0,0,0.2)"}),Fs=[90,45,30,20,10,5,2,1,.5,.2,.1,.05,.01,.005,.002,.001];function Gs(a,b,c,d,e,f,g){var h=g;c=Bs(b,c,d,a.j,e);h=void 0!==a.i[h]?a.i[h]:new O(null);h.ba("XY",c);qb(h.G(),f)&&(a.b&&(c=g,d=h.ga(),f=[d[0],Ca(f[1]+Math.abs(f[1]-f[3])*a.fa,Math.max(f[1],d[1]),Math.min(f[3],d[d.length-1]))],c=a.b[c]?a.b[c].Qd:new C(null),c.ma(f),a.b[g]={Qd:c,text:a.na(b)}),a.i[g++]=h);return g} -function Hs(a,b,c,d,e){var f=e;c=Cs(b,a.g,a.f,a.j,c);f=void 0!==a.c[f]?a.c[f]:new O(null);f.ba("XY",c);if(qb(f.G(),d)){if(a.a){c=e;var g=f.ga();d=[Ca(d[0]+Math.abs(d[0]-d[2])*a.T,Math.max(d[0],g[0]),Math.min(d[2],g[g.length-2])),g[1]];c=a.a[c]?a.a[c].Qd:new C(null);c.ma(d);a.a[e]={Qd:c,text:a.Ua(b)}}a.c[e++]=f}return e}k=Ds.prototype;k.Cm=function(){return this.v};k.al=function(){return this.i};k.hl=function(){return this.c}; -k.Kh=function(a){var b=a.vectorContext,c=a.frameState,d=c.extent;a=c.viewState;var e=a.center,f=a.projection,g=a.resolution;a=c.pixelRatio;a=g*g/(4*a*a);if(!this.j||!dc(this.j,f)){var h=Tb("EPSG:4326"),l=f.G(),m=f.g,n=hc(m,h,f),p=m[2],q=m[1],r=m[0],u=n[3],x=n[2],B=n[1],n=n[0];this.o=m[3];this.f=p;this.l=q;this.g=r;this.oa=u;this.ra=x;this.I=B;this.R=n;this.u=ec(h,f);this.D=ec(f,h);this.S=this.D(nb(l));this.j=f}f.i&&(f=f.G(),h=lb(f),c=c.focus[0],c<f[0]||c>f[2])&&(c=h*Math.ceil((f[0]-c)/h),d=[d[0]+ -c,d[1],d[2]+c,d[3]]);c=this.S[0];f=this.S[1];h=-1;m=Math.pow(this.Jb*g,2);p=[];q=[];g=0;for(l=Fs.length;g<l;++g){r=Fs[g]/2;p[0]=c-r;p[1]=f-r;q[0]=c+r;q[1]=f+r;this.u(p,p);this.u(q,q);r=Math.pow(q[0]-p[0],2)+Math.pow(q[1]-p[1],2);if(r<=m)break;h=Fs[g]}g=h;if(-1==g)this.i.length=this.c.length=0,this.b&&(this.b.length=0),this.a&&(this.a.length=0);else{c=this.D(e);e=c[0];c=c[1];f=this.fb;h=[Math.max(d[0],this.R),Math.max(d[1],this.I),Math.min(d[2],this.ra),Math.min(d[3],this.oa)];h=hc(h,this.j,"EPSG:4326"); -m=h[3];q=h[1];e=Math.floor(e/g)*g;p=Ca(e,this.g,this.f);l=Gs(this,p,q,m,a,d,0);for(h=0;p!=this.g&&h++<f;)p=Math.max(p-g,this.g),l=Gs(this,p,q,m,a,d,l);p=Ca(e,this.g,this.f);for(h=0;p!=this.f&&h++<f;)p=Math.min(p+g,this.f),l=Gs(this,p,q,m,a,d,l);this.i.length=l;this.b&&(this.b.length=l);c=Math.floor(c/g)*g;e=Ca(c,this.l,this.o);l=Hs(this,e,a,d,0);for(h=0;e!=this.l&&h++<f;)e=Math.max(e-g,this.l),l=Hs(this,e,a,d,l);e=Ca(c,this.l,this.o);for(h=0;e!=this.o&&h++<f;)e=Math.min(e+g,this.o),l=Hs(this,e,a, -d,l);this.c.length=l;this.a&&(this.a.length=l)}b.Ma(null,this.pa);a=0;for(e=this.i.length;a<e;++a)g=this.i[a],b.zb(g);a=0;for(e=this.c.length;a<e;++a)g=this.c[a],b.zb(g);if(this.b)for(a=0,e=this.b.length;a<e;++a)g=this.b[a],this.B.xd(g.text),b.Cb(this.B),b.zb(g.Qd);if(this.a)for(a=0,e=this.a.length;a<e;++a)g=this.a[a],this.C.xd(g.text),b.Cb(this.C),b.zb(g.Qd)}; -k.setMap=function(a){this.v&&(this.v.K("postcompose",this.Kh,this),this.v.render());a&&(a.J("postcompose",this.Kh,this),a.render());this.v=a};function Is(a,b,c,d,e){Qc.call(this);this.f=e;this.extent=a;this.a=c;this.resolution=b;this.state=d}v(Is,Qc);Is.prototype.s=function(){this.b("change")};Is.prototype.G=function(){return this.extent};Is.prototype.getState=function(){return this.state};function Js(a,b,c,d,e,f,g){Is.call(this,a,b,c,0,d);this.j=e;this.M=new Image;null!==f&&(this.M.crossOrigin=f);this.c={};this.i=null;this.state=0;this.g=g}v(Js,Is);k=Js.prototype;k.Y=function(a){if(void 0!==a){var b;a=w(a);if(a in this.c)return this.c[a];wb(this.c)?b=this.M:b=this.M.cloneNode(!1);return this.c[a]=b}return this.M};k.Fm=function(){this.state=3;this.i.forEach(Ec);this.i=null;this.s()}; -k.Gm=function(){void 0===this.resolution&&(this.resolution=mb(this.extent)/this.M.height);this.state=2;this.i.forEach(Ec);this.i=null;this.s()};k.load=function(){if(0==this.state||3==this.state)this.state=1,this.s(),this.i=[Jc(this.M,"error",this.Fm,this),Jc(this.M,"load",this.Gm,this)],this.g(this,this.j)};k.Og=function(a){this.M=a};function Ks(a,b,c,d,e,f){this.c=f?f:null;Is.call(this,a,b,c,f?0:2,d);this.i=e}v(Ks,Is);Ks.prototype.g=function(a){this.state=a?3:2;this.s()};Ks.prototype.load=function(){0==this.state&&(this.state=1,this.s(),this.c(this.g.bind(this)))};Ks.prototype.Y=function(){return this.i};function Ls(a,b){Qc.call(this);this.ta=a;this.state=b;this.i=null;this.key=""}v(Ls,Qc);Ls.prototype.s=function(){this.b("change")};Ls.prototype.bb=function(){return this.key+"/"+this.ta};function Ms(a){if(!a.i)return a;var b=a.i;do{if(2==b.getState())return b;b=b.i}while(b);return a}Ls.prototype.f=function(){return this.ta};Ls.prototype.getState=function(){return this.state};function Ns(a,b){a.state=b;a.s()};function Os(a,b,c,d,e){Ls.call(this,a,b);this.g=c;this.M=new Image;null!==d&&(this.M.crossOrigin=d);this.c=null;this.j=e}v(Os,Ls);k=Os.prototype;k.ka=function(){1==this.state&&Ps(this);this.i&&Nc(this.i);this.state=5;this.s();Ls.prototype.ka.call(this)};k.Y=function(){return this.M};k.bb=function(){return this.g};k.Dm=function(){this.state=3;this.M=Qs;Ps(this);this.s()};k.Em=function(){this.state=this.M.naturalWidth&&this.M.naturalHeight?2:4;Ps(this);this.s()}; -k.load=function(){if(0==this.state||3==this.state)this.state=1,this.s(),this.c=[Jc(this.M,"error",this.Dm,this),Jc(this.M,"load",this.Em,this)],this.j(this,this.g)};function Ps(a){a.c.forEach(Ec);a.c=null}var Qs=new Image;Qs.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";function Rs(a){a=a?a:{};ng.call(this,{handleEvent:mf});this.g=a.formatConstructors?a.formatConstructors:[];this.o=a.projection?Tb(a.projection):null;this.a=null;this.target=a.target?a.target:null}v(Rs,ng);function Ss(a){a=a.dataTransfer.files;var b;var c=0;for(b=a.length;c<b;++c){var d=a.item(c);var e=new FileReader;e.addEventListener("load",this.j.bind(this,d));e.readAsText(d)}}function Ts(a){a.stopPropagation();a.preventDefault();a.dataTransfer.dropEffect="copy"} -Rs.prototype.j=function(a,b){b=b.target.result;var c=this.v,d=this.o;d||(d=c.Z().v);var c=this.g,e=[],f;var g=0;for(f=c.length;g<f;++g){var h=new c[g];var l={featureProjection:d};try{e=h.Oa(b,l)}catch(m){e=null}if(e&&0<e.length)break}this.b(new Us(Vs,a,e,d))};function Ws(a){var b=a.v;b&&(b=a.target?a.target:b.a,a.a=[y(b,"drop",Ss,a),y(b,"dragenter",Ts,a),y(b,"dragover",Ts,a),y(b,"drop",Ts,a)])}Rs.prototype.Ha=function(a){ng.prototype.Ha.call(this,a);a?Ws(this):Xs(this)}; -Rs.prototype.setMap=function(a){Xs(this);ng.prototype.setMap.call(this,a);this.c()&&Ws(this)};function Xs(a){a.a&&(a.a.forEach(Ec),a.a=null)}var Vs="addfeatures";function Us(a,b,c,d){Oc.call(this,a);this.features=c;this.file=b;this.projection=d}v(Us,Oc);function Ys(a){a=a?a:{};Dg.call(this,{handleDownEvent:Zs,handleDragEvent:$s,handleUpEvent:at});this.l=a.condition?a.condition:yg;this.a=this.g=void 0;this.j=0;this.u=void 0!==a.duration?a.duration:400}v(Ys,Dg); -function $s(a){if(Bg(a)){var b=a.map,c=b.Ob(),d=a.pixel;a=d[0]-c[0]/2;d=c[1]/2-d[1];c=Math.atan2(d,a);a=Math.sqrt(a*a+d*d);b=b.Z();b.g.rotation!==Te&&void 0!==this.g&&(d=c-this.g,og(b,b.Qa()-d));this.g=c;void 0!==this.a&&(c=this.a*(b.Pa()/a),qg(b,c));void 0!==this.a&&(this.j=this.a/a);this.a=a}} -function at(a){if(!Bg(a))return!0;a=a.map.Z();cg(a,1,-1);var b=this.j-1,c=a.Qa(),c=a.constrainRotation(c,0);og(a,c,void 0,void 0);var c=a.Pa(),d=this.u,c=a.constrainResolution(c,0,b);qg(a,c,void 0,d);this.j=0;return!1}function Zs(a){return Bg(a)&&this.l(a)?(cg(a.map.Z(),1,1),this.a=this.g=void 0,!0):!1};function bt(a,b,c,d){this.fb=a;this.Ua=b;this.overlaps=d;this.c=0;this.resolution=c;this.ra=this.oa=null;this.a=[];this.coordinates=[];this.T=Bh();this.b=[];this.B=null;this.fa=Bh();this.na=Bh()}v(bt,Wh); -function ct(a,b,c,d,e,f,g){var h=a.coordinates.length,l=a.Sf();g&&(c+=e);g=[b[c],b[c+1]];var m=[NaN,NaN],n=!0,p;for(p=c+e;p<d;p+=e){m[0]=b[p];m[1]=b[p+1];var q=Wa(l,m);q!==r?(n&&(a.coordinates[h++]=g[0],a.coordinates[h++]=g[1]),a.coordinates[h++]=m[0],a.coordinates[h++]=m[1],n=!1):1===q?(a.coordinates[h++]=m[0],a.coordinates[h++]=m[1],n=!1):n=!0;g[0]=m[0];g[1]=m[1];var r=q}if(f&&n||p===c+e)a.coordinates[h++]=g[0],a.coordinates[h++]=g[1];return h} -function dt(a,b){a.oa=[0,b,0];a.a.push(a.oa);a.ra=[0,b,0];a.b.push(a.ra)}bt.prototype.Va=function(a,b){if(this.R){var c=Gh(this.T,this.R.slice());a.translate(c[0],c[1]);a.rotate(b)}a.fill();this.R&&a.setTransform.apply(a,this.na)}; -function et(a,b,c,d,e,f,g,h,l){if(a.B&&pa(d,a.T))var m=a.B;else a.B||(a.B=[]),m=pf(a.coordinates,0,a.coordinates.length,2,d,a.B),Fh(a.T,d);d=!wb(f);for(var n=0,p=g.length,q=0,r,u=a.fa,x=a.na,B,E,A,L,oa=0,ha=0,ga=a.a!=g||a.overlaps?0:200;n<p;){var z=g[n];switch(z[0]){case 0:q=z[1];d&&f[w(q).toString()]||!q.V()?n=z[2]:void 0===l||qb(l,q.V().G())?++n:n=z[2]+1;break;case 1:oa>ga&&(a.Va(b,e),oa=0);ha>ga&&(b.stroke(),ha=0);oa||ha||(b.beginPath(),B=E=NaN);++n;break;case 2:q=z[1];r=m[q];z=m[q+1];A=m[q+2]- -r;q=m[q+3]-z;q=Math.sqrt(A*A+q*q);b.moveTo(r+q,z);b.arc(r,z,q,0,2*Math.PI,!0);++n;break;case 3:b.closePath();++n;break;case 4:q=z[1];r=z[2];var M=z[3];var ba=z[4]*c;var da=z[5]*c;var fb=z[6],ca=z[7],Ub=z[8],uc=z[9];var bc=z[10];A=z[11];L=z[12];var Je=z[13],zg=z[14];for(bc&&(A+=e);q<r;q+=2){z=m[q]-ba;bc=m[q+1]-da;Je&&(z=Math.round(z),bc=Math.round(bc));if(1!=L||A){var ff=z+ba,rh=bc+da;Kh(u,ff,rh,L,L,A,-ff,-rh);b.setTransform.apply(b,u)}ff=b.globalAlpha;1!=ca&&(b.globalAlpha=ff*ca);var rh=zg+Ub>M.width? -M.width-Ub:zg,Bq=fb+uc>M.height?M.height-uc:fb;b.drawImage(M,Ub,uc,rh,Bq,z,bc,rh*c,Bq*c);1!=ca&&(b.globalAlpha=ff);(1!=L||A)&&b.setTransform.apply(b,x)}++n;break;case 5:q=z[1];r=z[2];da=z[3];fb=z[4]*c;ca=z[5]*c;A=z[6];L=z[7]*c;M=z[8];ba=z[9];for((bc=z[10])&&(A+=e);q<r;q+=2){z=m[q]+fb;bc=m[q+1]+ca;if(1!=L||A)Kh(u,z,bc,L,L,A,-z,-bc),b.setTransform.apply(b,u);Ub=da.split("\n");uc=Ub.length;1<uc?(Je=Math.round(1.5*b.measureText("M").width),bc-=(uc-1)/2*Je):Je=0;for(zg=0;zg<uc;zg++)ff=Ub[zg],ba&&b.strokeText(ff, -z,bc),M&&b.fillText(ff,z,bc),bc+=Je;(1!=L||A)&&b.setTransform.apply(b,x)}++n;break;case 6:if(h&&(q=z[1],q=h(q)))return q;++n;break;case 7:ga?oa++:a.Va(b,e);++n;break;case 8:q=z[1];r=z[2];z=m[q];bc=m[q+1];A=z+.5|0;L=bc+.5|0;if(A!==B||L!==E)b.moveTo(z,bc),B=A,E=L;for(q+=2;q<r;q+=2)if(z=m[q],bc=m[q+1],A=z+.5|0,L=bc+.5|0,q==r-2||A!==B||L!==E)b.lineTo(z,bc),B=A,E=L;++n;break;case 9:a.R=z[2];oa&&(a.Va(b,e),oa=0,ha&&(b.stroke(),ha=0));b.fillStyle=z[1];++n;break;case 10:var q=void 0!==z[8]?z[8]:!0,ul=z[9]; -r=z[2];ha&&(b.stroke(),ha=0);b.strokeStyle=z[1];b.lineWidth=q?r*c:r;b.lineCap=z[3];b.lineJoin=z[4];b.miterLimit=z[5];Td&&(r=z[6],A=z[7],q&&c!==ul&&(r=r.map(function(a){return a*c/ul}),A*=c/ul,z[6]=r,z[7]=A,z[9]=c),b.lineDashOffset=A,b.setLineDash(r));++n;break;case 11:b.font=z[1];b.textAlign=z[2];b.textBaseline=z[3];++n;break;case 12:ga?ha++:b.stroke();++n;break;default:++n}}oa&&a.Va(b,e);ha&&b.stroke()}bt.prototype.La=function(a,b,c,d,e){et(this,a,b,c,d,e,this.a,void 0,void 0)}; -function ft(a){var b=a.b;b.reverse();var c,d=b.length,e=-1;for(c=0;c<d;++c){var f=b[c];var g=f[0];if(6==g)e=c;else if(0==g){f[2]=c;f=a.b;for(g=c;e<g;){var h=f[e];f[e]=f[g];f[g]=h;++e;--g}e=-1}}}function gt(a,b){a.oa[2]=a.a.length;a.oa=null;a.ra[2]=a.b.length;a.ra=null;b=[6,b];a.a.push(b);a.b.push(b)}bt.prototype.Te=ua;bt.prototype.Sf=function(){return this.Ua};function ht(a,b,c,d){bt.call(this,a,b,c,d);this.M=this.I=null;this.C=this.D=this.S=this.u=this.v=this.l=this.o=this.j=this.g=this.f=this.i=void 0}v(ht,bt); -ht.prototype.qc=function(a,b){if(this.M){dt(this,b);var c=a.ga(),d=this.coordinates.length;a=ct(this,c,0,c.length,a.qa(),!1,!1);this.a.push([4,d,a,this.M,this.i,this.f,this.g,this.j,this.o,this.l,this.v,this.u,this.S,this.D,this.C]);this.b.push([4,d,a,this.I,this.i,this.f,this.g,this.j,this.o,this.l,this.v,this.u,this.S,this.D,this.C]);gt(this,b)}}; -ht.prototype.oc=function(a,b){if(this.M){dt(this,b);var c=a.ga(),d=this.coordinates.length;a=ct(this,c,0,c.length,a.qa(),!1,!1);this.a.push([4,d,a,this.M,this.i,this.f,this.g,this.j,this.o,this.l,this.v,this.u,this.S,this.D,this.C]);this.b.push([4,d,a,this.I,this.i,this.f,this.g,this.j,this.o,this.l,this.v,this.u,this.S,this.D,this.C]);gt(this,b)}};ht.prototype.Te=function(){ft(this);this.f=this.i=void 0;this.M=this.I=null;this.C=this.D=this.u=this.v=this.l=this.o=this.j=this.S=this.g=void 0}; -ht.prototype.Ub=function(a){var b=a.Hc(),c=a.ic(),d=a.qg(1),e=a.Y(1),f=a.Oc();this.i=b[0];this.f=b[1];this.I=d;this.M=e;this.g=c[1];this.j=a.f;this.o=f[0];this.l=f[1];this.v=a.l;this.u=a.g;this.S=a.a;this.D=a.v;this.C=c[0]};function it(a,b,c,d){bt.call(this,a,b,c,d);this.f=null;this.i={Md:void 0,Gd:void 0,Hd:null,Id:void 0,Jd:void 0,Kd:void 0,Ld:void 0,eg:0,strokeStyle:void 0,lineCap:void 0,lineDash:null,lineDashOffset:void 0,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0}}v(it,bt);function jt(a,b,c,d,e){var f=a.coordinates.length;b=ct(a,b,c,d,e,!1,!1);f=[8,f,b];a.a.push(f);a.b.push(f);return d}k=it.prototype;k.Sf=function(){this.f||(this.f=Ra(this.Ua),0<this.c&&Qa(this.f,this.resolution*(this.c+1)/2,this.f));return this.f}; -function kt(a){var b=a.i,c=b.strokeStyle,d=b.lineCap,e=b.lineDash,f=b.lineDashOffset,g=b.lineJoin,h=b.lineWidth,l=b.miterLimit;b.Md==c&&b.Gd==d&&pa(b.Hd,e)&&b.Id==f&&b.Jd==g&&b.Kd==h&&b.Ld==l||(b.eg!=a.coordinates.length&&(a.a.push([12]),b.eg=a.coordinates.length),a.a.push([10,c,h,d,g,l,e,f,!0,1],[1]),b.Md=c,b.Gd=d,b.Hd=e,b.Id=f,b.Jd=g,b.Kd=h,b.Ld=l)} -k.mc=function(a,b){var c=this.i,d=c.lineWidth;void 0!==c.strokeStyle&&void 0!==d&&(kt(this),dt(this,b),this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,c.lineDashOffset,!0,1],[1]),c=a.ga(),jt(this,c,0,c.length,a.qa()),this.b.push([12]),gt(this,b))}; -k.nc=function(a,b){var c=this.i,d=c.lineWidth;if(void 0!==c.strokeStyle&&void 0!==d){kt(this);dt(this,b);this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,c.lineDashOffset,!0,1],[1]);c=a.Bb();d=a.ga();a=a.qa();var e=0,f;var g=0;for(f=c.length;g<f;++g)e=jt(this,d,e,c[g],a);this.b.push([12]);gt(this,b)}};k.Te=function(){this.i.eg!=this.coordinates.length&&this.a.push([12]);ft(this);this.i=null}; -k.Ma=function(a,b){a=b.a;this.i.strokeStyle=id(a?a:Uh);a=b.f;this.i.lineCap=void 0!==a?a:"round";a=b.i;this.i.lineDash=a?a:Th;a=b.g;this.i.lineDashOffset=a?a:0;a=b.j;this.i.lineJoin=void 0!==a?a:"round";a=b.c;this.i.lineWidth=void 0!==a?a:1;b=b.o;this.i.miterLimit=void 0!==b?b:10;this.i.lineWidth>this.c&&(this.c=this.i.lineWidth,this.f=null)};function lt(a,b,c,d){bt.call(this,a,b,c,d);this.f=null;this.i={oh:void 0,Md:void 0,Gd:void 0,Hd:null,Id:void 0,Jd:void 0,Kd:void 0,Ld:void 0,fillStyle:void 0,strokeStyle:void 0,lineCap:void 0,lineDash:null,lineDashOffset:void 0,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0}}v(lt,bt); -function mt(a,b,c,d,e){var f=a.i,g=void 0!==f.fillStyle,f=void 0!=f.strokeStyle,h=d.length,l=[1];a.a.push(l);a.b.push(l);for(l=0;l<h;++l){var m=d[l],n=a.coordinates.length;c=ct(a,b,c,m,e,!0,!f);c=[8,n,c];a.a.push(c);a.b.push(c);f&&(c=[3],a.a.push(c),a.b.push(c));c=m}b=[7];a.b.push(b);g&&a.a.push(b);f&&(g=[12],a.a.push(g),a.b.push(g));return c}k=lt.prototype; -k.Zb=function(a,b){var c=this.i,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d){nt(this,a);dt(this,b);this.b.push([9,gd(Sh)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,c.lineDashOffset,!0,1]);var e=a.ga(),d=this.coordinates.length;ct(this,e,0,e.length,a.qa(),!1,!1);a=[1];d=[2,d];this.a.push(a,d);this.b.push(a,d);a=[7];this.b.push(a);void 0!==c.fillStyle&&this.a.push(a);void 0!==c.strokeStyle&&(c=[12],this.a.push(c),this.b.push(c)); -gt(this,b)}};k.rc=function(a,b){var c=this.i;nt(this,a);dt(this,b);this.b.push([9,gd(Sh)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,c.lineDashOffset,!0,1]);var c=a.Bb(),d=a.ec();mt(this,d,0,c,a.qa());gt(this,b)}; -k.pc=function(a,b){var c=this.i,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d){nt(this,a);dt(this,b);this.b.push([9,gd(Sh)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash,c.lineDashOffset,!0,1]);c=a.c;d=fi(a);a=a.qa();var e=0,f;var g=0;for(f=c.length;g<f;++g)e=mt(this,d,e,c[g],a);gt(this,b)}}; -k.Te=function(){ft(this);this.i=null;var a=this.fb;if(a){var b=this.coordinates,c;var d=0;for(c=b.length;d<c;++d)b[d]=a*Math.round(b[d]/a)}};k.Sf=function(){this.f||(this.f=Ra(this.Ua),0<this.c&&Qa(this.f,this.resolution*(this.c+1)/2,this.f));return this.f}; -k.Ma=function(a,b){var c=this.i;a?(a=a.b,c.fillStyle=id(a?a:Sh)):c.fillStyle=void 0;b?(a=b.a,c.strokeStyle=id(a?a:Uh),a=b.f,c.lineCap=void 0!==a?a:"round",a=b.i,c.lineDash=a?a.slice():Th,a=b.g,c.lineDashOffset=a?a:0,a=b.j,c.lineJoin=void 0!==a?a:"round",a=b.c,c.lineWidth=void 0!==a?a:1,b=b.o,c.miterLimit=void 0!==b?b:10,c.lineWidth>this.c&&(this.c=c.lineWidth,this.f=null)):(c.strokeStyle=void 0,c.lineCap=void 0,c.lineDash=null,c.lineDashOffset=void 0,c.lineJoin=void 0,c.lineWidth=void 0,c.miterLimit= -void 0)};function nt(a,b){var c=a.i,d=c.fillStyle,e=c.strokeStyle,f=c.lineCap,g=c.lineDash,h=c.lineDashOffset,l=c.lineJoin,m=c.lineWidth,n=c.miterLimit;if(void 0!==d&&("string"!==typeof d||c.oh!=d)){var p=[9,d];"string"!==typeof d&&(b=b.G(),p.push([b[0],b[3]]));a.a.push(p);c.oh=c.fillStyle}void 0===e||c.Md==e&&c.Gd==f&&pa(c.Hd,g)&&c.Id==h&&c.Jd==l&&c.Kd==m&&c.Ld==n||(a.a.push([10,e,m,f,l,n,g,h,!0,1]),c.Md=e,c.Gd=f,c.Hd=g,c.Id=h,c.Jd=l,c.Kd=m,c.Ld=n)};function ot(a,b,c,d){bt.call(this,a,b,c,d);this.C=this.D=this.S=null;this.Ia="";this.o=this.j=0;this.l=void 0;this.u=this.v=0;this.g=this.f=this.i=null}v(ot,bt); -ot.prototype.yc=function(a,b,c,d,e,f){if(""!==this.Ia&&this.g&&(this.i||this.f)){if(this.i){e=this.i;var g=this.S;if(!g||g.fillStyle!=e.fillStyle){var h=[9,e.fillStyle];this.a.push(h);this.b.push(h);g?g.fillStyle=e.fillStyle:this.S={fillStyle:e.fillStyle}}}this.f&&(e=this.f,g=this.D,g&&g.lineCap==e.lineCap&&g.lineDash==e.lineDash&&g.lineDashOffset==e.lineDashOffset&&g.lineJoin==e.lineJoin&&g.lineWidth==e.lineWidth&&g.miterLimit==e.miterLimit&&g.strokeStyle==e.strokeStyle||(h=[10,e.strokeStyle,e.lineWidth, -e.lineCap,e.lineJoin,e.miterLimit,e.lineDash,e.lineDashOffset,!1,1],this.a.push(h),this.b.push(h),g?(g.lineCap=e.lineCap,g.lineDash=e.lineDash,g.lineDashOffset=e.lineDashOffset,g.lineJoin=e.lineJoin,g.lineWidth=e.lineWidth,g.miterLimit=e.miterLimit,g.strokeStyle=e.strokeStyle):this.D={lineCap:e.lineCap,lineDash:e.lineDash,lineDashOffset:e.lineDashOffset,lineJoin:e.lineJoin,lineWidth:e.lineWidth,miterLimit:e.miterLimit,strokeStyle:e.strokeStyle}));e=this.g;g=this.C;g&&g.font==e.font&&g.textAlign== -e.textAlign&&g.textBaseline==e.textBaseline||(h=[11,e.font,e.textAlign,e.textBaseline],this.a.push(h),this.b.push(h),g?(g.font=e.font,g.textAlign=e.textAlign,g.textBaseline=e.textBaseline):this.C={font:e.font,textAlign:e.textAlign,textBaseline:e.textBaseline});dt(this,f);e=this.coordinates.length;a=ct(this,a,b,c,d,!1,!1);a=[5,e,a,this.Ia,this.j,this.o,this.v,this.u,!!this.i,!!this.f,this.l];this.a.push(a);this.b.push(a);gt(this,f)}}; -ot.prototype.Cb=function(a){if(a){var b=a.Fa();b?(b=b.b,b=id(b?b:Sh),this.i?this.i.fillStyle=b:this.i={fillStyle:b}):this.i=null;var c=a.Ga();if(c){var b=c.a,d=c.f,e=c.i,f=c.g,g=c.j,h=c.c,c=c.o,d=void 0!==d?d:"round",e=e?e.slice():Th,f=void 0!==f?f:0,g=void 0!==g?g:"round",h=void 0!==h?h:1,c=void 0!==c?c:10,b=id(b?b:Uh);if(this.f){var l=this.f;l.lineCap=d;l.lineDash=e;l.lineDashOffset=f;l.lineJoin=g;l.lineWidth=h;l.miterLimit=c;l.strokeStyle=b}else this.f={lineCap:d,lineDash:e,lineDashOffset:f,lineJoin:g, -lineWidth:h,miterLimit:c,strokeStyle:b}}else this.f=null;var m=a.a,b=a.i,d=a.c,e=a.o,h=a.f,c=a.b,f=a.Na(),g=a.g,l=a.j;a=void 0!==m?m:"10px sans-serif";g=void 0!==g?g:"center";l=void 0!==l?l:"middle";this.g?(m=this.g,m.font=a,m.textAlign=g,m.textBaseline=l):this.g={font:a,textAlign:g,textBaseline:l};this.Ia=void 0!==f?f:"";this.j=void 0!==b?b:0;this.o=void 0!==d?d:0;this.l=void 0!==e?e:!1;this.v=void 0!==h?h:0;this.u=void 0!==c?c:1}else this.Ia=""};function pt(a,b,c,d,e){this.v=a;this.c=b;this.o=d;this.l=c;this.f=e;this.a={};this.g=jd(1,1);this.j=Bh()}v(pt,ki);var qt={0:[[!0]]};function rt(a,b,c){var d,e=Math.floor(a.length/2);if(b>=e)for(d=e;d<b;d++)a[d][c]=!0;else if(b<e)for(d=b+1;d<e;d++)a[d][c]=!0} -function st(a){if(void 0!==qt[a])return qt[a];for(var b=2*a+1,c=Array(b),d=0;d<b;d++)c[d]=Array(b);for(var b=a,e=d=0;b>=d;)rt(c,a+b,a+d),rt(c,a+d,a+b),rt(c,a-d,a+b),rt(c,a-b,a+d),rt(c,a-b,a-d),rt(c,a-d,a-b),rt(c,a+d,a-b),rt(c,a+b,a-d),d++,e+=1+2*d,0<2*(e-b)+1&&(--b,e+=1-2*b);return qt[a]=c}function tt(a){for(var b in a.a){var c=a.a[b],d;for(d in c)c[d].Te()}} -pt.prototype.Ea=function(a,b,c,d,e,f){d=Math.round(d);var g=2*d+1,h=Kh(this.j,d+.5,d+.5,1/b,-1/b,-c,-a[0],-a[1]),l=this.g;l.canvas.width!==g||l.canvas.height!==g?(l.canvas.width=g,l.canvas.height=g):l.clearRect(0,0,g,g);if(void 0!==this.f){var m=Oa();Pa(m,a);Qa(m,b*(this.f+d),m)}var n=st(d);return ut(this,l,h,c,e,function(a){for(var b=l.getImageData(0,0,g,g).data,c=0;c<g;c++)for(var d=0;d<g;d++)if(n[c][d]&&0<b[4*(d*g+c)+3]){if(a=f(a))return a;l.clearRect(0,0,g,g);return}},m)}; -function vt(a,b){var c=a.c;a=c[0];var d=c[1],e=c[2],c=c[3];a=[a,d,a,c,e,c,e,d];pf(a,0,8,2,b,a);return a}pt.prototype.b=function(a,b){var c=void 0!==a?a.toString():"0";a=this.a[c];void 0===a&&(a={},this.a[c]=a);c=a[b];void 0===c&&(c=new wt[b](this.v,this.c,this.l,this.o),a[b]=c);return c};pt.prototype.i=function(){return wb(this.a)}; -pt.prototype.La=function(a,b,c,d,e,f){var g=Object.keys(this.a).map(Number);g.sort(ia);var h=vt(this,c);a.save();a.beginPath();a.moveTo(h[0],h[1]);a.lineTo(h[2],h[3]);a.lineTo(h[4],h[5]);a.lineTo(h[6],h[7]);a.clip();f=f?f:ji;var l,m,h=0;for(l=g.length;h<l;++h){var n=this.a[g[h].toString()];var p=0;for(m=f.length;p<m;++p){var q=n[f[p]];void 0!==q&&q.La(a,b,c,d,e)}}a.restore()}; -function ut(a,b,c,d,e,f,g){var h=Object.keys(a.a).map(Number);h.sort(function(a,b){return b-a});var l,m;var n=0;for(l=h.length;n<l;++n){var p=a.a[h[n].toString()];for(m=ji.length-1;0<=m;--m){var q=p[ji[m]];if(void 0!==q&&(q=et(q,b,1,c,d,e,q.b,f,g)))return q}}}var wt={Circle:lt,Image:ht,LineString:it,Polygon:lt,Text:ot};function xt(a){Sc.call(this);this.a=a}v(xt,Sc);xt.prototype.Ea=ua;xt.prototype.Ue=nf;xt.prototype.Nf=function(a,b,c){return function(d,e){return yt(a,b,d,e,function(a){c[d]||(c[d]={});c[d][a.ta.toString()]=a})}};xt.prototype.na=function(a){2===a.target.getState()&&zt(this)};function At(a,b){var c=b.getState();2!=c&&3!=c&&y(b,"change",a.na,a);0==c&&(b.load(),c=b.getState());return 2==c}function zt(a){var b=a.a;b.Mb()&&"ready"==b.$f()&&a.s()} -function Bt(a,b){b.Ki()&&a.postRenderFunctions.push(function(a,b,e){b=w(a).toString();a.fd(e.viewState.projection,e.usedTiles[b])}.bind(null,b))}function Ct(a,b){if(b){var c;var d=0;for(c=b.length;d<c;++d){var e=b[d];a[w(e).toString()]=e}}}function Dt(a,b){b=b.D;void 0!==b&&("string"===typeof b?a.logos[b]="":b&&(xa("string"==typeof b.href,44),xa("string"==typeof b.src,45),a.logos[b.src]=b.href))} -function Et(a,b,c,d){b=w(b).toString();c=c.toString();b in a?c in a[b]?(a=a[b][c],d.ca<a.ca&&(a.ca=d.ca),d.$>a.$&&(a.$=d.$),d.da<a.da&&(a.da=d.da),d.ia>a.ia&&(a.ia=d.ia)):a[b][c]=d:(a[b]={},a[b][c]=d)} -function Ft(a,b,c,d,e,f,g,h,l,m){var n=w(b).toString();n in a.wantedTiles||(a.wantedTiles[n]={});var p=a.wantedTiles[n];a=a.tileQueue;var q=c.minZoom,r,u,x;for(x=g;x>=q;--x){var B=oc(c,f,x,B);var E=c.Da(x);for(r=B.ca;r<=B.$;++r)for(u=B.da;u<=B.ia;++u)if(g-x<=h){var A=b.Nc(x,r,u,d,e);0==A.getState()&&(p[A.bb()]=!0,A.bb()in a.a||a.f([A,n,tc(c,A.ta),E]));l&&l.call(m,A)}else b.Ug(x,r,u,e)}};function Gt(a){xt.call(this,a);this.fa=Bh()}v(Gt,xt);function Ht(a,b,c){var d=b.pixelRatio,e=b.size[0]*d,f=b.size[1]*d,g=b.viewState.rotation,h=ib(c),l=hb(c),m=gb(c);c=eb(c);Gh(b.coordinateToPixelTransform,h);Gh(b.coordinateToPixelTransform,l);Gh(b.coordinateToPixelTransform,m);Gh(b.coordinateToPixelTransform,c);a.save();Vh(a,-g,e/2,f/2);a.beginPath();a.moveTo(h[0]*d,h[1]*d);a.lineTo(l[0]*d,l[1]*d);a.lineTo(m[0]*d,m[1]*d);a.lineTo(c[0]*d,c[1]*d);a.clip();Vh(a,g,e/2,f/2)} -function It(a,b,c,d,e){var f=a.a;if(Rc(f,b)){var g=d.size[0]*d.pixelRatio,h=d.size[1]*d.pixelRatio,l=d.viewState.rotation;Vh(c,-l,g/2,h/2);a=e?e:Jt(a,d,0);f.b(new Rh(b,new Xh(c,d.pixelRatio,d.extent,a,d.viewState.rotation),d,c,null));Vh(c,l,g/2,h/2)}}Gt.prototype.u=function(a,b,c,d){if(this.Ea(a,b,0,mf,this))return c.call(d,this.a,null)};Gt.prototype.ef=function(a,b,c,d){It(this,"postcompose",a,b,d)}; -function Jt(a,b,c){var d=b.viewState,e=b.pixelRatio,f=e/d.resolution;return Kh(a.fa,e*b.size[0]/2,e*b.size[1]/2,f,-f,-d.rotation,-d.center[0]+c,-d.center[1])};function Kt(a,b){return w(a)-w(b)}function Lt(a,b){a=.5*a/b;return a*a}function Mt(a,b,c,d,e,f){var g=!1,h;if(h=c.Y()){var l=h.Ye();2==l||3==l?h.Bj(e,f):(0==l&&h.load(),h.Nh(e,f),g=!0)}if(e=(0,c.Za)(b))d=e.Vd(d),(0,Nt[d.U()])(a,d,c,b);return g} -var Nt={Point:function(a,b,c,d){var e=c.Y();if(e){if(2!=e.Ye())return;var f=a.b(c.Ba(),"Image");f.Ub(e);f.qc(b,d)}if(e=c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),a.yc(b.ga(),0,2,2,b,d)},LineString:function(a,b,c,d){var e=c.Ga();if(e){var f=a.b(c.Ba(),"LineString");f.Ma(null,e);f.mc(b,d)}if(e=c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),a.yc(di(b),0,2,2,b,d)},Polygon:function(a,b,c,d){var e=c.Fa(),f=c.Ga();if(e||f){var g=a.b(c.Ba(),"Polygon");g.Ma(e,f);g.rc(b,d)}if(e=c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),a.yc(Wf(b), -0,2,2,b,d)},MultiPoint:function(a,b,c,d){var e=c.Y();if(e){if(2!=e.Ye())return;var f=a.b(c.Ba(),"Image");f.Ub(e);f.oc(b,d)}if(e=c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),c=b.ga(),a.yc(c,0,c.length,b.qa(),b,d)},MultiLineString:function(a,b,c,d){var e=c.Ga();if(e){var f=a.b(c.Ba(),"LineString");f.Ma(null,e);f.nc(b,d)}if(e=c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),c=ei(b),a.yc(c,0,c.length,2,b,d)},MultiPolygon:function(a,b,c,d){var e=c.Fa(),f=c.Ga();if(f||e){var g=a.b(c.Ba(),"Polygon");g.Ma(e,f);g.pc(b,d)}if(e= -c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),c=gi(b),a.yc(c,0,c.length,2,b,d)},GeometryCollection:function(a,b,c,d){b=b.a;var e;var f=0;for(e=b.length;f<e;++f)(0,Nt[b[f].U()])(a,b[f],c,d)},Circle:function(a,b,c,d){var e=c.Fa(),f=c.Ga();if(e||f){var g=a.b(c.Ba(),"Circle");g.Ma(e,f);g.Zb(b,d)}if(e=c.Na())a=a.b(c.Ba(),"Text"),a.Cb(e),a.yc(b.wa(),0,2,2,b,d)}};function Ot(a){Gt.call(this,a);this.c=!1;this.v=-1;this.l=NaN;this.j=Oa();this.f=this.o=null;this.g=jd()}v(Ot,Gt); -Ot.prototype.S=function(a,b,c){var d=a.extent,e=a.pixelRatio,f=b.Je?a.skippedFeatureUids:{},g=a.viewState,h=g.projection,g=g.rotation,l=h.G(),m=this.a.ha(),n=Jt(this,a,0);It(this,"precompose",c,a,n);var p=b.extent,q=void 0!==p;q&&Ht(c,a,p);if((p=this.f)&&!p.i()){var r=0,u=0;if(Rc(this.a,"render")){var x=c.canvas.width;var B=c.canvas.height;if(g){var E=Math.round(Math.sqrt(x*x+B*B)),r=(E-x)/2,u=(E-B)/2;x=B=E}this.g.canvas.width=x;this.g.canvas.height=B;x=this.g}else x=c;B=x.globalAlpha;x.globalAlpha= -b.opacity;x!=c&&x.translate(r,u);var E=a.size[0]*e,A=a.size[1]*e;Vh(x,-g,E/2,A/2);p.La(x,e,n,g,f);if(m.u&&h.i&&!Va(l,d)){for(var h=d[0],m=lb(l),L=0;h<l[0];)--L,n=m*L,n=Jt(this,a,n),p.La(x,e,n,g,f),h+=m;L=0;for(h=d[2];h>l[2];)++L,n=m*L,n=Jt(this,a,n),p.La(x,e,n,g,f),h-=m;n=Jt(this,a,0)}Vh(x,g,E/2,A/2);x!=c&&(It(this,"render",x,a,n),c.drawImage(x.canvas,-r,-u),x.translate(-r,-u));x.globalAlpha=B}q&&c.restore();this.ef(c,a,b,n)}; -Ot.prototype.Ea=function(a,b,c,d,e){if(this.f){var f=this.a,g={};return this.f.Ea(a,b.viewState.resolution,b.viewState.rotation,c,{},function(a){var b=w(a).toString();if(!(b in g))return g[b]=!0,d.call(e,a,f)})}};Ot.prototype.D=function(){zt(this)}; -Ot.prototype.sd=function(a){function b(a){var b=a.Lc();if(b)var d=b.call(a,m);else(b=c.f)&&(d=b(a,m));if(d){if(d){b=!1;if(Array.isArray(d))for(var e=0,f=d.length;e<f;++e)b=Mt(q,a,d[e],Lt(m,n),this.D,this)||b;else b=Mt(q,a,d,Lt(m,n),this.D,this)||b;a=b}else a=!1;this.c=this.c||a}}var c=this.a,d=c.ha();Ct(a.attributions,d.j);Dt(a,d);var e=a.viewHints[0],f=a.viewHints[1],g=c.T,h=c.na;if(!this.c&&!g&&e||!h&&f)return!0;var l=a.extent,h=a.viewState,e=h.projection,m=h.resolution,n=a.pixelRatio,f=c.i,p=c.c, -g=c.get(Pt);void 0===g&&(g=Kt);l=Qa(l,p*m);p=h.projection.G();d.u&&h.projection.i&&!Va(p,a.extent)&&(a=Math.max(lb(l)/2,lb(p)),l[0]=p[0]-a,l[2]=p[2]+a);if(!this.c&&this.l==m&&this.v==f&&this.o==g&&Va(this.j,l))return!0;this.f=null;this.c=!1;var q=new pt(.5*m/n,l,m,d.T,c.c);d.Yd(l,m,e);if(g){var r=[];d.$b(l,function(a){r.push(a)},this);r.sort(g);r.forEach(b,this)}else d.$b(l,b,this);tt(q);this.l=m;this.v=f;this.o=g;this.j=l;this.f=q;return!0};function Qt(){this.b="precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}"}v(Qt,mi);var Rt=new Qt;function St(){this.b="varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}"}v(St,ni);var Tt=new St; -function Ut(a,b){this.i=a.getUniformLocation(b,"f");this.c=a.getUniformLocation(b,"e");this.g=a.getUniformLocation(b,"d");this.f=a.getUniformLocation(b,"g");this.b=a.getAttribLocation(b,"b");this.a=a.getAttribLocation(b,"c")};function Vt(a,b){xt.call(this,b);this.c=a;this.T=new Di([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]);this.g=this.Ib=null;this.j=void 0;this.v=Bh();this.S=Bh();this.C=ti();this.u=null}v(Vt,xt); -function Wt(a,b,c){var d=a.c.i;if(void 0===a.j||a.j!=c){b.postRenderFunctions.push(function(a,b,c){a.isContextLost()||(a.deleteFramebuffer(b),a.deleteTexture(c))}.bind(null,d,a.g,a.Ib));b=Qi(d,c,c);var e=d.createFramebuffer();d.bindFramebuffer(36160,e);d.framebufferTexture2D(36160,36064,3553,b,0);a.Ib=b;a.g=e;a.j=c}else d.bindFramebuffer(36160,a.g)} -Vt.prototype.Gi=function(a,b,c){Xt(this,"precompose",c,a);wi(c,34962,this.T);var d=c.b,e=Hi(c,Rt,Tt);if(this.u)var f=this.u;else this.u=f=new Ut(d,e);c.Qc(e)&&(d.enableVertexAttribArray(f.b),d.vertexAttribPointer(f.b,2,5126,!1,16,0),d.enableVertexAttribArray(f.a),d.vertexAttribPointer(f.a,2,5126,!1,16,8),d.uniform1i(f.f,0));d.uniformMatrix4fv(f.g,!1,ui(this.C,this.v));d.uniformMatrix4fv(f.c,!1,ui(this.C,this.S));d.uniform1f(f.i,b.opacity);d.bindTexture(3553,this.Ib);d.drawArrays(5,0,4);Xt(this,"postcompose", -c,a)};function Xt(a,b,c,d){a=a.a;if(Rc(a,b)){var e=d.viewState;a.b(new Rh(b,new kk(c,e.center,e.resolution,e.rotation,d.size,d.extent,d.pixelRatio),d,null,c))}}Vt.prototype.mg=function(){this.g=this.Ib=null;this.j=void 0};function Yt(a,b){Vt.call(this,a,b);this.l=!1;this.R=-1;this.I=NaN;this.D=Oa();this.o=this.f=this.B=null}v(Yt,Vt);k=Yt.prototype;k.Gi=function(a,b,c){this.o=b;var d=a.viewState,e=this.f,f=a.size,g=a.pixelRatio,h=this.c.i;e&&!e.i()&&(h.enable(h.SCISSOR_TEST),h.scissor(0,0,f[0]*g,f[1]*g),e.La(c,d.center,d.resolution,d.rotation,f,g,b.opacity,b.Je?a.skippedFeatureUids:{}),h.disable(h.SCISSOR_TEST))};k.ka=function(){var a=this.f;a&&(ek(a,this.c.f)(),this.f=null);Vt.prototype.ka.call(this)}; -k.Ea=function(a,b,c,d,e){if(this.f&&this.o){c=b.viewState;var f=this.a,g={};return this.f.Ea(a,this.c.f,c.center,c.resolution,c.rotation,b.size,b.pixelRatio,this.o.opacity,{},function(a){var b=w(a).toString();if(!(b in g))return g[b]=!0,d.call(e,a,f)})}};k.Ue=function(a,b){if(this.f&&this.o){var c=b.viewState;return jk(this.f,a,this.c.f,c.resolution,c.rotation,b.pixelRatio,this.o.opacity,b.skippedFeatureUids)}return!1}; -k.lg=function(a,b,c,d){a=Gh(b.pixelToCoordinateTransform,a.slice());if(this.Ue(a,b))return c.call(d,this.a,null)};k.Hi=function(){zt(this)}; -k.ng=function(a,b,c){function d(a){var b=a.Lc();if(b)var c=b.call(a,m);else(b=e.f)&&(c=b(a,m));if(c){if(c){b=!1;if(Array.isArray(c))for(var d=c.length-1;0<=d;--d)b=Mt(q,a,c[d],Lt(m,n),this.Hi,this)||b;else b=Mt(q,a,c,Lt(m,n),this.Hi,this)||b;a=b}else a=!1;this.l=this.l||a}}var e=this.a;b=e.ha();Ct(a.attributions,b.j);Dt(a,b);var f=a.viewHints[0],g=a.viewHints[1],h=e.T,l=e.na;if(!this.l&&!h&&f||!l&&g)return!0;var g=a.extent,h=a.viewState,f=h.projection,m=h.resolution,n=a.pixelRatio,h=e.i,p=e.c,l=e.get(Pt); -void 0===l&&(l=Kt);g=Qa(g,p*m);if(!this.l&&this.I==m&&this.R==h&&this.B==l&&Va(this.D,g))return!0;this.f&&a.postRenderFunctions.push(ek(this.f,c));this.l=!1;var q=new dk(.5*m/n,g,e.c);b.Yd(g,m,f);if(l){var r=[];b.$b(g,function(a){r.push(a)},this);r.sort(l);r.forEach(d,this)}else b.$b(g,d,this);fk(q,c);this.I=m;this.R=h;this.B=l;this.D=g;this.f=q;return!0};function T(a){a=a?a:{};var b=tb({},a);delete b.style;delete b.renderBuffer;delete b.updateWhileAnimating;delete b.updateWhileInteracting;wh.call(this,b);this.c=void 0!==a.renderBuffer?a.renderBuffer:100;this.u=null;this.f=void 0;this.g(a.style);this.T=void 0!==a.updateWhileAnimating?a.updateWhileAnimating:!1;this.na=void 0!==a.updateWhileInteracting?a.updateWhileInteracting:!1}v(T,wh);T.prototype.Fd=function(a){var b=null,c=a.U();"canvas"===c?b=new Ot(this):"webgl"===c&&(b=new Yt(a,this));return b}; -T.prototype.D=function(){return this.u};T.prototype.C=function(){return this.f};T.prototype.g=function(a){this.u=void 0!==a?a:fl;this.f=null===a?void 0:dl(this.u);this.s()};var Pt="renderOrder";function Zt(){return[[-Infinity,-Infinity,Infinity,Infinity]]};function $t(a){Tc.call(this);this.c=Tb(a.projection);this.j=au(a.attributions);this.D=a.logo;this.na=void 0!==a.state?a.state:"ready";this.u=void 0!==a.wrapX?a.wrapX:!1}v($t,Tc);function au(a){if("string"===typeof a)return[new Ac({html:a})];if(a instanceof Ac)return[a];if(Array.isArray(a)){for(var b=a.length,c=Array(b),d=0;d<b;d++){var e=a[d];c[d]="string"===typeof e?new Ac({html:e}):e}return c}return null}k=$t.prototype;k.Ea=ua;k.ya=function(){return this.j};k.xa=function(){return this.D};k.za=function(){return this.c}; -k.getState=function(){return this.na};k.sa=function(){this.s()};k.ua=function(a){this.j=au(a);this.s()};function bu(a,b){a.na=b;a.s()};function U(a){a=a||{};$t.call(this,{attributions:a.attributions,logo:a.logo,projection:void 0,state:"ready",wrapX:void 0!==a.wrapX?a.wrapX:!0});this.B=ua;this.C=a.format;this.T=void 0==a.overlaps?!0:a.overlaps;this.I=a.url;a.loader?this.B=a.loader:void 0!==this.I&&(xa(this.C,7),this.B=Dl(this.I,this.C));this.fa=a.strategy?a.strategy:Zt;var b=void 0!==a.useSpatialIndex?a.useSpatialIndex:!0;this.a=b?new Gj:null;this.R=new Gj;this.g={};this.o={};this.l={};this.v={};this.f=null;if(a.features instanceof -Yc){var c=a.features;var d=c.a}else Array.isArray(a.features)&&(d=a.features);b||c||(c=new Yc(d));d&&cu(this,d);c&&du(this,c)}v(U,$t);k=U.prototype;k.yb=function(a){var b=w(a).toString();if(eu(this,b,a)){fu(this,b,a);var c=a.V();c?(b=c.G(),this.a&&this.a.Ca(b,a)):this.g[b]=a;this.b(new gu("addfeature",a))}this.s()};function fu(a,b,c){a.v[b]=[y(c,"change",a.Oi,a),y(c,"propertychange",a.Oi,a)]} -function eu(a,b,c){var d=!0,e=c.a;void 0!==e?e.toString()in a.o?d=!1:a.o[e.toString()]=c:(xa(!(b in a.l),30),a.l[b]=c);return d}k.cd=function(a){cu(this,a);this.s()};function cu(a,b){var c,d=[],e=[],f=[];var g=0;for(c=b.length;g<c;g++){var h=b[g];var l=w(h).toString();eu(a,l,h)&&e.push(h)}g=0;for(c=e.length;g<c;g++)h=e[g],l=w(h).toString(),fu(a,l,h),(b=h.V())?(l=b.G(),d.push(l),f.push(h)):a.g[l]=h;a.a&&a.a.load(d,f);g=0;for(c=e.length;g<c;g++)a.b(new gu("addfeature",e[g]))} -function du(a,b){var c=!1;y(a,"addfeature",function(a){c||(c=!0,b.push(a.feature),c=!1)});y(a,"removefeature",function(a){c||(c=!0,b.remove(a.feature),c=!1)});y(b,"add",function(a){c||(c=!0,this.yb(a.element),c=!1)},a);y(b,"remove",function(a){c||(c=!0,this.Gb(a.element),c=!1)},a);a.f=b} -k.clear=function(a){if(a){for(var b in this.v)this.v[b].forEach(Ec);this.f||(this.v={},this.o={},this.l={})}else if(this.a){this.a.forEach(this.Ig,this);for(var c in this.g)this.Ig(this.g[c])}this.f&&this.f.clear();this.a&&this.a.clear();this.R.clear();this.g={};this.b(new gu("clear"));this.s()};k.sh=function(a,b){if(this.a)return this.a.forEach(a,b);if(this.f)return this.f.forEach(a,b)};function hu(a,b,c){a.$b([b[0],b[1],b[0],b[1]],function(a){if(a.V().sb(b))return c.call(void 0,a)})} -k.$b=function(a,b,c){if(this.a)return Lj(this.a,a,b,c);if(this.f)return this.f.forEach(b,c)};k.th=function(a,b,c){return this.$b(a,function(d){if(d.V().Xa(a)&&(d=b.call(c,d)))return d})};k.Ah=function(){return this.f};k.Xe=function(){if(this.f)var a=this.f.a;else this.a&&(a=Ij(this.a),wb(this.g)||la(a,vb(this.g)));return a};k.zh=function(a){var b=[];hu(this,a,function(a){b.push(a)});return b};k.Uf=function(a){return Jj(this.a,a)}; -k.vh=function(a,b){var c=a[0],d=a[1],e=null,f=[NaN,NaN],g=Infinity,h=[-Infinity,-Infinity,Infinity,Infinity],l=b?b:mf;Lj(this.a,h,function(a){if(l(a)){var b=a.V(),m=g;g=b.Kb(c,d,f,g);g<m&&(e=a,a=Math.sqrt(g),h[0]=c-a,h[1]=d-a,h[2]=c+a,h[3]=d+a)}});return e};k.G=function(a){return this.a.G(a)};k.yh=function(a){a=this.o[a.toString()];return void 0!==a?a:null};k.Mi=function(){return this.C};k.Ni=function(){return this.I}; -k.Oi=function(a){a=a.target;var b=w(a).toString(),c=a.V();c?(c=c.G(),b in this.g?(delete this.g[b],this.a&&this.a.Ca(c,a)):this.a&&Hj(this.a,c,a)):b in this.g||(this.a&&this.a.remove(a),this.g[b]=a);c=a.a;void 0!==c?(c=c.toString(),b in this.l?(delete this.l[b],this.o[c]=a):this.o[c]!==a&&(iu(this,a),this.o[c]=a)):b in this.l||(iu(this,a),this.l[b]=a);this.s();this.b(new gu("changefeature",a))}; -k.Yd=function(a,b,c){var d=this.R;a=this.fa(a,b);var e;var f=0;for(e=a.length;f<e;++f){var g=a[f];Lj(d,g,function(a){return Va(a.extent,g)})||(this.B.call(this,g,b,c),d.Ca(g,{extent:g.slice()}))}};k.Gb=function(a){var b=w(a).toString();b in this.g?delete this.g[b]:this.a&&this.a.remove(a);this.Ig(a);this.s()};k.Ig=function(a){var b=w(a).toString();this.v[b].forEach(Ec);delete this.v[b];var c=a.a;void 0!==c?delete this.o[c.toString()]:delete this.l[b];this.b(new gu("removefeature",a))}; -function iu(a,b){for(var c in a.o)if(a.o[c]===b){delete a.o[c];break}}function gu(a,b){Oc.call(this,a);this.feature=b}v(gu,Oc);function ju(a){Dg.call(this,{handleDownEvent:ku,handleEvent:lu,handleUpEvent:mu});this.T=!1;this.fa=null;this.u=!1;this.Yb=a.source?a.source:null;this.$a=a.features?a.features:null;this.wk=a.snapTolerance?a.snapTolerance:12;this.R=a.type;this.g=nu(this.R);this.Sa=a.minPoints?a.minPoints:this.g===ou?3:2;this.va=a.maxPoints?a.maxPoints:Infinity;this.Cf=a.finishCondition?a.finishCondition:mf;var b=a.geometryFunction;if(!b)if("Circle"===this.R)b=function(a,b){b=b?b:new ys([NaN,NaN]);b.Ng(a[0],Math.sqrt(hf(a[0], -a[1])));return b};else{var c,d=this.g;d===pu?c=C:d===qu?c=O:d===ou&&(c=D);b=function(a,b){b?d===ou?b.ma([a[0].concat([a[0][0]])]):b.ma(a):b=new c(a);return b}}this.Za=b;this.I=this.C=this.a=this.B=this.j=this.l=null;this.ad=a.clickTolerance?a.clickTolerance*a.clickTolerance:36;this.pa=new T({source:new U({useSpatialIndex:!1,wrapX:a.wrapX?a.wrapX:!1}),style:a.style?a.style:ru()});this.xb=a.geometryName;this.vk=a.condition?a.condition:xg;this.Df=a.freehand?mf:a.freehandCondition?a.freehandCondition: -yg;y(this,Vc("active"),this.ri,this)}v(ju,Dg);function ru(){var a=gl();return function(b){return a[b.V().U()]}}k=ju.prototype;k.setMap=function(a){Dg.prototype.setMap.call(this,a);this.ri()};function lu(a){this.u=this.g!==pu&&this.Df(a);var b=!this.u;this.u&&"pointerdrag"===a.type&&null!==this.j?(su(this,a),b=!1):"pointermove"===a.type?b=tu(this,a):"dblclick"===a.type&&(b=!1);return Eg.call(this,a)&&b} -function ku(a){this.T=!this.u;return this.u?(this.fa=a.pixel,this.l||uu(this,a),!0):this.vk(a)?(this.fa=a.pixel,!0):!1}function mu(a){var b=!0;tu(this,a);var c=this.g===vu;this.T?(this.l?this.u||c?this.Pd():wu(this,a)?this.Cf(a)&&this.Pd():su(this,a):(uu(this,a),this.g===pu&&this.Pd()),b=!1):this.u&&(this.l=null,xu(this));return b} -function tu(a,b){if(a.fa&&(!a.u&&a.T||a.u&&!a.T)){var c=a.fa,d=b.pixel,e=c[0]-d[0],c=c[1]-d[1],e=e*e+c*c;a.T=a.u?e>a.ad:e<=a.ad}a.l?(e=b.coordinate,c=a.j.V(),a.g===pu?d=a.a:a.g===ou?(d=a.a[0],d=d[d.length-1],wu(a,b)&&(e=a.l.slice())):(d=a.a,d=d[d.length-1]),d[0]=e[0],d[1]=e[1],a.Za(a.a,c),a.B&&a.B.V().ma(e),c instanceof D&&a.g!==ou?(a.C||(a.C=new H(new O(null))),e=c.Ch(0),b=a.C.V(),b.ba(e.ja,e.ga())):a.I&&(b=a.C.V(),b.ma(a.I)),yu(a)):(b=b.coordinate.slice(),a.B?a.B.V().ma(b):(a.B=new H(new C(b)), -yu(a)));return!0}function wu(a,b){var c=!1;if(a.j){var d=!1,e=[a.l];a.g===qu?d=a.a.length>a.Sa:a.g===ou&&(d=a.a[0].length>a.Sa,e=[a.a[0][0],a.a[0][a.a[0].length-2]]);if(d)for(var d=b.map,f=0,g=e.length;f<g;f++){var h=e[f],l=d.Ja(h),m=b.pixel,c=m[0]-l[0],l=m[1]-l[1];if(c=Math.sqrt(c*c+l*l)<=(a.u?1:a.wk)){a.l=h;break}}}return c} -function uu(a,b){b=b.coordinate;a.l=b;a.g===pu?a.a=b.slice():a.g===ou?(a.a=[[b.slice(),b.slice()]],a.I=a.a[0]):(a.a=[b.slice(),b.slice()],a.g===vu&&(a.I=a.a));a.I&&(a.C=new H(new O(a.I)));b=a.Za(a.a);a.j=new H;a.xb&&a.j.Tc(a.xb);a.j.Ra(b);yu(a);a.b(new zu("drawstart",a.j))} -function su(a,b){b=b.coordinate;var c=a.j.V(),d;if(a.g===qu){a.l=b.slice();var e=a.a;e.length>=a.va&&(a.u?e.pop():d=!0);e.push(b.slice());a.Za(e,c)}else a.g===ou&&(e=a.a[0],e.length>=a.va&&(a.u?e.pop():d=!0),e.push(b.slice()),d&&(a.l=e[0]),a.Za(a.a,c));yu(a);d&&a.Pd()} -k.Op=function(){if(this.j){var a=this.j.V();if(this.g===qu){var b=this.a;b.splice(-2,1);this.Za(b,a);2<=b.length&&(this.l=b[b.length-2].slice())}else if(this.g===ou){b=this.a[0];b.splice(-2,1);var c=this.C.V();c.ma(b);this.Za(this.a,a)}0===b.length&&(this.l=null);yu(this)}}; -k.Pd=function(){var a=xu(this),b=this.a,c=a.V();this.g===qu?(b.pop(),this.Za(b,c)):this.g===ou&&(b[0].pop(),this.Za(b,c),b=c.X());"MultiPoint"===this.R?a.Ra(new Q([b])):"MultiLineString"===this.R?a.Ra(new P([b])):"MultiPolygon"===this.R&&a.Ra(new R([b]));this.b(new zu("drawend",a));this.$a&&this.$a.push(a);this.Yb&&this.Yb.yb(a)};function xu(a){a.l=null;var b=a.j;b&&(a.j=null,a.B=null,a.C=null,a.pa.ha().clear(!0));return b} -k.vn=function(a){var b=a.V();this.j=a;this.a=b.X();a=this.a[this.a.length-1];this.l=a.slice();this.a.push(a.slice());yu(this);this.b(new zu("drawstart",this.j))};k.Xc=nf;function yu(a){var b=[];a.j&&b.push(a.j);a.C&&b.push(a.C);a.B&&b.push(a.B);a=a.pa.ha();a.clear(!0);a.cd(b)}k.ri=function(){var a=this.v,b=this.c();a&&b||xu(this);this.pa.setMap(b?a:null)}; -function nu(a){var b;"Point"===a||"MultiPoint"===a?b=pu:"LineString"===a||"MultiLineString"===a?b=qu:"Polygon"===a||"MultiPolygon"===a?b=ou:"Circle"===a&&(b=vu);return b}var pu="Point",qu="LineString",ou="Polygon",vu="Circle";function zu(a,b){Oc.call(this,a);this.feature=b}v(zu,Oc);function Au(a){this.a=this.j=null;this.C=!1;this.B=this.l=null;a||(a={});a.extent&&this.g(a.extent);Dg.call(this,{handleDownEvent:Bu,handleDragEvent:Cu,handleEvent:Du,handleUpEvent:Eu});this.u=new T({source:new U({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.boxStyle?a.boxStyle:Fu(),updateWhileAnimating:!0,updateWhileInteracting:!0});this.I=new T({source:new U({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.pointerStyle?a.pointerStyle:Gu(),updateWhileAnimating:!0,updateWhileInteracting:!0})}v(Au,Dg); -function Du(a){if(!(a instanceof ee))return!0;if("pointermove"==a.type&&!this.D){var b=a.pixel,c=a.map,d=Hu(this,b,c);d||(d=c.Wa(b));Iu(this,d)}Eg.call(this,a);return!1} -function Bu(a){function b(a){var b=null,c=null;a[0]==e[0]?b=e[2]:a[0]==e[2]&&(b=e[0]);a[1]==e[1]?c=e[3]:a[1]==e[3]&&(c=e[1]);return null!==b&&null!==c?[b,c]:null}var c=a.pixel,d=a.map,e=this.G();(a=Hu(this,c,d))&&e?(c=a[0]==e[0]||a[0]==e[2]?a[0]:null,d=a[1]==e[1]||a[1]==e[3]?a[1]:null,null!==c&&null!==d?this.a=Ju(b(a)):null!==c?this.a=Ku(b([c,e[1]]),b([c,e[3]])):null!==d&&(this.a=Ku(b([e[0],d]),b([e[2],d])))):(a=d.Wa(c),this.g([a[0],a[1],a[0],a[1]]),this.a=Ju(a));return!0} -function Cu(a){this.a&&(a=a.coordinate,this.g(this.a(a)),Iu(this,a));return!0}function Eu(){this.a=null;var a=this.G();a&&jb(a)||this.g(null);return!1}function Fu(){var a=gl();return function(){return a.Polygon}}function Gu(){var a=gl();return function(){return a.Point}}function Ju(a){return function(b){return Na([a,b])}}function Ku(a,b){return a[0]==b[0]?function(c){return Na([a,[c[0],b[1]]])}:a[1]==b[1]?function(c){return Na([a,[b[0],c[1]]])}:null} -function Hu(a,b,c){function d(a,b){return kf(e,a)-kf(e,b)}var e=c.Wa(b),f=a.G();if(f){f=[[[f[0],f[1]],[f[0],f[3]]],[[f[0],f[3]],[f[2],f[3]]],[[f[2],f[3]],[f[2],f[1]]],[[f[2],f[1]],[f[0],f[1]]]];f.sort(d);var f=f[0],g=af(e,f),h=c.Ja(g);if(10>=jf(b,h))return b=c.Ja(f[0]),c=c.Ja(f[1]),b=hf(h,b),c=hf(h,c),a.C=10>=Math.sqrt(Math.min(b,c)),a.C&&(g=b>c?f[1]:f[0]),g}return null}function Iu(a,b){var c=a.B;c?c.V().ma(b):(c=new H(new C(b)),a.B=c,a.I.ha().yb(c))} -Au.prototype.setMap=function(a){this.u.setMap(a);this.I.setMap(a);Dg.prototype.setMap.call(this,a)};Au.prototype.G=function(){return this.j};Au.prototype.g=function(a){this.j=a?a:null;var b=this.l;b?a?b.Ra(Yf(a)):b.Ra(void 0):(this.l=b=a?new H(Yf(a)):new H({}),this.u.ha().yb(b));this.b(new Lu(this.j))};function Lu(a){Oc.call(this,Mu);this.b=a}v(Lu,Oc);var Mu="extentchanged";function Nu(a){Dg.call(this,{handleDownEvent:Ou,handleDragEvent:Pu,handleEvent:Qu,handleUpEvent:Ru});this.ad=a.condition?a.condition:Cg;this.$a=function(a){return xg(a)&&wg(a)};this.xb=a.deleteCondition?a.deleteCondition:this.$a;this.Yb=a.insertVertexCondition?a.insertVertexCondition:mf;this.Sa=this.g=null;this.va=[0,0];this.C=this.I=!1;this.a=new Gj;this.fa=void 0!==a.pixelTolerance?a.pixelTolerance:10;this.l=this.pa=!1;this.j=[];this.B=new T({source:new U({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.style? -a.style:Su(),updateWhileAnimating:!0,updateWhileInteracting:!0});this.T={Point:this.Dn,LineString:this.ti,LinearRing:this.ti,Polygon:this.En,MultiPoint:this.Bn,MultiLineString:this.An,MultiPolygon:this.Cn,Circle:this.yn,GeometryCollection:this.zn};this.u=a.features;this.u.forEach(this.kg,this);y(this.u,"add",this.wn,this);y(this.u,"remove",this.xn,this);this.R=null}v(Nu,Dg);k=Nu.prototype; -k.kg=function(a){var b=a.V();b&&b.U()in this.T&&this.T[b.U()].call(this,a,b);(b=this.v)&&b.c&&this.c()&&Tu(this,this.va,b);y(a,"change",this.si,this)};function Uu(a,b){a.C||(a.C=!0,a.b(new Vu("modifystart",a.u,b)))}function Wu(a,b){Xu(a,b);a.g&&!a.u.dc()&&(a.B.ha().Gb(a.g),a.g=null);Kc(b,"change",a.si,a)}function Xu(a,b){a=a.a;var c=[];a.forEach(function(a){b===a.feature&&c.push(a)});for(var d=c.length-1;0<=d;--d)a.remove(c[d])} -k.Ha=function(a){this.g&&!a&&(this.B.ha().Gb(this.g),this.g=null);Dg.prototype.Ha.call(this,a)};k.setMap=function(a){this.B.setMap(a);Dg.prototype.setMap.call(this,a)};k.wn=function(a){this.kg(a.element)};k.si=function(a){this.l||(a=a.target,Wu(this,a),this.kg(a))};k.xn=function(a){Wu(this,a.element)};k.Dn=function(a,b){var c=b.X();a={feature:a,geometry:b,la:[c,c]};this.a.Ca(b.G(),a)}; -k.Bn=function(a,b){var c=b.X(),d;var e=0;for(d=c.length;e<d;++e){var f=c[e];f={feature:a,geometry:b,depth:[e],index:e,la:[f,f]};this.a.Ca(b.G(),f)}};k.ti=function(a,b){var c=b.X(),d;var e=0;for(d=c.length-1;e<d;++e){var f=c.slice(e,e+2);var g={feature:a,geometry:b,index:e,la:f};this.a.Ca(Na(f),g)}}; -k.An=function(a,b){var c=b.X(),d,e;var f=0;for(e=c.length;f<e;++f){var g=c[f];var h=0;for(d=g.length-1;h<d;++h){var l=g.slice(h,h+2);var m={feature:a,geometry:b,depth:[f],index:h,la:l};this.a.Ca(Na(l),m)}}};k.En=function(a,b){var c=b.X(),d,e;var f=0;for(e=c.length;f<e;++f){var g=c[f];var h=0;for(d=g.length-1;h<d;++h){var l=g.slice(h,h+2);var m={feature:a,geometry:b,depth:[f],index:h,la:l};this.a.Ca(Na(l),m)}}}; -k.Cn=function(a,b){var c=b.X(),d,e,f;var g=0;for(f=c.length;g<f;++g){var h=c[g];var l=0;for(e=h.length;l<e;++l){var m=h[l];var n=0;for(d=m.length-1;n<d;++n){var p=m.slice(n,n+2);var q={feature:a,geometry:b,depth:[l,g],index:n,la:p};this.a.Ca(Na(p),q)}}}};k.yn=function(a,b){var c=b.wa(),d={feature:a,geometry:b,index:0,la:[c,c]};a={feature:a,geometry:b,index:1,la:[c,c]};d.Pf=a.Pf=[d,a];this.a.Ca(Za(c),d);this.a.Ca(b.G(),a)}; -k.zn=function(a,b){var c=b.a;for(b=0;b<c.length;++b)this.T[c[b].U()].call(this,a,c[b])};function Yu(a,b){var c=a.g;c?c.V().ma(b):(c=new H(new C(b)),a.g=c,a.B.ha().yb(c))}function Zu(a,b){return a.index-b.index} -function Ou(a){if(!this.ad(a))return!1;Tu(this,a.pixel,a.map);var b=a.map.Wa(a.pixel);this.j.length=0;this.C=!1;var c=this.g;if(c){var d=[],c=c.V().X(),e=Na([c]),e=Jj(this.a,e),f={};e.sort(Zu);for(var g=0,h=e.length;g<h;++g){var l=e[g],m=l.la,n=w(l.feature),p=l.depth;p&&(n+="-"+p.join("-"));f[n]||(f[n]=Array(2));if("Circle"===l.geometry.U()&&1===l.index)m=$u(b,l),df(m,c)&&!f[n][0]&&(this.j.push([l,0]),f[n][0]=l);else if(df(m[0],c)&&!f[n][0])this.j.push([l,0]),f[n][0]=l;else if(df(m[1],c)&&!f[n][1]){if("LineString"!== -l.geometry.U()&&"MultiLineString"!==l.geometry.U()||!f[n][0]||0!==f[n][0].index)this.j.push([l,1]),f[n][1]=l}else this.Yb(a)&&w(m)in this.Sa&&!f[n][0]&&!f[n][1]&&d.push([l,c])}d.length&&Uu(this,a);for(a=d.length-1;0<=a;--a)this.bm.apply(this,d[a])}return!!this.g} -function Pu(a){this.I=!1;Uu(this,a);a=a.coordinate;for(var b=0,c=this.j.length;b<c;++b){for(var d=this.j[b],e=d[0],f=e.depth,g=e.geometry,h,l=e.la,d=d[1];a.length<g.qa();)a.push(l[d][a.length]);switch(g.U()){case "Point":h=a;l[0]=l[1]=a;break;case "MultiPoint":h=g.X();h[e.index]=a;l[0]=l[1]=a;break;case "LineString":h=g.X();h[e.index+d]=a;l[d]=a;break;case "MultiLineString":h=g.X();h[f[0]][e.index+d]=a;l[d]=a;break;case "Polygon":h=g.X();h[f[0]][e.index+d]=a;l[d]=a;break;case "MultiPolygon":h=g.X(); -h[f[1]][f[0]][e.index+d]=a;l[d]=a;break;case "Circle":l[0]=l[1]=a,0===e.index?(this.l=!0,g.ob(a)):(this.l=!0,g.Uc(jf(g.wa(),a))),this.l=!1}h&&(e=g,f=h,this.l=!0,e.ma(f),this.l=!1)}Yu(this,a)}function Ru(a){for(var b,c,d=this.j.length-1;0<=d;--d)if(b=this.j[d][0],c=b.geometry,"Circle"===c.U()){var e=c.wa(),f=b.Pf[0];b=b.Pf[1];f.la[0]=f.la[1]=e;b.la[0]=b.la[1]=e;Hj(this.a,Za(e),f);Hj(this.a,c.G(),b)}else Hj(this.a,Na(b.la),b);this.C&&(this.b(new Vu("modifyend",this.u,a)),this.C=!1);return!1} -function Qu(a){if(!(a instanceof ee))return!0;this.R=a;var b;dg(a.map.Z())[1]||"pointermove"!=a.type||this.D||(this.va=a.pixel,Tu(this,a.pixel,a.map));this.g&&this.xb(a)&&(b="singleclick"==a.type&&this.I?!0:this.hj());"singleclick"==a.type&&(this.I=!1);return Eg.call(this,a)&&!b} -function Tu(a,b,c){function d(a,b){return av(e,a)-av(e,b)}var e=c.Wa(b),f=Qa(Za(e),c.Z().Pa()*a.fa),f=Jj(a.a,f);if(0<f.length){f.sort(d);var g=f[0],h=g.la,l=$u(e,g),m=c.Ja(l),n=jf(b,m);if(n<=a.fa){b={};if("Circle"===g.geometry.U()&&1===g.index)a.pa=!0,Yu(a,l);else for(n=c.Ja(h[0]),g=c.Ja(h[1]),c=hf(m,n),m=hf(m,g),n=Math.sqrt(Math.min(c,m)),a.pa=n<=a.fa,a.pa&&(l=c>m?h[1]:h[0]),Yu(a,l),m=1,c=f.length;m<c;++m)if(l=f[m].la,df(h[0],l[0])&&df(h[1],l[1])||df(h[0],l[1])&&df(h[1],l[0]))b[w(l)]=!0;else break; -b[w(h)]=!0;a.Sa=b;return}}a.g&&(a.B.ha().Gb(a.g),a.g=null)}function av(a,b){var c=b.geometry;return"Circle"===c.U()&&1===b.index?(a=hf(c.wa(),a),c=Math.sqrt(a)-c.pd(),c*c):kf(a,b.la)}function $u(a,b){var c=b.geometry;return"Circle"===c.U()&&1===b.index?c.Ab(a):af(a,b.la)} -k.bm=function(a,b){for(var c=a.la,d=a.feature,e=a.geometry,f=a.depth,g=a.index,h;b.length<e.qa();)b.push(0);switch(e.U()){case "MultiLineString":h=e.X();h[f[0]].splice(g+1,0,b);break;case "Polygon":h=e.X();h[f[0]].splice(g+1,0,b);break;case "MultiPolygon":h=e.X();h[f[1]][f[0]].splice(g+1,0,b);break;case "LineString":h=e.X();h.splice(g+1,0,b);break;default:return}this.l=!0;e.ma(h);this.l=!1;h=this.a;h.remove(a);bv(this,e,g,f,1);a={la:[c[0],b],feature:d,geometry:e,depth:f,index:g};h.Ca(Na(a.la),a); -this.j.push([a,1]);b={la:[b,c[1]],feature:d,geometry:e,depth:f,index:g+1};h.Ca(Na(b.la),b);this.j.push([b,0]);this.I=!0}; -k.hj=function(){if(this.R&&"pointerdrag"!=this.R.type){var a=this.R;Uu(this,a);var b=this.j,c={},d,e;for(e=b.length-1;0<=e;--e){var f=b[e];var g=f[0];var h=w(g.feature);g.depth&&(h+="-"+g.depth.join("-"));h in c||(c[h]={});0===f[1]?(c[h].right=g,c[h].index=g.index):1==f[1]&&(c[h].left=g,c[h].index=g.index+1)}for(h in c){var l=c[h].right;var m=c[h].left;e=c[h].index;var n=e-1;g=void 0!==m?m:l;0>n&&(n=0);f=g.geometry;var p=d=f.X();var q=!1;switch(f.U()){case "MultiLineString":2<d[g.depth[0]].length&& -(d[g.depth[0]].splice(e,1),q=!0);break;case "LineString":2<d.length&&(d.splice(e,1),q=!0);break;case "MultiPolygon":p=p[g.depth[1]];case "Polygon":p=p[g.depth[0]],4<p.length&&(e==p.length-1&&(e=0),p.splice(e,1),q=!0,0===e&&(p.pop(),p.push(p[0]),n=p.length-1))}q&&(q=f,this.l=!0,q.ma(d),this.l=!1,d=[],void 0!==m&&(this.a.remove(m),d.push(m.la[0])),void 0!==l&&(this.a.remove(l),d.push(l.la[1])),void 0!==m&&void 0!==l&&(m={depth:g.depth,feature:g.feature,geometry:g.geometry,index:n,la:d},this.a.Ca(Na(m.la), -m)),bv(this,f,e,g.depth,-1),this.g&&(this.B.ha().Gb(this.g),this.g=null),b.length=0)}this.b(new Vu("modifyend",this.u,a));this.C=!1;return!0}return!1};function bv(a,b,c,d,e){Lj(a.a,b.G(),function(a){a.geometry===b&&(void 0===d||void 0===a.depth||pa(a.depth,d))&&a.index>c&&(a.index+=e)})}function Su(){var a=gl();return function(){return a.Point}}function Vu(a,b,c){Oc.call(this,a);this.features=b;this.mapBrowserEvent=c}v(Vu,Oc);function cv(a){ng.call(this,{handleEvent:dv});a=a?a:{};this.C=a.condition?a.condition:wg;this.D=a.addCondition?a.addCondition:nf;this.B=a.removeCondition?a.removeCondition:nf;this.I=a.toggleCondition?a.toggleCondition:yg;this.l=a.multi?a.multi:!1;this.o=a.filter?a.filter:mf;this.j=a.hitTolerance?a.hitTolerance:0;this.g=new T({source:new U({useSpatialIndex:!1,features:a.features,wrapX:a.wrapX}),style:a.style?a.style:ev(),updateWhileAnimating:!0,updateWhileInteracting:!0});if(a.layers)if("function"=== -typeof a.layers)a=a.layers;else{var b=a.layers;a=function(a){return ja(b,a)}}else a=mf;this.u=a;this.a={};a=this.g.ha().f;y(a,"add",this.Fn,this);y(a,"remove",this.Jn,this)}v(cv,ng);k=cv.prototype;k.Gn=function(){return this.g.ha().f};k.Hn=function(){return this.j};k.In=function(a){a=w(a);return this.a[a]}; -function dv(a){if(!this.C(a))return!0;var b=this.D(a),c=this.B(a),d=this.I(a),e=!b&&!c&&!d,f=a.map,g=this.g.ha().f,h=[],l=[];if(e){ub(this.a);f.we(a.pixel,function(a,b){if(this.o(a,b))return l.push(a),a=w(a),this.a[a]=b,!this.l}.bind(this),{layerFilter:this.u,hitTolerance:this.j});for(e=g.dc()-1;0<=e;--e){var f=g.item(e),m=l.indexOf(f);-1<m?l.splice(m,1):(g.remove(f),h.push(f))}l.length&&g.fg(l)}else{f.we(a.pixel,function(a,e){if(this.o(a,e))return!b&&!d||ja(g.a,a)?(c||d)&&ja(g.a,a)&&(h.push(a),e= -w(a),delete this.a[e]):(l.push(a),a=w(a),this.a[a]=e),!this.l}.bind(this),{layerFilter:this.u,hitTolerance:this.j});for(e=h.length-1;0<=e;--e)g.remove(h[e]);g.fg(l)}(0<l.length||0<h.length)&&this.b(new fv(gv,l,h,a));return vg(a)}k.Kn=function(a){this.j=a};k.setMap=function(a){var b=this.v,c=this.g.ha().f;b&&c.forEach(b.Cj,b);ng.prototype.setMap.call(this,a);this.g.setMap(a);a&&c.forEach(a.xj,a)}; -function ev(){var a=gl();la(a.Polygon,a.LineString);la(a.GeometryCollection,a.LineString);return function(b){return b.V()?a[b.V().U()]:null}}k.Fn=function(a){var b=this.v;b&&b.xj(a.element)};k.Jn=function(a){var b=this.v;b&&b.Cj(a.element)};function fv(a,b,c,d){Oc.call(this,a);this.selected=b;this.deselected=c;this.mapBrowserEvent=d}v(fv,Oc);var gv="select";function hv(a){Dg.call(this,{handleEvent:iv,handleDownEvent:mf,handleUpEvent:jv});a=a?a:{};this.l=a.source?a.source:null;this.R=void 0!==a.vertex?a.vertex:!0;this.C=void 0!==a.edge?a.edge:!0;this.j=a.features?a.features:null;this.pa=[];this.B={};this.T={};this.u={};this.I=null;this.g=void 0!==a.pixelTolerance?a.pixelTolerance:10;this.va=kv.bind(this);this.a=new Gj;this.fa={Point:this.Rn,LineString:this.wi,LinearRing:this.wi,Polygon:this.Sn,MultiPoint:this.Pn,MultiLineString:this.On,MultiPolygon:this.Qn, -GeometryCollection:this.Nn,Circle:this.Mn}}v(hv,Dg);k=hv.prototype;k.yb=function(a,b){b=void 0!==b?b:!0;var c=w(a),d=a.V();if(d){var e=this.fa[d.U()];e&&(this.T[c]=d.G(Oa()),e.call(this,a,d))}b&&(this.B[c]=y(a,"change",this.Ln,this))};k.Ak=function(a){this.yb(a)};k.Bk=function(a){this.Gb(a)};k.ui=function(a){if(a instanceof gu)var b=a.feature;else a instanceof bd&&(b=a.element);this.yb(b)};k.vi=function(a){if(a instanceof gu)var b=a.feature;else a instanceof bd&&(b=a.element);this.Gb(b)}; -k.Ln=function(a){a=a.target;if(this.D){var b=w(a);b in this.u||(this.u[b]=a)}else this.Dj(a)};k.Gb=function(a,b){b=void 0!==b?b:!0;var c=w(a),d=this.T[c];if(d){var e=this.a,f=[];Lj(e,d,function(b){a===b.feature&&f.push(b)});for(d=f.length-1;0<=d;--d)e.remove(f[d])}b&&(Ec(this.B[c]),delete this.B[c])}; -k.setMap=function(a){var b=this.v,c=this.pa,d;this.j?d=this.j:this.l&&(d=this.l.Xe());b&&(c.forEach(Ec),c.length=0,d.forEach(this.Bk,this));Dg.prototype.setMap.call(this,a);a&&(this.j?c.push(y(this.j,"add",this.ui,this),y(this.j,"remove",this.vi,this)):this.l&&c.push(y(this.l,"addfeature",this.ui,this),y(this.l,"removefeature",this.vi,this)),d.forEach(this.Ak,this))};k.Xc=nf; -function lv(a,b,c,d){var e=d.Wa([b[0]-a.g,b[1]+a.g]),f=d.Wa([b[0]+a.g,b[1]-a.g]),e=Na([e,f]),g=Jj(a.a,e);a.R&&!a.C&&(g=g.filter(function(a){return"Circle"!==a.feature.V().U()}));var h=!1,e=!1,l=f=null;if(0<g.length){a.I=c;g.sort(a.va);var m=g[0].la,h="Circle"===g[0].feature.V().U();if(a.R&&!a.C){if(c=d.Ja(m[0]),h=d.Ja(m[1]),c=hf(b,c),b=hf(b,h),h=Math.sqrt(Math.min(c,b)),h=h<=a.g)e=!0,f=c>b?m[1]:m[0],l=d.Ja(f)}else a.C&&(f=h?$e(c,g[0].feature.V()):af(c,m),l=d.Ja(f),jf(b,l)<=a.g&&(e=!0,a.R&&!h&&(c= -d.Ja(m[0]),h=d.Ja(m[1]),c=hf(l,c),b=hf(l,h),h=Math.sqrt(Math.min(c,b)),h=h<=a.g)))&&(f=c>b?m[1]:m[0],l=d.Ja(f));e&&(l=[Math.round(l[0]),Math.round(l[1])])}return{nq:e,vertex:f,wq:l}}k.Dj=function(a){this.Gb(a,!1);this.yb(a,!1)};k.Mn=function(a,b){b=Zf(b).X()[0];var c;var d=0;for(c=b.length-1;d<c;++d){var e=b.slice(d,d+2);var f={feature:a,la:e};this.a.Ca(Na(e),f)}};k.Nn=function(a,b){var c=b.a;for(b=0;b<c.length;++b){var d=this.fa[c[b].U()];d&&d.call(this,a,c[b])}}; -k.wi=function(a,b){b=b.X();var c;var d=0;for(c=b.length-1;d<c;++d){var e=b.slice(d,d+2);var f={feature:a,la:e};this.a.Ca(Na(e),f)}};k.On=function(a,b){b=b.X();var c,d;var e=0;for(d=b.length;e<d;++e){var f=b[e];var g=0;for(c=f.length-1;g<c;++g){var h=f.slice(g,g+2);var l={feature:a,la:h};this.a.Ca(Na(h),l)}}};k.Pn=function(a,b){var c=b.X(),d;var e=0;for(d=c.length;e<d;++e){var f=c[e];f={feature:a,la:[f,f]};this.a.Ca(b.G(),f)}}; -k.Qn=function(a,b){b=b.X();var c,d,e;var f=0;for(e=b.length;f<e;++f){var g=b[f];var h=0;for(d=g.length;h<d;++h){var l=g[h];var m=0;for(c=l.length-1;m<c;++m){var n=l.slice(m,m+2);var p={feature:a,la:n};this.a.Ca(Na(n),p)}}}};k.Rn=function(a,b){var c=b.X();a={feature:a,la:[c,c]};this.a.Ca(b.G(),a)};k.Sn=function(a,b){b=b.X();var c,d;var e=0;for(d=b.length;e<d;++e){var f=b[e];var g=0;for(c=f.length-1;g<c;++g){var h=f.slice(g,g+2);var l={feature:a,la:h};this.a.Ca(Na(h),l)}}}; -function iv(a){var b=lv(this,a.pixel,a.coordinate,a.map);b.nq&&(a.coordinate=b.vertex.slice(0,2),a.pixel=b.wq);return Eg.call(this,a)}function jv(){var a=vb(this.u);a.length&&(a.forEach(this.Dj,this),this.u={});return!1}function kv(a,b){return kf(this.I,a.la)-kf(this.I,b.la)};function mv(a){Dg.call(this,{handleDownEvent:nv,handleDragEvent:ov,handleMoveEvent:pv,handleUpEvent:qv});a=a?a:{};this.a=null;this.j=void 0!==a.features?a.features:null;if(a.layers)if("function"===typeof a.layers)var b=a.layers;else{var c=a.layers;b=function(a){return ja(c,a)}}else b=mf;this.C=b;this.l=a.hitTolerance?a.hitTolerance:0;this.g=null;y(this,Vc("active"),this.u,this)}v(mv,Dg); -function nv(a){this.g=rv(this,a.pixel,a.map);if(!this.a&&this.g){this.a=a.coordinate;pv.call(this,a);var b=this.j||new Yc([this.g]);this.b(new sv("translatestart",b,a.coordinate));return!0}return!1}function qv(a){if(this.a){this.a=null;pv.call(this,a);var b=this.j||new Yc([this.g]);this.b(new sv("translateend",b,a.coordinate));return!0}return!1} -function ov(a){if(this.a){a=a.coordinate;var b=a[0]-this.a[0],c=a[1]-this.a[1],d=this.j||new Yc([this.g]);d.forEach(function(a){var d=a.V();d.translate(b,c);a.Ra(d)});this.a=a;this.b(new sv("translating",d,a))}}function pv(a){var b=a.map.a;rv(this,a.pixel,a.map)?(b.classList.remove(this.a?"ol-grab":"ol-grabbing"),b.classList.add(this.a?"ol-grabbing":"ol-grab")):b.classList.remove("ol-grab","ol-grabbing")} -function rv(a,b,c){return c.we(b,function(a){if(!this.j||ja(this.j.a,a))return a}.bind(a),{layerFilter:a.C,hitTolerance:a.l})}mv.prototype.B=function(){return this.l};mv.prototype.I=function(a){this.l=a};mv.prototype.setMap=function(a){var b=this.v;Dg.prototype.setMap.call(this,a);tv(this,b)};mv.prototype.u=function(){tv(this,null)};function tv(a,b){var c=a.v;a=a.c();c&&a||(c||(c=b),c.a.classList.remove("ol-grab","ol-grabbing"))} -function sv(a,b,c){Oc.call(this,a);this.features=b;this.coordinate=c}v(sv,Oc);function V(a){a=a?a:{};var b=tb({},a);delete b.gradient;delete b.radius;delete b.blur;delete b.shadow;delete b.weight;T.call(this,b);this.j=null;this.R=void 0!==a.shadow?a.shadow:250;this.I=void 0;this.B=null;y(this,Vc(uv),this.Bl,this);this.pj(a.gradient?a.gradient:vv);this.jj(void 0!==a.blur?a.blur:15);this.Uc(void 0!==a.radius?a.radius:8);y(this,Vc(wv),this.cg,this);y(this,Vc(xv),this.cg,this);this.cg();var c=a.weight?a.weight:"weight",d;"string"===typeof c?d=function(a){return a.get(c)}:d=c;this.g(function(a){a= -d(a);a=void 0!==a?Ca(a,0,1):1;var b=255*a|0,c=this.B[b];c||(c=[new bl({image:new eo({opacity:a,src:this.I})})],this.B[b]=c);return c}.bind(this));this.set(Pt,null);y(this,"render",this.Sl,this)}v(V,T);var vv=["#00f","#0ff","#0f0","#ff0","#f00"];k=V.prototype;k.uh=function(){return this.get(wv)};k.Bh=function(){return this.get(uv)};k.yi=function(){return this.get(xv)}; -k.Bl=function(){for(var a=this.Bh(),b=jd(1,256),c=b.createLinearGradient(0,0,1,256),d=1/(a.length-1),e=0,f=a.length;e<f;++e)c.addColorStop(e*d,a[e]);b.fillStyle=c;b.fillRect(0,0,1,256);this.j=b.getImageData(0,0,1,256).data};k.cg=function(){var a=this.yi(),b=this.uh(),c=a+b+1,d=2*c,d=jd(d,d);d.shadowOffsetX=d.shadowOffsetY=this.R;d.shadowBlur=b;d.shadowColor="#000";d.beginPath();b=c-this.R;d.arc(b,b,a,0,2*Math.PI,!0);d.fill();this.I=d.canvas.toDataURL();this.B=Array(256);this.s()}; -k.Sl=function(a){a=a.context;var b=a.canvas,b=a.getImageData(0,0,b.width,b.height),c=b.data,d,e;var f=0;for(d=c.length;f<d;f+=4)if(e=4*c[f+3])c[f]=this.j[e],c[f+1]=this.j[e+1],c[f+2]=this.j[e+2];a.putImageData(b,0,0)};k.jj=function(a){this.set(wv,a)};k.pj=function(a){this.set(uv,a)};k.Uc=function(a){this.set(xv,a)};var wv="blur",uv="gradient",xv="radius";function yv(a){Gt.call(this,a);this.v=Bh();this.j=null}v(yv,Gt);yv.prototype.S=function(a,b,c){It(this,"precompose",c,a,void 0);var d=this.Y();if(d){var e=b.extent,f=void 0!==e&&!Va(e,a.extent)&&qb(e,a.extent);f&&Ht(c,a,e);var e=this.C(),g=c.globalAlpha;c.globalAlpha=b.opacity;c.drawImage(d,0,0,+d.width,+d.height,Math.round(e[4]),Math.round(e[5]),Math.round(d.width*e[0]),Math.round(d.height*e[3]));c.globalAlpha=g;f&&c.restore()}this.ef(c,a,b)}; -yv.prototype.Ea=function(a,b,c,d,e){var f=this.a;return f.ha().Ea(a,b.viewState.resolution,b.viewState.rotation,c,b.skippedFeatureUids,function(a){return d.call(e,a,f)})}; -yv.prototype.u=function(a,b,c,d){if(this.Y()){if(this.a.ha().Ea!==ua)return Gt.prototype.u.apply(this,arguments);var e=Gh(this.v,a.slice());gf(e,b.viewState.resolution/this.f);this.j||(this.j=jd(1,1));this.j.clearRect(0,0,1,1);this.j.drawImage(this.Y(),e[0],e[1],1,1,0,0,1,1);e=this.j.getImageData(0,0,1,1).data;if(0<e[3])return c.call(d,this.a,e)}};function zv(a){yv.call(this,a);this.M=null;this.c=Bh()}v(zv,yv);zv.prototype.Y=function(){return this.M?this.M.Y():null};zv.prototype.C=function(){return this.c}; -zv.prototype.sd=function(a,b){var c=a.pixelRatio,d=a.size,e=a.viewState,f=e.center,g=e.resolution,h=this.a.ha(),l=a.viewHints,m=a.extent;void 0!==b.extent&&(m=pb(m,b.extent));l[0]||l[1]||kb(m)||(b=h.Y(m,g,c,e.projection))&&At(this,b)&&(this.M=b);if(this.M){b=this.M;var l=b.G(),m=b.resolution,e=b.a,n=c*m/(g*e),l=Kh(this.c,c*d[0]/2,c*d[1]/2,n,n,0,e*(l[0]-f[0])/m,e*(f[1]-l[3])/m);Kh(this.v,c*d[0]/2-l[4],c*d[1]/2-l[5],c/g,-c/g,0,-f[0],-f[1]);Ct(a.attributions,b.f);Dt(a,h);this.f=g*c/e}return!!this.M};function Av(a,b,c,d){var e=gc(c,b,a);c=Sb(b,d,c);b=b.sc();void 0!==b&&(c*=b);b=a.sc();void 0!==b&&(c/=b);a=Sb(a,c,e)/c;isFinite(a)&&0<a&&(c/=a);return c}function Bv(a,b,c,d){a=c-a;b=d-b;var e=Math.sqrt(a*a+b*b);return[Math.round(c+a/e),Math.round(d+b/e)]} -function Cv(a,b,c,d,e,f,g,h,l,m,n){var p=jd(Math.round(c*a),Math.round(c*b));if(!l.length)return p.canvas;p.scale(c,c);var q=Oa();l.forEach(function(a){cb(q,a.extent)});var r=jd(Math.round(c*lb(q)/d),Math.round(c*mb(q)/d)),u=c/d;l.forEach(function(a){r.drawImage(a.image,m,m,a.image.width-2*m,a.image.height-2*m,(a.extent[0]-q[0])*u,-(a.extent[3]-q[3])*u,lb(a.extent)*u,mb(a.extent)*u)});var x=ib(g);h.c.forEach(function(a){var b=a.source,e=a.target,g=b[1][0],h=b[1][1],l=b[2][0],m=b[2][1];a=(e[0][0]- -x[0])/f;var n=-(e[0][1]-x[1])/f,u=(e[1][0]-x[0])/f,B=-(e[1][1]-x[1])/f,da=(e[2][0]-x[0])/f,fb=-(e[2][1]-x[1])/f,e=b[0][0],b=b[0][1],g=g-e,h=h-b,l=l-e,m=m-b;a:{g=[[g,h,0,0,u-a],[l,m,0,0,da-a],[0,0,g,h,B-n],[0,0,l,m,fb-n]];h=g.length;for(l=0;l<h;l++){for(var m=l,ca=Math.abs(g[l][l]),Ub=l+1;Ub<h;Ub++){var uc=Math.abs(g[Ub][l]);uc>ca&&(ca=uc,m=Ub)}if(!ca){g=null;break a}ca=g[m];g[m]=g[l];g[l]=ca;for(m=l+1;m<h;m++)for(ca=-g[m][l]/g[l][l],Ub=l;Ub<h+1;Ub++)g[m][Ub]=l==Ub?0:g[m][Ub]+ca*g[l][Ub]}l=Array(h); -for(m=h-1;0<=m;m--)for(l[m]=g[m][h]/g[m][m],ca=m-1;0<=ca;ca--)g[ca][h]-=g[ca][m]*l[m];g=l}g&&(p.save(),p.beginPath(),l=(a+u+da)/3,m=(n+B+fb)/3,h=Bv(l,m,a,n),u=Bv(l,m,u,B),da=Bv(l,m,da,fb),p.moveTo(u[0],u[1]),p.lineTo(h[0],h[1]),p.lineTo(da[0],da[1]),p.clip(),p.transform(g[0],g[2],g[1],g[3],a,n),p.translate(q[0]-e,q[3]-b),p.scale(d/c,-d/c),p.drawImage(r.canvas,0,0),p.restore())});n&&(p.save(),p.strokeStyle="black",p.lineWidth=1,h.c.forEach(function(a){var b=a.target;a=(b[0][0]-x[0])/f;var c=-(b[0][1]- -x[1])/f,d=(b[1][0]-x[0])/f,e=-(b[1][1]-x[1])/f,g=(b[2][0]-x[0])/f,b=-(b[2][1]-x[1])/f;p.beginPath();p.moveTo(d,e);p.lineTo(a,c);p.lineTo(g,b);p.closePath();p.stroke()}),p.restore());return p.canvas};function Dv(a,b,c,d,e){this.i=a;this.f=b;var f={},g=ec(this.f,this.i);this.a=function(a){var b=a[0]+"/"+a[1];f[b]||(f[b]=g(a));return f[b]};this.g=d;this.v=e*e;this.c=[];this.o=!1;this.l=this.i.i&&!!d&&!!this.i.G()&&lb(d)==lb(this.i.G());this.b=this.i.G()?lb(this.i.G()):null;this.j=this.f.G()?lb(this.f.G()):null;a=ib(c);b=hb(c);d=gb(c);c=eb(c);e=this.a(a);var h=this.a(b),l=this.a(d),m=this.a(c);Ev(this,a,b,d,c,e,h,l,m,10);if(this.o){var n=Infinity;this.c.forEach(function(a){n=Math.min(n,a.source[0][0], -a.source[1][0],a.source[2][0])});this.c.forEach(function(a){if(Math.max(a.source[0][0],a.source[1][0],a.source[2][0])-n>this.b/2){var b=[[a.source[0][0],a.source[0][1]],[a.source[1][0],a.source[1][1]],[a.source[2][0],a.source[2][1]]];b[0][0]-n>this.b/2&&(b[0][0]-=this.b);b[1][0]-n>this.b/2&&(b[1][0]-=this.b);b[2][0]-n>this.b/2&&(b[2][0]-=this.b);Math.max(b[0][0],b[1][0],b[2][0])-Math.min(b[0][0],b[1][0],b[2][0])<this.b/2&&(a.source=b)}},this)}f={}} -function Ev(a,b,c,d,e,f,g,h,l,m){var n=Na([f,g,h,l]),p=a.b?lb(n)/a.b:null,q=a.b,r=a.i.i&&.5<p&&1>p,u=!1;if(0<m){if(a.f.c&&a.j)var x=Na([b,c,d,e]),u=u|.25<lb(x)/a.j;!r&&a.i.c&&p&&(u|=.25<p)}if(u||!a.g||qb(n,a.g)){if(!(u||isFinite(f[0])&&isFinite(f[1])&&isFinite(g[0])&&isFinite(g[1])&&isFinite(h[0])&&isFinite(h[1])&&isFinite(l[0])&&isFinite(l[1])))if(0<m)u=!0;else return;if(0<m&&(u||(n=a.a([(b[0]+d[0])/2,(b[1]+d[1])/2]),q=r?(Ia(f[0],q)+Ia(h[0],q))/2-Ia(n[0],q):(f[0]+h[0])/2-n[0],n=(f[1]+h[1])/2-n[1], -u=q*q+n*n>a.v),u)){Math.abs(b[0]-d[0])<=Math.abs(b[1]-d[1])?(r=[(c[0]+d[0])/2,(c[1]+d[1])/2],q=a.a(r),n=[(e[0]+b[0])/2,(e[1]+b[1])/2],p=a.a(n),Ev(a,b,c,r,n,f,g,q,p,m-1),Ev(a,n,r,d,e,p,q,h,l,m-1)):(r=[(b[0]+c[0])/2,(b[1]+c[1])/2],q=a.a(r),n=[(d[0]+e[0])/2,(d[1]+e[1])/2],p=a.a(n),Ev(a,b,r,n,e,f,q,p,l,m-1),Ev(a,r,c,d,n,q,g,h,p,m-1));return}if(r){if(!a.l)return;a.o=!0}a.c.push({source:[f,h,l],target:[b,d,e]});a.c.push({source:[f,g,h],target:[b,c,d]})}} -function Fv(a){var b=Oa();a.c.forEach(function(a){a=a.source;Pa(b,a[0]);Pa(b,a[1]);Pa(b,a[2])});return b};function Gv(a,b,c,d,e,f){this.v=b;this.l=a.G();var g=b.G(),h=g?pb(c,g):c,g=Av(a,b,nb(h),d);this.j=new Dv(a,b,h,this.l,.5*g);this.c=d;this.i=c;a=Fv(this.j);this.o=(this.Hb=f(a,g,e))?this.Hb.a:1;this.ee=this.g=null;e=2;f=[];this.Hb&&(e=0,f=this.Hb.f);Is.call(this,c,d,this.o,e,f)}v(Gv,Is);Gv.prototype.ka=function(){1==this.state&&(Ec(this.ee),this.ee=null);Is.prototype.ka.call(this)};Gv.prototype.Y=function(){return this.g}; -Gv.prototype.de=function(){var a=this.Hb.getState();2==a&&(this.g=Cv(lb(this.i)/this.c,mb(this.i)/this.c,this.o,this.Hb.resolution,0,this.c,this.i,this.j,[{extent:this.Hb.G(),image:this.Hb.Y()}],0));this.state=a;this.s()};Gv.prototype.load=function(){if(0==this.state){this.state=1;this.s();var a=this.Hb.getState();2==a||3==a?this.de():(this.ee=y(this.Hb,"change",function(){var a=this.Hb.getState();if(2==a||3==a)Ec(this.ee),this.ee=null,this.de()},this),this.Hb.load())}};function Hv(a){$t.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,state:a.state});this.C=void 0!==a.resolutions?a.resolutions:null;this.a=null;this.fa=0}v(Hv,$t);function Iv(a,b){a.C&&(b=a.C[ka(a.C,b,0)]);return b} -Hv.prototype.Y=function(a,b,c,d){var e=this.c;if(e&&d&&!dc(e,d)){if(this.a){if(this.fa==this.i&&dc(this.a.v,d)&&this.a.resolution==b&&this.a.a==c&&bb(this.a.G(),a))return this.a;Nc(this.a);this.a=null}this.a=new Gv(e,d,a,b,c,function(a,b,c){return this.Jc(a,b,c,e)}.bind(this));this.fa=this.i;return this.a}e&&(d=e);return this.Jc(a,b,c,d)};Hv.prototype.o=function(a){a=a.target;switch(a.getState()){case 1:this.b(new Jv(Kv,a));break;case 2:this.b(new Jv(Lv,a));break;case 3:this.b(new Jv(Mv,a))}}; -function Nv(a,b){a.Y().src=b}function Jv(a,b){Oc.call(this,a);this.image=b}v(Jv,Oc);var Kv="imageloadstart",Lv="imageloadend",Mv="imageloaderror";function Ov(a){Hv.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions,state:a.state});this.pa=a.canvasFunction;this.R=null;this.T=0;this.va=void 0!==a.ratio?a.ratio:1.5}v(Ov,Hv);Ov.prototype.Jc=function(a,b,c,d){b=Iv(this,b);var e=this.R;if(e&&this.T==this.i&&e.resolution==b&&e.a==c&&Va(e.G(),a))return e;a=a.slice();rb(a,this.va);(d=this.pa(a,b,c,[lb(a)/b*c,mb(a)/b*c],d))&&(e=new Ks(a,b,c,this.j,d));this.R=e;this.T=this.i;return e};function Pv(a){this.f=a.source;this.$a=Bh();this.g=jd();this.l=[0,0];this.Sa=void 0==a.renderBuffer?100:a.renderBuffer;this.B=null;Ov.call(this,{attributions:a.attributions,canvasFunction:this.tk.bind(this),logo:a.logo,projection:a.projection,ratio:a.ratio,resolutions:a.resolutions,state:this.f.getState()});this.I=null;this.v=void 0;this.Ii(a.style);y(this.f,"change",this.ro,this)}v(Pv,Ov);k=Pv.prototype; -k.tk=function(a,b,c,d,e){var f=new pt(.5*b/c,a,b,this.f.T,this.Sa);this.f.Yd(a,b,e);var g=!1;this.f.$b(a,function(a){var d;if(!(d=g)){var e;(d=a.Lc())?e=d.call(a,b):this.v&&(e=this.v(a,b));if(e){var h,p=!1;Array.isArray(e)||(e=[e]);d=0;for(h=e.length;d<h;++d)p=Mt(f,a,e[d],Lt(b,c),this.qo,this)||p;d=p}else d=!1}g=d},this);tt(f);if(g)return null;this.l[0]!=d[0]||this.l[1]!=d[1]?(this.g.canvas.width=d[0],this.g.canvas.height=d[1],this.l[0]=d[0],this.l[1]=d[1]):this.g.clearRect(0,0,d[0],d[1]);a=Qv(this, -nb(a),b,c,d);f.La(this.g,c,a,0,{});this.B=f;return this.g.canvas};k.Ea=function(a,b,c,d,e,f){if(this.B){var g={};return this.B.Ea(a,b,0,d,e,function(a){var b=w(a).toString();if(!(b in g))return g[b]=!0,f(a)})}};k.no=function(){return this.f};k.oo=function(){return this.I};k.po=function(){return this.v};function Qv(a,b,c,d,e){c=d/c;return Kh(a.$a,e[0]/2,e[1]/2,c,-c,0,-b[0],-b[1])}k.qo=function(){this.s()};k.ro=function(){bu(this,this.f.getState())}; -k.Ii=function(a){this.I=void 0!==a?a:fl;this.v=a?dl(this.I):void 0;this.s()};function Rv(a,b){Vt.call(this,a,b);this.o=this.f=this.M=null}v(Rv,Vt);function Sv(a,b){b=b.Y();return Ti(a.c.i,b)}Rv.prototype.Ea=function(a,b,c,d,e){var f=this.a;return f.ha().Ea(a,b.viewState.resolution,b.viewState.rotation,c,b.skippedFeatureUids,function(a){return d.call(e,a,f)})}; -Rv.prototype.ng=function(a,b){var c=this.c.i,d=a.pixelRatio,e=a.viewState,f=e.center,g=e.resolution,h=e.rotation,l=this.M,m=this.Ib,n=this.a.ha(),p=a.viewHints,q=a.extent;void 0!==b.extent&&(q=pb(q,b.extent));p[0]||p[1]||kb(q)||(b=n.Y(q,g,d,e.projection))&&At(this,b)&&(l=b,m=Sv(this,b),this.Ib&&a.postRenderFunctions.push(function(a,b){a.isContextLost()||a.deleteTexture(b)}.bind(null,c,this.Ib)));l&&(c=this.c.f.j,Tv(this,c.width,c.height,d,f,g,h,l.G()),this.o=null,d=this.v,Ch(d),Ih(d,1,-1),Jh(d,0, --1),this.M=l,this.Ib=m,Ct(a.attributions,l.f),Dt(a,n));return!!l};function Tv(a,b,c,d,e,f,g,h){b*=f;c*=f;a=a.S;Ch(a);Ih(a,2*d/b,2*d/c);Hh(a,-g);Jh(a,h[0]-e[0],h[1]-e[1]);Ih(a,(h[2]-h[0])/2,(h[3]-h[1])/2);Jh(a,1,1)}Rv.prototype.Ue=function(a,b){return void 0!==this.Ea(a,b,0,mf,this)}; -Rv.prototype.lg=function(a,b,c,d){if(this.M&&this.M.Y())if(this.a.ha()instanceof Pv){var e=Gh(b.pixelToCoordinateTransform,a.slice());if(this.Ea(e,b,0,mf,this))return c.call(d,this.a,null)}else{e=[this.M.Y().width,this.M.Y().height];if(!this.o){var f=b.size;b=Bh();Jh(b,-1,-1);Ih(b,2/f[0],2/f[1]);Jh(b,0,f[1]);Ih(b,1,-1);var f=Lh(this.S.slice()),g=Bh();Jh(g,0,e[1]);Ih(g,1,-1);Ih(g,e[0]/2,e[1]/2);Jh(g,1,1);Eh(g,f);Eh(g,b);this.o=g}a=Gh(this.o,a.slice());if(!(0>a[0]||a[0]>e[0]||0>a[1]||a[1]>e[1])&&(this.f|| -(this.f=jd(1,1)),this.f.clearRect(0,0,1,1),this.f.drawImage(this.M.Y(),a[0],a[1],1,1,0,0,1,1),e=this.f.getImageData(0,0,1,1).data,0<e[3]))return c.call(d,this.a,e)}};function Uv(a){wh.call(this,a?a:{})}v(Uv,wh);Uv.prototype.Fd=function(a){var b=null,c=a.U();"canvas"===c?b=new zv(this):"webgl"===c&&(b=new Rv(a,this));return b};function Vv(a){yv.call(this,a);this.c=null===this.c?null:jd();this.o=null;this.g=[];this.l=Oa();this.va=new ya(0,0,0,0);this.B=Bh();this.T=0}v(Vv,yv);function Wv(a,b){b=b.getState();a=a.a.kd();return 2==b||4==b||3==b&&!a} -Vv.prototype.sd=function(a,b){var c=a.pixelRatio,d=a.size,e=a.viewState,f=e.projection,g=e.resolution,e=e.center,h=this.a,l=h.ha(),m=l.i,n=l.Ta(f),p=n.tc(g,this.T),q=n.Da(p),r=Math.round(g/q)||1,u=a.extent;void 0!==b.extent&&(u=pb(u,b.extent));if(kb(u))return!1;var x=rc(n,u,q);var B=n.Pc(p);var E=n.Da(p),A=Ma(n.gb(p),n.j);B=Xa(B[0]+x.ca*A[0]*E,B[1]+x.da*A[1]*E,B[0]+(x.$+1)*A[0]*E,B[1]+(x.ia+1)*A[1]*E,void 0);E=l.nb(c);A={};A[p]={};var L=this.Nf(l,f,A),oa=this.l,ha=this.va,ga=!1,z,M;for(z=x.ca;z<= -x.$;++z)for(M=x.da;M<=x.ia;++M){var ba=l.Nc(p,z,M,c,f);3!=ba.getState()||this.a.kd()||Ns(ba,2);Wv(this,ba)||(ba=Ms(ba));Wv(this,ba)?2==ba.getState()&&(A[p][ba.ta.toString()]=ba,ga||-1!=this.g.indexOf(ba)||(ga=!0)):pc(n,ba.ta,L,ha,oa)||(ba=qc(n,ba.ta,ha,oa))&&L(p+1,ba)}z=a.viewHints;z=z[0]||z[1];if(!(this.f&&16<Date.now()-a.time&&z||!ga&&this.o&&Va(this.o,u)&&this.mf==m&&r==this.R&&(z||q*c/E*r==this.f))){if(z=this.c)M=l.Xd(p,c,f),ba=Math.round((x.$-x.ca+1)*M[0]/r),M=Math.round((x.ia-x.da+1)*M[1]/r), -ga=z.canvas,ga.width!=ba||ga.height!=M?(this.R=r,ga.width=ba,ga.height=M):(z.clearRect(0,0,ba,M),r=this.R);this.g.length=0;ga=Object.keys(A).map(Number);ga.sort(ia);var da,ha=0;for(da=ga.length;ha<da;++ha){z=ga[ha];L=l.Xd(z,c,f);ba=n.Da(z);var fb=ba/q;var ca=E*l.Wf(f);var Ub=A[z];for(var uc in Ub){ba=Ub[uc];M=n.Aa(ba.ta,oa);z=(M[0]-B[0])/q*E/r;M=(B[3]-M[3])/q*E/r;var bc=L[0]*fb/r;var Je=L[1]*fb/r;this.Of(ba,a,b,z,M,bc,Je,ca);this.g.push(ba)}}this.mf=m;this.f=q*c/E*r;this.o=B}b=this.f/g;b=Kh(this.B, -c*d[0]/2,c*d[1]/2,b,b,0,(this.o[0]-e[0])/this.f*c,(e[1]-this.o[3])/this.f*c);Kh(this.v,c*d[0]/2-b[4],c*d[1]/2-b[5],c/g,-c/g,0,-e[0],-e[1]);Et(a.usedTiles,l,p,x);Ft(a,l,n,c,f,u,p,h.Ud());Bt(a,l);Dt(a,l);return 0<this.g.length};Vv.prototype.Of=function(a,b,c,d,e,f,g,h){this.a.ha().Zf(b.viewState.projection)||this.c.clearRect(d,e,f,g);(a=a.Y())&&this.c.drawImage(a,h,h,a.width-2*h,a.height-2*h,d,e,f,g)};Vv.prototype.Y=function(){var a=this.c;return a?a.canvas:null};Vv.prototype.C=function(){return this.B};function Xv(){this.b="precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}"}v(Xv,mi);var Yv=new Xv;function Zv(){this.b="varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}"}v(Zv,ni);var $v=new Zv;function aw(a,b){this.i=a.getUniformLocation(b,"e");this.c=a.getUniformLocation(b,"d");this.b=a.getAttribLocation(b,"b");this.a=a.getAttribLocation(b,"c")};function bw(a,b){Vt.call(this,a,b);this.I=Yv;this.fa=$v;this.f=null;this.B=new Di([0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0]);this.D=this.o=null;this.l=-1;this.R=[0,0]}v(bw,Vt);k=bw.prototype;k.ka=function(){Gi(this.c.f,this.B);Vt.prototype.ka.call(this)};k.Nf=function(a,b,c){var d=this.c;return function(e,f){return yt(a,b,e,f,function(a){var b=d.a.b.hasOwnProperty(a.bb());b&&(c[e]||(c[e]={}),c[e][a.ta.toString()]=a);return b})}};k.mg=function(){Vt.prototype.mg.call(this);this.f=null}; -k.ng=function(a,b,c){var d=this.c,e=c.b,f=a.viewState,g=f.projection,h=this.a,l=h.ha(),m=l.Ta(g),n=m.tc(f.resolution),p=m.Da(n),q=l.Xd(n,a.pixelRatio,g),r=q[0]/Ma(m.gb(n),this.R)[0],u=p/r,x=l.nb(r)*l.Wf(g),B=f.center,E=a.extent,A=rc(m,E,p);if(this.o&&Aa(this.o,A)&&this.l==l.i)u=this.D;else{var L=[A.$-A.ca+1,A.ia-A.da+1],oa=Ea(Math.max(L[0]*q[0],L[1]*q[1])),L=u*oa,ha=m.Pc(n),ga=ha[0]+A.ca*q[0]*u,u=ha[1]+A.da*q[1]*u,u=[ga,u,ga+L,u+L];Wt(this,a,oa);e.viewport(0,0,oa,oa);e.clearColor(0,0,0,0);e.clear(16384); -e.disable(3042);oa=Hi(c,this.I,this.fa);c.Qc(oa);this.f||(this.f=new aw(e,oa));wi(c,34962,this.B);e.enableVertexAttribArray(this.f.b);e.vertexAttribPointer(this.f.b,2,5126,!1,16,0);e.enableVertexAttribArray(this.f.a);e.vertexAttribPointer(this.f.a,2,5126,!1,16,8);e.uniform1i(this.f.i,0);c={};c[n]={};var z=this.Nf(l,g,c),M=h.kd(),oa=!0,ga=Oa(),ba=new ya(0,0,0,0),da,fb;for(da=A.ca;da<=A.$;++da)for(fb=A.da;fb<=A.ia;++fb){ha=l.Nc(n,da,fb,r,g);if(void 0!==b.extent){var ca=m.Aa(ha.ta,ga);if(!qb(ca,b.extent))continue}ca= -ha.getState();(ca=2==ca||4==ca||3==ca&&!M)||(ha=Ms(ha));ca=ha.getState();if(2==ca){if(d.a.b.hasOwnProperty(ha.bb())){c[n][ha.ta.toString()]=ha;continue}}else if(4==ca||3==ca&&!M)continue;oa=!1;ca=pc(m,ha.ta,z,ba,ga);ca||(ha=qc(m,ha.ta,ba,ga))&&z(n+1,ha)}b=Object.keys(c).map(Number);b.sort(ia);for(var z=new Float32Array(4),Ub,M=0,ba=b.length;M<ba;++M)for(Ub in da=c[b[M]],da)ha=da[Ub],ca=m.Aa(ha.ta,ga),z[0]=2*(ca[2]-ca[0])/L,z[1]=2*(ca[3]-ca[1])/L,z[2]=2*(ca[0]-u[0])/L-1,z[3]=2*(ca[1]-u[1])/L-1,e.uniform4fv(this.f.c, -z),nk(d,ha,q,x*r),e.drawArrays(5,0,4);oa?(this.o=A,this.D=u,this.l=l.i):(this.D=this.o=null,this.l=-1,a.animate=!0)}Et(a.usedTiles,l,n,A);var uc=d.j;Ft(a,l,m,r,g,E,n,h.Ud(),function(a){2!=a.getState()||d.a.b.hasOwnProperty(a.bb())||a.bb()in uc.a||uc.f([a,tc(m,a.ta),m.Da(a.ta[0]),q,x*r])},this);Bt(a,l);Dt(a,l);e=this.v;Ch(e);Jh(e,(Math.round(B[0]/p)*p-u[0])/(u[2]-u[0]),(Math.round(B[1]/p)*p-u[1])/(u[3]-u[1]));f.rotation&&Hh(e,f.rotation);Ih(e,a.size[0]*f.resolution/(u[2]-u[0]),a.size[1]*f.resolution/ -(u[3]-u[1]));Jh(e,-.5,-.5);return!0};k.lg=function(a,b,c,d){if(this.g){a=Gh(this.v,[a[0]/b.size[0],(b.size[1]-a[1])/b.size[1]].slice());a=[a[0]*this.j,a[1]*this.j];b=this.c.f.b;b.bindFramebuffer(b.FRAMEBUFFER,this.g);var e=new Uint8Array(4);b.readPixels(a[0],a[1],1,1,b.RGBA,b.UNSIGNED_BYTE,e);if(0<e[3])return c.call(d,this.a,e)}};function cw(a){a=a?a:{};var b=tb({},a);delete b.preload;delete b.useInterimTilesOnError;wh.call(this,b);this.zi(void 0!==a.preload?a.preload:0);this.Ai(void 0!==a.useInterimTilesOnError?a.useInterimTilesOnError:!0)}v(cw,wh);k=cw.prototype;k.Fd=function(a){var b=null,c=a.U();"canvas"===c?b=new Vv(this):"webgl"===c&&(b=new bw(a,this));return b};k.Ud=function(){return this.get("preload")};k.zi=function(a){this.set("preload",a)};k.kd=function(){return this.get("useInterimTilesOnError")}; -k.Ai=function(a){this.set("useInterimTilesOnError",a)};function dw(a){this.c=null;Vv.call(this,a);this.I=!1;this.D=Bh();this.T="vector"==a.j?1:0}v(dw,Vv);var ew={image:ji,hybrid:["Polygon","LineString"]},fw={hybrid:["Image","Text"],vector:ji};k=dw.prototype;k.sd=function(a,b){var c=this.a,d=c.i;this.pa!=d&&(this.g.length=0,c=c.j,this.c||"vector"==c||(this.c=jd()),this.c&&"vector"==c&&(this.c=null));this.pa=d;return Vv.prototype.sd.apply(this,arguments)}; -k.Of=function(a,b,c,d,e,f,g,h){var l=a,m=this.a,n=b.pixelRatio,p=b.viewState.projection,q=m.i,r=m.get(Pt)||null,u=l.o;if(u.Nd||u.mf!=q||u.Kg!=r){for(var x=0,B=l.a.length;x<B;++x){var E=l.c[l.a[x]];E.S=null;u.Nd=!1;var A=m.ha(),L=A.tileGrid,oa=E.ta,ha=E.a,ga=A.Ta(p),z=ga.Da(l.ta[0]),M=L.Da(E.ta[0]),ga=ga.Aa(l.v),oa=L.Aa(oa),ga=pb(ga,oa);if("tile-pixels"==ha.a)var ba=L=A.nb(),M=Kh(this.D,0,0,1/M*ba,-1/M*ba,0,-oa[0],-oa[3]),M=Gh(M,[ga[0],ga[3]]).concat(Gh(M,[ga[2],ga[1]]));else if(L=z,M=ga,!dc(p,ha)){var da= -!0;E.ig(p)}u.Nd=!1;A=new pt(0,M,L,A.l,m.c);M=Lt(L,n);L=E.g;r&&r!==u.Kg&&L.sort(r);oa=0;for(ga=L.length;oa<ga;++oa){ba=L[oa];da&&ba.V().tb(ha,p);var fb=void 0,ca=ba.Lc();ca?fb=ca.call(ba,z):(ca=m.f)&&(fb=ca(ba,z));if(fb){Array.isArray(fb)||(fb=[fb]);var ca=M,Ub=A;if(fb){var uc=!1;if(Array.isArray(fb))for(var bc=0,Je=fb.length;bc<Je;++bc)uc=Mt(Ub,ba,fb[bc],ca,this.Fi,this)||uc;else uc=Mt(Ub,ba,fb,ca,this.Fi,this)||uc;ba=uc}else ba=!1;this.I=this.I||ba;u.Nd=u.Nd||ba}}tt(A);E.c[l.ta.toString()]=A}u.mf= -q;u.Kg=r}if(this.c){x=b;p=this.a;n=l.o;q=p.i;if((m=ew[p.j])&&n.Lg!==q)for(n.Lg=q,B=l.v,E=B[0],n=x.pixelRatio,z=p.ha(),p=z.tileGrid,ha=z.Ta(x.viewState.projection),q=ha.Da(E),r=z.nb(),l.j||(l.j=jd()),u=l.j,x=z.Xd(E,n,x.viewState.projection),u.canvas.width=x[0],u.canvas.height=x[1],x=ha.Aa(B),B=0,E=l.a.length;B<E;++B)ha=l.c[l.a[B]],A=ha.ta,da=n/q,z=Ch(this.D),"tile-pixels"==ha.a.a?(da=p.Aa(A,this.l),A=p.Da(A[0]),M=n/r*A/q,Ih(z,M,M),Jh(z,Math.round((da[0]-x[0])/A*r),Math.round((x[3]-da[3])/A*r))):(Ih(z, -da,-da),Jh(z,-x[0],-x[3])),ha.c[l.ta.toString()].La(u,n,z,0,{},m);Vv.prototype.Of.apply(this,arguments)}}; -k.Ea=function(a,b,c,d,e){var f=b.viewState.resolution,g=b.viewState.rotation;c=void 0==c?0:c;var h=this.a,l={},m=this.g,n=h.ha();b=n.Ta(b.viewState.projection);var p=n.tileGrid,q;var r=0;for(q=m.length;r<q;++r){var u=m[r];var x=u.ta;x=b.Aa(x,this.l);var B=Qa(x,c*f,B);if(Ta(B,a)){x=0;for(var E=u.a.length;x<E;++x){var A=u.c[u.a[x]];if("tile-pixels"===A.a.a){var L=A.ta;f=p.Aa(L,this.l);var oa=ib(f);f=n.nb();L=p.Da(L[0])/f;oa=[(a[0]-oa[0])/L,(oa[1]-a[1])/L]}else oa=a;A=A.c[u.ta];var ha=ha||A.Ea(oa,f, -g,c,{},function(a){var b=w(a).toString();if(!(b in l))return l[b]=!0,d.call(e,a,h)})}}}return ha};k.Fi=function(){zt(this)}; -k.ef=function(a,b,c){var d=this.a,e=d.ha(),f=fw[d.j];if(f)for(var g=b.pixelRatio,h=b.viewState.rotation,l=b.size,m=Math.round(g*l[0]/2),l=Math.round(g*l[1]/2),n=this.g,d=d.ha().nb(),p=e.tileGrid,e=e.Ta(b.viewState.projection),q=[],r=[],u=n.length-1;0<=u;--u){var x=n[u];if(5!=x.getState())for(var B=x.ta,E=e.Aa(B)[0]-e.Aa(x.v)[0],A=0,L=x.a.length;A<L;++A){var oa=x.c[x.a[A]],ha=oa.ta[0],ga=p.Da(ha);var z=oa;var M=b;if("tile-pixels"==z.a.a){var ba=this.a.ha(),da=ba.tileGrid,fb=z.ta,ba=da.Da(fb[0])/ba.nb(), -z=M.viewState,ca=M.pixelRatio,Ub=z.resolution/ca,fb=da.Aa(fb,this.l),da=z.center,fb=ib(fb);M=M.size;M=Kh(this.D,Math.round(ca*M[0]/2),Math.round(ca*M[1]/2),ba/Ub,ba/Ub,z.rotation,(fb[0]-da[0])/ba,(da[1]-fb[1])/ba)}else M=Jt(this,M,0);Jh(M,E*d/ga,0);oa=oa.c[B.toString()];ga=vt(oa,M);a.save();a.globalAlpha=c.opacity;Vh(a,-h,m,l);ba=0;for(z=q.length;ba<z;++ba)ca=q[ba],ha<r[ba]&&(a.beginPath(),a.moveTo(ga[0],ga[1]),a.lineTo(ga[2],ga[3]),a.lineTo(ga[4],ga[5]),a.lineTo(ga[6],ga[7]),a.moveTo(ca[6],ca[7]), -a.lineTo(ca[4],ca[5]),a.lineTo(ca[2],ca[3]),a.lineTo(ca[0],ca[1]),a.clip());oa.La(a,g,M,h,{},f);a.restore();q.push(ga);r.push(ha)}}Vv.prototype.ef.apply(this,arguments)};function W(a){a=a?a:{};var b=tb({},a);delete b.preload;delete b.useInterimTilesOnError;T.call(this,b);this.Bi(a.preload?a.preload:0);this.Ci(a.useInterimTilesOnError?a.useInterimTilesOnError:!0);xa(void 0==a.renderMode||"image"==a.renderMode||"hybrid"==a.renderMode||"vector"==a.renderMode,28);this.j=a.renderMode||"hybrid"}v(W,T);k=W.prototype;k.Fd=function(a){var b=null;"canvas"===a.U()&&(b=new dw(this));return b};k.Ud=function(){return this.get("preload")};k.kd=function(){return this.get("useInterimTilesOnError")}; -k.Bi=function(a){this.set("preload",a)};k.Ci=function(a){this.set("useInterimTilesOnError",a)};function gw(a,b,c,d){function e(){delete window[g];f.parentNode.removeChild(f)}var f=document.createElement("script"),g="olc_"+w(b);f.async=!0;f.src=a+(-1==a.indexOf("?")?"?":"&")+(d||"callback")+"="+g;var h=setTimeout(function(){e();c&&c()},1E4);window[g]=function(a){clearTimeout(h);e();b(a)};document.getElementsByTagName("head")[0].appendChild(f)};function hw(a,b,c,d,e,f,g,h,l,m,n){Ls.call(this,e,0);this.D=void 0!==n?n:!1;this.S=g;this.u=h;this.v=null;this.c=b;this.j=d;this.o=f?f:e;this.a=[];this.yd=null;this.g=0;f=d.Aa(this.o);h=this.j.G();e=this.c.G();f=h?pb(f,h):f;if(jb(f))if((h=a.G())&&(e?e=pb(e,h):e=h),d=Av(a,c,nb(f),d.Da(this.o[0])),!isFinite(d)||0>=d)this.state=4;else if(this.l=new Dv(a,c,f,e,d*(void 0!==m?m:.5)),this.l.c.length)if(this.g=b.tc(d),c=Fv(this.l),e&&(a.i?(c[1]=Ca(c[1],e[1],e[3]),c[3]=Ca(c[3],e[1],e[3])):c=pb(c,e)),jb(c)){a= -oc(b,c,this.g);for(b=a.ca;b<=a.$;b++)for(c=a.da;c<=a.ia;c++)(m=l(this.g,b,c,g))&&this.a.push(m);this.a.length||(this.state=4)}else this.state=4;else this.state=4;else this.state=4}v(hw,Ls);hw.prototype.ka=function(){1==this.state&&(this.yd.forEach(Ec),this.yd=null);Ls.prototype.ka.call(this)};hw.prototype.Y=function(){return this.v}; -hw.prototype.de=function(){var a=[];this.a.forEach(function(b){b&&2==b.getState()&&a.push({extent:this.c.Aa(b.ta),image:b.Y()})},this);this.a.length=0;if(a.length){var b=this.o[0],c=this.j.gb(b),d="number"===typeof c?c:c[0],c="number"===typeof c?c:c[1],b=this.j.Da(b),e=this.c.Da(this.g),f=this.j.Aa(this.o);this.v=Cv(d,c,this.S,e,this.c.G(),b,f,this.l,a,this.u,this.D);this.state=2}else this.state=3;this.s()}; -hw.prototype.load=function(){if(0==this.state){this.state=1;this.s();var a=0;this.yd=[];this.a.forEach(function(b){var c=b.getState();if(0==c||1==c){a++;var d=y(b,"change",function(){var c=b.getState();if(2==c||3==c||4==c)Ec(d),a--,a||(this.yd.forEach(Ec),this.yd=null,this.de())},this);this.yd.push(d)}},this);this.a.forEach(function(a){0==a.getState()&&a.load()});a||setTimeout(this.de.bind(this),0)}};function iw(a,b){var c=/\{z\}/g,d=/\{x\}/g,e=/\{y\}/g,f=/\{-y\}/g;return function(g){if(g)return a.replace(c,g[0].toString()).replace(d,g[1].toString()).replace(e,function(){return(-g[2]-1).toString()}).replace(f,function(){var a=b.a?b.a[g[0]]:null;xa(a,55);return(a.ia-a.da+1+g[2]).toString()})}}function jw(a,b){for(var c=a.length,d=Array(c),e=0;e<c;++e)d[e]=iw(a[e],b);return kw(d)}function kw(a){return 1===a.length?a[0]:function(b,c,d){if(b)return a[Ia((b[1]<<b[0])+b[2],a.length)](b,c,d)}} -function lw(){}function mw(a){var b=[],c=/\{([a-z])-([a-z])\}/.exec(a);if(c){var d=c[2].charCodeAt(0),e;for(e=c[1].charCodeAt(0);e<=d;++e)b.push(a.replace(c[0],String.fromCharCode(e)));return b}if(c=c=/\{(\d+)-(\d+)\}/.exec(a)){d=parseInt(c[2],10);for(e=parseInt(c[1],10);e<=d;e++)b.push(a.replace(c[0],e.toString()));return b}b.push(a);return b};function nw(a){lk.call(this);this.highWaterMark=void 0!==a?a:2048}v(nw,lk);function ow(a){return a.c>a.highWaterMark}nw.prototype.fd=function(a){for(var b,c;ow(this);){b=this.a.Yc;c=b.ta[0].toString();var d;if(d=c in a)b=b.ta,d=za(a[c],b[1],b[2]);if(d)break;else Nc(this.pop())}};function pw(a){$t.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,state:a.state,wrapX:a.wrapX});this.va=void 0!==a.opaque?a.opaque:!1;this.$a=void 0!==a.tilePixelRatio?a.tilePixelRatio:1;this.tileGrid=void 0!==a.tileGrid?a.tileGrid:null;this.a=new nw(a.cacheSize);this.o=[0,0];this.uc=""}v(pw,$t);k=pw.prototype;k.Ki=function(){return ow(this.a)};k.fd=function(a,b){(a=this.Wd(a))&&a.fd(b)}; -function yt(a,b,c,d,e){b=a.Wd(b);if(!b)return!1;for(var f=!0,g,h,l=d.ca;l<=d.$;++l)for(var m=d.da;m<=d.ia;++m)g=a.Sb(c,l,m),h=!1,b.b.hasOwnProperty(g)&&(g=b.get(g),(h=2===g.getState())&&(h=!1!==e(g))),h||(f=!1);return f}k.Wf=function(){return 0};function qw(a,b){a.uc!==b&&(a.uc=b,a.s())}k.Sb=function(a,b,c){return a+"/"+b+"/"+c};k.Zf=function(){return this.va};k.ab=function(){return this.tileGrid};k.Ta=function(a){return this.tileGrid?this.tileGrid:vc(a)}; -k.Wd=function(a){var b=this.c;return b&&!dc(b,a)?null:this.a};k.nb=function(){return this.$a};k.Xd=function(a,b,c){c=this.Ta(c);b=this.nb(b);a=Ma(c.gb(a),this.o);return 1==b?a:La(a,b,this.o)};function rw(a,b,c){var d=void 0!==c?c:a.c;c=a.Ta(d);if(a.u&&d.c){var e=b;b=e[0];a=tc(c,e);d=zc(d);Ta(d,a)?b=e:(e=lb(d),a[0]+=e*Math.ceil((d[0]-a[0])/e),b=c.bg(a,b))}e=b[0];d=b[1];a=b[2];if(c.minZoom>e||e>c.maxZoom)c=!1;else{var f=c.G();c=(c=f?oc(c,f,e):c.a?c.a[e]:null)?za(c,d,a):!0}return c?b:null} -k.sa=function(){this.a.clear();this.s()};k.Ug=ua;function sw(a,b){Oc.call(this,a);this.tile=b}v(sw,Oc);function tw(a){pw.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,extent:a.extent,logo:a.logo,opaque:a.opaque,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tilePixelRatio:a.tilePixelRatio,wrapX:a.wrapX});this.tileLoadFunction=a.tileLoadFunction;this.tileUrlFunction=this.Fc?this.Fc.bind(this):lw;this.urls=null;a.urls?this.eb(a.urls):a.url&&this.jb(a.url);a.tileUrlFunction&&this.cb(a.tileUrlFunction)}v(tw,pw);k=tw.prototype;k.pb=function(){return this.tileLoadFunction}; -k.qb=function(){return this.tileUrlFunction};k.rb=function(){return this.urls};k.Li=function(a){a=a.target;switch(a.getState()){case 1:this.b(new sw("tileloadstart",a));break;case 2:this.b(new sw("tileloadend",a));break;case 3:this.b(new sw("tileloaderror",a))}};k.vb=function(a){this.a.clear();this.tileLoadFunction=a;this.s()};k.cb=function(a,b){this.tileUrlFunction=a;"undefined"!==typeof b?qw(this,b):this.s()}; -k.jb=function(a){var b=this.urls=mw(a);this.cb(this.Fc?this.Fc.bind(this):jw(b,this.tileGrid),a)};k.eb=function(a){this.urls=a;var b=a.join("\n");this.cb(this.Fc?this.Fc.bind(this):jw(a,this.tileGrid),b)};k.Ug=function(a,b,c){a=this.Sb(a,b,c);this.a.b.hasOwnProperty(a)&&this.a.get(a)};function X(a){tw.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,extent:a.extent,logo:a.logo,opaque:a.opaque,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction?a.tileLoadFunction:uw,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:a.tileUrlFunction,url:a.url,urls:a.urls,wrapX:a.wrapX});this.crossOrigin=void 0!==a.crossOrigin?a.crossOrigin:null;this.tileClass=a.tileClass?a.tileClass:Os;this.g={};this.v={};this.Sa=a.reprojectionErrorThreshold;this.I= -!1}v(X,tw);k=X.prototype;k.Ki=function(){if(ow(this.a))return!0;for(var a in this.g)if(ow(this.g[a]))return!0;return!1};k.fd=function(a,b){a=this.Wd(a);this.a.fd(this.a==a?b:{});for(var c in this.g){var d=this.g[c];d.fd(d==a?b:{})}};k.Wf=function(a){return this.c&&a&&!dc(this.c,a)?0:this.Xf()};k.Xf=function(){return 0};k.Zf=function(a){return this.c&&a&&!dc(this.c,a)?!1:tw.prototype.Zf.call(this,a)}; -k.Ta=function(a){var b=this.c;return!this.tileGrid||b&&!dc(b,a)?(b=w(a).toString(),b in this.v||(this.v[b]=vc(a)),this.v[b]):this.tileGrid};k.Wd=function(a){var b=this.c;if(!b||dc(b,a))return this.a;a=w(a).toString();a in this.g||(this.g[a]=new nw(this.a.highWaterMark));return this.g[a]};function vw(a,b,c,d,e,f,g){b=[b,c,d];e=(c=rw(a,b,f))?a.tileUrlFunction(c,e,f):void 0;e=new a.tileClass(b,void 0!==e?0:4,void 0!==e?e:"",a.crossOrigin,a.tileLoadFunction);e.key=g;y(e,"change",a.Li,a);return e} -k.Nc=function(a,b,c,d,e){if(this.c&&e&&!dc(this.c,e)){var f=this.Wd(e);c=[a,b,c];var g;a=this.Sb.apply(this,c);f.b.hasOwnProperty(a)&&(g=f.get(a));b=this.uc;if(g&&g.key==b)return g;var h=this.c,l=this.Ta(h),m=this.Ta(e),n=rw(this,c,e);d=new hw(h,l,e,m,c,n,this.nb(d),this.Xf(),function(a,b,c,d){return ww(this,a,b,c,d,h)}.bind(this),this.Sa,this.I);d.key=b;g?(d.i=g,f.replace(a,d)):f.set(a,d);return d}return ww(this,a,b,c,d,e)}; -function ww(a,b,c,d,e,f){var g=a.Sb(b,c,d),h=a.uc;if(a.a.b.hasOwnProperty(g)){var l=a.a.get(g);if(l.key!=h){var m=l;l=vw(a,b,c,d,e,f,h);0==m.getState()?l.i=m.i:l.i=m;if(l.i){b=l.i;c=l;do{if(2==b.getState()){b.i=null;break}else 1==b.getState()?c=b:0==b.getState()?c.i=b.i:c=b;b=c.i}while(b)}a.a.replace(g,l)}}else l=vw(a,b,c,d,e,f,h),a.a.set(g,l);return l}k.Pb=function(a){if(this.I!=a){this.I=a;for(var b in this.g)this.g[b].clear();this.s()}}; -k.Qb=function(a,b){if(a=Tb(a))a=w(a).toString(),a in this.v||(this.v[a]=b)};function uw(a,b){a.Y().src=b};function xw(a){this.B=void 0!==a.hidpi?a.hidpi:!1;X.call(this,{cacheSize:a.cacheSize,crossOrigin:"anonymous",opaque:!0,projection:Tb("EPSG:3857"),reprojectionErrorThreshold:a.reprojectionErrorThreshold,state:"loading",tileLoadFunction:a.tileLoadFunction,tilePixelRatio:this.B?2:1,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.R=void 0!==a.culture?a.culture:"en-us";this.C=void 0!==a.maxZoom?a.maxZoom:-1;this.f=a.key;this.l=a.imagerySet;gw("https://dev.virtualearth.net/REST/v1/Imagery/Metadata/"+this.l+"?uriScheme=https&include=ImageryProviders&key="+ -this.f,this.pa.bind(this),void 0,"jsonp")}v(xw,X);var yw=new Ac({html:'<a class="ol-attribution-bing-tos" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a>'});xw.prototype.T=function(){return this.f};xw.prototype.fa=function(){return this.l}; -xw.prototype.pa=function(a){if(200!=a.statusCode||"OK"!=a.statusDescription||"ValidCredentials"!=a.authenticationResultCode||1!=a.resourceSets.length||1!=a.resourceSets[0].resources.length)bu(this,"error");else{var b=a.brandLogoUri;-1==b.indexOf("https")&&(b=b.replace("http","https"));var c=a.resourceSets[0].resources[0],d=-1==this.C?c.zoomMax:this.C;a=zc(this.c);var e=xc({extent:a,minZoom:c.zoomMin,maxZoom:d,tileSize:(c.imageWidth==c.imageHeight?c.imageWidth:[c.imageWidth,c.imageHeight])/this.nb()}); -this.tileGrid=e;var f=this.R,g=this.B;this.tileUrlFunction=kw(c.imageUrlSubdomains.map(function(a){var b=[0,0,0],d=c.imageUrl.replace("{subdomain}",a).replace("{culture}",f);return function(a){if(a)return jc(a[0],a[1],-a[2]-1,b),a=d,g&&(a+="&dpi=d1&device=mobile"),a.replace("{quadkey}",kc(b))}}));if(c.imageryProviders){var h=Vb(Tb("EPSG:4326"),this.c);a=c.imageryProviders.map(function(a){var b=a.attribution,c={};a.coverageAreas.forEach(function(a){var b=a.zoomMin,f=Math.min(a.zoomMax,d);a=a.bbox; -a=sb([a[1],a[0],a[3],a[2]],h);var g;for(g=b;g<=f;++g){var l=g.toString();b=oc(e,a,g);l in c?c[l].push(b):c[l]=[b]}});return new Ac({html:b,tileRanges:c})});a.push(yw);this.ua(a)}this.D=b;bu(this,"ready")}};function zw(a){a=a||{};var b=void 0!==a.projection?a.projection:"EPSG:3857",c=void 0!==a.tileGrid?a.tileGrid:xc({extent:zc(b),maxZoom:a.maxZoom,minZoom:a.minZoom,tileSize:a.tileSize});X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,opaque:a.opaque,projection:b,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:c,tileLoadFunction:a.tileLoadFunction,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:a.tileUrlFunction,url:a.url,urls:a.urls, -wrapX:void 0!==a.wrapX?a.wrapX:!0})}v(zw,X);function Aw(a){this.C=a.account;this.B=a.map||"";this.f=a.config||{};this.l={};zw.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,maxZoom:void 0!==a.maxZoom?a.maxZoom:18,minZoom:a.minZoom,projection:a.projection,state:"loading",wrapX:a.wrapX});Bw(this)}v(Aw,zw);k=Aw.prototype;k.Kk=function(){return this.f};k.tq=function(a){tb(this.f,a);Bw(this)};k.Xp=function(a){this.f=a||{};Bw(this)}; -function Bw(a){var b=JSON.stringify(a.f);if(a.l[b])Cw(a,a.l[b]);else{var c="https://"+a.C+".cartodb.com/api/v1/map";a.B&&(c+="/named/"+a.B);var d=new XMLHttpRequest;d.addEventListener("load",a.Dl.bind(a,b));d.addEventListener("error",a.Cl.bind(a));d.open("POST",c);d.setRequestHeader("Content-type","application/json");d.send(JSON.stringify(a.f))}} -k.Dl=function(a,b){b=b.target;if(!b.status||200<=b.status&&300>b.status){try{var c=JSON.parse(b.responseText)}catch(d){bu(this,"error");return}Cw(this,c);this.l[a]=c;bu(this,"ready")}else bu(this,"error")};k.Cl=function(){bu(this,"error")};function Cw(a,b){a.jb("https://"+b.cdn_url.https+"/"+a.C+"/api/v1/map/"+b.layergroupid+"/{z}/{x}/{y}.png")};function Y(a){U.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,wrapX:a.wrapX});this.resolution=void 0;this.distance=void 0!==a.distance?a.distance:20;this.features=[];this.geometryFunction=a.geometryFunction||function(a){a=a.V();xa(a instanceof C,10);return a};this.source=a.source;this.source.J("change",Y.prototype.sa,this)}v(Y,U);k=Y.prototype;k.$n=function(){return this.distance};k.ao=function(){return this.source}; -k.Yd=function(a,b,c){this.source.Yd(a,b,c);b!==this.resolution&&(this.clear(),this.resolution=b,Dw(this),this.cd(this.features))};k.Yp=function(a){this.distance=a;this.sa()};k.sa=function(){this.clear();Dw(this);this.cd(this.features);U.prototype.sa.call(this)}; -function Dw(a){if(void 0!==a.resolution){a.features.length=0;for(var b=Oa(),c=a.distance*a.resolution,d=a.source.Xe(),e={},f=0,g=d.length;f<g;f++){var h=d[f];w(h).toString()in e||!(h=a.geometryFunction(h))||(h=h.X(),Za(h,b),Qa(b,c,b),h=a.source.Uf(b),h=h.filter(function(a){a=w(a).toString();return a in e?!1:e[a]=!0}),a.features.push(Ew(a,h)))}}} -function Ew(a,b){for(var c=[0,0],d=b.length-1;0<=d;--d){var e=a.geometryFunction(b[d]);e?Ze(c,e.X()):b.splice(d,1)}gf(c,1/b.length);a=new H(new C(c));a.set("features",b);return a};function Fw(a,b){var c=[];Object.keys(b).forEach(function(a){null!==b[a]&&void 0!==b[a]&&c.push(a+"="+encodeURIComponent(b[a]))});var d=c.join("&");a=a.replace(/[?&]$/,"");a=-1===a.indexOf("?")?a+"?":a+"&";return a+d};function Gw(a){a=a||{};Hv.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions});this.R=void 0!==a.crossOrigin?a.crossOrigin:null;this.T=void 0!==a.hidpi?a.hidpi:!0;this.f=a.url;this.g=a.imageLoadFunction?a.imageLoadFunction:Nv;this.v=a.params||{};this.M=null;this.l=[0,0];this.I=0;this.B=void 0!==a.ratio?a.ratio:1.5}v(Gw,Hv);k=Gw.prototype;k.co=function(){return this.v}; -k.Jc=function(a,b,c,d){if(void 0===this.f)return null;b=Iv(this,b);c=this.T?c:1;var e=this.M;if(e&&this.I==this.i&&e.resolution==b&&e.a==c&&Va(e.G(),a))return e;e={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};tb(e,this.v);a=a.slice();var f=(a[0]+a[2])/2,g=(a[1]+a[3])/2;if(1!=this.B){var h=this.B*lb(a)/2,l=this.B*mb(a)/2;a[0]=f-h;a[1]=g-l;a[2]=f+h;a[3]=g+l}var h=b/c,l=Math.ceil(lb(a)/h),m=Math.ceil(mb(a)/h);a[0]=f-h*l/2;a[2]=f+h*l/2;a[1]=g-h*m/2;a[3]=g+h*m/2;this.l[0]=l;this.l[1]=m;f=a;g=this.l;h=c;d= -d.mb.split(":").pop();e.SIZE=g[0]+","+g[1];e.BBOX=f.join(",");e.BBOXSR=d;e.IMAGESR=d;e.DPI=Math.round(90*h);d=this.f;f=d.replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage");f==d&&xa(!1,50);e=Fw(f,e);this.M=new Js(a,b,c,this.j,e,this.R,this.g);this.I=this.i;y(this.M,"change",this.o,this);return this.M};k.bo=function(){return this.g};k.eo=function(){return this.f};k.fo=function(a){this.M=null;this.g=a;this.s()}; -k.ho=function(a){a!=this.f&&(this.f=a,this.M=null,this.s())};k.io=function(a){tb(this.v,a);this.M=null;this.s()};function Hw(a){Hv.call(this,{projection:a.projection,resolutions:a.resolutions});this.R=void 0!==a.crossOrigin?a.crossOrigin:null;this.l=void 0!==a.displayDpi?a.displayDpi:96;this.g=a.params||{};this.I=a.url;this.f=a.imageLoadFunction?a.imageLoadFunction:Nv;this.T=void 0!==a.hidpi?a.hidpi:!0;this.pa=void 0!==a.metersPerUnit?a.metersPerUnit:1;this.v=void 0!==a.ratio?a.ratio:1;this.va=void 0!==a.useOverlay?a.useOverlay:!1;this.M=null;this.B=0}v(Hw,Hv);k=Hw.prototype;k.ko=function(){return this.g}; -k.Jc=function(a,b,c){b=Iv(this,b);c=this.T?c:1;var d=this.M;if(d&&this.B==this.i&&d.resolution==b&&d.a==c&&Va(d.G(),a))return d;1!=this.v&&(a=a.slice(),rb(a,this.v));var e=[lb(a)/b*c,mb(a)/b*c];if(void 0!==this.I){var d=this.I,f=nb(a),g=this.pa,h=lb(a),l=mb(a),m=e[0],n=e[1],p=.0254/this.l,e={OPERATION:this.va?"GETDYNAMICMAPOVERLAYIMAGE":"GETMAPIMAGE",VERSION:"2.0.0",LOCALE:"en",CLIENTAGENT:"ol.source.ImageMapGuide source",CLIP:"1",SETDISPLAYDPI:this.l,SETDISPLAYWIDTH:Math.round(e[0]),SETDISPLAYHEIGHT:Math.round(e[1]), -SETVIEWSCALE:n*h>m*l?h*g/(m*p):l*g/(n*p),SETVIEWCENTERX:f[0],SETVIEWCENTERY:f[1]};tb(e,this.g);d=Fw(d,e);d=new Js(a,b,c,this.j,d,this.R,this.f);y(d,"change",this.o,this)}else d=null;this.M=d;this.B=this.i;return d};k.jo=function(){return this.f};k.mo=function(a){tb(this.g,a);this.s()};k.lo=function(a){this.M=null;this.f=a;this.s()};function Iw(a){var b=a.imageExtent,c=void 0!==a.crossOrigin?a.crossOrigin:null,d=a.imageLoadFunction?a.imageLoadFunction:Nv;Hv.call(this,{attributions:a.attributions,logo:a.logo,projection:Tb(a.projection)});this.M=new Js(b,void 0,1,this.j,a.url,c,d);this.f=a.imageSize?a.imageSize:null;y(this.M,"change",this.o,this)}v(Iw,Hv);Iw.prototype.Jc=function(a){return qb(a,this.M.G())?this.M:null}; -Iw.prototype.o=function(a){if(2==this.M.getState()){var b=this.M.G(),c=this.M.Y();if(this.f){var d=this.f[0];var e=this.f[1]}else d=c.width,e=c.height;b=Math.ceil(lb(b)/(mb(b)/e));if(b!=d){var b=jd(b,e),f=b.canvas;b.drawImage(c,0,0,d,e,0,0,f.width,f.height);this.M.Og(f)}}Hv.prototype.o.call(this,a)};function Jw(a){a=a||{};Hv.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions});this.pa=void 0!==a.crossOrigin?a.crossOrigin:null;this.g=a.url;this.v=a.imageLoadFunction?a.imageLoadFunction:Nv;this.f=a.params||{};this.l=!0;Kw(this);this.T=a.serverType;this.va=void 0!==a.hidpi?a.hidpi:!0;this.M=null;this.B=[0,0];this.R=0;this.I=void 0!==a.ratio?a.ratio:1.5}v(Jw,Hv);var Lw=[101,101];k=Jw.prototype; -k.so=function(a,b,c,d){if(void 0!==this.g){var e=ob(a,b,0,Lw),f={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.f.LAYERS};tb(f,this.f,d);d=Math.floor((e[3]-a[1])/b);f[this.l?"I":"X"]=Math.floor((a[0]-e[0])/b);f[this.l?"J":"Y"]=d;return Mw(this,e,Lw,1,Tb(c),f)}};k.uo=function(){return this.f}; -k.Jc=function(a,b,c,d){if(void 0===this.g)return null;b=Iv(this,b);1==c||this.va&&void 0!==this.T||(c=1);var e=b/c,f=nb(a),g=ob(f,e,0,[Math.ceil(lb(a)/e),Math.ceil(mb(a)/e)]);a=ob(f,e,0,[Math.ceil(this.I*lb(a)/e),Math.ceil(this.I*mb(a)/e)]);if((f=this.M)&&this.R==this.i&&f.resolution==b&&f.a==c&&Va(f.G(),g))return f;g={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};tb(g,this.f);this.B[0]=Math.round(lb(a)/e);this.B[1]=Math.round(mb(a)/e);d=Mw(this,a,this.B,c,d,g); -this.M=new Js(a,b,c,this.j,d,this.pa,this.v);this.R=this.i;y(this.M,"change",this.o,this);return this.M};k.to=function(){return this.v}; -function Mw(a,b,c,d,e,f){xa(void 0!==a.g,9);f[a.l?"CRS":"SRS"]=e.mb;"STYLES"in a.f||(f.STYLES="");if(1!=d)switch(a.T){case "geoserver":d=90*d+.5|0;f.FORMAT_OPTIONS="FORMAT_OPTIONS"in f?f.FORMAT_OPTIONS+(";dpi:"+d):"dpi:"+d;break;case "mapserver":f.MAP_RESOLUTION=90*d;break;case "carmentaserver":case "qgis":f.DPI=90*d;break;default:xa(!1,8)}f.WIDTH=c[0];f.HEIGHT=c[1];c=e.b;var g;a.l&&"ne"==c.substr(0,2)?g=[b[1],b[0],b[3],b[2]]:g=b;f.BBOX=g.join(",");return Fw(a.g,f)}k.vo=function(){return this.g}; -k.wo=function(a){this.M=null;this.v=a;this.s()};k.xo=function(a){a!=this.g&&(this.g=a,this.M=null,this.s())};k.yo=function(a){tb(this.f,a);Kw(this);this.M=null;this.s()};function Kw(a){a.l=0<=Ye(a.f.VERSION||"1.3.0")};function Nw(a){a=a||{};var b;void 0!==a.attributions?b=a.attributions:b=[Ow];zw.call(this,{attributions:b,cacheSize:a.cacheSize,crossOrigin:void 0!==a.crossOrigin?a.crossOrigin:"anonymous",opaque:void 0!==a.opaque?a.opaque:!0,maxZoom:void 0!==a.maxZoom?a.maxZoom:19,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileLoadFunction:a.tileLoadFunction,url:void 0!==a.url?a.url:"https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",wrapX:a.wrapX})}v(Nw,zw);var Ow=new Ac({html:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors.'});Fj.df={};Fj.df.Af=function(){}; -(function(a){function b(a,b,c){if(g)return new ImageData(a,b,c);b=h.createImageData(b,c);b.data.set(a);return b}function c(a){var b=!0;try{new ImageData(10,10)}catch(n){b=!1}return function(c){var d=c.buffers,e=c.meta,f=c.width,g=c.height,h=d.length,l=d[0].byteLength;if(c.imageOps){l=Array(h);for(c=0;c<h;++c){var m=c;var n=new Uint8ClampedArray(d[c]);var L=f,oa=g;n=b?new ImageData(n,L,oa):{data:n,width:L,height:oa};l[m]=n}f=a(l,e).data}else{f=new Uint8ClampedArray(l);g=Array(h);m=Array(h);for(c=0;c< -h;++c)g[c]=new Uint8ClampedArray(d[c]),m[c]=[0,0,0,0];for(d=0;d<l;d+=4){for(c=0;c<h;++c)n=g[c],m[c][0]=n[d],m[c][1]=n[d+1],m[c][2]=n[d+2],m[c][3]=n[d+3];c=a(m,e);f[d]=c[0];f[d+1]=c[1];f[d+2]=c[2];f[d+3]=c[3]}}return f.buffer}}function d(a,b){var d=Object.keys(a.lib||{}).map(function(b){return"var "+b+" = "+a.lib[b].toString()+";"}).concat(["var __minion__ = ("+c.toString()+")(",a.operation.toString(),");",'self.addEventListener("message", function(event) {'," var buffer = __minion__(event.data);", -" self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);","});"]),d=URL.createObjectURL(new Blob(d,{type:"text/javascript"})),d=new Worker(d);d.addEventListener("message",b);return d}function e(a,b){var d=c(a.operation);return{postMessage:function(a){setTimeout(function(){b({data:{buffer:d(a),meta:a.meta}})},0)}}}function f(a){this.Ff=!!a.$l;var b;0===a.threads?b=0:this.Ff?b=1:b=a.threads||1;var c=[];if(b)for(var f=0;f<b;++f)c[f]=d(a,this.gh.bind(this,f));else c[0]=e(a,this.gh.bind(this, -0));this.qe=c;this.Ed=[];this.fk=a.rp||Infinity;this.oe=0;this.bd={};this.Gf=null}var g=!0;try{new ImageData(10,10)}catch(l){g=!1}var h=document.createElement("canvas").getContext("2d");f.prototype.pp=function(a,b,c){this.dk({inputs:a,Qh:b,callback:c});this.dh()};f.prototype.dk=function(a){for(this.Ed.push(a);this.Ed.length>this.fk;)this.Ed.shift().callback(null,null)};f.prototype.dh=function(){if(0===this.oe&&0<this.Ed.length){var a=this.Gf=this.Ed.shift(),b=a.inputs[0].width,c=a.inputs[0].height, -d=a.inputs.map(function(a){return a.data.buffer}),e=this.qe.length;this.oe=e;if(1===e)this.qe[0].postMessage({buffers:d,meta:a.Qh,imageOps:this.Ff,width:b,height:c},d);else for(var f=4*Math.ceil(a.inputs[0].data.length/4/e),g=0;g<e;++g){for(var h=g*f,B=[],E=0,A=d.length;E<A;++E)B.push(d[g].slice(h,h+f));this.qe[g].postMessage({buffers:B,meta:a.Qh,imageOps:this.Ff,width:b,height:c},B)}}};f.prototype.gh=function(a,b){this.Jq||(this.bd[a]=b.data,--this.oe,0===this.oe&&this.gk())};f.prototype.gk=function(){var a= -this.Gf,c=this.qe.length;if(1===c){var d=new Uint8ClampedArray(this.bd[0].buffer);var e=this.bd[0].meta}else{var f=a.inputs[0].data.length;d=new Uint8ClampedArray(f);e=Array(f);for(var f=4*Math.ceil(f/4/c),g=0;g<c;++g){var h=g*f;d.set(new Uint8ClampedArray(this.bd[g].buffer),h);e[g]=this.bd[g].meta}}this.Gf=null;this.bd={};a.callback(null,b(d,a.inputs[0].width,a.inputs[0].height),e);this.dh()};a["default"]={Af:f};a.Af=f})(Fj.df=Fj.df||{});function Pw(a){this.B=null;this.va=void 0!==a.operationType?a.operationType:"pixel";this.Sa=void 0!==a.threads?a.threads:1;this.g=Qw(a.sources);for(var b=0,c=this.g.length;b<c;++b)y(this.g[b],"change",this.s,this);this.T=new Pe(function(){return 1},this.s.bind(this));for(var b=Rw(this.g),c={},d=0,e=b.length;d<e;++d)c[w(b[d].layer)]=b[d];this.f=null;this.I={animate:!1,attributions:{},coordinateToPixelTransform:Bh(),extent:null,focus:null,index:0,layerStates:c,layerStatesArray:b,logos:{},pixelRatio:1, -pixelToCoordinateTransform:Bh(),postRenderFunctions:[],size:[0,0],skippedFeatureUids:{},tileQueue:this.T,time:Date.now(),usedTiles:{},viewState:{rotation:0},viewHints:[],wantedTiles:{}};Hv.call(this,{});a.operation&&this.v(a.operation,a.lib)}v(Pw,Hv);Pw.prototype.v=function(a,b){this.B=new Fj.df.Af({operation:a,$l:"image"===this.va,rp:1,lib:b,threads:this.Sa});this.s()}; -Pw.prototype.Y=function(a,b,c,d){c=!0;for(var e,f=0,g=this.g.length;f<g;++f)if(e=this.g[f].a.ha(),"ready"!==e.getState()){c=!1;break}if(!c)return null;c=tb({},this.I);c.viewState=tb({},c.viewState);e=nb(a);c.extent=a.slice();c.focus=e;c.size[0]=Math.round(lb(a)/b);c.size[1]=Math.round(mb(a)/b);f=c.viewState;f.center=e;f.projection=d;f.resolution=b;this.l=c;Qe(c.tileQueue,16,16);this.f&&(d=this.f.resolution,c=this.f.G(),b===d&&bb(a,c)||(this.f=null));if(!this.f||this.i!==this.R)a:{a=this.l;d=this.g.length; -b=Array(d);for(c=0;c<d;++c){e=this.g[c];f=a;g=a.layerStatesArray[c];if(e.sd(f,g)){var h=f.size[0],l=f.size[1];if(Sw){var m=Sw.canvas;m.width!==h||m.height!==l?Sw=jd(h,l):Sw.clearRect(0,0,h,l)}else Sw=jd(h,l);e.S(f,g,Sw);e=Sw.getImageData(0,0,h,l)}else e=null;if(e)b[c]=e;else break a}d={};this.b(new Tw(Uw,a,d));this.B.pp(b,d,this.pa.bind(this,a))}return this.f}; -Pw.prototype.pa=function(a,b,c,d){if(!b&&c){b=a.extent;var e=a.viewState.resolution;if(e===this.l.viewState.resolution&&bb(b,this.l.extent)){if(this.f)var f=this.f.Y().getContext("2d");else f=jd(Math.round(lb(b)/e),Math.round(mb(b)/e)),this.f=new Ks(b,e,1,this.j,f.canvas);f.putImageData(c,0,0);this.s();this.R=this.i;this.b(new Tw(Vw,a,d))}}};var Sw=null;function Rw(a){return a.map(function(a){return th(a.a)})} -function Qw(a){for(var b=a.length,c=Array(b),d=0;d<b;++d){var e=d,f=a[d],g=null;f instanceof pw?(f=new cw({source:f}),g=new Vv(f)):f instanceof Hv&&(f=new Uv({source:f}),g=new zv(f));c[e]=g}return c}function Tw(a,b,c){Oc.call(this,a);this.extent=b.extent;this.resolution=b.viewState.resolution/b.pixelRatio;this.data=c}v(Tw,Oc);Pw.prototype.Jc=function(){return null};var Uw="beforeoperations",Vw="afteroperations";function Ww(a){var b=a.layer.indexOf("-"),b=Xw[-1==b?a.layer:a.layer.slice(0,b)],c=Yw[a.layer];zw.call(this,{attributions:Zw,cacheSize:a.cacheSize,crossOrigin:"anonymous",maxZoom:void 0!=a.maxZoom?a.maxZoom:b.maxZoom,minZoom:void 0!=a.minZoom?a.minZoom:b.minZoom,opaque:c.opaque,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileLoadFunction:a.tileLoadFunction,url:void 0!==a.url?a.url:"https://stamen-tiles-{a-d}.a.ssl.fastly.net/"+a.layer+"/{z}/{x}/{y}."+c.Lb,wrapX:a.wrapX})}v(Ww,zw); -var Zw=[new Ac({html:'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.'}),Ow],Yw={terrain:{Lb:"jpg",opaque:!0},"terrain-background":{Lb:"jpg",opaque:!0},"terrain-labels":{Lb:"png",opaque:!1},"terrain-lines":{Lb:"png",opaque:!1},"toner-background":{Lb:"png",opaque:!0},toner:{Lb:"png",opaque:!0},"toner-hybrid":{Lb:"png",opaque:!1},"toner-labels":{Lb:"png",opaque:!1},"toner-lines":{Lb:"png",opaque:!1},"toner-lite":{Lb:"png", -opaque:!0},watercolor:{Lb:"jpg",opaque:!0}},Xw={terrain:{minZoom:4,maxZoom:18},toner:{minZoom:0,maxZoom:20},watercolor:{minZoom:1,maxZoom:16}};function $w(a){a=a||{};X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction,url:a.url,urls:a.urls,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.f=a.params||{};this.l=Oa();qw(this,ax(this))}v($w,X);function ax(a){var b=0,c=[],d;for(d in a.f)c[b++]=d+"-"+a.f[d];return c.join("/")}$w.prototype.C=function(){return this.f}; -$w.prototype.nb=function(a){return a}; -$w.prototype.Fc=function(a,b,c){var d=this.tileGrid;d||(d=this.Ta(c));if(!(d.b.length<=a[0])){var e=d.Aa(a,this.l),f=Ma(d.gb(a[0]),this.o);1!=b&&(f=La(f,b,this.o));d={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};tb(d,this.f);var g=this.urls;g?(c=c.mb.split(":").pop(),d.SIZE=f[0]+","+f[1],d.BBOX=e.join(","),d.BBOXSR=c,d.IMAGESR=c,d.DPI=Math.round(d.DPI?d.DPI*b:90*b),a=(1==g.length?g[0]:g[Ia((a[1]<<a[0])+a[2],g.length)]).replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage"), -a=Fw(a,d)):a=void 0;return a}};$w.prototype.B=function(a){tb(this.f,a);qw(this,ax(this))};function bx(a){pw.call(this,{opaque:!1,projection:a.projection,tileGrid:a.tileGrid,wrapX:void 0!==a.wrapX?a.wrapX:!0})}v(bx,pw);bx.prototype.Nc=function(a,b,c){var d=this.Sb(a,b,c);if(this.a.b.hasOwnProperty(d))return this.a.get(d);var e=Ma(this.tileGrid.gb(a));a=[a,b,c];b=(b=rw(this,a))?rw(this,b).toString():"";e=new cx(a,e,b);this.a.set(d,e);return e};function cx(a,b,c){Ls.call(this,a,2);this.c=b;this.Ia=c;this.a=null}v(cx,Ls); -cx.prototype.Y=function(){if(this.a)return this.a;var a=this.c,b=jd(a[0],a[1]);b.strokeStyle="black";b.strokeRect(.5,.5,a[0]+.5,a[1]+.5);b.fillStyle="black";b.textAlign="center";b.textBaseline="middle";b.font="24px sans-serif";b.fillText(this.Ia,a[0]/2,a[1]/2);return this.a=b.canvas};cx.prototype.load=function(){};function dx(a){this.f=null;X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,projection:Tb("EPSG:3857"),reprojectionErrorThreshold:a.reprojectionErrorThreshold,state:"loading",tileLoadFunction:a.tileLoadFunction,wrapX:void 0!==a.wrapX?a.wrapX:!0});if(a.url)if(a.jsonp)gw(a.url,this.og.bind(this),this.Ve.bind(this));else{var b=new XMLHttpRequest;b.addEventListener("load",this.Ao.bind(this));b.addEventListener("error",this.zo.bind(this));b.open("GET",a.url);b.send()}else a.tileJSON? -this.og(a.tileJSON):xa(!1,51)}v(dx,X);k=dx.prototype;k.Ao=function(a){a=a.target;if(!a.status||200<=a.status&&300>a.status){try{var b=JSON.parse(a.responseText)}catch(c){this.Ve();return}this.og(b)}else this.Ve()};k.zo=function(){this.Ve()};k.pl=function(){return this.f}; -k.og=function(a){var b=Tb("EPSG:4326"),c=this.c;if(a.bounds){var d=Vb(b,c);var e=sb(a.bounds,d)}var f=a.minzoom||0,d=a.maxzoom||22;this.tileGrid=c=xc({extent:zc(c),maxZoom:d,minZoom:f});this.tileUrlFunction=jw(a.tiles,c);if(void 0!==a.attribution&&!this.j){b=void 0!==e?e:b.G();e={};for(var g;f<=d;++f)g=f.toString(),e[g]=[oc(c,b,f)];this.ua([new Ac({html:a.attribution,tileRanges:e})])}this.f=a;bu(this,"ready")};k.Ve=function(){bu(this,"error")};function ex(a){pw.call(this,{projection:Tb("EPSG:3857"),state:"loading"});this.v=void 0!==a.preemptive?a.preemptive:!0;this.l=lw;this.g=void 0;this.f=a.jsonp||!1;if(a.url)if(this.f)gw(a.url,this.pg.bind(this),this.We.bind(this));else{var b=new XMLHttpRequest;b.addEventListener("load",this.Eo.bind(this));b.addEventListener("error",this.Do.bind(this));b.open("GET",a.url);b.send()}else a.tileJSON?this.pg(a.tileJSON):xa(!1,51)}v(ex,pw);k=ex.prototype; -k.Eo=function(a){a=a.target;if(!a.status||200<=a.status&&300>a.status){try{var b=JSON.parse(a.responseText)}catch(c){this.We();return}this.pg(b)}else this.We()};k.Do=function(){this.We()};k.ml=function(){return this.g};k.zk=function(a,b,c,d,e){this.tileGrid?(b=this.tileGrid.Be(a,b),fx(this.Nc(b[0],b[1],b[2],1,this.c),a,c,d,e)):!0===e?setTimeout(function(){c.call(d,null)},0):c.call(d,null)};k.We=function(){bu(this,"error")}; -k.pg=function(a){var b=Tb("EPSG:4326"),c=this.c;if(a.bounds){var d=Vb(b,c);var e=sb(a.bounds,d)}var f=a.minzoom||0,d=a.maxzoom||22;this.tileGrid=c=xc({extent:zc(c),maxZoom:d,minZoom:f});this.g=a.template;var g=a.grids;if(g){this.l=jw(g,c);if(void 0!==a.attribution){b=void 0!==e?e:b.G();for(e={};f<=d;++f)g=f.toString(),e[g]=[oc(c,b,f)];this.ua([new Ac({html:a.attribution,tileRanges:e})])}bu(this,"ready")}else bu(this,"error")}; -k.Nc=function(a,b,c,d,e){var f=this.Sb(a,b,c);if(this.a.b.hasOwnProperty(f))return this.a.get(f);a=[a,b,c];b=rw(this,a,e);d=this.l(b,d,e);d=new gx(a,void 0!==d?0:4,void 0!==d?d:"",this.tileGrid.Aa(a),this.v,this.f);this.a.set(f,d);return d};k.Ug=function(a,b,c){a=this.Sb(a,b,c);this.a.b.hasOwnProperty(a)&&this.a.get(a)};function gx(a,b,c,d,e,f){Ls.call(this,a,b);this.o=c;this.a=d;this.v=e;this.c=this.j=this.g=null;this.l=f}v(gx,Ls);k=gx.prototype;k.Y=function(){return null}; -k.getData=function(a){if(!this.g||!this.j)return null;var b=this.g[Math.floor((1-(a[1]-this.a[1])/(this.a[3]-this.a[1]))*this.g.length)];if("string"!==typeof b)return null;b=b.charCodeAt(Math.floor((a[0]-this.a[0])/(this.a[2]-this.a[0])*b.length));93<=b&&b--;35<=b&&b--;b-=32;a=null;b in this.j&&(b=this.j[b],this.c&&b in this.c?a=this.c[b]:a=b);return a}; -function fx(a,b,c,d,e){0==a.state&&!0===e?(Jc(a,"change",function(){c.call(d,this.getData(b))},a),hx(a)):!0===e?setTimeout(function(){c.call(d,this.getData(b))}.bind(a),0):c.call(d,a.getData(b))}k.bb=function(){return this.o};k.De=function(){this.state=3;this.s()};k.Ji=function(a){this.g=a.grid;this.j=a.keys;this.c=a.data;this.state=4;this.s()}; -function hx(a){if(0==a.state)if(a.state=1,a.l)gw(a.o,a.Ji.bind(a),a.De.bind(a));else{var b=new XMLHttpRequest;b.addEventListener("load",a.Co.bind(a));b.addEventListener("error",a.Bo.bind(a));b.open("GET",a.o);b.send()}}k.Co=function(a){a=a.target;if(!a.status||200<=a.status&&300>a.status){try{var b=JSON.parse(a.responseText)}catch(c){this.De();return}this.Ji(b)}else this.De()};k.Bo=function(){this.De()};k.load=function(){this.v&&hx(this)};function ix(a){a=a||{};var b=a.params||{};X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,opaque:!("TRANSPARENT"in b?b.TRANSPARENT:1),projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction,url:a.url,urls:a.urls,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.C=void 0!==a.gutter?a.gutter:0;this.f=b;this.l=!0;this.B=a.serverType;this.T=void 0!==a.hidpi?a.hidpi:!0;this.R=""; -jx(this);this.fa=Oa();kx(this);qw(this,lx(this))}v(ix,X);k=ix.prototype; -k.Fo=function(a,b,c,d){c=Tb(c);var e=this.tileGrid;e||(e=this.Ta(c));b=e.Be(a,b);if(!(e.b.length<=b[0])){var f=e.Da(b[0]),g=e.Aa(b,this.fa),e=Ma(e.gb(b[0]),this.o),h=this.C;h&&(e=Ka(e,h,this.o),g=Qa(g,f*h,g));h={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.f.LAYERS};tb(h,this.f,d);d=Math.floor((g[3]-a[1])/f);h[this.l?"I":"X"]=Math.floor((a[0]-g[0])/f);h[this.l?"J":"Y"]=d;return mx(this,b,e,g,1,c,h)}};k.Xf=function(){return this.C}; -k.Sb=function(a,b,c){return this.R+X.prototype.Sb.call(this,a,b,c)};k.Go=function(){return this.f}; -function mx(a,b,c,d,e,f,g){var h=a.urls;if(h){g.WIDTH=c[0];g.HEIGHT=c[1];g[a.l?"CRS":"SRS"]=f.mb;"STYLES"in a.f||(g.STYLES="");if(1!=e)switch(a.B){case "geoserver":c=90*e+.5|0;g.FORMAT_OPTIONS="FORMAT_OPTIONS"in g?g.FORMAT_OPTIONS+(";dpi:"+c):"dpi:"+c;break;case "mapserver":g.MAP_RESOLUTION=90*e;break;case "carmentaserver":case "qgis":g.DPI=90*e;break;default:xa(!1,52)}f=f.b;a.l&&"ne"==f.substr(0,2)&&(a=d[0],d[0]=d[1],d[1]=a,a=d[2],d[2]=d[3],d[3]=a);g.BBOX=d.join(",");return Fw(1==h.length?h[0]:h[Ia((b[1]<< -b[0])+b[2],h.length)],g)}}k.nb=function(a){return this.T&&void 0!==this.B?a:1};function jx(a){var b=0,c=[];if(a.urls){var d;var e=0;for(d=a.urls.length;e<d;++e)c[b++]=a.urls[e]}a.R=c.join("#")}function lx(a){var b=0,c=[],d;for(d in a.f)c[b++]=d+"-"+a.f[d];return c.join("/")} -k.Fc=function(a,b,c){var d=this.tileGrid;d||(d=this.Ta(c));if(!(d.b.length<=a[0])){1==b||this.T&&void 0!==this.B||(b=1);var e=d.Da(a[0]),f=d.Aa(a,this.fa),d=Ma(d.gb(a[0]),this.o),g=this.C;g&&(d=Ka(d,g,this.o),f=Qa(f,e*g,f));1!=b&&(d=La(d,b,this.o));e={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};tb(e,this.f);return mx(this,a,d,f,b,c,e)}};k.eb=function(a){X.prototype.eb.call(this,a);jx(this)};k.Ho=function(a){tb(this.f,a);jx(this);kx(this);qw(this,lx(this))}; -function kx(a){a.l=0<=Ye(a.f.VERSION||"1.3.0")};function nx(a,b,c,d,e,f,g,h,l,m,n,p,q,r){Ls.call(this,a,b);this.j=null;this.o={Nd:!1,Kg:null,mf:-1,Lg:-1};this.c=m;this.a=[];this.u=c;this.v=f;this.g=[];this.l=[];if(f){var u=l.Aa(f),x=l.Da(a[0]);h.Rf(u,h.tc(x),function(a){var b=pb(u,h.Aa(a));if(.5<=lb(b)/x&&.5<=mb(b)/x){var b=a.toString(),c=m[b];c||(c=g(a,n,p),c=m[b]=new q(a,void 0==c?4:0,void 0==c?"":c,d,e),this.l.push(y(c,"change",r)));c.j++;this.a.push(b)}}.bind(this))}}v(nx,Ls); -nx.prototype.ka=function(){for(var a=0,b=this.a.length;a<b;++a){var c=this.a[a],d=this.c[c];d.j--;d.j||(delete this.c[c],Nc(d))}this.a.length=0;this.c=null;1==this.state&&(this.g.forEach(Ec),this.g.length=0);this.i&&Nc(this.i);this.state=5;this.s();this.l.forEach(Ec);this.l.length=0;Ls.prototype.ka.call(this)};nx.prototype.Y=function(){return-1==this.o.Lg?null:this.j.canvas};nx.prototype.bb=function(){return this.a.join("/")+"/"+this.u}; -nx.prototype.load=function(){var a=0,b=!1;0==this.state&&Ns(this,1);1==this.state&&this.a.forEach(function(c){var d=this.c[c];0==d.state?(d.Pg(this.S),d.load()):3==d.state?b=!0:4==d.state&&ma(this.a,c);if(1==d.state){var e=y(d,"change",function(){var f=d.getState();if(2==f||3==f)--a,Ec(e),ma(this.g,e),3==f&&(ma(this.a,c),b=!0),a||Ns(this,0<this.a.length?2:3)}.bind(this));this.g.push(e);++a}}.bind(this));a||setTimeout(function(){Ns(this,0<this.a.length?2:b?3:4)}.bind(this),0)}; -function ox(a,b){a.Pg(Cl(b,a.o,a.$o.bind(a),a.Zo.bind(a)))};function px(a,b,c,d,e){Ls.call(this,a,b);this.j=0;this.o=d;this.g=null;this.c={};this.u=e;this.l=c}v(px,Ls);k=px.prototype;k.ka=function(){this.g=null;this.c={};this.state=5;this.s();Ls.prototype.ka.call(this)};k.Lm=function(){return this.o};k.Km=function(){return this.g};k.bb=function(){return this.l};k.Mm=function(){return this.a};k.load=function(){0==this.state&&(Ns(this,1),this.u(this,this.l),this.v(null,NaN,null))};k.$o=function(a,b){this.ig(b);this.mj(a)};k.Zo=function(){Ns(this,3)}; -k.mj=function(a){this.g=a;Ns(this,2)};k.ig=function(a){this.a=a};k.Pg=function(a){this.v=a};function qx(a){tw.call(this,{attributions:a.attributions,cacheSize:void 0!==a.cacheSize?a.cacheSize:128,extent:a.extent,logo:a.logo,opaque:!1,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction?a.tileLoadFunction:ox,tileUrlFunction:a.tileUrlFunction,tilePixelRatio:a.tilePixelRatio,url:a.url,urls:a.urls,wrapX:void 0===a.wrapX?!0:a.wrapX});this.g=a.format?a.format:null;this.v={};this.l=void 0==a.overlaps?!0:a.overlaps;this.tileClass=a.tileClass?a.tileClass: -px;this.f={};this.tileGrid||(this.tileGrid=this.Ta(Tb(a.projection||"EPSG:3857")))}v(qx,tw);qx.prototype.Nc=function(a,b,c,d,e){var f=this.Sb(a,b,c);if(this.a.b.hasOwnProperty(f))return this.a.get(f);a=[a,b,c];c=(b=rw(this,a,e))?this.tileUrlFunction(b,d,e):void 0;d=new nx(a,void 0!==c?0:4,void 0!==c?c:"",this.g,this.tileLoadFunction,b,this.tileUrlFunction,this.tileGrid,this.Ta(e),this.v,d,e,this.tileClass,this.Li.bind(this));this.a.set(f,d);return d}; -qx.prototype.Ta=function(a){var b=a.mb,c=this.f[b];c||(c=this.tileGrid,c=this.f[b]=wc(a,void 0,c?c.gb(c.minZoom):void 0));return c};qx.prototype.nb=function(a){return void 0==a?tw.prototype.nb.call(this,a):a};qx.prototype.Xd=function(a,b,c){a=Ma(this.Ta(c).gb(a));return[Math.round(a[0]*b),Math.round(a[1]*b)]};function rx(a){this.o=a.matrixIds;lc.call(this,{extent:a.extent,origin:a.origin,origins:a.origins,resolutions:a.resolutions,tileSize:a.tileSize,tileSizes:a.tileSizes,sizes:a.sizes})}v(rx,lc);rx.prototype.l=function(){return this.o}; -function sx(a,b,c){var d=[],e=[],f=[],g=[],h=[],l=void 0!==c?c:[];c=Tb(a.SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"));var m=c.sc(),n="ne"==c.b.substr(0,2);a.TileMatrix.sort(function(a,b){return b.ScaleDenominator-a.ScaleDenominator});a.TileMatrix.forEach(function(a){var b;0<l.length?b=na(l,function(b){return a.Identifier==b.TileMatrix}):b=!0;if(b){e.push(a.Identifier);b=2.8E-4*a.ScaleDenominator/m;var c=a.TileWidth,p=a.TileHeight;n?f.push([a.TopLeftCorner[1],a.TopLeftCorner[0]]): -f.push(a.TopLeftCorner);d.push(b);g.push(c==p?c:[c,p]);h.push([a.MatrixWidth,-a.MatrixHeight])}});return new rx({extent:b,origins:f,resolutions:d,matrixIds:e,tileSizes:g,sizes:h})};function Z(a){function b(a){a="KVP"==d?Fw(a,f):a.replace(/\{(\w+?)\}/g,function(a,b){return b.toLowerCase()in f?f[b.toLowerCase()]:a});return function(b){if(b){var c={TileMatrix:e.o[b[0]],TileCol:b[1],TileRow:-b[2]-1};tb(c,g);b=a;return b="KVP"==d?Fw(b,c):b.replace(/\{(\w+?)\}/g,function(a,b){return c[b]})}}}this.fa=void 0!==a.version?a.version:"1.0.0";this.C=void 0!==a.format?a.format:"image/jpeg";this.f=a.dimensions?a.dimensions:{};this.B=a.layer;this.l=a.matrixSet;this.R=a.style;var c=a.urls;void 0=== -c&&void 0!==a.url&&(c=mw(a.url));var d=this.T=void 0!==a.requestEncoding?a.requestEncoding:"KVP",e=a.tileGrid,f={layer:this.B,style:this.R,tilematrixset:this.l};"KVP"==d&&tb(f,{Service:"WMTS",Request:"GetTile",Version:this.fa,Format:this.C});var g=this.f,h=c&&0<c.length?kw(c.map(b)):lw;X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileClass:a.tileClass,tileGrid:e, -tileLoadFunction:a.tileLoadFunction,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:h,urls:c,wrapX:void 0!==a.wrapX?a.wrapX:!1});qw(this,tx(this))}v(Z,X);k=Z.prototype;k.Mk=function(){return this.f};k.Io=function(){return this.C};k.Jo=function(){return this.B};k.$k=function(){return this.l};k.kl=function(){return this.T};k.Ko=function(){return this.R};k.rl=function(){return this.fa};function tx(a){var b=0,c=[],d;for(d in a.f)c[b++]=d+"-"+a.f[d];return c.join("/")} -k.uq=function(a){tb(this.f,a);qw(this,tx(this))};function ux(a){a=a||{};var b=a.size,c=b[0],d=b[1],e=[],f=256;switch(void 0!==a.tierSizeCalculation?a.tierSizeCalculation:vx){case vx:for(;c>f||d>f;)e.push([Math.ceil(c/f),Math.ceil(d/f)]),f+=f;break;case wx:for(;c>f||d>f;)e.push([Math.ceil(c/f),Math.ceil(d/f)]),c>>=1,d>>=1;break;default:xa(!1,53)}e.push([1,1]);e.reverse();for(var f=[1],g=[0],d=1,c=e.length;d<c;d++)f.push(1<<d),g.push(e[d-1][0]*e[d-1][1]+g[d-1]);f.reverse();b=[0,-b[1],b[0],0];b=new lc({extent:b,origin:ib(b),resolutions:f});(f=a.url)&& --1==f.indexOf("{TileGroup}")&&(f+="{TileGroup}/{z}-{x}-{y}.jpg");f=mw(f);f=kw(f.map(function(a){return function(b){if(b){var c=b[0],d=b[1];b=-b[2]-1;var f={z:c,x:d,y:b,TileGroup:"TileGroup"+((d+b*e[c][0]+g[c])/256|0)};return a.replace(/\{(\w+?)\}/g,function(a,b){return f[b]})}}}));X.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileClass:xx,tileGrid:b,tileUrlFunction:f})} -v(ux,X);function xx(a,b,c,d,e){Os.call(this,a,b,c,d,e);this.a=null}v(xx,Os);xx.prototype.Y=function(){if(this.a)return this.a;var a=Os.prototype.Y.call(this);if(2==this.state){if(256==a.width&&256==a.height)return this.a=a;var b=jd(256,256);b.drawImage(a,0,0);return this.a=b.canvas}return a};var vx="default",wx="truncated";function yx(a,b){this.b=b;this.a=[{x:0,y:0,width:a,height:a}];this.c={};this.i=jd(a,a);this.f=this.i.canvas}yx.prototype.get=function(a){return this.c[a]||null}; -yx.prototype.add=function(a,b,c,d,e){var f;var g=0;for(f=this.a.length;g<f;++g){var h=this.a[g];if(h.width>=b+this.b&&h.height>=c+this.b)return f={offsetX:h.x+this.b,offsetY:h.y+this.b,image:this.f},this.c[a]=f,d.call(e,this.i,h.x+this.b,h.y+this.b),a=g,b+=this.b,d=c+this.b,h.width-b>h.height-d?(c={x:h.x+b,y:h.y,width:h.width-b,height:h.height},b={x:h.x,y:h.y+d,width:b,height:h.height-d},zx(this,a,c,b)):(c={x:h.x+b,y:h.y,width:h.width-b,height:d},b={x:h.x,y:h.y+d,width:h.width,height:h.height-d}, -zx(this,a,c,b)),f}return null};function zx(a,b,c,d){b=[b,1];0<c.width&&0<c.height&&b.push(c);0<d.width&&0<d.height&&b.push(d);a.a.splice.apply(a.a,b)};function Ax(a){a=a||{};this.a=void 0!==a.initialSize?a.initialSize:256;this.i=void 0!==a.maxSize?a.maxSize:void 0!==ea?ea:2048;this.b=void 0!==a.space?a.space:1;this.f=[new yx(this.a,this.b)];this.c=this.a;this.g=[new yx(this.c,this.b)]}Ax.prototype.add=function(a,b,c,d,e,f){if(b+this.b>this.i||c+this.b>this.i)return null;d=Bx(this,!1,a,b,c,d,f);if(!d)return null;a=Bx(this,!0,a,b,c,e?e:ua,f);return{offsetX:d.offsetX,offsetY:d.offsetY,image:d.image,Zl:a.image}}; -function Bx(a,b,c,d,e,f,g){var h=b?a.g:a.f,l;var m=0;for(l=h.length;m<l;++m){var n=h[m];if(n=n.add(c,d,e,f,g))return n;n||m!==l-1||(b?(n=Math.min(2*a.c,a.i),a.c=n):(n=Math.min(2*a.a,a.i),a.a=n),n=new yx(n,a.b),h.push(n),++l)}return null};wa.prototype.code=wa.prototype.code;t("ol.Attribution",Ac);Ac.prototype.getHTML=Ac.prototype.i;t("ol.Collection",Yc);Yc.prototype.clear=Yc.prototype.clear;Yc.prototype.extend=Yc.prototype.fg;Yc.prototype.forEach=Yc.prototype.forEach;Yc.prototype.getArray=Yc.prototype.tm;Yc.prototype.item=Yc.prototype.item;Yc.prototype.getLength=Yc.prototype.dc;Yc.prototype.insertAt=Yc.prototype.He;Yc.prototype.pop=Yc.prototype.pop;Yc.prototype.push=Yc.prototype.push;Yc.prototype.remove=Yc.prototype.remove; -Yc.prototype.removeAt=Yc.prototype.Hg;Yc.prototype.setAt=Yc.prototype.Wp;bd.prototype.element=bd.prototype.element;t("ol.color.asArray",ed);t("ol.color.asString",gd);t("ol.colorlike.asColorLike",id);t("ol.control.defaults",xd);t("ol.coordinate.add",Ze);t("ol.coordinate.createStringXY",function(a){return function(b){return lf(b,a)}});t("ol.coordinate.format",cf);t("ol.coordinate.rotate",ef);t("ol.coordinate.toStringHDMS",function(a,b){return a?bf("NS",a[1],b)+" "+bf("EW",a[0],b):""}); -t("ol.coordinate.toStringXY",lf);t("ol.DeviceOrientation",Rk);Rk.prototype.getAlpha=Rk.prototype.Fk;Rk.prototype.getBeta=Rk.prototype.Ik;Rk.prototype.getGamma=Rk.prototype.Ok;Rk.prototype.getHeading=Rk.prototype.um;Rk.prototype.getTracking=Rk.prototype.Th;Rk.prototype.setTracking=Rk.prototype.gg;t("ol.easing.easeIn",qd);t("ol.easing.easeOut",rd);t("ol.easing.inAndOut",sd);t("ol.easing.linear",td);t("ol.easing.upAndDown",function(a){return.5>a?sd(2*a):1-sd(2*(a-.5))}); -t("ol.extent.boundingExtent",Na);t("ol.extent.buffer",Qa);t("ol.extent.containsCoordinate",Ta);t("ol.extent.containsExtent",Va);t("ol.extent.containsXY",Ua);t("ol.extent.createEmpty",Oa);t("ol.extent.equals",bb);t("ol.extent.extend",cb);t("ol.extent.getArea",jb);t("ol.extent.getBottomLeft",eb);t("ol.extent.getBottomRight",gb);t("ol.extent.getCenter",nb);t("ol.extent.getHeight",mb);t("ol.extent.getIntersection",pb);t("ol.extent.getSize",function(a){return[a[2]-a[0],a[3]-a[1]]}); -t("ol.extent.getTopLeft",ib);t("ol.extent.getTopRight",hb);t("ol.extent.getWidth",lb);t("ol.extent.intersects",qb);t("ol.extent.isEmpty",kb);t("ol.extent.applyTransform",sb);t("ol.Feature",H);H.prototype.clone=H.prototype.clone;H.prototype.getGeometry=H.prototype.V;H.prototype.getId=H.prototype.wm;H.prototype.getGeometryName=H.prototype.Qk;H.prototype.getStyle=H.prototype.xm;H.prototype.getStyleFunction=H.prototype.Lc;H.prototype.setGeometry=H.prototype.Ra;H.prototype.setStyle=H.prototype.hg; -H.prototype.setId=H.prototype.jc;H.prototype.setGeometryName=H.prototype.Tc;t("ol.featureloader.xhr",Dl);t("ol.Geolocation",xs);xs.prototype.getAccuracy=xs.prototype.Dk;xs.prototype.getAccuracyGeometry=xs.prototype.Ek;xs.prototype.getAltitude=xs.prototype.Gk;xs.prototype.getAltitudeAccuracy=xs.prototype.Hk;xs.prototype.getHeading=xs.prototype.ym;xs.prototype.getPosition=xs.prototype.zm;xs.prototype.getProjection=xs.prototype.Uh;xs.prototype.getSpeed=xs.prototype.ll;xs.prototype.getTracking=xs.prototype.Vh; -xs.prototype.getTrackingOptions=xs.prototype.Gh;xs.prototype.setProjection=xs.prototype.Wh;xs.prototype.setTracking=xs.prototype.Ke;xs.prototype.setTrackingOptions=xs.prototype.wj;t("ol.Graticule",Ds);Ds.prototype.getMap=Ds.prototype.Cm;Ds.prototype.getMeridians=Ds.prototype.al;Ds.prototype.getParallels=Ds.prototype.hl;Ds.prototype.setMap=Ds.prototype.setMap;t("ol.has.DEVICE_PIXEL_RATIO",Sd);t("ol.has.CANVAS",Ud);t("ol.has.DEVICE_ORIENTATION",Vd);t("ol.has.GEOLOCATION",Wd);t("ol.has.TOUCH",Xd); -t("ol.has.WEBGL",Md);Js.prototype.getImage=Js.prototype.Y;Js.prototype.load=Js.prototype.load;Os.prototype.getImage=Os.prototype.Y;t("ol.inherits",v);t("ol.interaction.defaults",qh);t("ol.Kinetic",kg);t("ol.loadingstrategy.all",Zt);t("ol.loadingstrategy.bbox",function(a){return[a]});t("ol.loadingstrategy.tile",function(a){return function(b,c){c=a.tc(c);b=oc(a,b,c);var d=[];c=[c,0,0];for(c[1]=b.ca;c[1]<=b.$;++c[1])for(c[2]=b.da;c[2]<=b.ia;++c[2])d.push(a.Aa(c));return d}});t("ol.Map",G); -G.prototype.addControl=G.prototype.kk;G.prototype.addInteraction=G.prototype.lk;G.prototype.addLayer=G.prototype.ih;G.prototype.addOverlay=G.prototype.jh;G.prototype.forEachFeatureAtPixel=G.prototype.we;G.prototype.forEachLayerAtPixel=G.prototype.Im;G.prototype.hasFeatureAtPixel=G.prototype.Yl;G.prototype.getEventCoordinate=G.prototype.Tf;G.prototype.getEventPixel=G.prototype.xe;G.prototype.getTarget=G.prototype.ag;G.prototype.getTargetElement=G.prototype.jd;G.prototype.getCoordinateFromPixel=G.prototype.Wa; -G.prototype.getControls=G.prototype.Lk;G.prototype.getOverlays=G.prototype.fl;G.prototype.getOverlayById=G.prototype.el;G.prototype.getInteractions=G.prototype.Sk;G.prototype.getLayerGroup=G.prototype.Kc;G.prototype.getLayers=G.prototype.Xh;G.prototype.getPixelFromCoordinate=G.prototype.Ja;G.prototype.getSize=G.prototype.Ob;G.prototype.getView=G.prototype.Z;G.prototype.getViewport=G.prototype.sl;G.prototype.renderSync=G.prototype.Tp;G.prototype.render=G.prototype.render; -G.prototype.removeControl=G.prototype.Mp;G.prototype.removeInteraction=G.prototype.Np;G.prototype.removeLayer=G.prototype.Pp;G.prototype.removeOverlay=G.prototype.Qp;G.prototype.setLayerGroup=G.prototype.qj;G.prototype.setSize=G.prototype.Qg;G.prototype.setTarget=G.prototype.Le;G.prototype.setView=G.prototype.iq;G.prototype.updateSize=G.prototype.Ad;Jd.prototype.originalEvent=Jd.prototype.originalEvent;Jd.prototype.pixel=Jd.prototype.pixel;Jd.prototype.coordinate=Jd.prototype.coordinate; -Jd.prototype.dragging=Jd.prototype.dragging;Id.prototype.map=Id.prototype.map;Id.prototype.frameState=Id.prototype.frameState;t("ol.Object",Tc);Tc.prototype.get=Tc.prototype.get;Tc.prototype.getKeys=Tc.prototype.O;Tc.prototype.getProperties=Tc.prototype.N;Tc.prototype.set=Tc.prototype.set;Tc.prototype.setProperties=Tc.prototype.H;Tc.prototype.unset=Tc.prototype.P;Xc.prototype.key=Xc.prototype.key;Xc.prototype.oldValue=Xc.prototype.oldValue;t("ol.Observable",Sc); -t("ol.Observable.unByKey",function(a){if(Array.isArray(a))for(var b=0,c=a.length;b<c;++b)Ec(a[b]);else Ec(a)});Sc.prototype.changed=Sc.prototype.s;Sc.prototype.dispatchEvent=Sc.prototype.b;Sc.prototype.getRevision=Sc.prototype.L;Sc.prototype.on=Sc.prototype.J;Sc.prototype.once=Sc.prototype.once;Sc.prototype.un=Sc.prototype.K;t("ol.Overlay",sk);sk.prototype.getElement=sk.prototype.Rd;sk.prototype.getId=sk.prototype.Jm;sk.prototype.getMap=sk.prototype.Me;sk.prototype.getOffset=sk.prototype.Dh; -sk.prototype.getPosition=sk.prototype.Yh;sk.prototype.getPositioning=sk.prototype.Eh;sk.prototype.setElement=sk.prototype.lj;sk.prototype.setMap=sk.prototype.setMap;sk.prototype.setOffset=sk.prototype.rj;sk.prototype.setPosition=sk.prototype.Ne;sk.prototype.setPositioning=sk.prototype.uj;t("ol.proj.METERS_PER_UNIT",zb);t("ol.proj.setProj4",function(a){Ab=a});t("ol.proj.getPointResolution",Sb);t("ol.proj.addEquivalentProjections",Wb);t("ol.proj.addProjection",Xb); -t("ol.proj.addCoordinateTransforms",ac);t("ol.proj.fromLonLat",function(a,b){return gc(a,"EPSG:4326",void 0!==b?b:"EPSG:3857")});t("ol.proj.toLonLat",function(a,b){return gc(a,void 0!==b?b:"EPSG:3857","EPSG:4326")});t("ol.proj.get",Tb);t("ol.proj.equivalent",dc);t("ol.proj.getTransform",ec);t("ol.proj.transform",gc);t("ol.proj.transformExtent",hc); -t("ol.render.toContext",function(a,b){var c=a.canvas,d=b?b:{};b=d.pixelRatio||Sd;if(d=d.size)c.width=d[0]*b,c.height=d[1]*b,c.style.width=d[0]+"px",c.style.height=d[1]+"px";c=[0,0,c.width,c.height];d=Ih(Bh(),b,b);return new Xh(a,b,c,d,0)});t("ol.size.toSize",Ma);t("ol.Sphere",xb);xb.prototype.geodesicArea=xb.prototype.a;xb.prototype.haversineDistance=xb.prototype.b;Ls.prototype.getTileCoord=Ls.prototype.f;Ls.prototype.load=Ls.prototype.load;t("ol.tilegrid.createXYZ",xc);px.prototype.getFormat=px.prototype.Lm; -px.prototype.getFeatures=px.prototype.Km;px.prototype.getProjection=px.prototype.Mm;px.prototype.setFeatures=px.prototype.mj;px.prototype.setProjection=px.prototype.ig;px.prototype.setLoader=px.prototype.Pg;t("ol.View",F);F.prototype.animate=F.prototype.animate;F.prototype.getAnimating=F.prototype.Ic;F.prototype.getInteracting=F.prototype.Rk;F.prototype.cancelAnimations=F.prototype.ed;F.prototype.constrainCenter=F.prototype.Ec;F.prototype.constrainResolution=F.prototype.constrainResolution; -F.prototype.constrainRotation=F.prototype.constrainRotation;F.prototype.getCenter=F.prototype.wa;F.prototype.calculateExtent=F.prototype.dd;F.prototype.getMaxResolution=F.prototype.Nm;F.prototype.getMinResolution=F.prototype.Pm;F.prototype.getMaxZoom=F.prototype.Om;F.prototype.setMaxZoom=F.prototype.eq;F.prototype.getMinZoom=F.prototype.Qm;F.prototype.setMinZoom=F.prototype.fq;F.prototype.getProjection=F.prototype.Rm;F.prototype.getResolution=F.prototype.Pa;F.prototype.getResolutions=F.prototype.Sm; -F.prototype.getResolutionForExtent=F.prototype.ze;F.prototype.getRotation=F.prototype.Qa;F.prototype.getZoom=F.prototype.Hh;F.prototype.getZoomForResolution=F.prototype.Ce;F.prototype.fit=F.prototype.Qf;F.prototype.centerOn=F.prototype.uk;F.prototype.rotate=F.prototype.rotate;F.prototype.setCenter=F.prototype.ob;F.prototype.setResolution=F.prototype.Vc;F.prototype.setRotation=F.prototype.Oe;F.prototype.setZoom=F.prototype.lq;t("ol.xml.getAllTextContent",kl);t("ol.xml.parse",pl); -Oi.prototype.getGL=Oi.prototype.Wo;Oi.prototype.useProgram=Oi.prototype.Qc;t("ol.tilegrid.TileGrid",lc);lc.prototype.forEachTileCoord=lc.prototype.Rf;lc.prototype.getMaxZoom=lc.prototype.Ti;lc.prototype.getMinZoom=lc.prototype.Ui;lc.prototype.getOrigin=lc.prototype.Pc;lc.prototype.getResolution=lc.prototype.Da;lc.prototype.getResolutions=lc.prototype.Vi;lc.prototype.getTileCoordExtent=lc.prototype.Aa;lc.prototype.getTileCoordForCoordAndResolution=lc.prototype.Be; -lc.prototype.getTileCoordForCoordAndZ=lc.prototype.bg;lc.prototype.getTileSize=lc.prototype.gb;lc.prototype.getZForResolution=lc.prototype.tc;t("ol.tilegrid.WMTS",rx);rx.prototype.getMatrixIds=rx.prototype.l;t("ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet",sx);t("ol.style.AtlasManager",Ax);t("ol.style.Circle",$k);$k.prototype.setRadius=$k.prototype.Uc;t("ol.style.Fill",al);al.prototype.clone=al.prototype.clone;al.prototype.getColor=al.prototype.i;al.prototype.setColor=al.prototype.c; -t("ol.style.Icon",eo);eo.prototype.clone=eo.prototype.clone;eo.prototype.getAnchor=eo.prototype.Hc;eo.prototype.getColor=eo.prototype.Lo;eo.prototype.getImage=eo.prototype.Y;eo.prototype.getOrigin=eo.prototype.Oc;eo.prototype.getSrc=eo.prototype.Mo;eo.prototype.getSize=eo.prototype.ic;eo.prototype.load=eo.prototype.load;t("ol.style.Image",Xk);Xk.prototype.getOpacity=Xk.prototype.Ze;Xk.prototype.getRotateWithView=Xk.prototype.$e;Xk.prototype.getRotation=Xk.prototype.af;Xk.prototype.getScale=Xk.prototype.bf; -Xk.prototype.getSnapToPixel=Xk.prototype.Ae;Xk.prototype.setOpacity=Xk.prototype.td;Xk.prototype.setRotation=Xk.prototype.cf;Xk.prototype.setScale=Xk.prototype.ud;t("ol.style.RegularShape",Yk);Yk.prototype.clone=Yk.prototype.clone;Yk.prototype.getAnchor=Yk.prototype.Hc;Yk.prototype.getAngle=Yk.prototype.Pi;Yk.prototype.getFill=Yk.prototype.Fa;Yk.prototype.getImage=Yk.prototype.Y;Yk.prototype.getOrigin=Yk.prototype.Oc;Yk.prototype.getPoints=Yk.prototype.Qi;Yk.prototype.getRadius=Yk.prototype.Ri; -Yk.prototype.getRadius2=Yk.prototype.Fh;Yk.prototype.getSize=Yk.prototype.ic;Yk.prototype.getStroke=Yk.prototype.Ga;t("ol.style.Stroke",wj);wj.prototype.clone=wj.prototype.clone;wj.prototype.getColor=wj.prototype.No;wj.prototype.getLineCap=wj.prototype.Vk;wj.prototype.getLineDash=wj.prototype.Oo;wj.prototype.getLineDashOffset=wj.prototype.Wk;wj.prototype.getLineJoin=wj.prototype.Xk;wj.prototype.getMiterLimit=wj.prototype.bl;wj.prototype.getWidth=wj.prototype.Po;wj.prototype.setColor=wj.prototype.Qo; -wj.prototype.setLineCap=wj.prototype.aq;wj.prototype.setLineDash=wj.prototype.setLineDash;wj.prototype.setLineDashOffset=wj.prototype.bq;wj.prototype.setLineJoin=wj.prototype.cq;wj.prototype.setMiterLimit=wj.prototype.gq;wj.prototype.setWidth=wj.prototype.jq;t("ol.style.Style",bl);bl.prototype.clone=bl.prototype.clone;bl.prototype.getGeometry=bl.prototype.V;bl.prototype.getGeometryFunction=bl.prototype.Pk;bl.prototype.getFill=bl.prototype.Fa;bl.prototype.setFill=bl.prototype.pf; -bl.prototype.getImage=bl.prototype.Y;bl.prototype.setImage=bl.prototype.Og;bl.prototype.getStroke=bl.prototype.Ga;bl.prototype.setStroke=bl.prototype.qf;bl.prototype.getText=bl.prototype.Na;bl.prototype.setText=bl.prototype.xd;bl.prototype.getZIndex=bl.prototype.Ba;bl.prototype.setGeometry=bl.prototype.Ra;bl.prototype.setZIndex=bl.prototype.Vb;t("ol.style.Text",fo);fo.prototype.clone=fo.prototype.clone;fo.prototype.getFont=fo.prototype.Nk;fo.prototype.getOffsetX=fo.prototype.cl; -fo.prototype.getOffsetY=fo.prototype.dl;fo.prototype.getFill=fo.prototype.Fa;fo.prototype.getRotateWithView=fo.prototype.Ro;fo.prototype.getRotation=fo.prototype.So;fo.prototype.getScale=fo.prototype.To;fo.prototype.getStroke=fo.prototype.Ga;fo.prototype.getText=fo.prototype.Na;fo.prototype.getTextAlign=fo.prototype.nl;fo.prototype.getTextBaseline=fo.prototype.ol;fo.prototype.setFont=fo.prototype.nj;fo.prototype.setOffsetX=fo.prototype.sj;fo.prototype.setOffsetY=fo.prototype.tj; -fo.prototype.setFill=fo.prototype.pf;fo.prototype.setRotation=fo.prototype.Uo;fo.prototype.setScale=fo.prototype.Si;fo.prototype.setStroke=fo.prototype.qf;fo.prototype.setText=fo.prototype.xd;fo.prototype.setTextAlign=fo.prototype.vj;fo.prototype.setTextBaseline=fo.prototype.hq;t("ol.source.BingMaps",xw);t("ol.source.BingMaps.TOS_ATTRIBUTION",yw);xw.prototype.getApiKey=xw.prototype.T;xw.prototype.getImagerySet=xw.prototype.fa;t("ol.source.CartoDB",Aw);Aw.prototype.getConfig=Aw.prototype.Kk; -Aw.prototype.updateConfig=Aw.prototype.tq;Aw.prototype.setConfig=Aw.prototype.Xp;t("ol.source.Cluster",Y);Y.prototype.getDistance=Y.prototype.$n;Y.prototype.getSource=Y.prototype.ao;Y.prototype.setDistance=Y.prototype.Yp;t("ol.source.Image",Hv);Jv.prototype.image=Jv.prototype.image;t("ol.source.ImageArcGISRest",Gw);Gw.prototype.getParams=Gw.prototype.co;Gw.prototype.getImageLoadFunction=Gw.prototype.bo;Gw.prototype.getUrl=Gw.prototype.eo;Gw.prototype.setImageLoadFunction=Gw.prototype.fo; -Gw.prototype.setUrl=Gw.prototype.ho;Gw.prototype.updateParams=Gw.prototype.io;t("ol.source.ImageCanvas",Ov);t("ol.source.ImageMapGuide",Hw);Hw.prototype.getParams=Hw.prototype.ko;Hw.prototype.getImageLoadFunction=Hw.prototype.jo;Hw.prototype.updateParams=Hw.prototype.mo;Hw.prototype.setImageLoadFunction=Hw.prototype.lo;t("ol.source.ImageStatic",Iw);t("ol.source.ImageVector",Pv);Pv.prototype.getSource=Pv.prototype.no;Pv.prototype.getStyle=Pv.prototype.oo;Pv.prototype.getStyleFunction=Pv.prototype.po; -Pv.prototype.setStyle=Pv.prototype.Ii;t("ol.source.ImageWMS",Jw);Jw.prototype.getGetFeatureInfoUrl=Jw.prototype.so;Jw.prototype.getParams=Jw.prototype.uo;Jw.prototype.getImageLoadFunction=Jw.prototype.to;Jw.prototype.getUrl=Jw.prototype.vo;Jw.prototype.setImageLoadFunction=Jw.prototype.wo;Jw.prototype.setUrl=Jw.prototype.xo;Jw.prototype.updateParams=Jw.prototype.yo;t("ol.source.OSM",Nw);t("ol.source.OSM.ATTRIBUTION",Ow);t("ol.source.Raster",Pw);Pw.prototype.setOperation=Pw.prototype.v; -Tw.prototype.extent=Tw.prototype.extent;Tw.prototype.resolution=Tw.prototype.resolution;Tw.prototype.data=Tw.prototype.data;t("ol.source.Source",$t);$t.prototype.getAttributions=$t.prototype.ya;$t.prototype.getLogo=$t.prototype.xa;$t.prototype.getProjection=$t.prototype.za;$t.prototype.getState=$t.prototype.getState;$t.prototype.refresh=$t.prototype.sa;$t.prototype.setAttributions=$t.prototype.ua;t("ol.source.Stamen",Ww);t("ol.source.Tile",pw);pw.prototype.getTileGrid=pw.prototype.ab; -sw.prototype.tile=sw.prototype.tile;t("ol.source.TileArcGISRest",$w);$w.prototype.getParams=$w.prototype.C;$w.prototype.updateParams=$w.prototype.B;t("ol.source.TileDebug",bx);t("ol.source.TileImage",X);X.prototype.setRenderReprojectionEdges=X.prototype.Pb;X.prototype.setTileGridForProjection=X.prototype.Qb;t("ol.source.TileJSON",dx);dx.prototype.getTileJSON=dx.prototype.pl;t("ol.source.TileUTFGrid",ex);ex.prototype.getTemplate=ex.prototype.ml;ex.prototype.forDataAtCoordinateAndResolution=ex.prototype.zk; -t("ol.source.TileWMS",ix);ix.prototype.getGetFeatureInfoUrl=ix.prototype.Fo;ix.prototype.getParams=ix.prototype.Go;ix.prototype.updateParams=ix.prototype.Ho;tw.prototype.getTileLoadFunction=tw.prototype.pb;tw.prototype.getTileUrlFunction=tw.prototype.qb;tw.prototype.getUrls=tw.prototype.rb;tw.prototype.setTileLoadFunction=tw.prototype.vb;tw.prototype.setTileUrlFunction=tw.prototype.cb;tw.prototype.setUrl=tw.prototype.jb;tw.prototype.setUrls=tw.prototype.eb;t("ol.source.Vector",U); -U.prototype.addFeature=U.prototype.yb;U.prototype.addFeatures=U.prototype.cd;U.prototype.clear=U.prototype.clear;U.prototype.forEachFeature=U.prototype.sh;U.prototype.forEachFeatureInExtent=U.prototype.$b;U.prototype.forEachFeatureIntersectingExtent=U.prototype.th;U.prototype.getFeaturesCollection=U.prototype.Ah;U.prototype.getFeatures=U.prototype.Xe;U.prototype.getFeaturesAtCoordinate=U.prototype.zh;U.prototype.getFeaturesInExtent=U.prototype.Uf;U.prototype.getClosestFeatureToCoordinate=U.prototype.vh; -U.prototype.getExtent=U.prototype.G;U.prototype.getFeatureById=U.prototype.yh;U.prototype.getFormat=U.prototype.Mi;U.prototype.getUrl=U.prototype.Ni;U.prototype.removeFeature=U.prototype.Gb;gu.prototype.feature=gu.prototype.feature;t("ol.source.VectorTile",qx);t("ol.source.WMTS",Z);Z.prototype.getDimensions=Z.prototype.Mk;Z.prototype.getFormat=Z.prototype.Io;Z.prototype.getLayer=Z.prototype.Jo;Z.prototype.getMatrixSet=Z.prototype.$k;Z.prototype.getRequestEncoding=Z.prototype.kl; -Z.prototype.getStyle=Z.prototype.Ko;Z.prototype.getVersion=Z.prototype.rl;Z.prototype.updateDimensions=Z.prototype.uq; -t("ol.source.WMTS.optionsFromCapabilities",function(a,b){var c=na(a.Contents.Layer,function(a){return a.Identifier==b.layer});if(null===c)return null;var d=a.Contents.TileMatrixSet;var e=1<c.TileMatrixSetLink.length?"projection"in b?sa(c.TileMatrixSetLink,function(a){var c=na(d,function(b){return b.Identifier==a.TileMatrixSet}).SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"),e=Tb(c),f=Tb(b.projection);return e&&f?dc(e,f):c==b.projection}):sa(c.TileMatrixSetLink,function(a){return a.TileMatrixSet== -b.matrixSet}):0;0>e&&(e=0);var f=c.TileMatrixSetLink[e].TileMatrixSet;var g=c.TileMatrixSetLink[e].TileMatrixSetLimits;var h=c.Format[0];"format"in b&&(h=b.format);e=sa(c.Style,function(a){return"style"in b?a.Title==b.style:a.isDefault});0>e&&(e=0);e=c.Style[e].Identifier;var l={};"Dimension"in c&&c.Dimension.forEach(function(a){var b=a.Identifier,c=a.Default;void 0===c&&(c=a.Value[0]);l[b]=c});var m=na(a.Contents.TileMatrixSet,function(a){return a.Identifier==f});var n="projection"in b?Tb(b.projection): -Tb(m.SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"));var p=c.WGS84BoundingBox;if(void 0!==p){var q=Tb("EPSG:4326").G();q=p[0]==q[0]&&p[2]==q[2];var r=hc(p,"EPSG:4326",n);(p=n.G())&&(Va(p,r)||(r=void 0))}g=sx(m,r,g);var u=[],m=b.requestEncoding,m=void 0!==m?m:"";if("OperationsMetadata"in a&&"GetTile"in a.OperationsMetadata)for(a=a.OperationsMetadata.GetTile.DCP.HTTP.Get,r=0,p=a.length;r<p;++r){var x=na(a[r].Constraint,function(a){return"GetEncoding"==a.name}).AllowedValues.Value; -""===m&&(m=x[0]);if("KVP"===m)ja(x,"KVP")&&u.push(a[r].href);else break}u.length||(m="REST",c.ResourceURL.forEach(function(a){"tile"===a.resourceType&&(h=a.format,u.push(a.template))}));return{urls:u,layer:b.layer,matrixSet:f,format:h,projection:n,requestEncoding:m,tileGrid:g,style:e,dimensions:l,wrapX:q,crossOrigin:b.crossOrigin}});t("ol.source.XYZ",zw);t("ol.source.Zoomify",ux);Rh.prototype.vectorContext=Rh.prototype.vectorContext;Rh.prototype.frameState=Rh.prototype.frameState; -Rh.prototype.context=Rh.prototype.context;Rh.prototype.glContext=Rh.prototype.glContext;kq.prototype.get=kq.prototype.get;kq.prototype.getExtent=kq.prototype.G;kq.prototype.getId=kq.prototype.Wn;kq.prototype.getGeometry=kq.prototype.V;kq.prototype.getProperties=kq.prototype.Xn;kq.prototype.getType=kq.prototype.U;t("ol.render.VectorContext",Wh);kk.prototype.setStyle=kk.prototype.rd;kk.prototype.drawGeometry=kk.prototype.zb;kk.prototype.drawFeature=kk.prototype.te;Xh.prototype.drawCircle=Xh.prototype.Zb; -Xh.prototype.setStyle=Xh.prototype.rd;Xh.prototype.drawGeometry=Xh.prototype.zb;Xh.prototype.drawFeature=Xh.prototype.te;t("ol.proj.common.add",ic);t("ol.proj.Projection",Bb);Bb.prototype.getCode=Bb.prototype.Jk;Bb.prototype.getExtent=Bb.prototype.G;Bb.prototype.getUnits=Bb.prototype.Un;Bb.prototype.getMetersPerUnit=Bb.prototype.sc;Bb.prototype.getWorldExtent=Bb.prototype.tl;Bb.prototype.isGlobal=Bb.prototype.dm;Bb.prototype.setGlobal=Bb.prototype.$p;Bb.prototype.setExtent=Bb.prototype.Vn; -Bb.prototype.setWorldExtent=Bb.prototype.kq;Bb.prototype.setGetPointResolution=Bb.prototype.Zp;t("ol.proj.Units.METERS_PER_UNIT",zb);t("ol.layer.Base",sh);sh.prototype.getExtent=sh.prototype.G;sh.prototype.getMaxResolution=sh.prototype.fc;sh.prototype.getMinResolution=sh.prototype.gc;sh.prototype.getOpacity=sh.prototype.hc;sh.prototype.getVisible=sh.prototype.Mb;sh.prototype.getZIndex=sh.prototype.Ba;sh.prototype.setExtent=sh.prototype.vc;sh.prototype.setMaxResolution=sh.prototype.Ac; -sh.prototype.setMinResolution=sh.prototype.Bc;sh.prototype.setOpacity=sh.prototype.wc;sh.prototype.setVisible=sh.prototype.xc;sh.prototype.setZIndex=sh.prototype.Vb;t("ol.layer.Group",uh);uh.prototype.getLayers=uh.prototype.qd;uh.prototype.setLayers=uh.prototype.xi;t("ol.layer.Heatmap",V);V.prototype.getBlur=V.prototype.uh;V.prototype.getGradient=V.prototype.Bh;V.prototype.getRadius=V.prototype.yi;V.prototype.setBlur=V.prototype.jj;V.prototype.setGradient=V.prototype.pj;V.prototype.setRadius=V.prototype.Uc; -t("ol.layer.Image",Uv);Uv.prototype.getSource=Uv.prototype.ha;t("ol.layer.Layer",wh);wh.prototype.getSource=wh.prototype.ha;wh.prototype.setMap=wh.prototype.setMap;wh.prototype.setSource=wh.prototype.Wc;t("ol.layer.Tile",cw);cw.prototype.getPreload=cw.prototype.Ud;cw.prototype.getSource=cw.prototype.ha;cw.prototype.setPreload=cw.prototype.zi;cw.prototype.getUseInterimTilesOnError=cw.prototype.kd;cw.prototype.setUseInterimTilesOnError=cw.prototype.Ai;t("ol.layer.Vector",T);T.prototype.getSource=T.prototype.ha; -T.prototype.getStyle=T.prototype.D;T.prototype.getStyleFunction=T.prototype.C;T.prototype.setStyle=T.prototype.g;t("ol.layer.VectorTile",W);W.prototype.getPreload=W.prototype.Ud;W.prototype.getUseInterimTilesOnError=W.prototype.kd;W.prototype.setPreload=W.prototype.Bi;W.prototype.setUseInterimTilesOnError=W.prototype.Ci;t("ol.interaction.DoubleClickZoom",rg);t("ol.interaction.DoubleClickZoom.handleEvent",sg);t("ol.interaction.DragAndDrop",Rs);t("ol.interaction.DragAndDrop.handleEvent",mf); -Us.prototype.features=Us.prototype.features;Us.prototype.file=Us.prototype.file;Us.prototype.projection=Us.prototype.projection;t("ol.interaction.DragBox",Rg);Rg.prototype.getGeometry=Rg.prototype.V;Wg.prototype.coordinate=Wg.prototype.coordinate;Wg.prototype.mapBrowserEvent=Wg.prototype.mapBrowserEvent;t("ol.interaction.DragPan",Gg);t("ol.interaction.DragRotate",Kg);t("ol.interaction.DragRotateAndZoom",Ys);t("ol.interaction.DragZoom",$g);t("ol.interaction.Draw",ju); -t("ol.interaction.Draw.handleEvent",lu);ju.prototype.removeLastPoint=ju.prototype.Op;ju.prototype.finishDrawing=ju.prototype.Pd;ju.prototype.extend=ju.prototype.vn;t("ol.interaction.Draw.createRegularPolygon",function(a,b){return function(c,d){var e=c[0];c=c[1];var f=Math.sqrt(hf(e,c));d=d?d:Zf(new ys(e),a);$f(d,e,f,b?b:Math.atan((c[1]-e[1])/(c[0]-e[0])));return d}}); -t("ol.interaction.Draw.createBox",function(){return function(a,b){a=Na(a);b=b||new D(null);b.ma([[eb(a),gb(a),hb(a),ib(a),eb(a)]]);return b}});zu.prototype.feature=zu.prototype.feature;t("ol.interaction.Extent",Au);Au.prototype.getExtent=Au.prototype.G;Au.prototype.setExtent=Au.prototype.g;Lu.prototype.extent_=Lu.prototype.b;t("ol.interaction.Interaction",ng);ng.prototype.getActive=ng.prototype.c;ng.prototype.getMap=ng.prototype.f;ng.prototype.setActive=ng.prototype.Ha; -t("ol.interaction.KeyboardPan",ah);t("ol.interaction.KeyboardPan.handleEvent",bh);t("ol.interaction.KeyboardZoom",ch);t("ol.interaction.KeyboardZoom.handleEvent",dh);t("ol.interaction.Modify",Nu);t("ol.interaction.Modify.handleEvent",Qu);Nu.prototype.removePoint=Nu.prototype.hj;Vu.prototype.features=Vu.prototype.features;Vu.prototype.mapBrowserEvent=Vu.prototype.mapBrowserEvent;t("ol.interaction.MouseWheelZoom",eh);t("ol.interaction.MouseWheelZoom.handleEvent",fh);eh.prototype.setMouseAnchor=eh.prototype.T; -t("ol.interaction.PinchRotate",ih);t("ol.interaction.PinchZoom",mh);t("ol.interaction.Pointer",Dg);t("ol.interaction.Pointer.handleEvent",Eg);t("ol.interaction.Select",cv);cv.prototype.getFeatures=cv.prototype.Gn;cv.prototype.getHitTolerance=cv.prototype.Hn;cv.prototype.getLayer=cv.prototype.In;t("ol.interaction.Select.handleEvent",dv);cv.prototype.setHitTolerance=cv.prototype.Kn;cv.prototype.setMap=cv.prototype.setMap;fv.prototype.selected=fv.prototype.selected;fv.prototype.deselected=fv.prototype.deselected; -fv.prototype.mapBrowserEvent=fv.prototype.mapBrowserEvent;t("ol.interaction.Snap",hv);hv.prototype.addFeature=hv.prototype.yb;hv.prototype.removeFeature=hv.prototype.Gb;t("ol.interaction.Translate",mv);mv.prototype.getHitTolerance=mv.prototype.B;mv.prototype.setHitTolerance=mv.prototype.I;sv.prototype.features=sv.prototype.features;sv.prototype.coordinate=sv.prototype.coordinate;t("ol.geom.Circle",ys);ys.prototype.clone=ys.prototype.clone;ys.prototype.getCenter=ys.prototype.wa; -ys.prototype.getRadius=ys.prototype.pd;ys.prototype.getType=ys.prototype.U;ys.prototype.intersectsExtent=ys.prototype.Xa;ys.prototype.setCenter=ys.prototype.ob;ys.prototype.setCenterAndRadius=ys.prototype.Ng;ys.prototype.setRadius=ys.prototype.Uc;ys.prototype.transform=ys.prototype.tb;t("ol.geom.Geometry",of);of.prototype.getClosestPoint=of.prototype.Ab;of.prototype.intersectsCoordinate=of.prototype.sb;of.prototype.getExtent=of.prototype.G;of.prototype.rotate=of.prototype.rotate; -of.prototype.scale=of.prototype.scale;of.prototype.simplify=of.prototype.Rb;of.prototype.transform=of.prototype.tb;t("ol.geom.GeometryCollection",tm);tm.prototype.clone=tm.prototype.clone;tm.prototype.getGeometries=tm.prototype.Vf;tm.prototype.getType=tm.prototype.U;tm.prototype.intersectsExtent=tm.prototype.Xa;tm.prototype.setGeometries=tm.prototype.oj;tm.prototype.applyTransform=tm.prototype.Dc;tm.prototype.translate=tm.prototype.translate;t("ol.geom.LinearRing",Jf);Jf.prototype.clone=Jf.prototype.clone; -Jf.prototype.getArea=Jf.prototype.qn;Jf.prototype.getCoordinates=Jf.prototype.X;Jf.prototype.getType=Jf.prototype.U;Jf.prototype.setCoordinates=Jf.prototype.ma;t("ol.geom.LineString",O);O.prototype.appendCoordinate=O.prototype.mk;O.prototype.clone=O.prototype.clone;O.prototype.forEachSegment=O.prototype.Ck;O.prototype.getCoordinateAtM=O.prototype.nn;O.prototype.getCoordinates=O.prototype.X;O.prototype.getCoordinateAt=O.prototype.wh;O.prototype.getLength=O.prototype.pn;O.prototype.getType=O.prototype.U; -O.prototype.intersectsExtent=O.prototype.Xa;O.prototype.setCoordinates=O.prototype.ma;t("ol.geom.MultiLineString",P);P.prototype.appendLineString=P.prototype.nk;P.prototype.clone=P.prototype.clone;P.prototype.getCoordinateAtM=P.prototype.rn;P.prototype.getCoordinates=P.prototype.X;P.prototype.getLineString=P.prototype.Yk;P.prototype.getLineStrings=P.prototype.gd;P.prototype.getType=P.prototype.U;P.prototype.intersectsExtent=P.prototype.Xa;P.prototype.setCoordinates=P.prototype.ma; -t("ol.geom.MultiPoint",Q);Q.prototype.appendPoint=Q.prototype.qk;Q.prototype.clone=Q.prototype.clone;Q.prototype.getCoordinates=Q.prototype.X;Q.prototype.getPoint=Q.prototype.il;Q.prototype.getPoints=Q.prototype.Zd;Q.prototype.getType=Q.prototype.U;Q.prototype.intersectsExtent=Q.prototype.Xa;Q.prototype.setCoordinates=Q.prototype.ma;t("ol.geom.MultiPolygon",R);R.prototype.appendPolygon=R.prototype.rk;R.prototype.clone=R.prototype.clone;R.prototype.getArea=R.prototype.sn; -R.prototype.getCoordinates=R.prototype.X;R.prototype.getInteriorPoints=R.prototype.Uk;R.prototype.getPolygon=R.prototype.jl;R.prototype.getPolygons=R.prototype.Td;R.prototype.getType=R.prototype.U;R.prototype.intersectsExtent=R.prototype.Xa;R.prototype.setCoordinates=R.prototype.ma;t("ol.geom.Point",C);C.prototype.clone=C.prototype.clone;C.prototype.getCoordinates=C.prototype.X;C.prototype.getType=C.prototype.U;C.prototype.intersectsExtent=C.prototype.Xa;C.prototype.setCoordinates=C.prototype.ma; -t("ol.geom.Polygon",D);D.prototype.appendLinearRing=D.prototype.pk;D.prototype.clone=D.prototype.clone;D.prototype.getArea=D.prototype.tn;D.prototype.getCoordinates=D.prototype.X;D.prototype.getInteriorPoint=D.prototype.Tk;D.prototype.getLinearRingCount=D.prototype.Zk;D.prototype.getLinearRing=D.prototype.Ch;D.prototype.getLinearRings=D.prototype.Sd;D.prototype.getType=D.prototype.U;D.prototype.intersectsExtent=D.prototype.Xa;D.prototype.setCoordinates=D.prototype.ma; -t("ol.geom.Polygon.circular",Xf);t("ol.geom.Polygon.fromExtent",Yf);t("ol.geom.Polygon.fromCircle",Zf);t("ol.geom.SimpleGeometry",rf);rf.prototype.getFirstCoordinate=rf.prototype.ac;rf.prototype.getLastCoordinate=rf.prototype.bc;rf.prototype.getLayout=rf.prototype.cc;rf.prototype.applyTransform=rf.prototype.Dc;rf.prototype.translate=rf.prototype.translate;t("ol.format.EsriJSON",Ql);Ql.prototype.readFeature=Ql.prototype.Tb;Ql.prototype.readFeatures=Ql.prototype.Oa;Ql.prototype.readGeometry=Ql.prototype.Sc; -Ql.prototype.readProjection=Ql.prototype.kb;Ql.prototype.writeGeometry=Ql.prototype.$c;Ql.prototype.writeGeometryObject=Ql.prototype.je;Ql.prototype.writeFeature=Ql.prototype.Bd;Ql.prototype.writeFeatureObject=Ql.prototype.Zc;Ql.prototype.writeFeatures=Ql.prototype.Wb;Ql.prototype.writeFeaturesObject=Ql.prototype.he;t("ol.format.Feature",El);t("ol.format.filter.and",rm); -t("ol.format.filter.or",function(a){var b=[null].concat(Array.prototype.slice.call(arguments));return new (Function.prototype.bind.apply(pm,b))});t("ol.format.filter.not",function(a){return new nm(a)});t("ol.format.filter.bbox",sm);t("ol.format.filter.intersects",function(a,b,c){return new hm(a,b,c)});t("ol.format.filter.within",function(a,b,c){return new qm(a,b,c)});t("ol.format.filter.equalTo",function(a,b,c){return new dm(a,b,c)}); -t("ol.format.filter.notEqualTo",function(a,b,c){return new om(a,b,c)});t("ol.format.filter.lessThan",function(a,b){return new lm(a,b)});t("ol.format.filter.lessThanOrEqualTo",function(a,b){return new mm(a,b)});t("ol.format.filter.greaterThan",function(a,b){return new em(a,b)});t("ol.format.filter.greaterThanOrEqualTo",function(a,b){return new fm(a,b)});t("ol.format.filter.isNull",function(a){return new km(a)});t("ol.format.filter.between",function(a,b,c){return new im(a,b,c)}); -t("ol.format.filter.like",function(a,b,c,d,e,f){return new jm(a,b,c,d,e,f)});t("ol.format.filter.during",function(a,b,c){return new bm(a,b,c)});t("ol.format.GeoJSON",xm);xm.prototype.readFeature=xm.prototype.Tb;xm.prototype.readFeatures=xm.prototype.Oa;xm.prototype.readGeometry=xm.prototype.Sc;xm.prototype.readProjection=xm.prototype.kb;xm.prototype.writeFeature=xm.prototype.Bd;xm.prototype.writeFeatureObject=xm.prototype.Zc;xm.prototype.writeFeatures=xm.prototype.Wb; -xm.prototype.writeFeaturesObject=xm.prototype.he;xm.prototype.writeGeometry=xm.prototype.$c;xm.prototype.writeGeometryObject=xm.prototype.je;t("ol.format.GML",Sm);Sm.prototype.writeFeatures=Sm.prototype.Wb;Sm.prototype.writeFeaturesNode=Sm.prototype.Xb;t("ol.format.GML2",an);t("ol.format.GML3",Sm);Sm.prototype.writeGeometryNode=Sm.prototype.ie;Sm.prototype.writeFeatures=Sm.prototype.Wb;Sm.prototype.writeFeaturesNode=Sm.prototype.Xb;Fm.prototype.readFeatures=Fm.prototype.Oa;t("ol.format.GPX",mn); -mn.prototype.readFeature=mn.prototype.Tb;mn.prototype.readFeatures=mn.prototype.Oa;mn.prototype.readProjection=mn.prototype.kb;mn.prototype.writeFeatures=mn.prototype.Wb;mn.prototype.writeFeaturesNode=mn.prototype.Xb;t("ol.format.IGC",Xn);Xn.prototype.readFeature=Xn.prototype.Tb;Xn.prototype.readFeatures=Xn.prototype.Oa;Xn.prototype.readProjection=Xn.prototype.kb;t("ol.format.KML",go);go.prototype.readFeature=go.prototype.Tb;go.prototype.readFeatures=go.prototype.Oa;go.prototype.readName=go.prototype.Cp; -go.prototype.readNetworkLinks=go.prototype.Dp;go.prototype.readRegion=go.prototype.Gp;go.prototype.readRegionFromNode=go.prototype.lf;go.prototype.readProjection=go.prototype.kb;go.prototype.writeFeatures=go.prototype.Wb;go.prototype.writeFeaturesNode=go.prototype.Xb;t("ol.format.MVT",lq);lq.prototype.readFeatures=lq.prototype.Oa;lq.prototype.readProjection=lq.prototype.kb;lq.prototype.setLayers=lq.prototype.mn;t("ol.format.OSMXML",nq);nq.prototype.readFeatures=nq.prototype.Oa; -nq.prototype.readProjection=nq.prototype.kb;t("ol.format.Polyline",Nq);t("ol.format.Polyline.encodeDeltas",Oq);t("ol.format.Polyline.decodeDeltas",Qq);t("ol.format.Polyline.encodeFloats",Pq);t("ol.format.Polyline.decodeFloats",Rq);Nq.prototype.readFeature=Nq.prototype.Tb;Nq.prototype.readFeatures=Nq.prototype.Oa;Nq.prototype.readGeometry=Nq.prototype.Sc;Nq.prototype.readProjection=Nq.prototype.kb;Nq.prototype.writeGeometry=Nq.prototype.$c;t("ol.format.TopoJSON",Sq);Sq.prototype.readFeatures=Sq.prototype.Oa; -Sq.prototype.readProjection=Sq.prototype.kb;t("ol.format.WFS",Yq);Yq.prototype.readFeatures=Yq.prototype.Oa;Yq.prototype.readTransactionResponse=Yq.prototype.j;Yq.prototype.readFeatureCollectionMetadata=Yq.prototype.g;t("ol.format.WFS.writeFilter",function(a){var b=jl("http://www.opengis.net/ogc","Filter");Bl({node:b},mr,wl(a.kc),[a],[]);return b});Yq.prototype.writeGetFeature=Yq.prototype.l;Yq.prototype.writeTransaction=Yq.prototype.v;Yq.prototype.readProjection=Yq.prototype.kb; -t("ol.format.WKT",sr);sr.prototype.readFeature=sr.prototype.Tb;sr.prototype.readFeatures=sr.prototype.Oa;sr.prototype.readGeometry=sr.prototype.Sc;sr.prototype.writeFeature=sr.prototype.Bd;sr.prototype.writeFeatures=sr.prototype.Wb;sr.prototype.writeGeometry=sr.prototype.$c;t("ol.format.WMSCapabilities",Lr);Lr.prototype.read=Lr.prototype.read;t("ol.format.WMSGetFeatureInfo",hs);hs.prototype.readFeatures=hs.prototype.Oa;t("ol.format.WMTSCapabilities",is);is.prototype.read=is.prototype.read; -t("ol.format.filter.And",Zl);t("ol.format.filter.Bbox",$l);t("ol.format.filter.Comparison",am);t("ol.format.filter.ComparisonBinary",cm);t("ol.format.filter.During",bm);t("ol.format.filter.EqualTo",dm);t("ol.format.filter.Filter",Xl);t("ol.format.filter.GreaterThan",em);t("ol.format.filter.GreaterThanOrEqualTo",fm);t("ol.format.filter.Intersects",hm);t("ol.format.filter.IsBetween",im);t("ol.format.filter.IsLike",jm);t("ol.format.filter.IsNull",km);t("ol.format.filter.LessThan",lm); -t("ol.format.filter.LessThanOrEqualTo",mm);t("ol.format.filter.Not",nm);t("ol.format.filter.NotEqualTo",om);t("ol.format.filter.Or",pm);t("ol.format.filter.Spatial",gm);t("ol.format.filter.Within",qm);t("ol.events.condition.altKeyOnly",function(a){a=a.originalEvent;return a.altKey&&!(a.metaKey||a.ctrlKey)&&!a.shiftKey});t("ol.events.condition.altShiftKeysOnly",tg);t("ol.events.condition.always",mf);t("ol.events.condition.click",function(a){return"click"==a.type});t("ol.events.condition.never",nf); -t("ol.events.condition.pointerMove",vg);t("ol.events.condition.singleClick",wg);t("ol.events.condition.doubleClick",function(a){return"dblclick"==a.type});t("ol.events.condition.noModifierKeys",xg);t("ol.events.condition.platformModifierKeyOnly",function(a){a=a.originalEvent;return!a.altKey&&(Rd?a.metaKey:a.ctrlKey)&&!a.shiftKey});t("ol.events.condition.shiftKeyOnly",yg);t("ol.events.condition.targetNotEditable",Ag);t("ol.events.condition.mouseOnly",Bg);t("ol.events.condition.primaryAction",Cg); -Oc.prototype.type=Oc.prototype.type;Oc.prototype.target=Oc.prototype.target;Oc.prototype.preventDefault=Oc.prototype.preventDefault;Oc.prototype.stopPropagation=Oc.prototype.stopPropagation;t("ol.control.Attribution",nd);t("ol.control.Attribution.render",od);nd.prototype.getCollapsible=nd.prototype.Um;nd.prototype.setCollapsible=nd.prototype.Xm;nd.prototype.setCollapsed=nd.prototype.Wm;nd.prototype.getCollapsed=nd.prototype.Tm;t("ol.control.Control",md);md.prototype.getMap=md.prototype.g; -md.prototype.setMap=md.prototype.setMap;md.prototype.setTarget=md.prototype.f;t("ol.control.FullScreen",yd);t("ol.control.MousePosition",Dd);t("ol.control.MousePosition.render",Ed);Dd.prototype.getCoordinateFormat=Dd.prototype.xh;Dd.prototype.getProjection=Dd.prototype.Zh;Dd.prototype.setCoordinateFormat=Dd.prototype.kj;Dd.prototype.setProjection=Dd.prototype.$h;t("ol.control.OverviewMap",Bk);t("ol.control.OverviewMap.render",Ck);Bk.prototype.getCollapsible=Bk.prototype.$m; -Bk.prototype.setCollapsible=Bk.prototype.cn;Bk.prototype.setCollapsed=Bk.prototype.bn;Bk.prototype.getCollapsed=Bk.prototype.Zm;Bk.prototype.getOverviewMap=Bk.prototype.gl;t("ol.control.Rotate",ud);t("ol.control.Rotate.render",vd);t("ol.control.ScaleLine",Gk);Gk.prototype.getUnits=Gk.prototype.C;t("ol.control.ScaleLine.render",Hk);Gk.prototype.setUnits=Gk.prototype.I;t("ol.control.Zoom",wd);t("ol.control.ZoomSlider",Lk);t("ol.control.ZoomSlider.render",Nk);t("ol.control.ZoomToExtent",Qk); -Tc.prototype.changed=Tc.prototype.s;Tc.prototype.dispatchEvent=Tc.prototype.b;Tc.prototype.getRevision=Tc.prototype.L;Tc.prototype.on=Tc.prototype.J;Tc.prototype.once=Tc.prototype.once;Tc.prototype.un=Tc.prototype.K;Yc.prototype.get=Yc.prototype.get;Yc.prototype.getKeys=Yc.prototype.O;Yc.prototype.getProperties=Yc.prototype.N;Yc.prototype.set=Yc.prototype.set;Yc.prototype.setProperties=Yc.prototype.H;Yc.prototype.unset=Yc.prototype.P;Yc.prototype.changed=Yc.prototype.s; -Yc.prototype.dispatchEvent=Yc.prototype.b;Yc.prototype.getRevision=Yc.prototype.L;Yc.prototype.on=Yc.prototype.J;Yc.prototype.once=Yc.prototype.once;Yc.prototype.un=Yc.prototype.K;bd.prototype.type=bd.prototype.type;bd.prototype.target=bd.prototype.target;bd.prototype.preventDefault=bd.prototype.preventDefault;bd.prototype.stopPropagation=bd.prototype.stopPropagation;Rk.prototype.get=Rk.prototype.get;Rk.prototype.getKeys=Rk.prototype.O;Rk.prototype.getProperties=Rk.prototype.N;Rk.prototype.set=Rk.prototype.set; -Rk.prototype.setProperties=Rk.prototype.H;Rk.prototype.unset=Rk.prototype.P;Rk.prototype.changed=Rk.prototype.s;Rk.prototype.dispatchEvent=Rk.prototype.b;Rk.prototype.getRevision=Rk.prototype.L;Rk.prototype.on=Rk.prototype.J;Rk.prototype.once=Rk.prototype.once;Rk.prototype.un=Rk.prototype.K;H.prototype.get=H.prototype.get;H.prototype.getKeys=H.prototype.O;H.prototype.getProperties=H.prototype.N;H.prototype.set=H.prototype.set;H.prototype.setProperties=H.prototype.H;H.prototype.unset=H.prototype.P; -H.prototype.changed=H.prototype.s;H.prototype.dispatchEvent=H.prototype.b;H.prototype.getRevision=H.prototype.L;H.prototype.on=H.prototype.J;H.prototype.once=H.prototype.once;H.prototype.un=H.prototype.K;xs.prototype.get=xs.prototype.get;xs.prototype.getKeys=xs.prototype.O;xs.prototype.getProperties=xs.prototype.N;xs.prototype.set=xs.prototype.set;xs.prototype.setProperties=xs.prototype.H;xs.prototype.unset=xs.prototype.P;xs.prototype.changed=xs.prototype.s;xs.prototype.dispatchEvent=xs.prototype.b; -xs.prototype.getRevision=xs.prototype.L;xs.prototype.on=xs.prototype.J;xs.prototype.once=xs.prototype.once;xs.prototype.un=xs.prototype.K;Os.prototype.getTileCoord=Os.prototype.f;Os.prototype.load=Os.prototype.load;G.prototype.get=G.prototype.get;G.prototype.getKeys=G.prototype.O;G.prototype.getProperties=G.prototype.N;G.prototype.set=G.prototype.set;G.prototype.setProperties=G.prototype.H;G.prototype.unset=G.prototype.P;G.prototype.changed=G.prototype.s;G.prototype.dispatchEvent=G.prototype.b; -G.prototype.getRevision=G.prototype.L;G.prototype.on=G.prototype.J;G.prototype.once=G.prototype.once;G.prototype.un=G.prototype.K;Id.prototype.type=Id.prototype.type;Id.prototype.target=Id.prototype.target;Id.prototype.preventDefault=Id.prototype.preventDefault;Id.prototype.stopPropagation=Id.prototype.stopPropagation;Jd.prototype.map=Jd.prototype.map;Jd.prototype.frameState=Jd.prototype.frameState;Jd.prototype.type=Jd.prototype.type;Jd.prototype.target=Jd.prototype.target; -Jd.prototype.preventDefault=Jd.prototype.preventDefault;Jd.prototype.stopPropagation=Jd.prototype.stopPropagation;ee.prototype.originalEvent=ee.prototype.originalEvent;ee.prototype.pixel=ee.prototype.pixel;ee.prototype.coordinate=ee.prototype.coordinate;ee.prototype.dragging=ee.prototype.dragging;ee.prototype.preventDefault=ee.prototype.preventDefault;ee.prototype.stopPropagation=ee.prototype.stopPropagation;ee.prototype.map=ee.prototype.map;ee.prototype.frameState=ee.prototype.frameState; -ee.prototype.type=ee.prototype.type;ee.prototype.target=ee.prototype.target;Xc.prototype.type=Xc.prototype.type;Xc.prototype.target=Xc.prototype.target;Xc.prototype.preventDefault=Xc.prototype.preventDefault;Xc.prototype.stopPropagation=Xc.prototype.stopPropagation;sk.prototype.get=sk.prototype.get;sk.prototype.getKeys=sk.prototype.O;sk.prototype.getProperties=sk.prototype.N;sk.prototype.set=sk.prototype.set;sk.prototype.setProperties=sk.prototype.H;sk.prototype.unset=sk.prototype.P; -sk.prototype.changed=sk.prototype.s;sk.prototype.dispatchEvent=sk.prototype.b;sk.prototype.getRevision=sk.prototype.L;sk.prototype.on=sk.prototype.J;sk.prototype.once=sk.prototype.once;sk.prototype.un=sk.prototype.K;nx.prototype.getTileCoord=nx.prototype.f;nx.prototype.load=nx.prototype.load;px.prototype.getTileCoord=px.prototype.f;px.prototype.load=px.prototype.load;F.prototype.get=F.prototype.get;F.prototype.getKeys=F.prototype.O;F.prototype.getProperties=F.prototype.N;F.prototype.set=F.prototype.set; -F.prototype.setProperties=F.prototype.H;F.prototype.unset=F.prototype.P;F.prototype.changed=F.prototype.s;F.prototype.dispatchEvent=F.prototype.b;F.prototype.getRevision=F.prototype.L;F.prototype.on=F.prototype.J;F.prototype.once=F.prototype.once;F.prototype.un=F.prototype.K;rx.prototype.forEachTileCoord=rx.prototype.Rf;rx.prototype.getMaxZoom=rx.prototype.Ti;rx.prototype.getMinZoom=rx.prototype.Ui;rx.prototype.getOrigin=rx.prototype.Pc;rx.prototype.getResolution=rx.prototype.Da; -rx.prototype.getResolutions=rx.prototype.Vi;rx.prototype.getTileCoordExtent=rx.prototype.Aa;rx.prototype.getTileCoordForCoordAndResolution=rx.prototype.Be;rx.prototype.getTileCoordForCoordAndZ=rx.prototype.bg;rx.prototype.getTileSize=rx.prototype.gb;rx.prototype.getZForResolution=rx.prototype.tc;Yk.prototype.getOpacity=Yk.prototype.Ze;Yk.prototype.getRotateWithView=Yk.prototype.$e;Yk.prototype.getRotation=Yk.prototype.af;Yk.prototype.getScale=Yk.prototype.bf;Yk.prototype.getSnapToPixel=Yk.prototype.Ae; -Yk.prototype.setOpacity=Yk.prototype.td;Yk.prototype.setRotation=Yk.prototype.cf;Yk.prototype.setScale=Yk.prototype.ud;$k.prototype.clone=$k.prototype.clone;$k.prototype.getAngle=$k.prototype.Pi;$k.prototype.getFill=$k.prototype.Fa;$k.prototype.getPoints=$k.prototype.Qi;$k.prototype.getRadius=$k.prototype.Ri;$k.prototype.getRadius2=$k.prototype.Fh;$k.prototype.getStroke=$k.prototype.Ga;$k.prototype.getOpacity=$k.prototype.Ze;$k.prototype.getRotateWithView=$k.prototype.$e; -$k.prototype.getRotation=$k.prototype.af;$k.prototype.getScale=$k.prototype.bf;$k.prototype.getSnapToPixel=$k.prototype.Ae;$k.prototype.setOpacity=$k.prototype.td;$k.prototype.setRotation=$k.prototype.cf;$k.prototype.setScale=$k.prototype.ud;eo.prototype.getOpacity=eo.prototype.Ze;eo.prototype.getRotateWithView=eo.prototype.$e;eo.prototype.getRotation=eo.prototype.af;eo.prototype.getScale=eo.prototype.bf;eo.prototype.getSnapToPixel=eo.prototype.Ae;eo.prototype.setOpacity=eo.prototype.td; -eo.prototype.setRotation=eo.prototype.cf;eo.prototype.setScale=eo.prototype.ud;$t.prototype.get=$t.prototype.get;$t.prototype.getKeys=$t.prototype.O;$t.prototype.getProperties=$t.prototype.N;$t.prototype.set=$t.prototype.set;$t.prototype.setProperties=$t.prototype.H;$t.prototype.unset=$t.prototype.P;$t.prototype.changed=$t.prototype.s;$t.prototype.dispatchEvent=$t.prototype.b;$t.prototype.getRevision=$t.prototype.L;$t.prototype.on=$t.prototype.J;$t.prototype.once=$t.prototype.once; -$t.prototype.un=$t.prototype.K;pw.prototype.getAttributions=pw.prototype.ya;pw.prototype.getLogo=pw.prototype.xa;pw.prototype.getProjection=pw.prototype.za;pw.prototype.getState=pw.prototype.getState;pw.prototype.refresh=pw.prototype.sa;pw.prototype.setAttributions=pw.prototype.ua;pw.prototype.get=pw.prototype.get;pw.prototype.getKeys=pw.prototype.O;pw.prototype.getProperties=pw.prototype.N;pw.prototype.set=pw.prototype.set;pw.prototype.setProperties=pw.prototype.H;pw.prototype.unset=pw.prototype.P; -pw.prototype.changed=pw.prototype.s;pw.prototype.dispatchEvent=pw.prototype.b;pw.prototype.getRevision=pw.prototype.L;pw.prototype.on=pw.prototype.J;pw.prototype.once=pw.prototype.once;pw.prototype.un=pw.prototype.K;tw.prototype.getTileGrid=tw.prototype.ab;tw.prototype.refresh=tw.prototype.sa;tw.prototype.getAttributions=tw.prototype.ya;tw.prototype.getLogo=tw.prototype.xa;tw.prototype.getProjection=tw.prototype.za;tw.prototype.getState=tw.prototype.getState;tw.prototype.setAttributions=tw.prototype.ua; -tw.prototype.get=tw.prototype.get;tw.prototype.getKeys=tw.prototype.O;tw.prototype.getProperties=tw.prototype.N;tw.prototype.set=tw.prototype.set;tw.prototype.setProperties=tw.prototype.H;tw.prototype.unset=tw.prototype.P;tw.prototype.changed=tw.prototype.s;tw.prototype.dispatchEvent=tw.prototype.b;tw.prototype.getRevision=tw.prototype.L;tw.prototype.on=tw.prototype.J;tw.prototype.once=tw.prototype.once;tw.prototype.un=tw.prototype.K;X.prototype.getTileLoadFunction=X.prototype.pb; -X.prototype.getTileUrlFunction=X.prototype.qb;X.prototype.getUrls=X.prototype.rb;X.prototype.setTileLoadFunction=X.prototype.vb;X.prototype.setTileUrlFunction=X.prototype.cb;X.prototype.setUrl=X.prototype.jb;X.prototype.setUrls=X.prototype.eb;X.prototype.getTileGrid=X.prototype.ab;X.prototype.refresh=X.prototype.sa;X.prototype.getAttributions=X.prototype.ya;X.prototype.getLogo=X.prototype.xa;X.prototype.getProjection=X.prototype.za;X.prototype.getState=X.prototype.getState; -X.prototype.setAttributions=X.prototype.ua;X.prototype.get=X.prototype.get;X.prototype.getKeys=X.prototype.O;X.prototype.getProperties=X.prototype.N;X.prototype.set=X.prototype.set;X.prototype.setProperties=X.prototype.H;X.prototype.unset=X.prototype.P;X.prototype.changed=X.prototype.s;X.prototype.dispatchEvent=X.prototype.b;X.prototype.getRevision=X.prototype.L;X.prototype.on=X.prototype.J;X.prototype.once=X.prototype.once;X.prototype.un=X.prototype.K;xw.prototype.setRenderReprojectionEdges=xw.prototype.Pb; -xw.prototype.setTileGridForProjection=xw.prototype.Qb;xw.prototype.getTileLoadFunction=xw.prototype.pb;xw.prototype.getTileUrlFunction=xw.prototype.qb;xw.prototype.getUrls=xw.prototype.rb;xw.prototype.setTileLoadFunction=xw.prototype.vb;xw.prototype.setTileUrlFunction=xw.prototype.cb;xw.prototype.setUrl=xw.prototype.jb;xw.prototype.setUrls=xw.prototype.eb;xw.prototype.getTileGrid=xw.prototype.ab;xw.prototype.refresh=xw.prototype.sa;xw.prototype.getAttributions=xw.prototype.ya; -xw.prototype.getLogo=xw.prototype.xa;xw.prototype.getProjection=xw.prototype.za;xw.prototype.getState=xw.prototype.getState;xw.prototype.setAttributions=xw.prototype.ua;xw.prototype.get=xw.prototype.get;xw.prototype.getKeys=xw.prototype.O;xw.prototype.getProperties=xw.prototype.N;xw.prototype.set=xw.prototype.set;xw.prototype.setProperties=xw.prototype.H;xw.prototype.unset=xw.prototype.P;xw.prototype.changed=xw.prototype.s;xw.prototype.dispatchEvent=xw.prototype.b;xw.prototype.getRevision=xw.prototype.L; -xw.prototype.on=xw.prototype.J;xw.prototype.once=xw.prototype.once;xw.prototype.un=xw.prototype.K;zw.prototype.setRenderReprojectionEdges=zw.prototype.Pb;zw.prototype.setTileGridForProjection=zw.prototype.Qb;zw.prototype.getTileLoadFunction=zw.prototype.pb;zw.prototype.getTileUrlFunction=zw.prototype.qb;zw.prototype.getUrls=zw.prototype.rb;zw.prototype.setTileLoadFunction=zw.prototype.vb;zw.prototype.setTileUrlFunction=zw.prototype.cb;zw.prototype.setUrl=zw.prototype.jb;zw.prototype.setUrls=zw.prototype.eb; -zw.prototype.getTileGrid=zw.prototype.ab;zw.prototype.refresh=zw.prototype.sa;zw.prototype.getAttributions=zw.prototype.ya;zw.prototype.getLogo=zw.prototype.xa;zw.prototype.getProjection=zw.prototype.za;zw.prototype.getState=zw.prototype.getState;zw.prototype.setAttributions=zw.prototype.ua;zw.prototype.get=zw.prototype.get;zw.prototype.getKeys=zw.prototype.O;zw.prototype.getProperties=zw.prototype.N;zw.prototype.set=zw.prototype.set;zw.prototype.setProperties=zw.prototype.H;zw.prototype.unset=zw.prototype.P; -zw.prototype.changed=zw.prototype.s;zw.prototype.dispatchEvent=zw.prototype.b;zw.prototype.getRevision=zw.prototype.L;zw.prototype.on=zw.prototype.J;zw.prototype.once=zw.prototype.once;zw.prototype.un=zw.prototype.K;Aw.prototype.setRenderReprojectionEdges=Aw.prototype.Pb;Aw.prototype.setTileGridForProjection=Aw.prototype.Qb;Aw.prototype.getTileLoadFunction=Aw.prototype.pb;Aw.prototype.getTileUrlFunction=Aw.prototype.qb;Aw.prototype.getUrls=Aw.prototype.rb;Aw.prototype.setTileLoadFunction=Aw.prototype.vb; -Aw.prototype.setTileUrlFunction=Aw.prototype.cb;Aw.prototype.setUrl=Aw.prototype.jb;Aw.prototype.setUrls=Aw.prototype.eb;Aw.prototype.getTileGrid=Aw.prototype.ab;Aw.prototype.refresh=Aw.prototype.sa;Aw.prototype.getAttributions=Aw.prototype.ya;Aw.prototype.getLogo=Aw.prototype.xa;Aw.prototype.getProjection=Aw.prototype.za;Aw.prototype.getState=Aw.prototype.getState;Aw.prototype.setAttributions=Aw.prototype.ua;Aw.prototype.get=Aw.prototype.get;Aw.prototype.getKeys=Aw.prototype.O; -Aw.prototype.getProperties=Aw.prototype.N;Aw.prototype.set=Aw.prototype.set;Aw.prototype.setProperties=Aw.prototype.H;Aw.prototype.unset=Aw.prototype.P;Aw.prototype.changed=Aw.prototype.s;Aw.prototype.dispatchEvent=Aw.prototype.b;Aw.prototype.getRevision=Aw.prototype.L;Aw.prototype.on=Aw.prototype.J;Aw.prototype.once=Aw.prototype.once;Aw.prototype.un=Aw.prototype.K;U.prototype.getAttributions=U.prototype.ya;U.prototype.getLogo=U.prototype.xa;U.prototype.getProjection=U.prototype.za; -U.prototype.getState=U.prototype.getState;U.prototype.refresh=U.prototype.sa;U.prototype.setAttributions=U.prototype.ua;U.prototype.get=U.prototype.get;U.prototype.getKeys=U.prototype.O;U.prototype.getProperties=U.prototype.N;U.prototype.set=U.prototype.set;U.prototype.setProperties=U.prototype.H;U.prototype.unset=U.prototype.P;U.prototype.changed=U.prototype.s;U.prototype.dispatchEvent=U.prototype.b;U.prototype.getRevision=U.prototype.L;U.prototype.on=U.prototype.J;U.prototype.once=U.prototype.once; -U.prototype.un=U.prototype.K;Y.prototype.addFeature=Y.prototype.yb;Y.prototype.addFeatures=Y.prototype.cd;Y.prototype.clear=Y.prototype.clear;Y.prototype.forEachFeature=Y.prototype.sh;Y.prototype.forEachFeatureInExtent=Y.prototype.$b;Y.prototype.forEachFeatureIntersectingExtent=Y.prototype.th;Y.prototype.getFeaturesCollection=Y.prototype.Ah;Y.prototype.getFeatures=Y.prototype.Xe;Y.prototype.getFeaturesAtCoordinate=Y.prototype.zh;Y.prototype.getFeaturesInExtent=Y.prototype.Uf; -Y.prototype.getClosestFeatureToCoordinate=Y.prototype.vh;Y.prototype.getExtent=Y.prototype.G;Y.prototype.getFeatureById=Y.prototype.yh;Y.prototype.getFormat=Y.prototype.Mi;Y.prototype.getUrl=Y.prototype.Ni;Y.prototype.removeFeature=Y.prototype.Gb;Y.prototype.getAttributions=Y.prototype.ya;Y.prototype.getLogo=Y.prototype.xa;Y.prototype.getProjection=Y.prototype.za;Y.prototype.getState=Y.prototype.getState;Y.prototype.refresh=Y.prototype.sa;Y.prototype.setAttributions=Y.prototype.ua; -Y.prototype.get=Y.prototype.get;Y.prototype.getKeys=Y.prototype.O;Y.prototype.getProperties=Y.prototype.N;Y.prototype.set=Y.prototype.set;Y.prototype.setProperties=Y.prototype.H;Y.prototype.unset=Y.prototype.P;Y.prototype.changed=Y.prototype.s;Y.prototype.dispatchEvent=Y.prototype.b;Y.prototype.getRevision=Y.prototype.L;Y.prototype.on=Y.prototype.J;Y.prototype.once=Y.prototype.once;Y.prototype.un=Y.prototype.K;Hv.prototype.getAttributions=Hv.prototype.ya;Hv.prototype.getLogo=Hv.prototype.xa; -Hv.prototype.getProjection=Hv.prototype.za;Hv.prototype.getState=Hv.prototype.getState;Hv.prototype.refresh=Hv.prototype.sa;Hv.prototype.setAttributions=Hv.prototype.ua;Hv.prototype.get=Hv.prototype.get;Hv.prototype.getKeys=Hv.prototype.O;Hv.prototype.getProperties=Hv.prototype.N;Hv.prototype.set=Hv.prototype.set;Hv.prototype.setProperties=Hv.prototype.H;Hv.prototype.unset=Hv.prototype.P;Hv.prototype.changed=Hv.prototype.s;Hv.prototype.dispatchEvent=Hv.prototype.b;Hv.prototype.getRevision=Hv.prototype.L; -Hv.prototype.on=Hv.prototype.J;Hv.prototype.once=Hv.prototype.once;Hv.prototype.un=Hv.prototype.K;Jv.prototype.type=Jv.prototype.type;Jv.prototype.target=Jv.prototype.target;Jv.prototype.preventDefault=Jv.prototype.preventDefault;Jv.prototype.stopPropagation=Jv.prototype.stopPropagation;Gw.prototype.getAttributions=Gw.prototype.ya;Gw.prototype.getLogo=Gw.prototype.xa;Gw.prototype.getProjection=Gw.prototype.za;Gw.prototype.getState=Gw.prototype.getState;Gw.prototype.refresh=Gw.prototype.sa; -Gw.prototype.setAttributions=Gw.prototype.ua;Gw.prototype.get=Gw.prototype.get;Gw.prototype.getKeys=Gw.prototype.O;Gw.prototype.getProperties=Gw.prototype.N;Gw.prototype.set=Gw.prototype.set;Gw.prototype.setProperties=Gw.prototype.H;Gw.prototype.unset=Gw.prototype.P;Gw.prototype.changed=Gw.prototype.s;Gw.prototype.dispatchEvent=Gw.prototype.b;Gw.prototype.getRevision=Gw.prototype.L;Gw.prototype.on=Gw.prototype.J;Gw.prototype.once=Gw.prototype.once;Gw.prototype.un=Gw.prototype.K; -Ov.prototype.getAttributions=Ov.prototype.ya;Ov.prototype.getLogo=Ov.prototype.xa;Ov.prototype.getProjection=Ov.prototype.za;Ov.prototype.getState=Ov.prototype.getState;Ov.prototype.refresh=Ov.prototype.sa;Ov.prototype.setAttributions=Ov.prototype.ua;Ov.prototype.get=Ov.prototype.get;Ov.prototype.getKeys=Ov.prototype.O;Ov.prototype.getProperties=Ov.prototype.N;Ov.prototype.set=Ov.prototype.set;Ov.prototype.setProperties=Ov.prototype.H;Ov.prototype.unset=Ov.prototype.P;Ov.prototype.changed=Ov.prototype.s; -Ov.prototype.dispatchEvent=Ov.prototype.b;Ov.prototype.getRevision=Ov.prototype.L;Ov.prototype.on=Ov.prototype.J;Ov.prototype.once=Ov.prototype.once;Ov.prototype.un=Ov.prototype.K;Hw.prototype.getAttributions=Hw.prototype.ya;Hw.prototype.getLogo=Hw.prototype.xa;Hw.prototype.getProjection=Hw.prototype.za;Hw.prototype.getState=Hw.prototype.getState;Hw.prototype.refresh=Hw.prototype.sa;Hw.prototype.setAttributions=Hw.prototype.ua;Hw.prototype.get=Hw.prototype.get;Hw.prototype.getKeys=Hw.prototype.O; -Hw.prototype.getProperties=Hw.prototype.N;Hw.prototype.set=Hw.prototype.set;Hw.prototype.setProperties=Hw.prototype.H;Hw.prototype.unset=Hw.prototype.P;Hw.prototype.changed=Hw.prototype.s;Hw.prototype.dispatchEvent=Hw.prototype.b;Hw.prototype.getRevision=Hw.prototype.L;Hw.prototype.on=Hw.prototype.J;Hw.prototype.once=Hw.prototype.once;Hw.prototype.un=Hw.prototype.K;Iw.prototype.getAttributions=Iw.prototype.ya;Iw.prototype.getLogo=Iw.prototype.xa;Iw.prototype.getProjection=Iw.prototype.za; -Iw.prototype.getState=Iw.prototype.getState;Iw.prototype.refresh=Iw.prototype.sa;Iw.prototype.setAttributions=Iw.prototype.ua;Iw.prototype.get=Iw.prototype.get;Iw.prototype.getKeys=Iw.prototype.O;Iw.prototype.getProperties=Iw.prototype.N;Iw.prototype.set=Iw.prototype.set;Iw.prototype.setProperties=Iw.prototype.H;Iw.prototype.unset=Iw.prototype.P;Iw.prototype.changed=Iw.prototype.s;Iw.prototype.dispatchEvent=Iw.prototype.b;Iw.prototype.getRevision=Iw.prototype.L;Iw.prototype.on=Iw.prototype.J; -Iw.prototype.once=Iw.prototype.once;Iw.prototype.un=Iw.prototype.K;Pv.prototype.getAttributions=Pv.prototype.ya;Pv.prototype.getLogo=Pv.prototype.xa;Pv.prototype.getProjection=Pv.prototype.za;Pv.prototype.getState=Pv.prototype.getState;Pv.prototype.refresh=Pv.prototype.sa;Pv.prototype.setAttributions=Pv.prototype.ua;Pv.prototype.get=Pv.prototype.get;Pv.prototype.getKeys=Pv.prototype.O;Pv.prototype.getProperties=Pv.prototype.N;Pv.prototype.set=Pv.prototype.set;Pv.prototype.setProperties=Pv.prototype.H; -Pv.prototype.unset=Pv.prototype.P;Pv.prototype.changed=Pv.prototype.s;Pv.prototype.dispatchEvent=Pv.prototype.b;Pv.prototype.getRevision=Pv.prototype.L;Pv.prototype.on=Pv.prototype.J;Pv.prototype.once=Pv.prototype.once;Pv.prototype.un=Pv.prototype.K;Jw.prototype.getAttributions=Jw.prototype.ya;Jw.prototype.getLogo=Jw.prototype.xa;Jw.prototype.getProjection=Jw.prototype.za;Jw.prototype.getState=Jw.prototype.getState;Jw.prototype.refresh=Jw.prototype.sa;Jw.prototype.setAttributions=Jw.prototype.ua; -Jw.prototype.get=Jw.prototype.get;Jw.prototype.getKeys=Jw.prototype.O;Jw.prototype.getProperties=Jw.prototype.N;Jw.prototype.set=Jw.prototype.set;Jw.prototype.setProperties=Jw.prototype.H;Jw.prototype.unset=Jw.prototype.P;Jw.prototype.changed=Jw.prototype.s;Jw.prototype.dispatchEvent=Jw.prototype.b;Jw.prototype.getRevision=Jw.prototype.L;Jw.prototype.on=Jw.prototype.J;Jw.prototype.once=Jw.prototype.once;Jw.prototype.un=Jw.prototype.K;Nw.prototype.setRenderReprojectionEdges=Nw.prototype.Pb; -Nw.prototype.setTileGridForProjection=Nw.prototype.Qb;Nw.prototype.getTileLoadFunction=Nw.prototype.pb;Nw.prototype.getTileUrlFunction=Nw.prototype.qb;Nw.prototype.getUrls=Nw.prototype.rb;Nw.prototype.setTileLoadFunction=Nw.prototype.vb;Nw.prototype.setTileUrlFunction=Nw.prototype.cb;Nw.prototype.setUrl=Nw.prototype.jb;Nw.prototype.setUrls=Nw.prototype.eb;Nw.prototype.getTileGrid=Nw.prototype.ab;Nw.prototype.refresh=Nw.prototype.sa;Nw.prototype.getAttributions=Nw.prototype.ya; -Nw.prototype.getLogo=Nw.prototype.xa;Nw.prototype.getProjection=Nw.prototype.za;Nw.prototype.getState=Nw.prototype.getState;Nw.prototype.setAttributions=Nw.prototype.ua;Nw.prototype.get=Nw.prototype.get;Nw.prototype.getKeys=Nw.prototype.O;Nw.prototype.getProperties=Nw.prototype.N;Nw.prototype.set=Nw.prototype.set;Nw.prototype.setProperties=Nw.prototype.H;Nw.prototype.unset=Nw.prototype.P;Nw.prototype.changed=Nw.prototype.s;Nw.prototype.dispatchEvent=Nw.prototype.b;Nw.prototype.getRevision=Nw.prototype.L; -Nw.prototype.on=Nw.prototype.J;Nw.prototype.once=Nw.prototype.once;Nw.prototype.un=Nw.prototype.K;Pw.prototype.getAttributions=Pw.prototype.ya;Pw.prototype.getLogo=Pw.prototype.xa;Pw.prototype.getProjection=Pw.prototype.za;Pw.prototype.getState=Pw.prototype.getState;Pw.prototype.refresh=Pw.prototype.sa;Pw.prototype.setAttributions=Pw.prototype.ua;Pw.prototype.get=Pw.prototype.get;Pw.prototype.getKeys=Pw.prototype.O;Pw.prototype.getProperties=Pw.prototype.N;Pw.prototype.set=Pw.prototype.set; -Pw.prototype.setProperties=Pw.prototype.H;Pw.prototype.unset=Pw.prototype.P;Pw.prototype.changed=Pw.prototype.s;Pw.prototype.dispatchEvent=Pw.prototype.b;Pw.prototype.getRevision=Pw.prototype.L;Pw.prototype.on=Pw.prototype.J;Pw.prototype.once=Pw.prototype.once;Pw.prototype.un=Pw.prototype.K;Tw.prototype.type=Tw.prototype.type;Tw.prototype.target=Tw.prototype.target;Tw.prototype.preventDefault=Tw.prototype.preventDefault;Tw.prototype.stopPropagation=Tw.prototype.stopPropagation; -Ww.prototype.setRenderReprojectionEdges=Ww.prototype.Pb;Ww.prototype.setTileGridForProjection=Ww.prototype.Qb;Ww.prototype.getTileLoadFunction=Ww.prototype.pb;Ww.prototype.getTileUrlFunction=Ww.prototype.qb;Ww.prototype.getUrls=Ww.prototype.rb;Ww.prototype.setTileLoadFunction=Ww.prototype.vb;Ww.prototype.setTileUrlFunction=Ww.prototype.cb;Ww.prototype.setUrl=Ww.prototype.jb;Ww.prototype.setUrls=Ww.prototype.eb;Ww.prototype.getTileGrid=Ww.prototype.ab;Ww.prototype.refresh=Ww.prototype.sa; -Ww.prototype.getAttributions=Ww.prototype.ya;Ww.prototype.getLogo=Ww.prototype.xa;Ww.prototype.getProjection=Ww.prototype.za;Ww.prototype.getState=Ww.prototype.getState;Ww.prototype.setAttributions=Ww.prototype.ua;Ww.prototype.get=Ww.prototype.get;Ww.prototype.getKeys=Ww.prototype.O;Ww.prototype.getProperties=Ww.prototype.N;Ww.prototype.set=Ww.prototype.set;Ww.prototype.setProperties=Ww.prototype.H;Ww.prototype.unset=Ww.prototype.P;Ww.prototype.changed=Ww.prototype.s;Ww.prototype.dispatchEvent=Ww.prototype.b; -Ww.prototype.getRevision=Ww.prototype.L;Ww.prototype.on=Ww.prototype.J;Ww.prototype.once=Ww.prototype.once;Ww.prototype.un=Ww.prototype.K;sw.prototype.type=sw.prototype.type;sw.prototype.target=sw.prototype.target;sw.prototype.preventDefault=sw.prototype.preventDefault;sw.prototype.stopPropagation=sw.prototype.stopPropagation;$w.prototype.setRenderReprojectionEdges=$w.prototype.Pb;$w.prototype.setTileGridForProjection=$w.prototype.Qb;$w.prototype.getTileLoadFunction=$w.prototype.pb; -$w.prototype.getTileUrlFunction=$w.prototype.qb;$w.prototype.getUrls=$w.prototype.rb;$w.prototype.setTileLoadFunction=$w.prototype.vb;$w.prototype.setTileUrlFunction=$w.prototype.cb;$w.prototype.setUrl=$w.prototype.jb;$w.prototype.setUrls=$w.prototype.eb;$w.prototype.getTileGrid=$w.prototype.ab;$w.prototype.refresh=$w.prototype.sa;$w.prototype.getAttributions=$w.prototype.ya;$w.prototype.getLogo=$w.prototype.xa;$w.prototype.getProjection=$w.prototype.za;$w.prototype.getState=$w.prototype.getState; -$w.prototype.setAttributions=$w.prototype.ua;$w.prototype.get=$w.prototype.get;$w.prototype.getKeys=$w.prototype.O;$w.prototype.getProperties=$w.prototype.N;$w.prototype.set=$w.prototype.set;$w.prototype.setProperties=$w.prototype.H;$w.prototype.unset=$w.prototype.P;$w.prototype.changed=$w.prototype.s;$w.prototype.dispatchEvent=$w.prototype.b;$w.prototype.getRevision=$w.prototype.L;$w.prototype.on=$w.prototype.J;$w.prototype.once=$w.prototype.once;$w.prototype.un=$w.prototype.K; -bx.prototype.getTileGrid=bx.prototype.ab;bx.prototype.refresh=bx.prototype.sa;bx.prototype.getAttributions=bx.prototype.ya;bx.prototype.getLogo=bx.prototype.xa;bx.prototype.getProjection=bx.prototype.za;bx.prototype.getState=bx.prototype.getState;bx.prototype.setAttributions=bx.prototype.ua;bx.prototype.get=bx.prototype.get;bx.prototype.getKeys=bx.prototype.O;bx.prototype.getProperties=bx.prototype.N;bx.prototype.set=bx.prototype.set;bx.prototype.setProperties=bx.prototype.H;bx.prototype.unset=bx.prototype.P; -bx.prototype.changed=bx.prototype.s;bx.prototype.dispatchEvent=bx.prototype.b;bx.prototype.getRevision=bx.prototype.L;bx.prototype.on=bx.prototype.J;bx.prototype.once=bx.prototype.once;bx.prototype.un=bx.prototype.K;dx.prototype.setRenderReprojectionEdges=dx.prototype.Pb;dx.prototype.setTileGridForProjection=dx.prototype.Qb;dx.prototype.getTileLoadFunction=dx.prototype.pb;dx.prototype.getTileUrlFunction=dx.prototype.qb;dx.prototype.getUrls=dx.prototype.rb;dx.prototype.setTileLoadFunction=dx.prototype.vb; -dx.prototype.setTileUrlFunction=dx.prototype.cb;dx.prototype.setUrl=dx.prototype.jb;dx.prototype.setUrls=dx.prototype.eb;dx.prototype.getTileGrid=dx.prototype.ab;dx.prototype.refresh=dx.prototype.sa;dx.prototype.getAttributions=dx.prototype.ya;dx.prototype.getLogo=dx.prototype.xa;dx.prototype.getProjection=dx.prototype.za;dx.prototype.getState=dx.prototype.getState;dx.prototype.setAttributions=dx.prototype.ua;dx.prototype.get=dx.prototype.get;dx.prototype.getKeys=dx.prototype.O; -dx.prototype.getProperties=dx.prototype.N;dx.prototype.set=dx.prototype.set;dx.prototype.setProperties=dx.prototype.H;dx.prototype.unset=dx.prototype.P;dx.prototype.changed=dx.prototype.s;dx.prototype.dispatchEvent=dx.prototype.b;dx.prototype.getRevision=dx.prototype.L;dx.prototype.on=dx.prototype.J;dx.prototype.once=dx.prototype.once;dx.prototype.un=dx.prototype.K;ex.prototype.getTileGrid=ex.prototype.ab;ex.prototype.refresh=ex.prototype.sa;ex.prototype.getAttributions=ex.prototype.ya; -ex.prototype.getLogo=ex.prototype.xa;ex.prototype.getProjection=ex.prototype.za;ex.prototype.getState=ex.prototype.getState;ex.prototype.setAttributions=ex.prototype.ua;ex.prototype.get=ex.prototype.get;ex.prototype.getKeys=ex.prototype.O;ex.prototype.getProperties=ex.prototype.N;ex.prototype.set=ex.prototype.set;ex.prototype.setProperties=ex.prototype.H;ex.prototype.unset=ex.prototype.P;ex.prototype.changed=ex.prototype.s;ex.prototype.dispatchEvent=ex.prototype.b;ex.prototype.getRevision=ex.prototype.L; -ex.prototype.on=ex.prototype.J;ex.prototype.once=ex.prototype.once;ex.prototype.un=ex.prototype.K;ix.prototype.setRenderReprojectionEdges=ix.prototype.Pb;ix.prototype.setTileGridForProjection=ix.prototype.Qb;ix.prototype.getTileLoadFunction=ix.prototype.pb;ix.prototype.getTileUrlFunction=ix.prototype.qb;ix.prototype.getUrls=ix.prototype.rb;ix.prototype.setTileLoadFunction=ix.prototype.vb;ix.prototype.setTileUrlFunction=ix.prototype.cb;ix.prototype.setUrl=ix.prototype.jb;ix.prototype.setUrls=ix.prototype.eb; -ix.prototype.getTileGrid=ix.prototype.ab;ix.prototype.refresh=ix.prototype.sa;ix.prototype.getAttributions=ix.prototype.ya;ix.prototype.getLogo=ix.prototype.xa;ix.prototype.getProjection=ix.prototype.za;ix.prototype.getState=ix.prototype.getState;ix.prototype.setAttributions=ix.prototype.ua;ix.prototype.get=ix.prototype.get;ix.prototype.getKeys=ix.prototype.O;ix.prototype.getProperties=ix.prototype.N;ix.prototype.set=ix.prototype.set;ix.prototype.setProperties=ix.prototype.H;ix.prototype.unset=ix.prototype.P; -ix.prototype.changed=ix.prototype.s;ix.prototype.dispatchEvent=ix.prototype.b;ix.prototype.getRevision=ix.prototype.L;ix.prototype.on=ix.prototype.J;ix.prototype.once=ix.prototype.once;ix.prototype.un=ix.prototype.K;gu.prototype.type=gu.prototype.type;gu.prototype.target=gu.prototype.target;gu.prototype.preventDefault=gu.prototype.preventDefault;gu.prototype.stopPropagation=gu.prototype.stopPropagation;qx.prototype.getTileLoadFunction=qx.prototype.pb;qx.prototype.getTileUrlFunction=qx.prototype.qb; -qx.prototype.getUrls=qx.prototype.rb;qx.prototype.setTileLoadFunction=qx.prototype.vb;qx.prototype.setTileUrlFunction=qx.prototype.cb;qx.prototype.setUrl=qx.prototype.jb;qx.prototype.setUrls=qx.prototype.eb;qx.prototype.getTileGrid=qx.prototype.ab;qx.prototype.refresh=qx.prototype.sa;qx.prototype.getAttributions=qx.prototype.ya;qx.prototype.getLogo=qx.prototype.xa;qx.prototype.getProjection=qx.prototype.za;qx.prototype.getState=qx.prototype.getState;qx.prototype.setAttributions=qx.prototype.ua; -qx.prototype.get=qx.prototype.get;qx.prototype.getKeys=qx.prototype.O;qx.prototype.getProperties=qx.prototype.N;qx.prototype.set=qx.prototype.set;qx.prototype.setProperties=qx.prototype.H;qx.prototype.unset=qx.prototype.P;qx.prototype.changed=qx.prototype.s;qx.prototype.dispatchEvent=qx.prototype.b;qx.prototype.getRevision=qx.prototype.L;qx.prototype.on=qx.prototype.J;qx.prototype.once=qx.prototype.once;qx.prototype.un=qx.prototype.K;Z.prototype.setRenderReprojectionEdges=Z.prototype.Pb; -Z.prototype.setTileGridForProjection=Z.prototype.Qb;Z.prototype.getTileLoadFunction=Z.prototype.pb;Z.prototype.getTileUrlFunction=Z.prototype.qb;Z.prototype.getUrls=Z.prototype.rb;Z.prototype.setTileLoadFunction=Z.prototype.vb;Z.prototype.setTileUrlFunction=Z.prototype.cb;Z.prototype.setUrl=Z.prototype.jb;Z.prototype.setUrls=Z.prototype.eb;Z.prototype.getTileGrid=Z.prototype.ab;Z.prototype.refresh=Z.prototype.sa;Z.prototype.getAttributions=Z.prototype.ya;Z.prototype.getLogo=Z.prototype.xa; -Z.prototype.getProjection=Z.prototype.za;Z.prototype.getState=Z.prototype.getState;Z.prototype.setAttributions=Z.prototype.ua;Z.prototype.get=Z.prototype.get;Z.prototype.getKeys=Z.prototype.O;Z.prototype.getProperties=Z.prototype.N;Z.prototype.set=Z.prototype.set;Z.prototype.setProperties=Z.prototype.H;Z.prototype.unset=Z.prototype.P;Z.prototype.changed=Z.prototype.s;Z.prototype.dispatchEvent=Z.prototype.b;Z.prototype.getRevision=Z.prototype.L;Z.prototype.on=Z.prototype.J;Z.prototype.once=Z.prototype.once; -Z.prototype.un=Z.prototype.K;ux.prototype.setRenderReprojectionEdges=ux.prototype.Pb;ux.prototype.setTileGridForProjection=ux.prototype.Qb;ux.prototype.getTileLoadFunction=ux.prototype.pb;ux.prototype.getTileUrlFunction=ux.prototype.qb;ux.prototype.getUrls=ux.prototype.rb;ux.prototype.setTileLoadFunction=ux.prototype.vb;ux.prototype.setTileUrlFunction=ux.prototype.cb;ux.prototype.setUrl=ux.prototype.jb;ux.prototype.setUrls=ux.prototype.eb;ux.prototype.getTileGrid=ux.prototype.ab; -ux.prototype.refresh=ux.prototype.sa;ux.prototype.getAttributions=ux.prototype.ya;ux.prototype.getLogo=ux.prototype.xa;ux.prototype.getProjection=ux.prototype.za;ux.prototype.getState=ux.prototype.getState;ux.prototype.setAttributions=ux.prototype.ua;ux.prototype.get=ux.prototype.get;ux.prototype.getKeys=ux.prototype.O;ux.prototype.getProperties=ux.prototype.N;ux.prototype.set=ux.prototype.set;ux.prototype.setProperties=ux.prototype.H;ux.prototype.unset=ux.prototype.P;ux.prototype.changed=ux.prototype.s; -ux.prototype.dispatchEvent=ux.prototype.b;ux.prototype.getRevision=ux.prototype.L;ux.prototype.on=ux.prototype.J;ux.prototype.once=ux.prototype.once;ux.prototype.un=ux.prototype.K;hw.prototype.getTileCoord=hw.prototype.f;hw.prototype.load=hw.prototype.load;xt.prototype.changed=xt.prototype.s;xt.prototype.dispatchEvent=xt.prototype.b;xt.prototype.getRevision=xt.prototype.L;xt.prototype.on=xt.prototype.J;xt.prototype.once=xt.prototype.once;xt.prototype.un=xt.prototype.K;Vt.prototype.changed=Vt.prototype.s; -Vt.prototype.dispatchEvent=Vt.prototype.b;Vt.prototype.getRevision=Vt.prototype.L;Vt.prototype.on=Vt.prototype.J;Vt.prototype.once=Vt.prototype.once;Vt.prototype.un=Vt.prototype.K;Rv.prototype.changed=Rv.prototype.s;Rv.prototype.dispatchEvent=Rv.prototype.b;Rv.prototype.getRevision=Rv.prototype.L;Rv.prototype.on=Rv.prototype.J;Rv.prototype.once=Rv.prototype.once;Rv.prototype.un=Rv.prototype.K;bw.prototype.changed=bw.prototype.s;bw.prototype.dispatchEvent=bw.prototype.b;bw.prototype.getRevision=bw.prototype.L; -bw.prototype.on=bw.prototype.J;bw.prototype.once=bw.prototype.once;bw.prototype.un=bw.prototype.K;Yt.prototype.changed=Yt.prototype.s;Yt.prototype.dispatchEvent=Yt.prototype.b;Yt.prototype.getRevision=Yt.prototype.L;Yt.prototype.on=Yt.prototype.J;Yt.prototype.once=Yt.prototype.once;Yt.prototype.un=Yt.prototype.K;Gt.prototype.changed=Gt.prototype.s;Gt.prototype.dispatchEvent=Gt.prototype.b;Gt.prototype.getRevision=Gt.prototype.L;Gt.prototype.on=Gt.prototype.J;Gt.prototype.once=Gt.prototype.once; -Gt.prototype.un=Gt.prototype.K;yv.prototype.changed=yv.prototype.s;yv.prototype.dispatchEvent=yv.prototype.b;yv.prototype.getRevision=yv.prototype.L;yv.prototype.on=yv.prototype.J;yv.prototype.once=yv.prototype.once;yv.prototype.un=yv.prototype.K;zv.prototype.changed=zv.prototype.s;zv.prototype.dispatchEvent=zv.prototype.b;zv.prototype.getRevision=zv.prototype.L;zv.prototype.on=zv.prototype.J;zv.prototype.once=zv.prototype.once;zv.prototype.un=zv.prototype.K;Vv.prototype.changed=Vv.prototype.s; -Vv.prototype.dispatchEvent=Vv.prototype.b;Vv.prototype.getRevision=Vv.prototype.L;Vv.prototype.on=Vv.prototype.J;Vv.prototype.once=Vv.prototype.once;Vv.prototype.un=Vv.prototype.K;Ot.prototype.changed=Ot.prototype.s;Ot.prototype.dispatchEvent=Ot.prototype.b;Ot.prototype.getRevision=Ot.prototype.L;Ot.prototype.on=Ot.prototype.J;Ot.prototype.once=Ot.prototype.once;Ot.prototype.un=Ot.prototype.K;dw.prototype.changed=dw.prototype.s;dw.prototype.dispatchEvent=dw.prototype.b;dw.prototype.getRevision=dw.prototype.L; -dw.prototype.on=dw.prototype.J;dw.prototype.once=dw.prototype.once;dw.prototype.un=dw.prototype.K;Rh.prototype.type=Rh.prototype.type;Rh.prototype.target=Rh.prototype.target;Rh.prototype.preventDefault=Rh.prototype.preventDefault;Rh.prototype.stopPropagation=Rh.prototype.stopPropagation;pe.prototype.type=pe.prototype.type;pe.prototype.target=pe.prototype.target;pe.prototype.preventDefault=pe.prototype.preventDefault;pe.prototype.stopPropagation=pe.prototype.stopPropagation;sh.prototype.get=sh.prototype.get; -sh.prototype.getKeys=sh.prototype.O;sh.prototype.getProperties=sh.prototype.N;sh.prototype.set=sh.prototype.set;sh.prototype.setProperties=sh.prototype.H;sh.prototype.unset=sh.prototype.P;sh.prototype.changed=sh.prototype.s;sh.prototype.dispatchEvent=sh.prototype.b;sh.prototype.getRevision=sh.prototype.L;sh.prototype.on=sh.prototype.J;sh.prototype.once=sh.prototype.once;sh.prototype.un=sh.prototype.K;uh.prototype.getExtent=uh.prototype.G;uh.prototype.getMaxResolution=uh.prototype.fc; -uh.prototype.getMinResolution=uh.prototype.gc;uh.prototype.getOpacity=uh.prototype.hc;uh.prototype.getVisible=uh.prototype.Mb;uh.prototype.getZIndex=uh.prototype.Ba;uh.prototype.setExtent=uh.prototype.vc;uh.prototype.setMaxResolution=uh.prototype.Ac;uh.prototype.setMinResolution=uh.prototype.Bc;uh.prototype.setOpacity=uh.prototype.wc;uh.prototype.setVisible=uh.prototype.xc;uh.prototype.setZIndex=uh.prototype.Vb;uh.prototype.get=uh.prototype.get;uh.prototype.getKeys=uh.prototype.O; -uh.prototype.getProperties=uh.prototype.N;uh.prototype.set=uh.prototype.set;uh.prototype.setProperties=uh.prototype.H;uh.prototype.unset=uh.prototype.P;uh.prototype.changed=uh.prototype.s;uh.prototype.dispatchEvent=uh.prototype.b;uh.prototype.getRevision=uh.prototype.L;uh.prototype.on=uh.prototype.J;uh.prototype.once=uh.prototype.once;uh.prototype.un=uh.prototype.K;wh.prototype.getExtent=wh.prototype.G;wh.prototype.getMaxResolution=wh.prototype.fc;wh.prototype.getMinResolution=wh.prototype.gc; -wh.prototype.getOpacity=wh.prototype.hc;wh.prototype.getVisible=wh.prototype.Mb;wh.prototype.getZIndex=wh.prototype.Ba;wh.prototype.setExtent=wh.prototype.vc;wh.prototype.setMaxResolution=wh.prototype.Ac;wh.prototype.setMinResolution=wh.prototype.Bc;wh.prototype.setOpacity=wh.prototype.wc;wh.prototype.setVisible=wh.prototype.xc;wh.prototype.setZIndex=wh.prototype.Vb;wh.prototype.get=wh.prototype.get;wh.prototype.getKeys=wh.prototype.O;wh.prototype.getProperties=wh.prototype.N;wh.prototype.set=wh.prototype.set; -wh.prototype.setProperties=wh.prototype.H;wh.prototype.unset=wh.prototype.P;wh.prototype.changed=wh.prototype.s;wh.prototype.dispatchEvent=wh.prototype.b;wh.prototype.getRevision=wh.prototype.L;wh.prototype.on=wh.prototype.J;wh.prototype.once=wh.prototype.once;wh.prototype.un=wh.prototype.K;T.prototype.setMap=T.prototype.setMap;T.prototype.setSource=T.prototype.Wc;T.prototype.getExtent=T.prototype.G;T.prototype.getMaxResolution=T.prototype.fc;T.prototype.getMinResolution=T.prototype.gc; -T.prototype.getOpacity=T.prototype.hc;T.prototype.getVisible=T.prototype.Mb;T.prototype.getZIndex=T.prototype.Ba;T.prototype.setExtent=T.prototype.vc;T.prototype.setMaxResolution=T.prototype.Ac;T.prototype.setMinResolution=T.prototype.Bc;T.prototype.setOpacity=T.prototype.wc;T.prototype.setVisible=T.prototype.xc;T.prototype.setZIndex=T.prototype.Vb;T.prototype.get=T.prototype.get;T.prototype.getKeys=T.prototype.O;T.prototype.getProperties=T.prototype.N;T.prototype.set=T.prototype.set; -T.prototype.setProperties=T.prototype.H;T.prototype.unset=T.prototype.P;T.prototype.changed=T.prototype.s;T.prototype.dispatchEvent=T.prototype.b;T.prototype.getRevision=T.prototype.L;T.prototype.on=T.prototype.J;T.prototype.once=T.prototype.once;T.prototype.un=T.prototype.K;V.prototype.getSource=V.prototype.ha;V.prototype.getStyle=V.prototype.D;V.prototype.getStyleFunction=V.prototype.C;V.prototype.setStyle=V.prototype.g;V.prototype.setMap=V.prototype.setMap;V.prototype.setSource=V.prototype.Wc; -V.prototype.getExtent=V.prototype.G;V.prototype.getMaxResolution=V.prototype.fc;V.prototype.getMinResolution=V.prototype.gc;V.prototype.getOpacity=V.prototype.hc;V.prototype.getVisible=V.prototype.Mb;V.prototype.getZIndex=V.prototype.Ba;V.prototype.setExtent=V.prototype.vc;V.prototype.setMaxResolution=V.prototype.Ac;V.prototype.setMinResolution=V.prototype.Bc;V.prototype.setOpacity=V.prototype.wc;V.prototype.setVisible=V.prototype.xc;V.prototype.setZIndex=V.prototype.Vb;V.prototype.get=V.prototype.get; -V.prototype.getKeys=V.prototype.O;V.prototype.getProperties=V.prototype.N;V.prototype.set=V.prototype.set;V.prototype.setProperties=V.prototype.H;V.prototype.unset=V.prototype.P;V.prototype.changed=V.prototype.s;V.prototype.dispatchEvent=V.prototype.b;V.prototype.getRevision=V.prototype.L;V.prototype.on=V.prototype.J;V.prototype.once=V.prototype.once;V.prototype.un=V.prototype.K;Uv.prototype.setMap=Uv.prototype.setMap;Uv.prototype.setSource=Uv.prototype.Wc;Uv.prototype.getExtent=Uv.prototype.G; -Uv.prototype.getMaxResolution=Uv.prototype.fc;Uv.prototype.getMinResolution=Uv.prototype.gc;Uv.prototype.getOpacity=Uv.prototype.hc;Uv.prototype.getVisible=Uv.prototype.Mb;Uv.prototype.getZIndex=Uv.prototype.Ba;Uv.prototype.setExtent=Uv.prototype.vc;Uv.prototype.setMaxResolution=Uv.prototype.Ac;Uv.prototype.setMinResolution=Uv.prototype.Bc;Uv.prototype.setOpacity=Uv.prototype.wc;Uv.prototype.setVisible=Uv.prototype.xc;Uv.prototype.setZIndex=Uv.prototype.Vb;Uv.prototype.get=Uv.prototype.get; -Uv.prototype.getKeys=Uv.prototype.O;Uv.prototype.getProperties=Uv.prototype.N;Uv.prototype.set=Uv.prototype.set;Uv.prototype.setProperties=Uv.prototype.H;Uv.prototype.unset=Uv.prototype.P;Uv.prototype.changed=Uv.prototype.s;Uv.prototype.dispatchEvent=Uv.prototype.b;Uv.prototype.getRevision=Uv.prototype.L;Uv.prototype.on=Uv.prototype.J;Uv.prototype.once=Uv.prototype.once;Uv.prototype.un=Uv.prototype.K;cw.prototype.setMap=cw.prototype.setMap;cw.prototype.setSource=cw.prototype.Wc; -cw.prototype.getExtent=cw.prototype.G;cw.prototype.getMaxResolution=cw.prototype.fc;cw.prototype.getMinResolution=cw.prototype.gc;cw.prototype.getOpacity=cw.prototype.hc;cw.prototype.getVisible=cw.prototype.Mb;cw.prototype.getZIndex=cw.prototype.Ba;cw.prototype.setExtent=cw.prototype.vc;cw.prototype.setMaxResolution=cw.prototype.Ac;cw.prototype.setMinResolution=cw.prototype.Bc;cw.prototype.setOpacity=cw.prototype.wc;cw.prototype.setVisible=cw.prototype.xc;cw.prototype.setZIndex=cw.prototype.Vb; -cw.prototype.get=cw.prototype.get;cw.prototype.getKeys=cw.prototype.O;cw.prototype.getProperties=cw.prototype.N;cw.prototype.set=cw.prototype.set;cw.prototype.setProperties=cw.prototype.H;cw.prototype.unset=cw.prototype.P;cw.prototype.changed=cw.prototype.s;cw.prototype.dispatchEvent=cw.prototype.b;cw.prototype.getRevision=cw.prototype.L;cw.prototype.on=cw.prototype.J;cw.prototype.once=cw.prototype.once;cw.prototype.un=cw.prototype.K;W.prototype.getSource=W.prototype.ha;W.prototype.getStyle=W.prototype.D; -W.prototype.getStyleFunction=W.prototype.C;W.prototype.setStyle=W.prototype.g;W.prototype.setMap=W.prototype.setMap;W.prototype.setSource=W.prototype.Wc;W.prototype.getExtent=W.prototype.G;W.prototype.getMaxResolution=W.prototype.fc;W.prototype.getMinResolution=W.prototype.gc;W.prototype.getOpacity=W.prototype.hc;W.prototype.getVisible=W.prototype.Mb;W.prototype.getZIndex=W.prototype.Ba;W.prototype.setExtent=W.prototype.vc;W.prototype.setMaxResolution=W.prototype.Ac;W.prototype.setMinResolution=W.prototype.Bc; -W.prototype.setOpacity=W.prototype.wc;W.prototype.setVisible=W.prototype.xc;W.prototype.setZIndex=W.prototype.Vb;W.prototype.get=W.prototype.get;W.prototype.getKeys=W.prototype.O;W.prototype.getProperties=W.prototype.N;W.prototype.set=W.prototype.set;W.prototype.setProperties=W.prototype.H;W.prototype.unset=W.prototype.P;W.prototype.changed=W.prototype.s;W.prototype.dispatchEvent=W.prototype.b;W.prototype.getRevision=W.prototype.L;W.prototype.on=W.prototype.J;W.prototype.once=W.prototype.once; -W.prototype.un=W.prototype.K;ng.prototype.get=ng.prototype.get;ng.prototype.getKeys=ng.prototype.O;ng.prototype.getProperties=ng.prototype.N;ng.prototype.set=ng.prototype.set;ng.prototype.setProperties=ng.prototype.H;ng.prototype.unset=ng.prototype.P;ng.prototype.changed=ng.prototype.s;ng.prototype.dispatchEvent=ng.prototype.b;ng.prototype.getRevision=ng.prototype.L;ng.prototype.on=ng.prototype.J;ng.prototype.once=ng.prototype.once;ng.prototype.un=ng.prototype.K;rg.prototype.getActive=rg.prototype.c; -rg.prototype.getMap=rg.prototype.f;rg.prototype.setActive=rg.prototype.Ha;rg.prototype.get=rg.prototype.get;rg.prototype.getKeys=rg.prototype.O;rg.prototype.getProperties=rg.prototype.N;rg.prototype.set=rg.prototype.set;rg.prototype.setProperties=rg.prototype.H;rg.prototype.unset=rg.prototype.P;rg.prototype.changed=rg.prototype.s;rg.prototype.dispatchEvent=rg.prototype.b;rg.prototype.getRevision=rg.prototype.L;rg.prototype.on=rg.prototype.J;rg.prototype.once=rg.prototype.once;rg.prototype.un=rg.prototype.K; -Rs.prototype.getActive=Rs.prototype.c;Rs.prototype.getMap=Rs.prototype.f;Rs.prototype.setActive=Rs.prototype.Ha;Rs.prototype.get=Rs.prototype.get;Rs.prototype.getKeys=Rs.prototype.O;Rs.prototype.getProperties=Rs.prototype.N;Rs.prototype.set=Rs.prototype.set;Rs.prototype.setProperties=Rs.prototype.H;Rs.prototype.unset=Rs.prototype.P;Rs.prototype.changed=Rs.prototype.s;Rs.prototype.dispatchEvent=Rs.prototype.b;Rs.prototype.getRevision=Rs.prototype.L;Rs.prototype.on=Rs.prototype.J; -Rs.prototype.once=Rs.prototype.once;Rs.prototype.un=Rs.prototype.K;Us.prototype.type=Us.prototype.type;Us.prototype.target=Us.prototype.target;Us.prototype.preventDefault=Us.prototype.preventDefault;Us.prototype.stopPropagation=Us.prototype.stopPropagation;Dg.prototype.getActive=Dg.prototype.c;Dg.prototype.getMap=Dg.prototype.f;Dg.prototype.setActive=Dg.prototype.Ha;Dg.prototype.get=Dg.prototype.get;Dg.prototype.getKeys=Dg.prototype.O;Dg.prototype.getProperties=Dg.prototype.N;Dg.prototype.set=Dg.prototype.set; -Dg.prototype.setProperties=Dg.prototype.H;Dg.prototype.unset=Dg.prototype.P;Dg.prototype.changed=Dg.prototype.s;Dg.prototype.dispatchEvent=Dg.prototype.b;Dg.prototype.getRevision=Dg.prototype.L;Dg.prototype.on=Dg.prototype.J;Dg.prototype.once=Dg.prototype.once;Dg.prototype.un=Dg.prototype.K;Rg.prototype.getActive=Rg.prototype.c;Rg.prototype.getMap=Rg.prototype.f;Rg.prototype.setActive=Rg.prototype.Ha;Rg.prototype.get=Rg.prototype.get;Rg.prototype.getKeys=Rg.prototype.O; -Rg.prototype.getProperties=Rg.prototype.N;Rg.prototype.set=Rg.prototype.set;Rg.prototype.setProperties=Rg.prototype.H;Rg.prototype.unset=Rg.prototype.P;Rg.prototype.changed=Rg.prototype.s;Rg.prototype.dispatchEvent=Rg.prototype.b;Rg.prototype.getRevision=Rg.prototype.L;Rg.prototype.on=Rg.prototype.J;Rg.prototype.once=Rg.prototype.once;Rg.prototype.un=Rg.prototype.K;Wg.prototype.type=Wg.prototype.type;Wg.prototype.target=Wg.prototype.target;Wg.prototype.preventDefault=Wg.prototype.preventDefault; -Wg.prototype.stopPropagation=Wg.prototype.stopPropagation;Gg.prototype.getActive=Gg.prototype.c;Gg.prototype.getMap=Gg.prototype.f;Gg.prototype.setActive=Gg.prototype.Ha;Gg.prototype.get=Gg.prototype.get;Gg.prototype.getKeys=Gg.prototype.O;Gg.prototype.getProperties=Gg.prototype.N;Gg.prototype.set=Gg.prototype.set;Gg.prototype.setProperties=Gg.prototype.H;Gg.prototype.unset=Gg.prototype.P;Gg.prototype.changed=Gg.prototype.s;Gg.prototype.dispatchEvent=Gg.prototype.b;Gg.prototype.getRevision=Gg.prototype.L; -Gg.prototype.on=Gg.prototype.J;Gg.prototype.once=Gg.prototype.once;Gg.prototype.un=Gg.prototype.K;Kg.prototype.getActive=Kg.prototype.c;Kg.prototype.getMap=Kg.prototype.f;Kg.prototype.setActive=Kg.prototype.Ha;Kg.prototype.get=Kg.prototype.get;Kg.prototype.getKeys=Kg.prototype.O;Kg.prototype.getProperties=Kg.prototype.N;Kg.prototype.set=Kg.prototype.set;Kg.prototype.setProperties=Kg.prototype.H;Kg.prototype.unset=Kg.prototype.P;Kg.prototype.changed=Kg.prototype.s;Kg.prototype.dispatchEvent=Kg.prototype.b; -Kg.prototype.getRevision=Kg.prototype.L;Kg.prototype.on=Kg.prototype.J;Kg.prototype.once=Kg.prototype.once;Kg.prototype.un=Kg.prototype.K;Ys.prototype.getActive=Ys.prototype.c;Ys.prototype.getMap=Ys.prototype.f;Ys.prototype.setActive=Ys.prototype.Ha;Ys.prototype.get=Ys.prototype.get;Ys.prototype.getKeys=Ys.prototype.O;Ys.prototype.getProperties=Ys.prototype.N;Ys.prototype.set=Ys.prototype.set;Ys.prototype.setProperties=Ys.prototype.H;Ys.prototype.unset=Ys.prototype.P;Ys.prototype.changed=Ys.prototype.s; -Ys.prototype.dispatchEvent=Ys.prototype.b;Ys.prototype.getRevision=Ys.prototype.L;Ys.prototype.on=Ys.prototype.J;Ys.prototype.once=Ys.prototype.once;Ys.prototype.un=Ys.prototype.K;$g.prototype.getGeometry=$g.prototype.V;$g.prototype.getActive=$g.prototype.c;$g.prototype.getMap=$g.prototype.f;$g.prototype.setActive=$g.prototype.Ha;$g.prototype.get=$g.prototype.get;$g.prototype.getKeys=$g.prototype.O;$g.prototype.getProperties=$g.prototype.N;$g.prototype.set=$g.prototype.set; -$g.prototype.setProperties=$g.prototype.H;$g.prototype.unset=$g.prototype.P;$g.prototype.changed=$g.prototype.s;$g.prototype.dispatchEvent=$g.prototype.b;$g.prototype.getRevision=$g.prototype.L;$g.prototype.on=$g.prototype.J;$g.prototype.once=$g.prototype.once;$g.prototype.un=$g.prototype.K;ju.prototype.getActive=ju.prototype.c;ju.prototype.getMap=ju.prototype.f;ju.prototype.setActive=ju.prototype.Ha;ju.prototype.get=ju.prototype.get;ju.prototype.getKeys=ju.prototype.O; -ju.prototype.getProperties=ju.prototype.N;ju.prototype.set=ju.prototype.set;ju.prototype.setProperties=ju.prototype.H;ju.prototype.unset=ju.prototype.P;ju.prototype.changed=ju.prototype.s;ju.prototype.dispatchEvent=ju.prototype.b;ju.prototype.getRevision=ju.prototype.L;ju.prototype.on=ju.prototype.J;ju.prototype.once=ju.prototype.once;ju.prototype.un=ju.prototype.K;zu.prototype.type=zu.prototype.type;zu.prototype.target=zu.prototype.target;zu.prototype.preventDefault=zu.prototype.preventDefault; -zu.prototype.stopPropagation=zu.prototype.stopPropagation;Au.prototype.getActive=Au.prototype.c;Au.prototype.getMap=Au.prototype.f;Au.prototype.setActive=Au.prototype.Ha;Au.prototype.get=Au.prototype.get;Au.prototype.getKeys=Au.prototype.O;Au.prototype.getProperties=Au.prototype.N;Au.prototype.set=Au.prototype.set;Au.prototype.setProperties=Au.prototype.H;Au.prototype.unset=Au.prototype.P;Au.prototype.changed=Au.prototype.s;Au.prototype.dispatchEvent=Au.prototype.b;Au.prototype.getRevision=Au.prototype.L; -Au.prototype.on=Au.prototype.J;Au.prototype.once=Au.prototype.once;Au.prototype.un=Au.prototype.K;Lu.prototype.type=Lu.prototype.type;Lu.prototype.target=Lu.prototype.target;Lu.prototype.preventDefault=Lu.prototype.preventDefault;Lu.prototype.stopPropagation=Lu.prototype.stopPropagation;ah.prototype.getActive=ah.prototype.c;ah.prototype.getMap=ah.prototype.f;ah.prototype.setActive=ah.prototype.Ha;ah.prototype.get=ah.prototype.get;ah.prototype.getKeys=ah.prototype.O;ah.prototype.getProperties=ah.prototype.N; -ah.prototype.set=ah.prototype.set;ah.prototype.setProperties=ah.prototype.H;ah.prototype.unset=ah.prototype.P;ah.prototype.changed=ah.prototype.s;ah.prototype.dispatchEvent=ah.prototype.b;ah.prototype.getRevision=ah.prototype.L;ah.prototype.on=ah.prototype.J;ah.prototype.once=ah.prototype.once;ah.prototype.un=ah.prototype.K;ch.prototype.getActive=ch.prototype.c;ch.prototype.getMap=ch.prototype.f;ch.prototype.setActive=ch.prototype.Ha;ch.prototype.get=ch.prototype.get;ch.prototype.getKeys=ch.prototype.O; -ch.prototype.getProperties=ch.prototype.N;ch.prototype.set=ch.prototype.set;ch.prototype.setProperties=ch.prototype.H;ch.prototype.unset=ch.prototype.P;ch.prototype.changed=ch.prototype.s;ch.prototype.dispatchEvent=ch.prototype.b;ch.prototype.getRevision=ch.prototype.L;ch.prototype.on=ch.prototype.J;ch.prototype.once=ch.prototype.once;ch.prototype.un=ch.prototype.K;Nu.prototype.getActive=Nu.prototype.c;Nu.prototype.getMap=Nu.prototype.f;Nu.prototype.setActive=Nu.prototype.Ha;Nu.prototype.get=Nu.prototype.get; -Nu.prototype.getKeys=Nu.prototype.O;Nu.prototype.getProperties=Nu.prototype.N;Nu.prototype.set=Nu.prototype.set;Nu.prototype.setProperties=Nu.prototype.H;Nu.prototype.unset=Nu.prototype.P;Nu.prototype.changed=Nu.prototype.s;Nu.prototype.dispatchEvent=Nu.prototype.b;Nu.prototype.getRevision=Nu.prototype.L;Nu.prototype.on=Nu.prototype.J;Nu.prototype.once=Nu.prototype.once;Nu.prototype.un=Nu.prototype.K;Vu.prototype.type=Vu.prototype.type;Vu.prototype.target=Vu.prototype.target; -Vu.prototype.preventDefault=Vu.prototype.preventDefault;Vu.prototype.stopPropagation=Vu.prototype.stopPropagation;eh.prototype.getActive=eh.prototype.c;eh.prototype.getMap=eh.prototype.f;eh.prototype.setActive=eh.prototype.Ha;eh.prototype.get=eh.prototype.get;eh.prototype.getKeys=eh.prototype.O;eh.prototype.getProperties=eh.prototype.N;eh.prototype.set=eh.prototype.set;eh.prototype.setProperties=eh.prototype.H;eh.prototype.unset=eh.prototype.P;eh.prototype.changed=eh.prototype.s; -eh.prototype.dispatchEvent=eh.prototype.b;eh.prototype.getRevision=eh.prototype.L;eh.prototype.on=eh.prototype.J;eh.prototype.once=eh.prototype.once;eh.prototype.un=eh.prototype.K;ih.prototype.getActive=ih.prototype.c;ih.prototype.getMap=ih.prototype.f;ih.prototype.setActive=ih.prototype.Ha;ih.prototype.get=ih.prototype.get;ih.prototype.getKeys=ih.prototype.O;ih.prototype.getProperties=ih.prototype.N;ih.prototype.set=ih.prototype.set;ih.prototype.setProperties=ih.prototype.H;ih.prototype.unset=ih.prototype.P; -ih.prototype.changed=ih.prototype.s;ih.prototype.dispatchEvent=ih.prototype.b;ih.prototype.getRevision=ih.prototype.L;ih.prototype.on=ih.prototype.J;ih.prototype.once=ih.prototype.once;ih.prototype.un=ih.prototype.K;mh.prototype.getActive=mh.prototype.c;mh.prototype.getMap=mh.prototype.f;mh.prototype.setActive=mh.prototype.Ha;mh.prototype.get=mh.prototype.get;mh.prototype.getKeys=mh.prototype.O;mh.prototype.getProperties=mh.prototype.N;mh.prototype.set=mh.prototype.set; -mh.prototype.setProperties=mh.prototype.H;mh.prototype.unset=mh.prototype.P;mh.prototype.changed=mh.prototype.s;mh.prototype.dispatchEvent=mh.prototype.b;mh.prototype.getRevision=mh.prototype.L;mh.prototype.on=mh.prototype.J;mh.prototype.once=mh.prototype.once;mh.prototype.un=mh.prototype.K;cv.prototype.getActive=cv.prototype.c;cv.prototype.getMap=cv.prototype.f;cv.prototype.setActive=cv.prototype.Ha;cv.prototype.get=cv.prototype.get;cv.prototype.getKeys=cv.prototype.O; -cv.prototype.getProperties=cv.prototype.N;cv.prototype.set=cv.prototype.set;cv.prototype.setProperties=cv.prototype.H;cv.prototype.unset=cv.prototype.P;cv.prototype.changed=cv.prototype.s;cv.prototype.dispatchEvent=cv.prototype.b;cv.prototype.getRevision=cv.prototype.L;cv.prototype.on=cv.prototype.J;cv.prototype.once=cv.prototype.once;cv.prototype.un=cv.prototype.K;fv.prototype.type=fv.prototype.type;fv.prototype.target=fv.prototype.target;fv.prototype.preventDefault=fv.prototype.preventDefault; -fv.prototype.stopPropagation=fv.prototype.stopPropagation;hv.prototype.getActive=hv.prototype.c;hv.prototype.getMap=hv.prototype.f;hv.prototype.setActive=hv.prototype.Ha;hv.prototype.get=hv.prototype.get;hv.prototype.getKeys=hv.prototype.O;hv.prototype.getProperties=hv.prototype.N;hv.prototype.set=hv.prototype.set;hv.prototype.setProperties=hv.prototype.H;hv.prototype.unset=hv.prototype.P;hv.prototype.changed=hv.prototype.s;hv.prototype.dispatchEvent=hv.prototype.b;hv.prototype.getRevision=hv.prototype.L; -hv.prototype.on=hv.prototype.J;hv.prototype.once=hv.prototype.once;hv.prototype.un=hv.prototype.K;mv.prototype.getActive=mv.prototype.c;mv.prototype.getMap=mv.prototype.f;mv.prototype.setActive=mv.prototype.Ha;mv.prototype.get=mv.prototype.get;mv.prototype.getKeys=mv.prototype.O;mv.prototype.getProperties=mv.prototype.N;mv.prototype.set=mv.prototype.set;mv.prototype.setProperties=mv.prototype.H;mv.prototype.unset=mv.prototype.P;mv.prototype.changed=mv.prototype.s;mv.prototype.dispatchEvent=mv.prototype.b; -mv.prototype.getRevision=mv.prototype.L;mv.prototype.on=mv.prototype.J;mv.prototype.once=mv.prototype.once;mv.prototype.un=mv.prototype.K;sv.prototype.type=sv.prototype.type;sv.prototype.target=sv.prototype.target;sv.prototype.preventDefault=sv.prototype.preventDefault;sv.prototype.stopPropagation=sv.prototype.stopPropagation;of.prototype.get=of.prototype.get;of.prototype.getKeys=of.prototype.O;of.prototype.getProperties=of.prototype.N;of.prototype.set=of.prototype.set; -of.prototype.setProperties=of.prototype.H;of.prototype.unset=of.prototype.P;of.prototype.changed=of.prototype.s;of.prototype.dispatchEvent=of.prototype.b;of.prototype.getRevision=of.prototype.L;of.prototype.on=of.prototype.J;of.prototype.once=of.prototype.once;of.prototype.un=of.prototype.K;rf.prototype.getClosestPoint=rf.prototype.Ab;rf.prototype.intersectsCoordinate=rf.prototype.sb;rf.prototype.getExtent=rf.prototype.G;rf.prototype.rotate=rf.prototype.rotate;rf.prototype.scale=rf.prototype.scale; -rf.prototype.simplify=rf.prototype.Rb;rf.prototype.transform=rf.prototype.tb;rf.prototype.get=rf.prototype.get;rf.prototype.getKeys=rf.prototype.O;rf.prototype.getProperties=rf.prototype.N;rf.prototype.set=rf.prototype.set;rf.prototype.setProperties=rf.prototype.H;rf.prototype.unset=rf.prototype.P;rf.prototype.changed=rf.prototype.s;rf.prototype.dispatchEvent=rf.prototype.b;rf.prototype.getRevision=rf.prototype.L;rf.prototype.on=rf.prototype.J;rf.prototype.once=rf.prototype.once;rf.prototype.un=rf.prototype.K; -ys.prototype.getFirstCoordinate=ys.prototype.ac;ys.prototype.getLastCoordinate=ys.prototype.bc;ys.prototype.getLayout=ys.prototype.cc;ys.prototype.rotate=ys.prototype.rotate;ys.prototype.scale=ys.prototype.scale;ys.prototype.getClosestPoint=ys.prototype.Ab;ys.prototype.intersectsCoordinate=ys.prototype.sb;ys.prototype.getExtent=ys.prototype.G;ys.prototype.simplify=ys.prototype.Rb;ys.prototype.get=ys.prototype.get;ys.prototype.getKeys=ys.prototype.O;ys.prototype.getProperties=ys.prototype.N; -ys.prototype.set=ys.prototype.set;ys.prototype.setProperties=ys.prototype.H;ys.prototype.unset=ys.prototype.P;ys.prototype.changed=ys.prototype.s;ys.prototype.dispatchEvent=ys.prototype.b;ys.prototype.getRevision=ys.prototype.L;ys.prototype.on=ys.prototype.J;ys.prototype.once=ys.prototype.once;ys.prototype.un=ys.prototype.K;tm.prototype.getClosestPoint=tm.prototype.Ab;tm.prototype.intersectsCoordinate=tm.prototype.sb;tm.prototype.getExtent=tm.prototype.G;tm.prototype.rotate=tm.prototype.rotate; -tm.prototype.scale=tm.prototype.scale;tm.prototype.simplify=tm.prototype.Rb;tm.prototype.transform=tm.prototype.tb;tm.prototype.get=tm.prototype.get;tm.prototype.getKeys=tm.prototype.O;tm.prototype.getProperties=tm.prototype.N;tm.prototype.set=tm.prototype.set;tm.prototype.setProperties=tm.prototype.H;tm.prototype.unset=tm.prototype.P;tm.prototype.changed=tm.prototype.s;tm.prototype.dispatchEvent=tm.prototype.b;tm.prototype.getRevision=tm.prototype.L;tm.prototype.on=tm.prototype.J; -tm.prototype.once=tm.prototype.once;tm.prototype.un=tm.prototype.K;Jf.prototype.getFirstCoordinate=Jf.prototype.ac;Jf.prototype.getLastCoordinate=Jf.prototype.bc;Jf.prototype.getLayout=Jf.prototype.cc;Jf.prototype.rotate=Jf.prototype.rotate;Jf.prototype.scale=Jf.prototype.scale;Jf.prototype.getClosestPoint=Jf.prototype.Ab;Jf.prototype.intersectsCoordinate=Jf.prototype.sb;Jf.prototype.getExtent=Jf.prototype.G;Jf.prototype.simplify=Jf.prototype.Rb;Jf.prototype.transform=Jf.prototype.tb; -Jf.prototype.get=Jf.prototype.get;Jf.prototype.getKeys=Jf.prototype.O;Jf.prototype.getProperties=Jf.prototype.N;Jf.prototype.set=Jf.prototype.set;Jf.prototype.setProperties=Jf.prototype.H;Jf.prototype.unset=Jf.prototype.P;Jf.prototype.changed=Jf.prototype.s;Jf.prototype.dispatchEvent=Jf.prototype.b;Jf.prototype.getRevision=Jf.prototype.L;Jf.prototype.on=Jf.prototype.J;Jf.prototype.once=Jf.prototype.once;Jf.prototype.un=Jf.prototype.K;O.prototype.getFirstCoordinate=O.prototype.ac; -O.prototype.getLastCoordinate=O.prototype.bc;O.prototype.getLayout=O.prototype.cc;O.prototype.rotate=O.prototype.rotate;O.prototype.scale=O.prototype.scale;O.prototype.getClosestPoint=O.prototype.Ab;O.prototype.intersectsCoordinate=O.prototype.sb;O.prototype.getExtent=O.prototype.G;O.prototype.simplify=O.prototype.Rb;O.prototype.transform=O.prototype.tb;O.prototype.get=O.prototype.get;O.prototype.getKeys=O.prototype.O;O.prototype.getProperties=O.prototype.N;O.prototype.set=O.prototype.set; -O.prototype.setProperties=O.prototype.H;O.prototype.unset=O.prototype.P;O.prototype.changed=O.prototype.s;O.prototype.dispatchEvent=O.prototype.b;O.prototype.getRevision=O.prototype.L;O.prototype.on=O.prototype.J;O.prototype.once=O.prototype.once;O.prototype.un=O.prototype.K;P.prototype.getFirstCoordinate=P.prototype.ac;P.prototype.getLastCoordinate=P.prototype.bc;P.prototype.getLayout=P.prototype.cc;P.prototype.rotate=P.prototype.rotate;P.prototype.scale=P.prototype.scale; -P.prototype.getClosestPoint=P.prototype.Ab;P.prototype.intersectsCoordinate=P.prototype.sb;P.prototype.getExtent=P.prototype.G;P.prototype.simplify=P.prototype.Rb;P.prototype.transform=P.prototype.tb;P.prototype.get=P.prototype.get;P.prototype.getKeys=P.prototype.O;P.prototype.getProperties=P.prototype.N;P.prototype.set=P.prototype.set;P.prototype.setProperties=P.prototype.H;P.prototype.unset=P.prototype.P;P.prototype.changed=P.prototype.s;P.prototype.dispatchEvent=P.prototype.b; -P.prototype.getRevision=P.prototype.L;P.prototype.on=P.prototype.J;P.prototype.once=P.prototype.once;P.prototype.un=P.prototype.K;Q.prototype.getFirstCoordinate=Q.prototype.ac;Q.prototype.getLastCoordinate=Q.prototype.bc;Q.prototype.getLayout=Q.prototype.cc;Q.prototype.rotate=Q.prototype.rotate;Q.prototype.scale=Q.prototype.scale;Q.prototype.getClosestPoint=Q.prototype.Ab;Q.prototype.intersectsCoordinate=Q.prototype.sb;Q.prototype.getExtent=Q.prototype.G;Q.prototype.simplify=Q.prototype.Rb; -Q.prototype.transform=Q.prototype.tb;Q.prototype.get=Q.prototype.get;Q.prototype.getKeys=Q.prototype.O;Q.prototype.getProperties=Q.prototype.N;Q.prototype.set=Q.prototype.set;Q.prototype.setProperties=Q.prototype.H;Q.prototype.unset=Q.prototype.P;Q.prototype.changed=Q.prototype.s;Q.prototype.dispatchEvent=Q.prototype.b;Q.prototype.getRevision=Q.prototype.L;Q.prototype.on=Q.prototype.J;Q.prototype.once=Q.prototype.once;Q.prototype.un=Q.prototype.K;R.prototype.getFirstCoordinate=R.prototype.ac; -R.prototype.getLastCoordinate=R.prototype.bc;R.prototype.getLayout=R.prototype.cc;R.prototype.rotate=R.prototype.rotate;R.prototype.scale=R.prototype.scale;R.prototype.getClosestPoint=R.prototype.Ab;R.prototype.intersectsCoordinate=R.prototype.sb;R.prototype.getExtent=R.prototype.G;R.prototype.simplify=R.prototype.Rb;R.prototype.transform=R.prototype.tb;R.prototype.get=R.prototype.get;R.prototype.getKeys=R.prototype.O;R.prototype.getProperties=R.prototype.N;R.prototype.set=R.prototype.set; -R.prototype.setProperties=R.prototype.H;R.prototype.unset=R.prototype.P;R.prototype.changed=R.prototype.s;R.prototype.dispatchEvent=R.prototype.b;R.prototype.getRevision=R.prototype.L;R.prototype.on=R.prototype.J;R.prototype.once=R.prototype.once;R.prototype.un=R.prototype.K;C.prototype.getFirstCoordinate=C.prototype.ac;C.prototype.getLastCoordinate=C.prototype.bc;C.prototype.getLayout=C.prototype.cc;C.prototype.rotate=C.prototype.rotate;C.prototype.scale=C.prototype.scale; -C.prototype.getClosestPoint=C.prototype.Ab;C.prototype.intersectsCoordinate=C.prototype.sb;C.prototype.getExtent=C.prototype.G;C.prototype.simplify=C.prototype.Rb;C.prototype.transform=C.prototype.tb;C.prototype.get=C.prototype.get;C.prototype.getKeys=C.prototype.O;C.prototype.getProperties=C.prototype.N;C.prototype.set=C.prototype.set;C.prototype.setProperties=C.prototype.H;C.prototype.unset=C.prototype.P;C.prototype.changed=C.prototype.s;C.prototype.dispatchEvent=C.prototype.b; -C.prototype.getRevision=C.prototype.L;C.prototype.on=C.prototype.J;C.prototype.once=C.prototype.once;C.prototype.un=C.prototype.K;D.prototype.getFirstCoordinate=D.prototype.ac;D.prototype.getLastCoordinate=D.prototype.bc;D.prototype.getLayout=D.prototype.cc;D.prototype.rotate=D.prototype.rotate;D.prototype.scale=D.prototype.scale;D.prototype.getClosestPoint=D.prototype.Ab;D.prototype.intersectsCoordinate=D.prototype.sb;D.prototype.getExtent=D.prototype.G;D.prototype.simplify=D.prototype.Rb; -D.prototype.transform=D.prototype.tb;D.prototype.get=D.prototype.get;D.prototype.getKeys=D.prototype.O;D.prototype.getProperties=D.prototype.N;D.prototype.set=D.prototype.set;D.prototype.setProperties=D.prototype.H;D.prototype.unset=D.prototype.P;D.prototype.changed=D.prototype.s;D.prototype.dispatchEvent=D.prototype.b;D.prototype.getRevision=D.prototype.L;D.prototype.on=D.prototype.J;D.prototype.once=D.prototype.once;D.prototype.un=D.prototype.K;Sm.prototype.readFeatures=Sm.prototype.Oa; -an.prototype.readFeatures=an.prototype.Oa;Sm.prototype.readFeatures=Sm.prototype.Oa;md.prototype.get=md.prototype.get;md.prototype.getKeys=md.prototype.O;md.prototype.getProperties=md.prototype.N;md.prototype.set=md.prototype.set;md.prototype.setProperties=md.prototype.H;md.prototype.unset=md.prototype.P;md.prototype.changed=md.prototype.s;md.prototype.dispatchEvent=md.prototype.b;md.prototype.getRevision=md.prototype.L;md.prototype.on=md.prototype.J;md.prototype.once=md.prototype.once; -md.prototype.un=md.prototype.K;nd.prototype.getMap=nd.prototype.g;nd.prototype.setMap=nd.prototype.setMap;nd.prototype.setTarget=nd.prototype.f;nd.prototype.get=nd.prototype.get;nd.prototype.getKeys=nd.prototype.O;nd.prototype.getProperties=nd.prototype.N;nd.prototype.set=nd.prototype.set;nd.prototype.setProperties=nd.prototype.H;nd.prototype.unset=nd.prototype.P;nd.prototype.changed=nd.prototype.s;nd.prototype.dispatchEvent=nd.prototype.b;nd.prototype.getRevision=nd.prototype.L;nd.prototype.on=nd.prototype.J; -nd.prototype.once=nd.prototype.once;nd.prototype.un=nd.prototype.K;yd.prototype.getMap=yd.prototype.g;yd.prototype.setMap=yd.prototype.setMap;yd.prototype.setTarget=yd.prototype.f;yd.prototype.get=yd.prototype.get;yd.prototype.getKeys=yd.prototype.O;yd.prototype.getProperties=yd.prototype.N;yd.prototype.set=yd.prototype.set;yd.prototype.setProperties=yd.prototype.H;yd.prototype.unset=yd.prototype.P;yd.prototype.changed=yd.prototype.s;yd.prototype.dispatchEvent=yd.prototype.b; -yd.prototype.getRevision=yd.prototype.L;yd.prototype.on=yd.prototype.J;yd.prototype.once=yd.prototype.once;yd.prototype.un=yd.prototype.K;Dd.prototype.getMap=Dd.prototype.g;Dd.prototype.setMap=Dd.prototype.setMap;Dd.prototype.setTarget=Dd.prototype.f;Dd.prototype.get=Dd.prototype.get;Dd.prototype.getKeys=Dd.prototype.O;Dd.prototype.getProperties=Dd.prototype.N;Dd.prototype.set=Dd.prototype.set;Dd.prototype.setProperties=Dd.prototype.H;Dd.prototype.unset=Dd.prototype.P;Dd.prototype.changed=Dd.prototype.s; -Dd.prototype.dispatchEvent=Dd.prototype.b;Dd.prototype.getRevision=Dd.prototype.L;Dd.prototype.on=Dd.prototype.J;Dd.prototype.once=Dd.prototype.once;Dd.prototype.un=Dd.prototype.K;Bk.prototype.getMap=Bk.prototype.g;Bk.prototype.setMap=Bk.prototype.setMap;Bk.prototype.setTarget=Bk.prototype.f;Bk.prototype.get=Bk.prototype.get;Bk.prototype.getKeys=Bk.prototype.O;Bk.prototype.getProperties=Bk.prototype.N;Bk.prototype.set=Bk.prototype.set;Bk.prototype.setProperties=Bk.prototype.H;Bk.prototype.unset=Bk.prototype.P; -Bk.prototype.changed=Bk.prototype.s;Bk.prototype.dispatchEvent=Bk.prototype.b;Bk.prototype.getRevision=Bk.prototype.L;Bk.prototype.on=Bk.prototype.J;Bk.prototype.once=Bk.prototype.once;Bk.prototype.un=Bk.prototype.K;ud.prototype.getMap=ud.prototype.g;ud.prototype.setMap=ud.prototype.setMap;ud.prototype.setTarget=ud.prototype.f;ud.prototype.get=ud.prototype.get;ud.prototype.getKeys=ud.prototype.O;ud.prototype.getProperties=ud.prototype.N;ud.prototype.set=ud.prototype.set; -ud.prototype.setProperties=ud.prototype.H;ud.prototype.unset=ud.prototype.P;ud.prototype.changed=ud.prototype.s;ud.prototype.dispatchEvent=ud.prototype.b;ud.prototype.getRevision=ud.prototype.L;ud.prototype.on=ud.prototype.J;ud.prototype.once=ud.prototype.once;ud.prototype.un=ud.prototype.K;Gk.prototype.getMap=Gk.prototype.g;Gk.prototype.setMap=Gk.prototype.setMap;Gk.prototype.setTarget=Gk.prototype.f;Gk.prototype.get=Gk.prototype.get;Gk.prototype.getKeys=Gk.prototype.O; -Gk.prototype.getProperties=Gk.prototype.N;Gk.prototype.set=Gk.prototype.set;Gk.prototype.setProperties=Gk.prototype.H;Gk.prototype.unset=Gk.prototype.P;Gk.prototype.changed=Gk.prototype.s;Gk.prototype.dispatchEvent=Gk.prototype.b;Gk.prototype.getRevision=Gk.prototype.L;Gk.prototype.on=Gk.prototype.J;Gk.prototype.once=Gk.prototype.once;Gk.prototype.un=Gk.prototype.K;wd.prototype.getMap=wd.prototype.g;wd.prototype.setMap=wd.prototype.setMap;wd.prototype.setTarget=wd.prototype.f;wd.prototype.get=wd.prototype.get; -wd.prototype.getKeys=wd.prototype.O;wd.prototype.getProperties=wd.prototype.N;wd.prototype.set=wd.prototype.set;wd.prototype.setProperties=wd.prototype.H;wd.prototype.unset=wd.prototype.P;wd.prototype.changed=wd.prototype.s;wd.prototype.dispatchEvent=wd.prototype.b;wd.prototype.getRevision=wd.prototype.L;wd.prototype.on=wd.prototype.J;wd.prototype.once=wd.prototype.once;wd.prototype.un=wd.prototype.K;Lk.prototype.getMap=Lk.prototype.g;Lk.prototype.setMap=Lk.prototype.setMap; -Lk.prototype.setTarget=Lk.prototype.f;Lk.prototype.get=Lk.prototype.get;Lk.prototype.getKeys=Lk.prototype.O;Lk.prototype.getProperties=Lk.prototype.N;Lk.prototype.set=Lk.prototype.set;Lk.prototype.setProperties=Lk.prototype.H;Lk.prototype.unset=Lk.prototype.P;Lk.prototype.changed=Lk.prototype.s;Lk.prototype.dispatchEvent=Lk.prototype.b;Lk.prototype.getRevision=Lk.prototype.L;Lk.prototype.on=Lk.prototype.J;Lk.prototype.once=Lk.prototype.once;Lk.prototype.un=Lk.prototype.K;Qk.prototype.getMap=Qk.prototype.g; -Qk.prototype.setMap=Qk.prototype.setMap;Qk.prototype.setTarget=Qk.prototype.f;Qk.prototype.get=Qk.prototype.get;Qk.prototype.getKeys=Qk.prototype.O;Qk.prototype.getProperties=Qk.prototype.N;Qk.prototype.set=Qk.prototype.set;Qk.prototype.setProperties=Qk.prototype.H;Qk.prototype.unset=Qk.prototype.P;Qk.prototype.changed=Qk.prototype.s;Qk.prototype.dispatchEvent=Qk.prototype.b;Qk.prototype.getRevision=Qk.prototype.L;Qk.prototype.on=Qk.prototype.J;Qk.prototype.once=Qk.prototype.once; -Qk.prototype.un=Qk.prototype.K; - return OPENLAYERS.ol; -})); diff --git a/themes/bootstrap3/js/vendor/slick.min.js b/themes/bootstrap3/js/vendor/slick.min.js new file mode 100644 index 0000000000000000000000000000000000000000..6f298a7709381363121f1e1992ec589a2ac33a9b --- /dev/null +++ b/themes/bootstrap3/js/vendor/slick.min.js @@ -0,0 +1,3037 @@ +/* + _ _ _ _ + ___| (_) ___| | __ (_)___ +/ __| | |/ __| |/ / | / __| +\__ \ | | (__| < _ | \__ \ +|___/_|_|\___|_|\_(_)/ |___/ + |__/ + + Version: 1.9.0 + Author: Ken Wheeler + Website: http://kenwheeler.github.io + Docs: http://kenwheeler.github.io/slick + Repo: http://github.com/kenwheeler/slick + Issues: http://github.com/kenwheeler/slick/issues + + */ +/* global window, document, define, jQuery, setInterval, clearInterval */ +;(function(factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof exports !== 'undefined') { + module.exports = factory(require('jquery')); + } else { + factory(jQuery); + } + +}(function($) { + 'use strict'; + var Slick = window.Slick || {}; + + Slick = (function() { + + var instanceUid = 0; + + function Slick(element, settings) { + + var _ = this, dataSettings; + + _.defaults = { + accessibility: true, + adaptiveHeight: false, + appendArrows: $(element), + appendDots: $(element), + arrows: true, + asNavFor: null, + prevArrow: '<button class="slick-prev" aria-label="Previous" type="button">Previous</button>', + nextArrow: '<button class="slick-next" aria-label="Next" type="button">Next</button>', + autoplay: false, + autoplaySpeed: 3000, + centerMode: false, + centerPadding: '50px', + cssEase: 'ease', + customPaging: function(slider, i) { + return $('<button type="button" />').text(i + 1); + }, + dots: false, + dotsClass: 'slick-dots', + draggable: true, + easing: 'linear', + edgeFriction: 0.35, + fade: false, + focusOnSelect: false, + focusOnChange: false, + infinite: true, + initialSlide: 0, + lazyLoad: 'ondemand', + mobileFirst: false, + pauseOnHover: true, + pauseOnFocus: true, + pauseOnDotsHover: false, + respondTo: 'window', + responsive: null, + rows: 1, + rtl: false, + slide: '', + slidesPerRow: 1, + slidesToShow: 1, + slidesToScroll: 1, + speed: 500, + swipe: true, + swipeToSlide: false, + touchMove: true, + touchThreshold: 5, + useCSS: true, + useTransform: true, + variableWidth: false, + vertical: false, + verticalSwiping: false, + waitForAnimate: true, + zIndex: 1000 + }; + + _.initials = { + animating: false, + dragging: false, + autoPlayTimer: null, + currentDirection: 0, + currentLeft: null, + currentSlide: 0, + direction: 1, + $dots: null, + listWidth: null, + listHeight: null, + loadIndex: 0, + $nextArrow: null, + $prevArrow: null, + scrolling: false, + slideCount: null, + slideWidth: null, + $slideTrack: null, + $slides: null, + sliding: false, + slideOffset: 0, + swipeLeft: null, + swiping: false, + $list: null, + touchObject: {}, + transformsEnabled: false, + unslicked: false + }; + + $.extend(_, _.initials); + + _.activeBreakpoint = null; + _.animType = null; + _.animProp = null; + _.breakpoints = []; + _.breakpointSettings = []; + _.cssTransitions = false; + _.focussed = false; + _.interrupted = false; + _.hidden = 'hidden'; + _.paused = true; + _.positionProp = null; + _.respondTo = null; + _.rowCount = 1; + _.shouldClick = true; + _.$slider = $(element); + _.$slidesCache = null; + _.transformType = null; + _.transitionType = null; + _.visibilityChange = 'visibilitychange'; + _.windowWidth = 0; + _.windowTimer = null; + + dataSettings = $(element).data('slick') || {}; + + _.options = $.extend({}, _.defaults, settings, dataSettings); + + _.currentSlide = _.options.initialSlide; + + _.originalSettings = _.options; + + if (typeof document.mozHidden !== 'undefined') { + _.hidden = 'mozHidden'; + _.visibilityChange = 'mozvisibilitychange'; + } else if (typeof document.webkitHidden !== 'undefined') { + _.hidden = 'webkitHidden'; + _.visibilityChange = 'webkitvisibilitychange'; + } + + _.autoPlay = $.proxy(_.autoPlay, _); + _.autoPlayClear = $.proxy(_.autoPlayClear, _); + _.autoPlayIterator = $.proxy(_.autoPlayIterator, _); + _.changeSlide = $.proxy(_.changeSlide, _); + _.clickHandler = $.proxy(_.clickHandler, _); + _.selectHandler = $.proxy(_.selectHandler, _); + _.setPosition = $.proxy(_.setPosition, _); + _.swipeHandler = $.proxy(_.swipeHandler, _); + _.dragHandler = $.proxy(_.dragHandler, _); + _.keyHandler = $.proxy(_.keyHandler, _); + + _.instanceUid = instanceUid++; + + // A simple way to check for HTML strings + // Strict HTML recognition (must start with <) + // Extracted from jQuery v1.11 source + _.htmlExpr = /^(?:\s*(<[\w\W]+>)[^>]*)$/; + + + _.registerBreakpoints(); + _.init(true); + + } + + return Slick; + + }()); + + Slick.prototype.activateADA = function() { + var _ = this; + + _.$slideTrack.find('.slick-active').attr({ + 'aria-hidden': 'false' + }).find('a, input, button, select').attr({ + 'tabindex': '0' + }); + + }; + + Slick.prototype.addSlide = Slick.prototype.slickAdd = function(markup, index, addBefore) { + + var _ = this; + + if (typeof(index) === 'boolean') { + addBefore = index; + index = null; + } else if (index < 0 || (index >= _.slideCount)) { + return false; + } + + _.unload(); + + if (typeof(index) === 'number') { + if (index === 0 && _.$slides.length === 0) { + $(markup).appendTo(_.$slideTrack); + } else if (addBefore) { + $(markup).insertBefore(_.$slides.eq(index)); + } else { + $(markup).insertAfter(_.$slides.eq(index)); + } + } else { + if (addBefore === true) { + $(markup).prependTo(_.$slideTrack); + } else { + $(markup).appendTo(_.$slideTrack); + } + } + + _.$slides = _.$slideTrack.children(this.options.slide); + + _.$slideTrack.children(this.options.slide).detach(); + + _.$slideTrack.append(_.$slides); + + _.$slides.each(function(index, element) { + $(element).attr('data-slick-index', index); + }); + + _.$slidesCache = _.$slides; + + _.reinit(); + + }; + + Slick.prototype.animateHeight = function() { + var _ = this; + if (_.options.slidesToShow === 1 && _.options.adaptiveHeight === true && _.options.vertical === false) { + var targetHeight = _.$slides.eq(_.currentSlide).outerHeight(true); + _.$list.animate({ + height: targetHeight + }, _.options.speed); + } + }; + + Slick.prototype.animateSlide = function(targetLeft, callback) { + + var animProps = {}, + _ = this; + + _.animateHeight(); + + if (_.options.rtl === true && _.options.vertical === false) { + targetLeft = -targetLeft; + } + if (_.transformsEnabled === false) { + if (_.options.vertical === false) { + _.$slideTrack.animate({ + left: targetLeft + }, _.options.speed, _.options.easing, callback); + } else { + _.$slideTrack.animate({ + top: targetLeft + }, _.options.speed, _.options.easing, callback); + } + + } else { + + if (_.cssTransitions === false) { + if (_.options.rtl === true) { + _.currentLeft = -(_.currentLeft); + } + $({ + animStart: _.currentLeft + }).animate({ + animStart: targetLeft + }, { + duration: _.options.speed, + easing: _.options.easing, + step: function(now) { + now = Math.ceil(now); + if (_.options.vertical === false) { + animProps[_.animType] = 'translate(' + + now + 'px, 0px)'; + _.$slideTrack.css(animProps); + } else { + animProps[_.animType] = 'translate(0px,' + + now + 'px)'; + _.$slideTrack.css(animProps); + } + }, + complete: function() { + if (callback) { + callback.call(); + } + } + }); + + } else { + + _.applyTransition(); + targetLeft = Math.ceil(targetLeft); + + if (_.options.vertical === false) { + animProps[_.animType] = 'translate3d(' + targetLeft + 'px, 0px, 0px)'; + } else { + animProps[_.animType] = 'translate3d(0px,' + targetLeft + 'px, 0px)'; + } + _.$slideTrack.css(animProps); + + if (callback) { + setTimeout(function() { + + _.disableTransition(); + + callback.call(); + }, _.options.speed); + } + + } + + } + + }; + + Slick.prototype.getNavTarget = function() { + + var _ = this, + asNavFor = _.options.asNavFor; + + if ( asNavFor && asNavFor !== null ) { + asNavFor = $(asNavFor).not(_.$slider); + } + + return asNavFor; + + }; + + Slick.prototype.asNavFor = function(index) { + + var _ = this, + asNavFor = _.getNavTarget(); + + if ( asNavFor !== null && typeof asNavFor === 'object' ) { + asNavFor.each(function() { + var target = $(this).slick('getSlick'); + if(!target.unslicked) { + target.slideHandler(index, true); + } + }); + } + + }; + + Slick.prototype.applyTransition = function(slide) { + + var _ = this, + transition = {}; + + if (_.options.fade === false) { + transition[_.transitionType] = _.transformType + ' ' + _.options.speed + 'ms ' + _.options.cssEase; + } else { + transition[_.transitionType] = 'opacity ' + _.options.speed + 'ms ' + _.options.cssEase; + } + + if (_.options.fade === false) { + _.$slideTrack.css(transition); + } else { + _.$slides.eq(slide).css(transition); + } + + }; + + Slick.prototype.autoPlay = function() { + + var _ = this; + + _.autoPlayClear(); + + if ( _.slideCount > _.options.slidesToShow ) { + _.autoPlayTimer = setInterval( _.autoPlayIterator, _.options.autoplaySpeed ); + } + + }; + + Slick.prototype.autoPlayClear = function() { + + var _ = this; + + if (_.autoPlayTimer) { + clearInterval(_.autoPlayTimer); + } + + }; + + Slick.prototype.autoPlayIterator = function() { + + var _ = this, + slideTo = _.currentSlide + _.options.slidesToScroll; + + if ( !_.paused && !_.interrupted && !_.focussed ) { + + if ( _.options.infinite === false ) { + + if ( _.direction === 1 && ( _.currentSlide + 1 ) === ( _.slideCount - 1 )) { + _.direction = 0; + } + + else if ( _.direction === 0 ) { + + slideTo = _.currentSlide - _.options.slidesToScroll; + + if ( _.currentSlide - 1 === 0 ) { + _.direction = 1; + } + + } + + } + + _.slideHandler( slideTo ); + + } + + }; + + Slick.prototype.buildArrows = function() { + + var _ = this; + + if (_.options.arrows === true ) { + + _.$prevArrow = $(_.options.prevArrow).addClass('slick-arrow'); + _.$nextArrow = $(_.options.nextArrow).addClass('slick-arrow'); + + if( _.slideCount > _.options.slidesToShow ) { + + _.$prevArrow.removeClass('slick-hidden').removeAttr('aria-hidden tabindex'); + _.$nextArrow.removeClass('slick-hidden').removeAttr('aria-hidden tabindex'); + + if (_.htmlExpr.test(_.options.prevArrow)) { + _.$prevArrow.prependTo(_.options.appendArrows); + } + + if (_.htmlExpr.test(_.options.nextArrow)) { + _.$nextArrow.appendTo(_.options.appendArrows); + } + + if (_.options.infinite !== true) { + _.$prevArrow + .addClass('slick-disabled') + .attr('aria-disabled', 'true'); + } + + } else { + + _.$prevArrow.add( _.$nextArrow ) + + .addClass('slick-hidden') + .attr({ + 'aria-disabled': 'true', + 'tabindex': '-1' + }); + + } + + } + + }; + + Slick.prototype.buildDots = function() { + + var _ = this, + i, dot; + + if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { + + _.$slider.addClass('slick-dotted'); + + dot = $('<ul />').addClass(_.options.dotsClass); + + for (i = 0; i <= _.getDotCount(); i += 1) { + dot.append($('<li />').append(_.options.customPaging.call(this, _, i))); + } + + _.$dots = dot.appendTo(_.options.appendDots); + + _.$dots.find('li').first().addClass('slick-active'); + + } + + }; + + Slick.prototype.buildOut = function() { + + var _ = this; + + _.$slides = + _.$slider + .children( _.options.slide + ':not(.slick-cloned)') + .addClass('slick-slide'); + + _.slideCount = _.$slides.length; + + _.$slides.each(function(index, element) { + $(element) + .attr('data-slick-index', index) + .data('originalStyling', $(element).attr('style') || ''); + }); + + _.$slider.addClass('slick-slider'); + + _.$slideTrack = (_.slideCount === 0) ? + $('<div class="slick-track"/>').appendTo(_.$slider) : + _.$slides.wrapAll('<div class="slick-track"/>').parent(); + + _.$list = _.$slideTrack.wrap( + '<div class="slick-list"/>').parent(); + _.$slideTrack.css('opacity', 0); + + if (_.options.centerMode === true || _.options.swipeToSlide === true) { + _.options.slidesToScroll = 1; + } + + $('img[data-lazy]', _.$slider).not('[src]').addClass('slick-loading'); + + _.setupInfinite(); + + _.buildArrows(); + + _.buildDots(); + + _.updateDots(); + + + _.setSlideClasses(typeof _.currentSlide === 'number' ? _.currentSlide : 0); + + if (_.options.draggable === true) { + _.$list.addClass('draggable'); + } + + }; + + Slick.prototype.buildRows = function() { + + var _ = this, a, b, c, newSlides, numOfSlides, originalSlides,slidesPerSection; + + newSlides = document.createDocumentFragment(); + originalSlides = _.$slider.children(); + + if(_.options.rows > 0) { + + slidesPerSection = _.options.slidesPerRow * _.options.rows; + numOfSlides = Math.ceil( + originalSlides.length / slidesPerSection + ); + + for(a = 0; a < numOfSlides; a++){ + var slide = document.createElement('div'); + for(b = 0; b < _.options.rows; b++) { + var row = document.createElement('div'); + for(c = 0; c < _.options.slidesPerRow; c++) { + var target = (a * slidesPerSection + ((b * _.options.slidesPerRow) + c)); + if (originalSlides.get(target)) { + row.appendChild(originalSlides.get(target)); + } + } + slide.appendChild(row); + } + newSlides.appendChild(slide); + } + + _.$slider.empty().append(newSlides); + _.$slider.children().children().children() + .css({ + 'width':(100 / _.options.slidesPerRow) + '%', + 'display': 'inline-block' + }); + + } + + }; + + Slick.prototype.checkResponsive = function(initial, forceUpdate) { + + var _ = this, + breakpoint, targetBreakpoint, respondToWidth, triggerBreakpoint = false; + var sliderWidth = _.$slider.width(); + var windowWidth = window.innerWidth || $(window).width(); + + if (_.respondTo === 'window') { + respondToWidth = windowWidth; + } else if (_.respondTo === 'slider') { + respondToWidth = sliderWidth; + } else if (_.respondTo === 'min') { + respondToWidth = Math.min(windowWidth, sliderWidth); + } + + if ( _.options.responsive && + _.options.responsive.length && + _.options.responsive !== null) { + + targetBreakpoint = null; + + for (breakpoint in _.breakpoints) { + if (_.breakpoints.hasOwnProperty(breakpoint)) { + if (_.originalSettings.mobileFirst === false) { + if (respondToWidth < _.breakpoints[breakpoint]) { + targetBreakpoint = _.breakpoints[breakpoint]; + } + } else { + if (respondToWidth > _.breakpoints[breakpoint]) { + targetBreakpoint = _.breakpoints[breakpoint]; + } + } + } + } + + if (targetBreakpoint !== null) { + if (_.activeBreakpoint !== null) { + if (targetBreakpoint !== _.activeBreakpoint || forceUpdate) { + _.activeBreakpoint = + targetBreakpoint; + if (_.breakpointSettings[targetBreakpoint] === 'unslick') { + _.unslick(targetBreakpoint); + } else { + _.options = $.extend({}, _.originalSettings, + _.breakpointSettings[ + targetBreakpoint]); + if (initial === true) { + _.currentSlide = _.options.initialSlide; + } + _.refresh(initial); + } + triggerBreakpoint = targetBreakpoint; + } + } else { + _.activeBreakpoint = targetBreakpoint; + if (_.breakpointSettings[targetBreakpoint] === 'unslick') { + _.unslick(targetBreakpoint); + } else { + _.options = $.extend({}, _.originalSettings, + _.breakpointSettings[ + targetBreakpoint]); + if (initial === true) { + _.currentSlide = _.options.initialSlide; + } + _.refresh(initial); + } + triggerBreakpoint = targetBreakpoint; + } + } else { + if (_.activeBreakpoint !== null) { + _.activeBreakpoint = null; + _.options = _.originalSettings; + if (initial === true) { + _.currentSlide = _.options.initialSlide; + } + _.refresh(initial); + triggerBreakpoint = targetBreakpoint; + } + } + + // only trigger breakpoints during an actual break. not on initialize. + if( !initial && triggerBreakpoint !== false ) { + _.$slider.trigger('breakpoint', [_, triggerBreakpoint]); + } + } + + }; + + Slick.prototype.changeSlide = function(event, dontAnimate) { + + var _ = this, + $target = $(event.currentTarget), + indexOffset, slideOffset, unevenOffset; + + // If target is a link, prevent default action. + if($target.is('a')) { + event.preventDefault(); + } + + // If target is not the <li> element (ie: a child), find the <li>. + if(!$target.is('li')) { + $target = $target.closest('li'); + } + + unevenOffset = (_.slideCount % _.options.slidesToScroll !== 0); + indexOffset = unevenOffset ? 0 : (_.slideCount - _.currentSlide) % _.options.slidesToScroll; + + switch (event.data.message) { + + case 'previous': + slideOffset = indexOffset === 0 ? _.options.slidesToScroll : _.options.slidesToShow - indexOffset; + if (_.slideCount > _.options.slidesToShow) { + _.slideHandler(_.currentSlide - slideOffset, false, dontAnimate); + } + break; + + case 'next': + slideOffset = indexOffset === 0 ? _.options.slidesToScroll : indexOffset; + if (_.slideCount > _.options.slidesToShow) { + _.slideHandler(_.currentSlide + slideOffset, false, dontAnimate); + } + break; + + case 'index': + var index = event.data.index === 0 ? 0 : + event.data.index || $target.index() * _.options.slidesToScroll; + + _.slideHandler(_.checkNavigable(index), false, dontAnimate); + $target.children().trigger('focus'); + break; + + default: + return; + } + + }; + + Slick.prototype.checkNavigable = function(index) { + + var _ = this, + navigables, prevNavigable; + + navigables = _.getNavigableIndexes(); + prevNavigable = 0; + if (index > navigables[navigables.length - 1]) { + index = navigables[navigables.length - 1]; + } else { + for (var n in navigables) { + if (index < navigables[n]) { + index = prevNavigable; + break; + } + prevNavigable = navigables[n]; + } + } + + return index; + }; + + Slick.prototype.cleanUpEvents = function() { + + var _ = this; + + if (_.options.dots && _.$dots !== null) { + + $('li', _.$dots) + .off('click.slick', _.changeSlide) + .off('mouseenter.slick', $.proxy(_.interrupt, _, true)) + .off('mouseleave.slick', $.proxy(_.interrupt, _, false)); + + if (_.options.accessibility === true) { + _.$dots.off('keydown.slick', _.keyHandler); + } + } + + _.$slider.off('focus.slick blur.slick'); + + if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { + _.$prevArrow && _.$prevArrow.off('click.slick', _.changeSlide); + _.$nextArrow && _.$nextArrow.off('click.slick', _.changeSlide); + + if (_.options.accessibility === true) { + _.$prevArrow && _.$prevArrow.off('keydown.slick', _.keyHandler); + _.$nextArrow && _.$nextArrow.off('keydown.slick', _.keyHandler); + } + } + + _.$list.off('touchstart.slick mousedown.slick', _.swipeHandler); + _.$list.off('touchmove.slick mousemove.slick', _.swipeHandler); + _.$list.off('touchend.slick mouseup.slick', _.swipeHandler); + _.$list.off('touchcancel.slick mouseleave.slick', _.swipeHandler); + + _.$list.off('click.slick', _.clickHandler); + + $(document).off(_.visibilityChange, _.visibility); + + _.cleanUpSlideEvents(); + + if (_.options.accessibility === true) { + _.$list.off('keydown.slick', _.keyHandler); + } + + if (_.options.focusOnSelect === true) { + $(_.$slideTrack).children().off('click.slick', _.selectHandler); + } + + $(window).off('orientationchange.slick.slick-' + _.instanceUid, _.orientationChange); + + $(window).off('resize.slick.slick-' + _.instanceUid, _.resize); + + $('[draggable!=true]', _.$slideTrack).off('dragstart', _.preventDefault); + + $(window).off('load.slick.slick-' + _.instanceUid, _.setPosition); + + }; + + Slick.prototype.cleanUpSlideEvents = function() { + + var _ = this; + + _.$list.off('mouseenter.slick', $.proxy(_.interrupt, _, true)); + _.$list.off('mouseleave.slick', $.proxy(_.interrupt, _, false)); + + }; + + Slick.prototype.cleanUpRows = function() { + + var _ = this, originalSlides; + + if(_.options.rows > 0) { + originalSlides = _.$slides.children().children(); + originalSlides.removeAttr('style'); + _.$slider.empty().append(originalSlides); + } + + }; + + Slick.prototype.clickHandler = function(event) { + + var _ = this; + + if (_.shouldClick === false) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } + + }; + + Slick.prototype.destroy = function(refresh) { + + var _ = this; + + _.autoPlayClear(); + + _.touchObject = {}; + + _.cleanUpEvents(); + + $('.slick-cloned', _.$slider).detach(); + + if (_.$dots) { + _.$dots.remove(); + } + + if ( _.$prevArrow && _.$prevArrow.length ) { + + _.$prevArrow + .removeClass('slick-disabled slick-arrow slick-hidden') + .removeAttr('aria-hidden aria-disabled tabindex') + .css('display',''); + + if ( _.htmlExpr.test( _.options.prevArrow )) { + _.$prevArrow.remove(); + } + } + + if ( _.$nextArrow && _.$nextArrow.length ) { + + _.$nextArrow + .removeClass('slick-disabled slick-arrow slick-hidden') + .removeAttr('aria-hidden aria-disabled tabindex') + .css('display',''); + + if ( _.htmlExpr.test( _.options.nextArrow )) { + _.$nextArrow.remove(); + } + } + + + if (_.$slides) { + + _.$slides + .removeClass('slick-slide slick-active slick-center slick-visible slick-current') + .removeAttr('aria-hidden') + .removeAttr('data-slick-index') + .each(function(){ + $(this).attr('style', $(this).data('originalStyling')); + }); + + _.$slideTrack.children(this.options.slide).detach(); + + _.$slideTrack.detach(); + + _.$list.detach(); + + _.$slider.append(_.$slides); + } + + _.cleanUpRows(); + + _.$slider.removeClass('slick-slider'); + _.$slider.removeClass('slick-initialized'); + _.$slider.removeClass('slick-dotted'); + + _.unslicked = true; + + if(!refresh) { + _.$slider.trigger('destroy', [_]); + } + + }; + + Slick.prototype.disableTransition = function(slide) { + + var _ = this, + transition = {}; + + transition[_.transitionType] = ''; + + if (_.options.fade === false) { + _.$slideTrack.css(transition); + } else { + _.$slides.eq(slide).css(transition); + } + + }; + + Slick.prototype.fadeSlide = function(slideIndex, callback) { + + var _ = this; + + if (_.cssTransitions === false) { + + _.$slides.eq(slideIndex).css({ + zIndex: _.options.zIndex + }); + + _.$slides.eq(slideIndex).animate({ + opacity: 1 + }, _.options.speed, _.options.easing, callback); + + } else { + + _.applyTransition(slideIndex); + + _.$slides.eq(slideIndex).css({ + opacity: 1, + zIndex: _.options.zIndex + }); + + if (callback) { + setTimeout(function() { + + _.disableTransition(slideIndex); + + callback.call(); + }, _.options.speed); + } + + } + + }; + + Slick.prototype.fadeSlideOut = function(slideIndex) { + + var _ = this; + + if (_.cssTransitions === false) { + + _.$slides.eq(slideIndex).animate({ + opacity: 0, + zIndex: _.options.zIndex - 2 + }, _.options.speed, _.options.easing); + + } else { + + _.applyTransition(slideIndex); + + _.$slides.eq(slideIndex).css({ + opacity: 0, + zIndex: _.options.zIndex - 2 + }); + + } + + }; + + Slick.prototype.filterSlides = Slick.prototype.slickFilter = function(filter) { + + var _ = this; + + if (filter !== null) { + + _.$slidesCache = _.$slides; + + _.unload(); + + _.$slideTrack.children(this.options.slide).detach(); + + _.$slidesCache.filter(filter).appendTo(_.$slideTrack); + + _.reinit(); + + } + + }; + + Slick.prototype.focusHandler = function() { + + var _ = this; + + // If any child element receives focus within the slider we need to pause the autoplay + _.$slider + .off('focus.slick blur.slick') + .on( + 'focus.slick', + '*', + function(event) { + var $sf = $(this); + + setTimeout(function() { + if( _.options.pauseOnFocus ) { + if ($sf.is(':focus')) { + _.focussed = true; + _.autoPlay(); + } + } + }, 0); + } + ).on( + 'blur.slick', + '*', + function(event) { + var $sf = $(this); + + // When a blur occurs on any elements within the slider we become unfocused + if( _.options.pauseOnFocus ) { + _.focussed = false; + _.autoPlay(); + } + } + ); + }; + + Slick.prototype.getCurrent = Slick.prototype.slickCurrentSlide = function() { + + var _ = this; + return _.currentSlide; + + }; + + Slick.prototype.getDotCount = function() { + + var _ = this; + + var breakPoint = 0; + var counter = 0; + var pagerQty = 0; + + if (_.options.infinite === true) { + if (_.slideCount <= _.options.slidesToShow) { + ++pagerQty; + } else { + while (breakPoint < _.slideCount) { + ++pagerQty; + breakPoint = counter + _.options.slidesToScroll; + counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow; + } + } + } else if (_.options.centerMode === true) { + pagerQty = _.slideCount; + } else if(!_.options.asNavFor) { + pagerQty = 1 + Math.ceil((_.slideCount - _.options.slidesToShow) / _.options.slidesToScroll); + }else { + while (breakPoint < _.slideCount) { + ++pagerQty; + breakPoint = counter + _.options.slidesToScroll; + counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow; + } + } + + return pagerQty - 1; + + }; + + Slick.prototype.getLeft = function(slideIndex) { + + var _ = this, + targetLeft, + verticalHeight, + verticalOffset = 0, + targetSlide, + coef; + + _.slideOffset = 0; + verticalHeight = _.$slides.first().outerHeight(true); + + if (_.options.infinite === true) { + if (_.slideCount > _.options.slidesToShow) { + _.slideOffset = (_.slideWidth * _.options.slidesToShow) * -1; + coef = -1 + + if (_.options.vertical === true && _.options.centerMode === true) { + if (_.options.slidesToShow === 2) { + coef = -1.5; + } else if (_.options.slidesToShow === 1) { + coef = -2 + } + } + verticalOffset = (verticalHeight * _.options.slidesToShow) * coef; + } + if (_.slideCount % _.options.slidesToScroll !== 0) { + if (slideIndex + _.options.slidesToScroll > _.slideCount && _.slideCount > _.options.slidesToShow) { + if (slideIndex > _.slideCount) { + _.slideOffset = ((_.options.slidesToShow - (slideIndex - _.slideCount)) * _.slideWidth) * -1; + verticalOffset = ((_.options.slidesToShow - (slideIndex - _.slideCount)) * verticalHeight) * -1; + } else { + _.slideOffset = ((_.slideCount % _.options.slidesToScroll) * _.slideWidth) * -1; + verticalOffset = ((_.slideCount % _.options.slidesToScroll) * verticalHeight) * -1; + } + } + } + } else { + if (slideIndex + _.options.slidesToShow > _.slideCount) { + _.slideOffset = ((slideIndex + _.options.slidesToShow) - _.slideCount) * _.slideWidth; + verticalOffset = ((slideIndex + _.options.slidesToShow) - _.slideCount) * verticalHeight; + } + } + + if (_.slideCount <= _.options.slidesToShow) { + _.slideOffset = 0; + verticalOffset = 0; + } + + if (_.options.centerMode === true && _.slideCount <= _.options.slidesToShow) { + _.slideOffset = ((_.slideWidth * Math.floor(_.options.slidesToShow)) / 2) - ((_.slideWidth * _.slideCount) / 2); + } else if (_.options.centerMode === true && _.options.infinite === true) { + _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2) - _.slideWidth; + } else if (_.options.centerMode === true) { + _.slideOffset = 0; + _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2); + } + + if (_.options.vertical === false) { + targetLeft = ((slideIndex * _.slideWidth) * -1) + _.slideOffset; + } else { + targetLeft = ((slideIndex * verticalHeight) * -1) + verticalOffset; + } + + if (_.options.variableWidth === true) { + + if (_.slideCount <= _.options.slidesToShow || _.options.infinite === false) { + targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex); + } else { + targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex + _.options.slidesToShow); + } + + if (_.options.rtl === true) { + if (targetSlide[0]) { + targetLeft = (_.$slideTrack.width() - targetSlide[0].offsetLeft - targetSlide.width()) * -1; + } else { + targetLeft = 0; + } + } else { + targetLeft = targetSlide[0] ? targetSlide[0].offsetLeft * -1 : 0; + } + + if (_.options.centerMode === true) { + if (_.slideCount <= _.options.slidesToShow || _.options.infinite === false) { + targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex); + } else { + targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex + _.options.slidesToShow + 1); + } + + if (_.options.rtl === true) { + if (targetSlide[0]) { + targetLeft = (_.$slideTrack.width() - targetSlide[0].offsetLeft - targetSlide.width()) * -1; + } else { + targetLeft = 0; + } + } else { + targetLeft = targetSlide[0] ? targetSlide[0].offsetLeft * -1 : 0; + } + + targetLeft += (_.$list.width() - targetSlide.outerWidth()) / 2; + } + } + + return targetLeft; + + }; + + Slick.prototype.getOption = Slick.prototype.slickGetOption = function(option) { + + var _ = this; + + return _.options[option]; + + }; + + Slick.prototype.getNavigableIndexes = function() { + + var _ = this, + breakPoint = 0, + counter = 0, + indexes = [], + max; + + if (_.options.infinite === false) { + max = _.slideCount; + } else { + breakPoint = _.options.slidesToScroll * -1; + counter = _.options.slidesToScroll * -1; + max = _.slideCount * 2; + } + + while (breakPoint < max) { + indexes.push(breakPoint); + breakPoint = counter + _.options.slidesToScroll; + counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow; + } + + return indexes; + + }; + + Slick.prototype.getSlick = function() { + + return this; + + }; + + Slick.prototype.getSlideCount = function() { + + var _ = this, + slidesTraversed, swipedSlide, swipeTarget, centerOffset; + + centerOffset = _.options.centerMode === true ? Math.floor(_.$list.width() / 2) : 0; + swipeTarget = (_.swipeLeft * -1) + centerOffset; + + if (_.options.swipeToSlide === true) { + + _.$slideTrack.find('.slick-slide').each(function(index, slide) { + + var slideOuterWidth, slideOffset, slideRightBoundary; + slideOuterWidth = $(slide).outerWidth(); + slideOffset = slide.offsetLeft; + if (_.options.centerMode !== true) { + slideOffset += (slideOuterWidth / 2); + } + + slideRightBoundary = slideOffset + (slideOuterWidth); + + if (swipeTarget < slideRightBoundary) { + swipedSlide = slide; + return false; + } + }); + + slidesTraversed = Math.abs($(swipedSlide).attr('data-slick-index') - _.currentSlide) || 1; + + return slidesTraversed; + + } else { + return _.options.slidesToScroll; + } + + }; + + Slick.prototype.goTo = Slick.prototype.slickGoTo = function(slide, dontAnimate) { + + var _ = this; + + _.changeSlide({ + data: { + message: 'index', + index: parseInt(slide) + } + }, dontAnimate); + + }; + + Slick.prototype.init = function(creation) { + + var _ = this; + + if (!$(_.$slider).hasClass('slick-initialized')) { + + $(_.$slider).addClass('slick-initialized'); + + _.buildRows(); + _.buildOut(); + _.setProps(); + _.startLoad(); + _.loadSlider(); + _.initializeEvents(); + _.updateArrows(); + _.updateDots(); + _.checkResponsive(true); + _.focusHandler(); + + } + + if (creation) { + _.$slider.trigger('init', [_]); + } + + if (_.options.accessibility === true) { + _.initADA(); + } + + if ( _.options.autoplay ) { + + _.paused = false; + _.autoPlay(); + + } + + }; + + Slick.prototype.initADA = function() { + var _ = this, + numDotGroups = Math.ceil(_.slideCount / _.options.slidesToShow), + tabControlIndexes = _.getNavigableIndexes().filter(function(val) { + return (val >= 0) && (val < _.slideCount); + }); + + _.$slides.add(_.$slideTrack.find('.slick-cloned')).attr({ + 'aria-hidden': 'true', + 'tabindex': '-1' + }).find('a, input, button, select').attr({ + 'tabindex': '-1' + }); + + if (_.$dots !== null) { + _.$slides.not(_.$slideTrack.find('.slick-cloned')).each(function(i) { + var slideControlIndex = tabControlIndexes.indexOf(i); + + $(this).attr({ + 'role': 'tabpanel', + 'id': 'slick-slide' + _.instanceUid + i, + 'tabindex': -1 + }); + + if (slideControlIndex !== -1) { + var ariaButtonControl = 'slick-slide-control' + _.instanceUid + slideControlIndex + if ($('#' + ariaButtonControl).length) { + $(this).attr({ + 'aria-describedby': ariaButtonControl + }); + } + } + }); + + _.$dots.attr('role', 'tablist').find('li').each(function(i) { + var mappedSlideIndex = tabControlIndexes[i]; + + $(this).attr({ + 'role': 'presentation' + }); + + $(this).find('button').first().attr({ + 'role': 'tab', + 'id': 'slick-slide-control' + _.instanceUid + i, + 'aria-controls': 'slick-slide' + _.instanceUid + mappedSlideIndex, + 'aria-label': (i + 1) + ' of ' + numDotGroups, + 'aria-selected': null, + 'tabindex': '-1' + }); + + }).eq(_.currentSlide).find('button').attr({ + 'aria-selected': 'true', + 'tabindex': '0' + }).end(); + } + + for (var i=_.currentSlide, max=i+_.options.slidesToShow; i < max; i++) { + if (_.options.focusOnChange) { + _.$slides.eq(i).attr({'tabindex': '0'}); + } else { + _.$slides.eq(i).removeAttr('tabindex'); + } + } + + _.activateADA(); + + }; + + Slick.prototype.initArrowEvents = function() { + + var _ = this; + + if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { + _.$prevArrow + .off('click.slick') + .on('click.slick', { + message: 'previous' + }, _.changeSlide); + _.$nextArrow + .off('click.slick') + .on('click.slick', { + message: 'next' + }, _.changeSlide); + + if (_.options.accessibility === true) { + _.$prevArrow.on('keydown.slick', _.keyHandler); + _.$nextArrow.on('keydown.slick', _.keyHandler); + } + } + + }; + + Slick.prototype.initDotEvents = function() { + + var _ = this; + + if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { + $('li', _.$dots).on('click.slick', { + message: 'index' + }, _.changeSlide); + + if (_.options.accessibility === true) { + _.$dots.on('keydown.slick', _.keyHandler); + } + } + + if (_.options.dots === true && _.options.pauseOnDotsHover === true && _.slideCount > _.options.slidesToShow) { + + $('li', _.$dots) + .on('mouseenter.slick', $.proxy(_.interrupt, _, true)) + .on('mouseleave.slick', $.proxy(_.interrupt, _, false)); + + } + + }; + + Slick.prototype.initSlideEvents = function() { + + var _ = this; + + if ( _.options.pauseOnHover ) { + + _.$list.on('mouseenter.slick', $.proxy(_.interrupt, _, true)); + _.$list.on('mouseleave.slick', $.proxy(_.interrupt, _, false)); + + } + + }; + + Slick.prototype.initializeEvents = function() { + + var _ = this; + + _.initArrowEvents(); + + _.initDotEvents(); + _.initSlideEvents(); + + _.$list.on('touchstart.slick mousedown.slick', { + action: 'start' + }, _.swipeHandler); + _.$list.on('touchmove.slick mousemove.slick', { + action: 'move' + }, _.swipeHandler); + _.$list.on('touchend.slick mouseup.slick', { + action: 'end' + }, _.swipeHandler); + _.$list.on('touchcancel.slick mouseleave.slick', { + action: 'end' + }, _.swipeHandler); + + _.$list.on('click.slick', _.clickHandler); + + $(document).on(_.visibilityChange, $.proxy(_.visibility, _)); + + if (_.options.accessibility === true) { + _.$list.on('keydown.slick', _.keyHandler); + } + + if (_.options.focusOnSelect === true) { + $(_.$slideTrack).children().on('click.slick', _.selectHandler); + } + + $(window).on('orientationchange.slick.slick-' + _.instanceUid, $.proxy(_.orientationChange, _)); + + $(window).on('resize.slick.slick-' + _.instanceUid, $.proxy(_.resize, _)); + + $('[draggable!=true]', _.$slideTrack).on('dragstart', _.preventDefault); + + $(window).on('load.slick.slick-' + _.instanceUid, _.setPosition); + $(_.setPosition); + + }; + + Slick.prototype.initUI = function() { + + var _ = this; + + if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { + + _.$prevArrow.show(); + _.$nextArrow.show(); + + } + + if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { + + _.$dots.show(); + + } + + }; + + Slick.prototype.keyHandler = function(event) { + + var _ = this; + //Dont slide if the cursor is inside the form fields and arrow keys are pressed + if(!event.target.tagName.match('TEXTAREA|INPUT|SELECT')) { + if (event.keyCode === 37 && _.options.accessibility === true) { + _.changeSlide({ + data: { + message: _.options.rtl === true ? 'next' : 'previous' + } + }); + } else if (event.keyCode === 39 && _.options.accessibility === true) { + _.changeSlide({ + data: { + message: _.options.rtl === true ? 'previous' : 'next' + } + }); + } + } + + }; + + Slick.prototype.lazyLoad = function() { + + var _ = this, + loadRange, cloneRange, rangeStart, rangeEnd; + + function loadImages(imagesScope) { + + $('img[data-lazy]', imagesScope).each(function() { + + var image = $(this), + imageSource = $(this).attr('data-lazy'), + imageSrcSet = $(this).attr('data-srcset'), + imageSizes = $(this).attr('data-sizes') || _.$slider.attr('data-sizes'), + imageToLoad = document.createElement('img'); + + imageToLoad.onload = function() { + + image + .animate({ opacity: 0 }, 100, function() { + + if (imageSrcSet) { + image + .attr('srcset', imageSrcSet ); + + if (imageSizes) { + image + .attr('sizes', imageSizes ); + } + } + + image + .attr('src', imageSource) + .animate({ opacity: 1 }, 200, function() { + image + .removeAttr('data-lazy data-srcset data-sizes') + .removeClass('slick-loading'); + }); + _.$slider.trigger('lazyLoaded', [_, image, imageSource]); + }); + + }; + + imageToLoad.onerror = function() { + + image + .removeAttr( 'data-lazy' ) + .removeClass( 'slick-loading' ) + .addClass( 'slick-lazyload-error' ); + + _.$slider.trigger('lazyLoadError', [ _, image, imageSource ]); + + }; + + imageToLoad.src = imageSource; + + }); + + } + + if (_.options.centerMode === true) { + if (_.options.infinite === true) { + rangeStart = _.currentSlide + (_.options.slidesToShow / 2 + 1); + rangeEnd = rangeStart + _.options.slidesToShow + 2; + } else { + rangeStart = Math.max(0, _.currentSlide - (_.options.slidesToShow / 2 + 1)); + rangeEnd = 2 + (_.options.slidesToShow / 2 + 1) + _.currentSlide; + } + } else { + rangeStart = _.options.infinite ? _.options.slidesToShow + _.currentSlide : _.currentSlide; + rangeEnd = Math.ceil(rangeStart + _.options.slidesToShow); + if (_.options.fade === true) { + if (rangeStart > 0) rangeStart--; + if (rangeEnd <= _.slideCount) rangeEnd++; + } + } + + loadRange = _.$slider.find('.slick-slide').slice(rangeStart, rangeEnd); + + if (_.options.lazyLoad === 'anticipated') { + var prevSlide = rangeStart - 1, + nextSlide = rangeEnd, + $slides = _.$slider.find('.slick-slide'); + + for (var i = 0; i < _.options.slidesToScroll; i++) { + if (prevSlide < 0) prevSlide = _.slideCount - 1; + loadRange = loadRange.add($slides.eq(prevSlide)); + loadRange = loadRange.add($slides.eq(nextSlide)); + prevSlide--; + nextSlide++; + } + } + + loadImages(loadRange); + + if (_.slideCount <= _.options.slidesToShow) { + cloneRange = _.$slider.find('.slick-slide'); + loadImages(cloneRange); + } else + if (_.currentSlide >= _.slideCount - _.options.slidesToShow) { + cloneRange = _.$slider.find('.slick-cloned').slice(0, _.options.slidesToShow); + loadImages(cloneRange); + } else if (_.currentSlide === 0) { + cloneRange = _.$slider.find('.slick-cloned').slice(_.options.slidesToShow * -1); + loadImages(cloneRange); + } + + }; + + Slick.prototype.loadSlider = function() { + + var _ = this; + + _.setPosition(); + + _.$slideTrack.css({ + opacity: 1 + }); + + _.$slider.removeClass('slick-loading'); + + _.initUI(); + + if (_.options.lazyLoad === 'progressive') { + _.progressiveLazyLoad(); + } + + }; + + Slick.prototype.next = Slick.prototype.slickNext = function() { + + var _ = this; + + _.changeSlide({ + data: { + message: 'next' + } + }); + + }; + + Slick.prototype.orientationChange = function() { + + var _ = this; + + _.checkResponsive(); + _.setPosition(); + + }; + + Slick.prototype.pause = Slick.prototype.slickPause = function() { + + var _ = this; + + _.autoPlayClear(); + _.paused = true; + + }; + + Slick.prototype.play = Slick.prototype.slickPlay = function() { + + var _ = this; + + _.autoPlay(); + _.options.autoplay = true; + _.paused = false; + _.focussed = false; + _.interrupted = false; + + }; + + Slick.prototype.postSlide = function(index) { + + var _ = this; + + if( !_.unslicked ) { + + _.$slider.trigger('afterChange', [_, index]); + + _.animating = false; + + if (_.slideCount > _.options.slidesToShow) { + _.setPosition(); + } + + _.swipeLeft = null; + + if ( _.options.autoplay ) { + _.autoPlay(); + } + + if (_.options.accessibility === true) { + _.initADA(); + + if (_.options.focusOnChange) { + var $currentSlide = $(_.$slides.get(_.currentSlide)); + $currentSlide.attr('tabindex', 0).focus(); + } + } + + } + + }; + + Slick.prototype.prev = Slick.prototype.slickPrev = function() { + + var _ = this; + + _.changeSlide({ + data: { + message: 'previous' + } + }); + + }; + + Slick.prototype.preventDefault = function(event) { + + event.preventDefault(); + + }; + + Slick.prototype.progressiveLazyLoad = function( tryCount ) { + + tryCount = tryCount || 1; + + var _ = this, + $imgsToLoad = $( 'img[data-lazy]', _.$slider ), + image, + imageSource, + imageSrcSet, + imageSizes, + imageToLoad; + + if ( $imgsToLoad.length ) { + + image = $imgsToLoad.first(); + imageSource = image.attr('data-lazy'); + imageSrcSet = image.attr('data-srcset'); + imageSizes = image.attr('data-sizes') || _.$slider.attr('data-sizes'); + imageToLoad = document.createElement('img'); + + imageToLoad.onload = function() { + + if (imageSrcSet) { + image + .attr('srcset', imageSrcSet ); + + if (imageSizes) { + image + .attr('sizes', imageSizes ); + } + } + + image + .attr( 'src', imageSource ) + .removeAttr('data-lazy data-srcset data-sizes') + .removeClass('slick-loading'); + + if ( _.options.adaptiveHeight === true ) { + _.setPosition(); + } + + _.$slider.trigger('lazyLoaded', [ _, image, imageSource ]); + _.progressiveLazyLoad(); + + }; + + imageToLoad.onerror = function() { + + if ( tryCount < 3 ) { + + /** + * try to load the image 3 times, + * leave a slight delay so we don't get + * servers blocking the request. + */ + setTimeout( function() { + _.progressiveLazyLoad( tryCount + 1 ); + }, 500 ); + + } else { + + image + .removeAttr( 'data-lazy' ) + .removeClass( 'slick-loading' ) + .addClass( 'slick-lazyload-error' ); + + _.$slider.trigger('lazyLoadError', [ _, image, imageSource ]); + + _.progressiveLazyLoad(); + + } + + }; + + imageToLoad.src = imageSource; + + } else { + + _.$slider.trigger('allImagesLoaded', [ _ ]); + + } + + }; + + Slick.prototype.refresh = function( initializing ) { + + var _ = this, currentSlide, lastVisibleIndex; + + lastVisibleIndex = _.slideCount - _.options.slidesToShow; + + // in non-infinite sliders, we don't want to go past the + // last visible index. + if( !_.options.infinite && ( _.currentSlide > lastVisibleIndex )) { + _.currentSlide = lastVisibleIndex; + } + + // if less slides than to show, go to start. + if ( _.slideCount <= _.options.slidesToShow ) { + _.currentSlide = 0; + + } + + currentSlide = _.currentSlide; + + _.destroy(true); + + $.extend(_, _.initials, { currentSlide: currentSlide }); + + _.init(); + + if( !initializing ) { + + _.changeSlide({ + data: { + message: 'index', + index: currentSlide + } + }, false); + + } + + }; + + Slick.prototype.registerBreakpoints = function() { + + var _ = this, breakpoint, currentBreakpoint, l, + responsiveSettings = _.options.responsive || null; + + if ( $.type(responsiveSettings) === 'array' && responsiveSettings.length ) { + + _.respondTo = _.options.respondTo || 'window'; + + for ( breakpoint in responsiveSettings ) { + + l = _.breakpoints.length-1; + + if (responsiveSettings.hasOwnProperty(breakpoint)) { + currentBreakpoint = responsiveSettings[breakpoint].breakpoint; + + // loop through the breakpoints and cut out any existing + // ones with the same breakpoint number, we don't want dupes. + while( l >= 0 ) { + if( _.breakpoints[l] && _.breakpoints[l] === currentBreakpoint ) { + _.breakpoints.splice(l,1); + } + l--; + } + + _.breakpoints.push(currentBreakpoint); + _.breakpointSettings[currentBreakpoint] = responsiveSettings[breakpoint].settings; + + } + + } + + _.breakpoints.sort(function(a, b) { + return ( _.options.mobileFirst ) ? a-b : b-a; + }); + + } + + }; + + Slick.prototype.reinit = function() { + + var _ = this; + + _.$slides = + _.$slideTrack + .children(_.options.slide) + .addClass('slick-slide'); + + _.slideCount = _.$slides.length; + + if (_.currentSlide >= _.slideCount && _.currentSlide !== 0) { + _.currentSlide = _.currentSlide - _.options.slidesToScroll; + } + + if (_.slideCount <= _.options.slidesToShow) { + _.currentSlide = 0; + } + + _.registerBreakpoints(); + + _.setProps(); + _.setupInfinite(); + _.buildArrows(); + _.updateArrows(); + _.initArrowEvents(); + _.buildDots(); + _.updateDots(); + _.initDotEvents(); + _.cleanUpSlideEvents(); + _.initSlideEvents(); + + _.checkResponsive(false, true); + + if (_.options.focusOnSelect === true) { + $(_.$slideTrack).children().on('click.slick', _.selectHandler); + } + + _.setSlideClasses(typeof _.currentSlide === 'number' ? _.currentSlide : 0); + + _.setPosition(); + _.focusHandler(); + + _.paused = !_.options.autoplay; + _.autoPlay(); + + _.$slider.trigger('reInit', [_]); + + }; + + Slick.prototype.resize = function() { + + var _ = this; + + if ($(window).width() !== _.windowWidth) { + clearTimeout(_.windowDelay); + _.windowDelay = window.setTimeout(function() { + _.windowWidth = $(window).width(); + _.checkResponsive(); + if( !_.unslicked ) { _.setPosition(); } + }, 50); + } + }; + + Slick.prototype.removeSlide = Slick.prototype.slickRemove = function(index, removeBefore, removeAll) { + + var _ = this; + + if (typeof(index) === 'boolean') { + removeBefore = index; + index = removeBefore === true ? 0 : _.slideCount - 1; + } else { + index = removeBefore === true ? --index : index; + } + + if (_.slideCount < 1 || index < 0 || index > _.slideCount - 1) { + return false; + } + + _.unload(); + + if (removeAll === true) { + _.$slideTrack.children().remove(); + } else { + _.$slideTrack.children(this.options.slide).eq(index).remove(); + } + + _.$slides = _.$slideTrack.children(this.options.slide); + + _.$slideTrack.children(this.options.slide).detach(); + + _.$slideTrack.append(_.$slides); + + _.$slidesCache = _.$slides; + + _.reinit(); + + }; + + Slick.prototype.setCSS = function(position) { + + var _ = this, + positionProps = {}, + x, y; + + if (_.options.rtl === true) { + position = -position; + } + x = _.positionProp == 'left' ? Math.ceil(position) + 'px' : '0px'; + y = _.positionProp == 'top' ? Math.ceil(position) + 'px' : '0px'; + + positionProps[_.positionProp] = position; + + if (_.transformsEnabled === false) { + _.$slideTrack.css(positionProps); + } else { + positionProps = {}; + if (_.cssTransitions === false) { + positionProps[_.animType] = 'translate(' + x + ', ' + y + ')'; + _.$slideTrack.css(positionProps); + } else { + positionProps[_.animType] = 'translate3d(' + x + ', ' + y + ', 0px)'; + _.$slideTrack.css(positionProps); + } + } + + }; + + Slick.prototype.setDimensions = function() { + + var _ = this; + + if (_.options.vertical === false) { + if (_.options.centerMode === true) { + _.$list.css({ + padding: ('0px ' + _.options.centerPadding) + }); + } + } else { + _.$list.height(_.$slides.first().outerHeight(true) * _.options.slidesToShow); + if (_.options.centerMode === true) { + _.$list.css({ + padding: (_.options.centerPadding + ' 0px') + }); + } + } + + _.listWidth = _.$list.width(); + _.listHeight = _.$list.height(); + + + if (_.options.vertical === false && _.options.variableWidth === false) { + _.slideWidth = Math.ceil(_.listWidth / _.options.slidesToShow); + _.$slideTrack.width(Math.ceil((_.slideWidth * _.$slideTrack.children('.slick-slide').length))); + + } else if (_.options.variableWidth === true) { + _.$slideTrack.width(5000 * _.slideCount); + } else { + _.slideWidth = Math.ceil(_.listWidth); + _.$slideTrack.height(Math.ceil((_.$slides.first().outerHeight(true) * _.$slideTrack.children('.slick-slide').length))); + } + + var offset = _.$slides.first().outerWidth(true) - _.$slides.first().width(); + if (_.options.variableWidth === false) _.$slideTrack.children('.slick-slide').width(_.slideWidth - offset); + + }; + + Slick.prototype.setFade = function() { + + var _ = this, + targetLeft; + + _.$slides.each(function(index, element) { + targetLeft = (_.slideWidth * index) * -1; + if (_.options.rtl === true) { + $(element).css({ + position: 'relative', + right: targetLeft, + top: 0, + zIndex: _.options.zIndex - 2, + opacity: 0 + }); + } else { + $(element).css({ + position: 'relative', + left: targetLeft, + top: 0, + zIndex: _.options.zIndex - 2, + opacity: 0 + }); + } + }); + + _.$slides.eq(_.currentSlide).css({ + zIndex: _.options.zIndex - 1, + opacity: 1 + }); + + }; + + Slick.prototype.setHeight = function() { + + var _ = this; + + if (_.options.slidesToShow === 1 && _.options.adaptiveHeight === true && _.options.vertical === false) { + var targetHeight = _.$slides.eq(_.currentSlide).outerHeight(true); + _.$list.css('height', targetHeight); + } + + }; + + Slick.prototype.setOption = + Slick.prototype.slickSetOption = function() { + + /** + * accepts arguments in format of: + * + * - for changing a single option's value: + * .slick("setOption", option, value, refresh ) + * + * - for changing a set of responsive options: + * .slick("setOption", 'responsive', [{}, ...], refresh ) + * + * - for updating multiple values at once (not responsive) + * .slick("setOption", { 'option': value, ... }, refresh ) + */ + + var _ = this, l, item, option, value, refresh = false, type; + + if( $.type( arguments[0] ) === 'object' ) { + + option = arguments[0]; + refresh = arguments[1]; + type = 'multiple'; + + } else if ( $.type( arguments[0] ) === 'string' ) { + + option = arguments[0]; + value = arguments[1]; + refresh = arguments[2]; + + if ( arguments[0] === 'responsive' && $.type( arguments[1] ) === 'array' ) { + + type = 'responsive'; + + } else if ( typeof arguments[1] !== 'undefined' ) { + + type = 'single'; + + } + + } + + if ( type === 'single' ) { + + _.options[option] = value; + + + } else if ( type === 'multiple' ) { + + $.each( option , function( opt, val ) { + + _.options[opt] = val; + + }); + + + } else if ( type === 'responsive' ) { + + for ( item in value ) { + + if( $.type( _.options.responsive ) !== 'array' ) { + + _.options.responsive = [ value[item] ]; + + } else { + + l = _.options.responsive.length-1; + + // loop through the responsive object and splice out duplicates. + while( l >= 0 ) { + + if( _.options.responsive[l].breakpoint === value[item].breakpoint ) { + + _.options.responsive.splice(l,1); + + } + + l--; + + } + + _.options.responsive.push( value[item] ); + + } + + } + + } + + if ( refresh ) { + + _.unload(); + _.reinit(); + + } + + }; + + Slick.prototype.setPosition = function() { + + var _ = this; + + _.setDimensions(); + + _.setHeight(); + + if (_.options.fade === false) { + _.setCSS(_.getLeft(_.currentSlide)); + } else { + _.setFade(); + } + + _.$slider.trigger('setPosition', [_]); + + }; + + Slick.prototype.setProps = function() { + + var _ = this, + bodyStyle = document.body.style; + + _.positionProp = _.options.vertical === true ? 'top' : 'left'; + + if (_.positionProp === 'top') { + _.$slider.addClass('slick-vertical'); + } else { + _.$slider.removeClass('slick-vertical'); + } + + if (bodyStyle.WebkitTransition !== undefined || + bodyStyle.MozTransition !== undefined || + bodyStyle.msTransition !== undefined) { + if (_.options.useCSS === true) { + _.cssTransitions = true; + } + } + + if ( _.options.fade ) { + if ( typeof _.options.zIndex === 'number' ) { + if( _.options.zIndex < 3 ) { + _.options.zIndex = 3; + } + } else { + _.options.zIndex = _.defaults.zIndex; + } + } + + if (bodyStyle.OTransform !== undefined) { + _.animType = 'OTransform'; + _.transformType = '-o-transform'; + _.transitionType = 'OTransition'; + if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false; + } + if (bodyStyle.MozTransform !== undefined) { + _.animType = 'MozTransform'; + _.transformType = '-moz-transform'; + _.transitionType = 'MozTransition'; + if (bodyStyle.perspectiveProperty === undefined && bodyStyle.MozPerspective === undefined) _.animType = false; + } + if (bodyStyle.webkitTransform !== undefined) { + _.animType = 'webkitTransform'; + _.transformType = '-webkit-transform'; + _.transitionType = 'webkitTransition'; + if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false; + } + if (bodyStyle.msTransform !== undefined) { + _.animType = 'msTransform'; + _.transformType = '-ms-transform'; + _.transitionType = 'msTransition'; + if (bodyStyle.msTransform === undefined) _.animType = false; + } + if (bodyStyle.transform !== undefined && _.animType !== false) { + _.animType = 'transform'; + _.transformType = 'transform'; + _.transitionType = 'transition'; + } + _.transformsEnabled = _.options.useTransform && (_.animType !== null && _.animType !== false); + }; + + + Slick.prototype.setSlideClasses = function(index) { + + var _ = this, + centerOffset, allSlides, indexOffset, remainder; + + allSlides = _.$slider + .find('.slick-slide') + .removeClass('slick-active slick-center slick-current') + .attr('aria-hidden', 'true'); + + _.$slides + .eq(index) + .addClass('slick-current'); + + if (_.options.centerMode === true) { + + var evenCoef = _.options.slidesToShow % 2 === 0 ? 1 : 0; + + centerOffset = Math.floor(_.options.slidesToShow / 2); + + if (_.options.infinite === true) { + + if (index >= centerOffset && index <= (_.slideCount - 1) - centerOffset) { + _.$slides + .slice(index - centerOffset + evenCoef, index + centerOffset + 1) + .addClass('slick-active') + .attr('aria-hidden', 'false'); + + } else { + + indexOffset = _.options.slidesToShow + index; + allSlides + .slice(indexOffset - centerOffset + 1 + evenCoef, indexOffset + centerOffset + 2) + .addClass('slick-active') + .attr('aria-hidden', 'false'); + + } + + if (index === 0) { + + allSlides + .eq(allSlides.length - 1 - _.options.slidesToShow) + .addClass('slick-center'); + + } else if (index === _.slideCount - 1) { + + allSlides + .eq(_.options.slidesToShow) + .addClass('slick-center'); + + } + + } + + _.$slides + .eq(index) + .addClass('slick-center'); + + } else { + + if (index >= 0 && index <= (_.slideCount - _.options.slidesToShow)) { + + _.$slides + .slice(index, index + _.options.slidesToShow) + .addClass('slick-active') + .attr('aria-hidden', 'false'); + + } else if (allSlides.length <= _.options.slidesToShow) { + + allSlides + .addClass('slick-active') + .attr('aria-hidden', 'false'); + + } else { + + remainder = _.slideCount % _.options.slidesToShow; + indexOffset = _.options.infinite === true ? _.options.slidesToShow + index : index; + + if (_.options.slidesToShow == _.options.slidesToScroll && (_.slideCount - index) < _.options.slidesToShow) { + + allSlides + .slice(indexOffset - (_.options.slidesToShow - remainder), indexOffset + remainder) + .addClass('slick-active') + .attr('aria-hidden', 'false'); + + } else { + + allSlides + .slice(indexOffset, indexOffset + _.options.slidesToShow) + .addClass('slick-active') + .attr('aria-hidden', 'false'); + + } + + } + + } + + if (_.options.lazyLoad === 'ondemand' || _.options.lazyLoad === 'anticipated') { + _.lazyLoad(); + } + }; + + Slick.prototype.setupInfinite = function() { + + var _ = this, + i, slideIndex, infiniteCount; + + if (_.options.fade === true) { + _.options.centerMode = false; + } + + if (_.options.infinite === true && _.options.fade === false) { + + slideIndex = null; + + if (_.slideCount > _.options.slidesToShow) { + + if (_.options.centerMode === true) { + infiniteCount = _.options.slidesToShow + 1; + } else { + infiniteCount = _.options.slidesToShow; + } + + for (i = _.slideCount; i > (_.slideCount - + infiniteCount); i -= 1) { + slideIndex = i - 1; + $(_.$slides[slideIndex]).clone(true).attr('id', '') + .attr('data-slick-index', slideIndex - _.slideCount) + .prependTo(_.$slideTrack).addClass('slick-cloned'); + } + for (i = 0; i < infiniteCount + _.slideCount; i += 1) { + slideIndex = i; + $(_.$slides[slideIndex]).clone(true).attr('id', '') + .attr('data-slick-index', slideIndex + _.slideCount) + .appendTo(_.$slideTrack).addClass('slick-cloned'); + } + _.$slideTrack.find('.slick-cloned').find('[id]').each(function() { + $(this).attr('id', ''); + }); + + } + + } + + }; + + Slick.prototype.interrupt = function( toggle ) { + + var _ = this; + + if( !toggle ) { + _.autoPlay(); + } + _.interrupted = toggle; + + }; + + Slick.prototype.selectHandler = function(event) { + + var _ = this; + + var targetElement = + $(event.target).is('.slick-slide') ? + $(event.target) : + $(event.target).parents('.slick-slide'); + + var index = parseInt(targetElement.attr('data-slick-index')); + + if (!index) index = 0; + + if (_.slideCount <= _.options.slidesToShow) { + + _.slideHandler(index, false, true); + return; + + } + + _.slideHandler(index); + + }; + + Slick.prototype.slideHandler = function(index, sync, dontAnimate) { + + var targetSlide, animSlide, oldSlide, slideLeft, targetLeft = null, + _ = this, navTarget; + + sync = sync || false; + + if (_.animating === true && _.options.waitForAnimate === true) { + return; + } + + if (_.options.fade === true && _.currentSlide === index) { + return; + } + + if (sync === false) { + _.asNavFor(index); + } + + targetSlide = index; + targetLeft = _.getLeft(targetSlide); + slideLeft = _.getLeft(_.currentSlide); + + _.currentLeft = _.swipeLeft === null ? slideLeft : _.swipeLeft; + + if (_.options.infinite === false && _.options.centerMode === false && (index < 0 || index > _.getDotCount() * _.options.slidesToScroll)) { + if (_.options.fade === false) { + targetSlide = _.currentSlide; + if (dontAnimate !== true && _.slideCount > _.options.slidesToShow) { + _.animateSlide(slideLeft, function() { + _.postSlide(targetSlide); + }); + } else { + _.postSlide(targetSlide); + } + } + return; + } else if (_.options.infinite === false && _.options.centerMode === true && (index < 0 || index > (_.slideCount - _.options.slidesToScroll))) { + if (_.options.fade === false) { + targetSlide = _.currentSlide; + if (dontAnimate !== true && _.slideCount > _.options.slidesToShow) { + _.animateSlide(slideLeft, function() { + _.postSlide(targetSlide); + }); + } else { + _.postSlide(targetSlide); + } + } + return; + } + + if ( _.options.autoplay ) { + clearInterval(_.autoPlayTimer); + } + + if (targetSlide < 0) { + if (_.slideCount % _.options.slidesToScroll !== 0) { + animSlide = _.slideCount - (_.slideCount % _.options.slidesToScroll); + } else { + animSlide = _.slideCount + targetSlide; + } + } else if (targetSlide >= _.slideCount) { + if (_.slideCount % _.options.slidesToScroll !== 0) { + animSlide = 0; + } else { + animSlide = targetSlide - _.slideCount; + } + } else { + animSlide = targetSlide; + } + + _.animating = true; + + _.$slider.trigger('beforeChange', [_, _.currentSlide, animSlide]); + + oldSlide = _.currentSlide; + _.currentSlide = animSlide; + + _.setSlideClasses(_.currentSlide); + + if ( _.options.asNavFor ) { + + navTarget = _.getNavTarget(); + navTarget = navTarget.slick('getSlick'); + + if ( navTarget.slideCount <= navTarget.options.slidesToShow ) { + navTarget.setSlideClasses(_.currentSlide); + } + + } + + _.updateDots(); + _.updateArrows(); + + if (_.options.fade === true) { + if (dontAnimate !== true) { + + _.fadeSlideOut(oldSlide); + + _.fadeSlide(animSlide, function() { + _.postSlide(animSlide); + }); + + } else { + _.postSlide(animSlide); + } + _.animateHeight(); + return; + } + + if (dontAnimate !== true && _.slideCount > _.options.slidesToShow) { + _.animateSlide(targetLeft, function() { + _.postSlide(animSlide); + }); + } else { + _.postSlide(animSlide); + } + + }; + + Slick.prototype.startLoad = function() { + + var _ = this; + + if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { + + _.$prevArrow.hide(); + _.$nextArrow.hide(); + + } + + if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { + + _.$dots.hide(); + + } + + _.$slider.addClass('slick-loading'); + + }; + + Slick.prototype.swipeDirection = function() { + + var xDist, yDist, r, swipeAngle, _ = this; + + xDist = _.touchObject.startX - _.touchObject.curX; + yDist = _.touchObject.startY - _.touchObject.curY; + r = Math.atan2(yDist, xDist); + + swipeAngle = Math.round(r * 180 / Math.PI); + if (swipeAngle < 0) { + swipeAngle = 360 - Math.abs(swipeAngle); + } + + if ((swipeAngle <= 45) && (swipeAngle >= 0)) { + return (_.options.rtl === false ? 'left' : 'right'); + } + if ((swipeAngle <= 360) && (swipeAngle >= 315)) { + return (_.options.rtl === false ? 'left' : 'right'); + } + if ((swipeAngle >= 135) && (swipeAngle <= 225)) { + return (_.options.rtl === false ? 'right' : 'left'); + } + if (_.options.verticalSwiping === true) { + if ((swipeAngle >= 35) && (swipeAngle <= 135)) { + return 'down'; + } else { + return 'up'; + } + } + + return 'vertical'; + + }; + + Slick.prototype.swipeEnd = function(event) { + + var _ = this, + slideCount, + direction; + + _.dragging = false; + _.swiping = false; + + if (_.scrolling) { + _.scrolling = false; + return false; + } + + _.interrupted = false; + _.shouldClick = ( _.touchObject.swipeLength > 10 ) ? false : true; + + if ( _.touchObject.curX === undefined ) { + return false; + } + + if ( _.touchObject.edgeHit === true ) { + _.$slider.trigger('edge', [_, _.swipeDirection() ]); + } + + if ( _.touchObject.swipeLength >= _.touchObject.minSwipe ) { + + direction = _.swipeDirection(); + + switch ( direction ) { + + case 'left': + case 'down': + + slideCount = + _.options.swipeToSlide ? + _.checkNavigable( _.currentSlide + _.getSlideCount() ) : + _.currentSlide + _.getSlideCount(); + + _.currentDirection = 0; + + break; + + case 'right': + case 'up': + + slideCount = + _.options.swipeToSlide ? + _.checkNavigable( _.currentSlide - _.getSlideCount() ) : + _.currentSlide - _.getSlideCount(); + + _.currentDirection = 1; + + break; + + default: + + + } + + if( direction != 'vertical' ) { + + _.slideHandler( slideCount ); + _.touchObject = {}; + _.$slider.trigger('swipe', [_, direction ]); + + } + + } else { + + if ( _.touchObject.startX !== _.touchObject.curX ) { + + _.slideHandler( _.currentSlide ); + _.touchObject = {}; + + } + + } + + }; + + Slick.prototype.swipeHandler = function(event) { + + var _ = this; + + if ((_.options.swipe === false) || ('ontouchend' in document && _.options.swipe === false)) { + return; + } else if (_.options.draggable === false && event.type.indexOf('mouse') !== -1) { + return; + } + + _.touchObject.fingerCount = event.originalEvent && event.originalEvent.touches !== undefined ? + event.originalEvent.touches.length : 1; + + _.touchObject.minSwipe = _.listWidth / _.options + .touchThreshold; + + if (_.options.verticalSwiping === true) { + _.touchObject.minSwipe = _.listHeight / _.options + .touchThreshold; + } + + switch (event.data.action) { + + case 'start': + _.swipeStart(event); + break; + + case 'move': + _.swipeMove(event); + break; + + case 'end': + _.swipeEnd(event); + break; + + } + + }; + + Slick.prototype.swipeMove = function(event) { + + var _ = this, + edgeWasHit = false, + curLeft, swipeDirection, swipeLength, positionOffset, touches, verticalSwipeLength; + + touches = event.originalEvent !== undefined ? event.originalEvent.touches : null; + + if (!_.dragging || _.scrolling || touches && touches.length !== 1) { + return false; + } + + curLeft = _.getLeft(_.currentSlide); + + _.touchObject.curX = touches !== undefined ? touches[0].pageX : event.clientX; + _.touchObject.curY = touches !== undefined ? touches[0].pageY : event.clientY; + + _.touchObject.swipeLength = Math.round(Math.sqrt( + Math.pow(_.touchObject.curX - _.touchObject.startX, 2))); + + verticalSwipeLength = Math.round(Math.sqrt( + Math.pow(_.touchObject.curY - _.touchObject.startY, 2))); + + if (!_.options.verticalSwiping && !_.swiping && verticalSwipeLength > 4) { + _.scrolling = true; + return false; + } + + if (_.options.verticalSwiping === true) { + _.touchObject.swipeLength = verticalSwipeLength; + } + + swipeDirection = _.swipeDirection(); + + if (event.originalEvent !== undefined && _.touchObject.swipeLength > 4) { + _.swiping = true; + event.preventDefault(); + } + + positionOffset = (_.options.rtl === false ? 1 : -1) * (_.touchObject.curX > _.touchObject.startX ? 1 : -1); + if (_.options.verticalSwiping === true) { + positionOffset = _.touchObject.curY > _.touchObject.startY ? 1 : -1; + } + + + swipeLength = _.touchObject.swipeLength; + + _.touchObject.edgeHit = false; + + if (_.options.infinite === false) { + if ((_.currentSlide === 0 && swipeDirection === 'right') || (_.currentSlide >= _.getDotCount() && swipeDirection === 'left')) { + swipeLength = _.touchObject.swipeLength * _.options.edgeFriction; + _.touchObject.edgeHit = true; + } + } + + if (_.options.vertical === false) { + _.swipeLeft = curLeft + swipeLength * positionOffset; + } else { + _.swipeLeft = curLeft + (swipeLength * (_.$list.height() / _.listWidth)) * positionOffset; + } + if (_.options.verticalSwiping === true) { + _.swipeLeft = curLeft + swipeLength * positionOffset; + } + + if (_.options.fade === true || _.options.touchMove === false) { + return false; + } + + if (_.animating === true) { + _.swipeLeft = null; + return false; + } + + _.setCSS(_.swipeLeft); + + }; + + Slick.prototype.swipeStart = function(event) { + + var _ = this, + touches; + + _.interrupted = true; + + if (_.touchObject.fingerCount !== 1 || _.slideCount <= _.options.slidesToShow) { + _.touchObject = {}; + return false; + } + + if (event.originalEvent !== undefined && event.originalEvent.touches !== undefined) { + touches = event.originalEvent.touches[0]; + } + + _.touchObject.startX = _.touchObject.curX = touches !== undefined ? touches.pageX : event.clientX; + _.touchObject.startY = _.touchObject.curY = touches !== undefined ? touches.pageY : event.clientY; + + _.dragging = true; + + }; + + Slick.prototype.unfilterSlides = Slick.prototype.slickUnfilter = function() { + + var _ = this; + + if (_.$slidesCache !== null) { + + _.unload(); + + _.$slideTrack.children(this.options.slide).detach(); + + _.$slidesCache.appendTo(_.$slideTrack); + + _.reinit(); + + } + + }; + + Slick.prototype.unload = function() { + + var _ = this; + + $('.slick-cloned', _.$slider).remove(); + + if (_.$dots) { + _.$dots.remove(); + } + + if (_.$prevArrow && _.htmlExpr.test(_.options.prevArrow)) { + _.$prevArrow.remove(); + } + + if (_.$nextArrow && _.htmlExpr.test(_.options.nextArrow)) { + _.$nextArrow.remove(); + } + + _.$slides + .removeClass('slick-slide slick-active slick-visible slick-current') + .attr('aria-hidden', 'true') + .css('width', ''); + + }; + + Slick.prototype.unslick = function(fromBreakpoint) { + + var _ = this; + _.$slider.trigger('unslick', [_, fromBreakpoint]); + _.destroy(); + + }; + + Slick.prototype.updateArrows = function() { + + var _ = this, + centerOffset; + + centerOffset = Math.floor(_.options.slidesToShow / 2); + + if ( _.options.arrows === true && + _.slideCount > _.options.slidesToShow && + !_.options.infinite ) { + + _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); + _.$nextArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); + + if (_.currentSlide === 0) { + + _.$prevArrow.addClass('slick-disabled').attr('aria-disabled', 'true'); + _.$nextArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); + + } else if (_.currentSlide >= _.slideCount - _.options.slidesToShow && _.options.centerMode === false) { + + _.$nextArrow.addClass('slick-disabled').attr('aria-disabled', 'true'); + _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); + + } else if (_.currentSlide >= _.slideCount - 1 && _.options.centerMode === true) { + + _.$nextArrow.addClass('slick-disabled').attr('aria-disabled', 'true'); + _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); + + } + + } + + }; + + Slick.prototype.updateDots = function() { + + var _ = this; + + if (_.$dots !== null) { + + _.$dots + .find('li') + .removeClass('slick-active') + .end(); + + _.$dots + .find('li') + .eq(Math.floor(_.currentSlide / _.options.slidesToScroll)) + .addClass('slick-active'); + + } + + }; + + Slick.prototype.visibility = function() { + + var _ = this; + + if ( _.options.autoplay ) { + + if ( document[_.hidden] ) { + + _.interrupted = true; + + } else { + + _.interrupted = false; + + } + + } + + }; + + $.fn.slick = function() { + var _ = this, + opt = arguments[0], + args = Array.prototype.slice.call(arguments, 1), + l = _.length, + i, + ret; + for (i = 0; i < l; i++) { + if (typeof opt == 'object' || typeof opt == 'undefined') + _[i].slick = new Slick(_[i], opt); + else + ret = _[i].slick[opt].apply(_[i].slick, args); + if (typeof ret != 'undefined') return ret; + } + return _; + }; + +})); \ No newline at end of file diff --git a/themes/bootstrap3/less/bootstrap.less b/themes/bootstrap3/less/bootstrap.less index bf00503fd2db5ca7d17a4944acd339bdd250802e..6a8482e821feea7929dc06b4f8cbea4e5078f9e3 100644 --- a/themes/bootstrap3/less/bootstrap.less +++ b/themes/bootstrap3/less/bootstrap.less @@ -113,6 +113,11 @@ footer { float: right; padding-left: 1rem; } + .mainbody.solo { + width: auto; + padding-left: 0; + padding-right: 0; + } } /* ------ Form Errors ------ */ @@ -214,18 +219,6 @@ footer { .date-from { padding-right: .25rem; } .date-to { padding-left: .25rem; } } -.top-facets { - margin-left: -.5rem; - margin-right: -.5rem; -} -.top-facets .facet, -.top-facets .narrow-toggle, -.top-facets > strong { - display: inline-block; - padding: .25rem .5rem; - white-space: nowrap; -} -.top-facets .narrow-toggle { text-decoration: underline; } @media (min-width: 768px) { .author-facets { display: flex; } .author-list { flex: 0 1 100%; } @@ -296,3 +289,41 @@ span[class^="services-"], span[class*=" services-"] span::before { content: ", "; } span[class^="services-"], span[class*=" services-"] span:first-of-type::before { content: ""; } + +/* --- extended holdings --- */ + +div.holding-details { + display: flex; + + .holding-field { + flex: 1 1 0; + padding-left: 0.5rem; + padding-right: 0.5rem; + } + + .holding-field.barcode, + .holding-field.place-hold, + .holding-field.storage-retrieval, + .holding-field.ill-request, + .holding-field.recall { + flex: 1 1 0; + } + + .holding-field.availability{ + flex: 2 1 0; + } + + .due-date:before { + content: "\2013"; + padding-right: 0.3em; + } + + .due-date { + white-space: nowrap; + } +} + +div.holding-notes { + padding-left: 0.5rem; + padding-right: 0.5rem; +} diff --git a/themes/bootstrap3/less/components/channels.less b/themes/bootstrap3/less/components/channels.less index 272c687b9d4a28d6fb663e0f54b474017914e5c5..f5816024e622594d7dcb7392309b7a1afb1a2970 100644 --- a/themes/bootstrap3/less/components/channels.less +++ b/themes/bootstrap3/less/components/channels.less @@ -1,15 +1,47 @@ +.channel-wrapper { + margin-top: 1rem; + margin-bottom: 2rem; +} +@media (max-width: 767px) { + .channel-wrapper { + margin-left: 1rem; + } +} .channel { position: relative; width: 100%; - height: 300px; - border: 1px solid @btn-default-border; + max-height: 300px; + padding: 1rem; + background-color: #eee; border-radius: @border-radius-base; } -.channel .slider-menu { - border-top-color: @btn-default-border; - border-radius: 0 0 @border-radius-base @border-radius-base; - .channel-links .btn { margin-left: 4px; } + +.channel-add-menu { + position: relative; + margin-bottom: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid @gray; + text-align: center; } +@media (min-width: 768px) { + .channel-add-menu { + float: right; + margin: 0; + padding: 0; + border: 0; + + body.rtl & { + float: left; + } + } +} +.channel-add-menu .dropdown-menu { + left: auto; + right: 0; + max-width: 90vw; + overflow: hidden; +} + .channel .channel-record.slide { display: inline-block; min-width: 100px; @@ -21,16 +53,32 @@ } .channel .channel-record.is-selected { &:extend(a:hover); } .channel .channel-record .thumb { + display: flex; height: 200px; - background-position: center center; - background-repeat: no-repeat; - background-size: contain; + align-items: center; + justify-content: center; +} +.channel .channel-record .thumb img { + max-width: 100%; + max-height: 100%; } + .channel .popover { width: 500px; max-width: none; } +.channel .popover-title { + font-size: 1.4rem; + font-weight: bold; +} +.channel-title h2 { + display: inline-block; + margin: 0; +} +.channel-title .dropdown { + display: inline-block; +} .channel-title a:hover { text-decoration: none; } .channel-title i.fa-fw { width: 1.4em; @@ -40,3 +88,17 @@ vertical-align: top; } .channel-title .placeholder { width: 25px; } + +.channel-title .dropdown button { + font-size: 18px; + color: #777; + vertical-align: baseline; +} +.channel .slick-prev::before, +.channel .slick-next::before { + color: #555; +} + +.channel-search { + margin-top: 1rem; +} diff --git a/themes/bootstrap3/less/components/js-tree.less b/themes/bootstrap3/less/components/js-tree.less index 05aa8f2976b797650ceed8f29e9f82d78904bfa7..7f4c55daa7dede3a8d1390dea70c52b0ddf9626d 100644 --- a/themes/bootstrap3/less/components/js-tree.less +++ b/themes/bootstrap3/less/components/js-tree.less @@ -48,8 +48,6 @@ } .jstree-initial-node {display: none;} .jstree-clicked { - color: @list-group-active-color; - background-color: @list-group-active-bg; .jstree-icon { color: #fff; } diff --git a/themes/bootstrap3/less/components/lightbox.less b/themes/bootstrap3/less/components/lightbox.less index 9361fab53c8332b260db43ea81f248ccf5d7c0a8..d24c96b3f65bd1b6465a5a7d7725c13d54a5722c 100644 --- a/themes/bootstrap3/less/components/lightbox.less +++ b/themes/bootstrap3/less/components/lightbox.less @@ -1,17 +1,30 @@ #modal { background-color: rgba(0,0,0,.2); } -#modal .modal-content > .close { // Fancy side X button + +#modal .modal-content > .close { position: absolute; - right: -50px; top: 0; + right: 0; z-index: 2; - font-size: 32pt; - color: #fff; + padding: .5rem 1rem; opacity: .7; + body.rtl & { - left: -50px; + left: 0; right: auto; } } +@media (min-width: 768px) { + #modal .modal-content > .close { // Fancy side X button + right: -50px; + font-size: 32pt; + color: #fff; + body.rtl & { + left: -50px; + right: auto; + } + } +} + #modal .modal-content > .close:hover { opacity: 1; } #modal .modal-body h1, #modal .modal-body h2 { diff --git a/themes/bootstrap3/less/components/record.less b/themes/bootstrap3/less/components/record.less index 5b671da99b746bfba20a63ff3e7f1b7ded911fb1..06dab9c85fff75e85c5b79335ac94ee7feec277e 100644 --- a/themes/bootstrap3/less/components/record.less +++ b/themes/bootstrap3/less/components/record.less @@ -4,6 +4,9 @@ margin-top: 2px; margin-bottom: 4px; box-shadow: 1px 1px 3px rgba(47,52,64,.72); + max-height: 130px; + width: 105px; + height: auto; } @media (max-width: 767px) { .record .media-left, @@ -122,3 +125,6 @@ .marc-row-008 { white-space: pre-wrap; } + +/* ------ Relais ------ */ +.relaisLink { display: inline-block; } \ No newline at end of file diff --git a/themes/bootstrap3/less/components/search.less b/themes/bootstrap3/less/components/search.less index c9e9f7b7c1ceaea385cd4243eba7e3402530e0b7..acb510013ab2ec71298d4d468e04914fdc3ab850 100644 --- a/themes/bootstrap3/less/components/search.less +++ b/themes/bootstrap3/less/components/search.less @@ -98,6 +98,8 @@ header .container.navbar { margin-bottom: 0; } .media { flex: 1; margin: 0; + padding-right: 10px; + padding-left: 10px; } .media-left, .media-right { @@ -114,13 +116,13 @@ header .container.navbar { margin-bottom: 0; } max-width: none; } } + .media-left { margin-right: 10px; } + .media-right { margin-left: 10px; } @media (min-width: 768px) { .media-left, .media-right { max-width: 25%; - padding-right: 10px; - padding-left: 10px; a { display: inline-block; @@ -304,3 +306,24 @@ body.rtl { font-size: inherit; font-style: italic; } + +/* ------ TOP FACETS ------ */ +.top-title { + display: inline-block; + padding-top: .5rem; + padding-left: .5rem; + font-weight: bold; + color: #000; +} +.top-title .fa { + width: 1rem; + text-align: center; +} +@media (min-width: 768px) { + .top-title { padding-top: 0; } + .top-title .fa { display: none; } + .top-facets .collapse { + display: block; + height: auto !important; + } +} diff --git a/themes/bootstrap3/less/components/sidebar.less b/themes/bootstrap3/less/components/sidebar.less index 59ad67a7511176cdb75869d9a7ef51e8a4556c2e..d63c21b5d50d3fd6d667755f53ede11960bda1cd 100644 --- a/themes/bootstrap3/less/components/sidebar.less +++ b/themes/bootstrap3/less/components/sidebar.less @@ -1,30 +1,7 @@ .facet-group { display: block; margin-bottom: 1rem; - - .facet, - .title { - display: block; - padding: 8px 15px; - line-height: 1.5rem; - - .badge, - .status { - float: right; - - body.rtl & { float: left; } - } - .badge { - max-height: 19px; - line-height: 1.1rem; - } - } - .facet { border-bottom: 1px solid @list-group-border; } - .title { - padding-right: 8px; - border: 1px solid @list-group-border; - font-weight: bold; - } + padding-left: 0; [data-toggle="collapse"] { cursor: pointer; @@ -45,32 +22,58 @@ border-right: 1px solid @list-group-border; } & > .facet:first-child { border-top: 1px solid @list-group-border; } +} - .facetOR .text { - display: inline-block; - padding-left: 0.5rem; - text-indent: -0.5rem; - } - .excludable { - display: flex; - flex-direction: row-reverse; - padding-right: 0; +.facet, +.facet-group .title { + display: flex; + padding: .5rem; + width: 100%; + line-height: 1.5rem; + background-color: transparent; - .badge { flex-shrink: 0; } - .text { flex-grow: 1; } - .exclude { - flex-basis: 2rem; - flex-shrink: 0; - text-align: center; - } + .text { + flex-grow: 1; + } - body.rtl & { - padding-left: 0; - padding-right: 15px; - } + .badge { + flex-shrink: 0; + max-height: 19px; + line-height: 1.1rem; } + + .exclude { + flex-shrink: 0; + text-align: center; + padding-left: 0.5rem; + } +} +.facet { + border-bottom: 1px solid @list-group-border; +} +.facet-group .title { + display: block; + padding-right: 8px; + border: 1px solid @list-group-border; + font-weight: bold; + text-align: inherit; +} + +.top-facets { + margin-left: -.5rem; + margin-right: -.5rem; } +.top-facets .facet, +.top-facets .narrow-toggle, +.top-facets > strong { + display: inline-block; + width: auto; + padding: .25rem .5rem; + border: 0; + white-space: nowrap; +} +.top-facets .narrow-toggle { text-decoration: underline; } .checkbox-filter { display: block; @@ -86,7 +89,6 @@ .active-filters .facet, .facet-group .active { - padding-right: .65rem; background-color: @list-group-active-bg; color: #fff; @@ -117,10 +119,26 @@ } .full-facet-list { margin-top: 1rem; } -.full-facet-list .active .fa { +.full-facet-list .active .fa.fa-times { float: right; margin-top: .25rem; - margin-right: 0.5rem; margin-left: 0.5rem; } -body.rtl .full-facet-list .active .fa { float: left; } +body.rtl .full-facet-list .active .fa.fa-times { float: left; } + +.jstree-node { + .facet { + padding: 0 0.5rem 0 0; + border: none; + } +} +.jstree-facet { + .jstree-anchor { + padding: 0; + } + .list-group-item { + padding-right: 0; + border: 0; + border-bottom: 1px solid @list-group-border; + } +} diff --git a/themes/bootstrap3/scss/bootstrap.scss b/themes/bootstrap3/scss/bootstrap.scss index aff292f7d0ed6129f1e26b24226c68fe1ec9e6e9..5433b8e62e22a5dc822b091b7b033f60809f3df5 100644 --- a/themes/bootstrap3/scss/bootstrap.scss +++ b/themes/bootstrap3/scss/bootstrap.scss @@ -199,17 +199,6 @@ footer { } } -/* ------ Collections ------ */ -// Layout -@media (min-width: 768px) { - .collection-hierarchytree { display: flex; } - .collection-hierarchytree .tree-panel, - #tree-preview { - flex-basis: 50%; - padding: 1rem; - } -} - /* ------ Devtools ------ */ .translation-output { width: 100%; @@ -308,3 +297,41 @@ span[class^="services-"], span[class*=" services-"] span::before { content: ", "; } span[class^="services-"], span[class*=" services-"] span:first-of-type::before { content: ""; } + +/* --- extended holdings --- */ + +div.holding-details { + display: flex; + + .holding-field { + flex: 1 1 0; + padding-left: 0.5rem; + padding-right: 0.5rem; + } + + .holding-field.barcode, + .holding-field.place-hold, + .holding-field.storage-retrieval, + .holding-field.ill-request, + .holding-field.recall { + flex: 1 1 0; + } + + .holding-field.availability{ + flex: 2 1 0; + } + + .due-date:before { + content: "\2013"; + padding-right: 0.3em; + } + + .due-date { + white-space: nowrap; + } +} + +div.holding-notes { + padding-left: 0.5rem; + padding-right: 0.5rem; +} diff --git a/themes/bootstrap3/scss/components/js-tree.scss b/themes/bootstrap3/scss/components/js-tree.scss index 3a0c314052aa6b51be60d2ed790726e0ea876b04..03b7358f2bfecbcf99599e8d07e66d89b064fcf9 100644 --- a/themes/bootstrap3/scss/components/js-tree.scss +++ b/themes/bootstrap3/scss/components/js-tree.scss @@ -1,3 +1,24 @@ +/* --- Layout --- */ +.hierarchy-tree { + max-height: 75vh; + overflow-y: auto; +} +@media (min-width: 768px) { + .collection-hierarchytree { display: flex; } + .collection-hierarchytree .tree-panel, + .collection-hierarchytree #tree-preview { + flex-basis: 50%; + padding: 1rem; + } + #modal .collection-hierarchytree { display: block; } + #modal .tree-panel { + flex-basis: 100%; + padding: 0; + } + #modal #tree-preview { display: none; } +} + +/* --- Look --- */ .hierarchy-tree, .jstree-facet { /* jsTree arrows */ @@ -20,18 +41,13 @@ width: 16px; color: #000; } - .jstree-anchor { - padding-left: 5px; - white-space: nowrap; - } + .jstree-anchor { padding-left: 5px; } .jstree-container-ul, .jstree-children { padding-left: 16px; } .jstree-initial-node {display: none;} .jstree-clicked { - color: $list-group-active-color; - background-color: $list-group-active-bg; .jstree-icon { color: #fff; } @@ -88,13 +104,11 @@ margin-top: 2px; margin-right: 4px; } -.jstree-facet .jstree-container-ul { - padding: 0; - & > li.active, - & > li.active a.jstree-anchor { - background-color: #265680; - color: #fff; - } +.jstree-facet .jstree-container-ul { padding: 0; } +.jstree-facet .active, +.jstree-facet .active a.jstree-anchor { + background-color: $list-group-active-bg; + color: #fff; } li.jstree-facet, li.jstree-node { list-style: none; } diff --git a/themes/bootstrap3/scss/components/offcanvas.scss b/themes/bootstrap3/scss/components/offcanvas.scss index 559279c66884e6b48cc80c4dc94cf98650a83abd..2371997017aaa38ee66db8f79003276dcf9b70f8 100644 --- a/themes/bootstrap3/scss/components/offcanvas.scss +++ b/themes/bootstrap3/scss/components/offcanvas.scss @@ -65,7 +65,7 @@ $offcanvas-padding: 30px !default; // Body offset when offcanvas active, also wi display: block; position: fixed; top: 0; - width: calc($offcanvas-padding - 5px); + width: calc(#{$offcanvas-padding} - 5px); height: 100%; border-left: 1px solid $gray-lighter; border-right: 1px solid $gray-lighter; diff --git a/themes/bootstrap3/scss/components/record.scss b/themes/bootstrap3/scss/components/record.scss index 8a37106df68375af577ecb07c5e62acbbafbfc62..d0ebcdb79ac2a591bc516e911628717b91503ba4 100644 --- a/themes/bootstrap3/scss/components/record.scss +++ b/themes/bootstrap3/scss/components/record.scss @@ -4,6 +4,9 @@ margin-top: 2px; margin-bottom: 4px; box-shadow: 1px 1px 3px rgba(47,52,64,.72); + max-height: 130px; + width: 105px; + height: auto; } @media (max-width: 767px) { .record .media-left, @@ -122,3 +125,6 @@ .marc-row-008 { white-space: pre-wrap; } + +/* ------ Relais ------ */ +.relaisLink { display: inline-block; } \ No newline at end of file diff --git a/themes/bootstrap3/scss/components/search.scss b/themes/bootstrap3/scss/components/search.scss index e4e26dc69f2ef4d3700a326bca5f72c25b88c17d..bb0ecf8dcc196312c02dd96444434041c6e42d74 100644 --- a/themes/bootstrap3/scss/components/search.scss +++ b/themes/bootstrap3/scss/components/search.scss @@ -304,3 +304,24 @@ body.rtl { font-size: inherit; font-style: italic; } + +/* ------ TOP FACETS ------ */ +.top-title { + display: inline-block; + padding-top: .5rem; + padding-left: .5rem; + font-weight: bold; + color: #000; +} +.top-title .fa { + width: 1rem; + text-align: center; +} +@media (min-width: 768px) { + .top-title { padding-top: 0; } + .top-title .fa { display: none; } + .top-facets .collapse { + display: block; + height: auto !important; + } +} diff --git a/themes/bootstrap3/scss/components/sidebar.scss b/themes/bootstrap3/scss/components/sidebar.scss index 6f550f08f72d99371ff139c2dd3dca9a58d92576..9c51e5151652df245e4a1f1e8e3038c36d69cc5d 100644 --- a/themes/bootstrap3/scss/components/sidebar.scss +++ b/themes/bootstrap3/scss/components/sidebar.scss @@ -1,12 +1,16 @@ .facet-group { display: block; margin-bottom: 1rem; + padding-left: 0; .facet, .title { display: block; padding: 8px 15px; line-height: 1.5rem; + width: 100%; + text-align: left; + background-color: transparent; .badge, .status { @@ -86,10 +90,10 @@ .active-filters .facet, .facet-group .active { - padding-right: .65rem; - background-color: $brand-primary; + background-color: $list-group-active-bg; color: #fff; + .jstree-icon, a { color: #fff; } } .facet.active .badge, @@ -123,3 +127,20 @@ margin-left: 0.5rem; } body.rtl .full-facet-list .active .fa { float: left; } + +.jstree-node { + .facet { + padding: 0; + border: none; + } +} +.jstree-facet { + .jstree-anchor { + padding: 0; + } + .list-group-item { + padding-right: 0; + border: 0; + border-bottom: 1px solid $list-group-border; + } +} diff --git a/themes/bootstrap3/templates/Auth/AbstractBase/login.phtml b/themes/bootstrap3/templates/Auth/AbstractBase/login.phtml index 77b1f5caf4a093079bf7c6d4bac443f531517770..8f4b1ed2aecbdd2916a9e128c7aafe23e165073b 100644 --- a/themes/bootstrap3/templates/Auth/AbstractBase/login.phtml +++ b/themes/bootstrap3/templates/Auth/AbstractBase/login.phtml @@ -1,20 +1,20 @@ -<? $account = $this->auth()->getManager(); ?> -<? $sessionInitiator = $account->getSessionInitiator($this->serverUrl($this->url('myresearch-home'))); ?> -<? if (!$sessionInitiator): // display default login form if no login URL provided ?> +<?php $account = $this->auth()->getManager(); ?> +<?php $sessionInitiator = $account->getSessionInitiator($this->serverUrl($this->url('myresearch-home'))); ?> +<?php if (!$sessionInitiator): // display default login form if no login URL provided ?> <form method="post" action="<?=$this->url('myresearch-home')?>" name="loginForm" class="form-login"> <?=$this->auth()->getLoginFields()?> <input type="hidden" name="auth_method" value="<?=$account->getAuthMethod()?>"> - <input type="hidden" name="csrf" value="<?=$this->escapeHtmlAttr($account->getCsrfHash(true))?>" /> + <input type="hidden" name="csrf" value="<?=$this->escapeHtmlAttr($account->getCsrfHash())?>" /> <div class="form-group"> <input class="btn btn-primary" type="submit" name="processLogin" value="<?=$this->transEsc('Login')?>"> - <? if ($account->supportsCreation()): ?> + <?php if ($account->supportsCreation()): ?> <a class="btn btn-link createAccountLink" href="<?=$this->url('myresearch-account') ?>?auth_method=<?=$account->getAuthMethod()?>"><?=$this->transEsc('Create New Account')?></a> - <? endif; ?> - <? if ($account->supportsRecovery()): ?> + <?php endif; ?> + <?php if ($account->supportsRecovery()): ?> <a class="btn btn-link" href="<?=$this->url('myresearch-recover') ?>?auth_method=<?=$account->getAuthMethod()?>"><?=$this->transEsc('Forgot Password')?></a> - <? endif; ?> + <?php endif; ?> </div> </form> -<? else: ?> +<?php else: ?> <a href="<?=$this->escapeHtmlAttr($sessionInitiator)?>" class="btn btn-link" data-lightbox-ignore><?=$this->transEsc("Institutional Login")?></a> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Auth/AbstractBase/newpassword.phtml b/themes/bootstrap3/templates/Auth/AbstractBase/newpassword.phtml index 934bd49360ec4ec314d00e18bc568f440c0c12e9..b0183754b480c6944417e234883ca5e864eafbea 100644 --- a/themes/bootstrap3/templates/Auth/AbstractBase/newpassword.phtml +++ b/themes/bootstrap3/templates/Auth/AbstractBase/newpassword.phtml @@ -1,17 +1,17 @@ -<? if (isset($this->username)): ?> +<?php if (isset($this->username)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('Username') ?>:</label> <p class="form-control-static"><?=$this->username ?></p> </div> -<? endif; ?> -<? if (isset($this->verifyold) && $this->verifyold || isset($this->oldpwd)): ?> +<?php endif; ?> +<?php if (isset($this->verifyold) && $this->verifyold || isset($this->oldpwd)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('old_password') ?>:</label> <input type="password" name="oldpwd" class="form-control"/> <div class="help-block with-errors"></div> </div> -<? endif; ?> -<? +<?php endif; ?> +<?php $pattern = ''; if (isset($this->passwordPolicy['pattern'])) { if ($this->passwordPolicy['pattern'] == 'numeric') { @@ -26,13 +26,13 @@ <div class="form-group"> <label class="control-label"><?=$this->transEsc('new_password') ?>:</label> <input type="password" id="password" name="password" class="form-control" required aria-required="true" - <?=isset($this->passwordPolicy['minLength']) ? ' data-minlength="' . $this->passwordPolicy['minLength'] . '" data-minlength-error="' . $this->escapeHtmlAttr($this->translate('password_minimum_length', array('%%minlength%%' => $this->passwordPolicy['minLength']))) . '"' : '' ?> + <?=isset($this->passwordPolicy['minLength']) ? ' data-minlength="' . $this->passwordPolicy['minLength'] . '" data-minlength-error="' . $this->escapeHtmlAttr($this->translate('password_minimum_length', ['%%minlength%%' => $this->passwordPolicy['minLength']])) . '"' : '' ?> <?=isset($this->passwordPolicy['maxLength']) ? ' maxlength="' . $this->passwordPolicy['maxLength'] . '"' : '' ?> <?=$pattern ? ' pattern="' . $pattern . '"' : '' ?> /> - <? if ($this->passwordPolicy['hint']): ?> + <?php if ($this->passwordPolicy['hint']): ?> <div class="help-block"><?=$this->transEsc($this->passwordPolicy['hint']) ?></div> - <? endif; ?> + <?php endif; ?> <div class="help-block with-errors"></div> </div> <div class="form-group"> diff --git a/themes/bootstrap3/templates/Auth/ChoiceAuth/login.phtml b/themes/bootstrap3/templates/Auth/ChoiceAuth/login.phtml index 34a651e5aa2d382a60ab3bcccf6ec1b3f4641b64..afe453d569c05108c6d0406cfae11b936edf97e9 100644 --- a/themes/bootstrap3/templates/Auth/ChoiceAuth/login.phtml +++ b/themes/bootstrap3/templates/Auth/ChoiceAuth/login.phtml @@ -1,13 +1,13 @@ <p><?=$this->transEsc('choose_login_method')?></p> <div id="authcontainer"> -<? $methods = $this->auth()->getManager()->getSelectableAuthOptions(); ?> -<? $count = count($methods); ?> -<? foreach ($methods as $loop=>$method):?> - <div class="authmethod<?=$loop?> span<?=floor(12/$count) ?>"> - <? $this->auth()->getManager()->setAuthMethod($method) ?> +<?php $methods = $this->auth()->getManager()->getSelectableAuthOptions(); ?> +<?php $count = count($methods); ?> +<?php foreach ($methods as $loop => $method):?> + <div class="authmethod<?=$loop?> span<?=floor(12 / $count) ?>"> + <?php $this->auth()->getManager()->setAuthMethod($method) ?> <?=$this->auth()->getLoginDesc() ?> <?=$this->auth()->getLogin() ?> </div> -<? endforeach ?> +<?php endforeach ?> </div> -<? $this->auth()->getManager()->setAuthMethod('ChoiceAuth') ?> +<?php $this->auth()->getManager()->setAuthMethod('ChoiceAuth') ?> diff --git a/themes/bootstrap3/templates/Auth/Database/create.phtml b/themes/bootstrap3/templates/Auth/Database/create.phtml index 4fe5955feb576ed60f7ff696b5314df22e9d2ddd..69aede867fbad60d094d6d7ead00d1167be54cd8 100644 --- a/themes/bootstrap3/templates/Auth/Database/create.phtml +++ b/themes/bootstrap3/templates/Auth/Database/create.phtml @@ -1,4 +1,4 @@ -<? +<?php $pattern = ''; if (isset($this->passwordPolicy['pattern'])) { if ($this->passwordPolicy['pattern'] == 'numeric') { @@ -31,13 +31,13 @@ <div class="form-group"> <label class="control-label" for="account_password"><?=$this->transEsc('Password')?>:</label> <input id="account_password" type="password" name="password" required aria-required="true" class="form-control" - <?=isset($this->passwordPolicy['minLength']) ? ' data-minlength="' . $this->passwordPolicy['minLength'] . '" data-minlength-error="' . $this->escapeHtmlAttr($this->translate('password_minimum_length', array('%%minlength%%' => $this->passwordPolicy['minLength']))) . '"' : ''?> + <?=isset($this->passwordPolicy['minLength']) ? ' data-minlength="' . $this->passwordPolicy['minLength'] . '" data-minlength-error="' . $this->escapeHtmlAttr($this->translate('password_minimum_length', ['%%minlength%%' => $this->passwordPolicy['minLength']])) . '"' : ''?> <?=isset($this->passwordPolicy['maxLength']) ? ' maxlength="' . $this->passwordPolicy['maxLength'] . '"' : ''?> <?=$pattern ? ' pattern="' . $pattern . '"' : '' ?> /> - <? if ($this->passwordPolicy['hint']): ?> + <?php if ($this->passwordPolicy['hint']): ?> <div class="help-block"><?=$this->transEsc($this->passwordPolicy['hint']) ?></div> - <? endif; ?> + <?php endif; ?> <div class="help-block with-errors"></div> </div> <div class="form-group"> diff --git a/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml b/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml index 246cee6a636e01f9b188a76eb0a9cb3bf555e5dd..e7136a43d327f4895a8fda87acd5db459f83be33 100644 --- a/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml +++ b/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml @@ -1,10 +1,10 @@ <div class="form-group"> <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> - <?$currentTarget = $this->request->get('target'); if (!$currentTarget) $currentTarget = $this->auth()->getManager()->getDefaultLoginTarget();?> + <?php $currentTarget = $this->request->get('target'); if (!$currentTarget) $currentTarget = $this->auth()->getManager()->getDefaultLoginTarget();?> <select id="login_target" name="target" class="form-control"> - <?foreach ($this->auth()->getManager()->getLoginTargets() as $target):?> + <?php foreach ($this->auth()->getManager()->getLoginTargets() as $target):?> <option value="<?=$this->escapeHtmlAttr($target)?>"<?=($target == $currentTarget ? ' selected="selected"' : '')?>><?=$this->transEsc("source_$target", null, $target)?></option> - <? endforeach ?> + <?php endforeach ?> </select> </div> <div class="form-group"> diff --git a/themes/bootstrap3/templates/ContentBlock/Channels.phtml b/themes/bootstrap3/templates/ContentBlock/Channels.phtml new file mode 100644 index 0000000000000000000000000000000000000000..5047df7b340af31b4478809dc0a52bf292f10e37 --- /dev/null +++ b/themes/bootstrap3/templates/ContentBlock/Channels.phtml @@ -0,0 +1 @@ +<?=$this->render('channels/channelList.phtml')?> diff --git a/themes/bootstrap3/templates/ContentBlock/FacetList.phtml b/themes/bootstrap3/templates/ContentBlock/FacetList.phtml new file mode 100644 index 0000000000000000000000000000000000000000..fe60f9bd34ade42cf3068350233d13b126af4bd4 --- /dev/null +++ b/themes/bootstrap3/templates/ContentBlock/FacetList.phtml @@ -0,0 +1,83 @@ +<?php + // Load search actions and settings (if any): + $options = $this->searchOptions($searchClassId); + $basicSearch = $options->getSearchAction(); + $advSearch = $options->getAdvancedSearchAction(); + $noJsSupport = $this->config()->nonJavascriptSupportEnabled(); +?> +<?php if (!empty($facetList)): ?> + <div class="search-home-facets"> + <?php foreach ($facetList as $field => $details): ?> + <?php if ($isHierarchy = in_array($field, $hierarchicalFacets ?? [])): + $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); + $this->headScript()->appendFile('facets.js'); + $sort = $hierarchicalFacetSortOptions[$field] ?? ''; + $script = <<<JS +$(document).ready(function() { + $('#facet_{$this->escapeHtml($field)}_container').removeClass('hide'); + initFacetTree($('#facet_{$this->escapeHtml($field)}'), false); +}); +JS; + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); + ?> + <div id="facet_<?=$this->escapeHtml($field)?>_container" class="home-facet <?=$this->escapeHtmlAttr($field)?> hide"> + <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> + <div id="facet_<?=$this->escapeHtml($field)?>" class="jstree-facet" + data-source="<?=$this->escapeHtml($this->searchClassId)?>" + data-facet="<?=$this->escapeHtml($field)?>" + data-path="<?=$this->url($basicSearch)?>" + data-exclude="0" + data-operator="AND" + data-exclude-title="<?=$this->transEsc('exclude_facet')?>" + data-sort="all"> + </div> + </div> + <noscript> + <?php if (!$noJsSupport): ?> + <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> + <?=$this->transEsc('Please enable JavaScript.')?> + <?php endif; ?> + <?php endif; ?> + <?php if (!$isHierarchy || $noJsSupport): // do we need regular display? ?> + <?php $sortedList = $this->sortFacetList($results, $field, $details['list'], $basicSearch); ?> + <div class="home-facet <?=$this->escapeHtmlAttr($field) ?>"> + <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> + <div class="home-facet-container"> + <ul class="home-facet-list"> + <?php + // Special case: two columns for LC call numbers... + $maxListLength = $field == 'callnumber-first' + ? $columnSize * 2 : $columnSize; + + // Special case: custom URLs for collections... + $moreUrl = $field == 'hierarchy_top_title' + ? $this->url('collections-home') : $this->url($advSearch); + + // Convenience variable: + $currentListLength = count($sortedList); + ?> + <?php $i = 0; foreach ($sortedList as $url => $value): + // Special case: custom URLs for collections... + if ($field == 'hierarchy_top_title') { + $url = $this->url('collections-bytitle') . '?title=' . urlencode($value); + } + ?> + <li><a href="<?=$url?>"><?=$this->escapeHtml(empty($value) ? '-' : $value)?></a></li> + <?php if (++$i >= $currentListLength) break; // end of list? bail out! ?> + <?php if ($i >= $maxListLength): // list too long? show more link! ?> + <li><a href="<?=$moreUrl?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li> + <?php break; ?> + <?php elseif ($i % $columnSize === 0): // end of column? insert break! ?> + </ul><ul class="home-facet-list"> + <?php endif; ?> + <?php endforeach; ?> + </ul> + </div> + </div> + <?php endif; ?> + <?php if ($isHierarchy): // close tag opened in matching if above ?> + </noscript> + <?php endif; ?> + <?php endforeach; ?> + </div> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/ContentBlock/IlsStatusMonitor.phtml b/themes/bootstrap3/templates/ContentBlock/IlsStatusMonitor.phtml new file mode 100644 index 0000000000000000000000000000000000000000..36d97c56bcd8d0a14d695f8dabf59f3dbe645554 --- /dev/null +++ b/themes/bootstrap3/templates/ContentBlock/IlsStatusMonitor.phtml @@ -0,0 +1,16 @@ +<?php +$ilsStatusScript = <<<JS +$(document).ready(function() { + $.ajax({ + dataType: 'json', + method: 'GET', + data: {'offlineModeMsg':'ils_offline_home_message'}, + url: VuFind.path + '/AJAX/JSON?method=getIlsStatus', + success: function(response) { + $('{$target}').prepend(response.data.html); + } + }); +}); +JS; +echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $ilsStatusScript, 'SET'); +?> diff --git a/themes/bootstrap3/templates/Helpers/email-form-fields.phtml b/themes/bootstrap3/templates/Helpers/email-form-fields.phtml index df938f4081960e4e08b0bcaa4813534d89fc8f6f..a672715edc4b2d3fa682d8563a35b67fb06b4ce7 100644 --- a/themes/bootstrap3/templates/Helpers/email-form-fields.phtml +++ b/themes/bootstrap3/templates/Helpers/email-form-fields.phtml @@ -1,31 +1,31 @@ <div class="form-group"> <label class="control-label" for="email_to"><?=$this->transEsc('To')?>:</label> <input type="<?=$this->maxRecipients != 1 ? 'text' : 'email'?>" id="email_to" class="form-control" oninvalid="$('#modal .fa-spinner').remove()" name="to" value="<?=isset($this->to) ? $this->to : ''?>"/> - <? if ($this->maxRecipients != 1): ?> + <?php if ($this->maxRecipients != 1): ?> <br /> <?=$this->transEsc('email_multiple_recipients_note')?> - <? if ($this->maxRecipients > 1): ?> + <?php if ($this->maxRecipients > 1): ?> <?=$this->transEsc('email_maximum_recipients_note', ['%%max%%' => $this->maxRecipients])?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </div> -<? if (!$this->disableFrom): ?> +<?php if (!$this->disableFrom): ?> <div class="form-group"> <label class="control-label" for="email_from"><?=$this->transEsc('From')?>:</label> <input type="email" id="email_from" oninvalid="$('#modal .fa-spinner').remove()" name="from" value="<?=isset($this->from) ? $this->from : ''?>" size="40" class="form-control"/> </div> -<? endif; ?> -<? if ($this->editableSubject): ?> +<?php endif; ?> +<?php if ($this->editableSubject): ?> <div class="form-group"> <label class="control-label" for="email_subject"><?=$this->transEsc('email_subject')?>:</label> <input type="text" id="email_subject" oninvalid="$('#modal .fa-spinner').remove()" name="subject" value="<?=isset($this->subject) ? $this->subject : ''?>" size="40" class="form-control"/> </div> -<? endif; ?> +<?php endif; ?> <div class="form-group"> <label class="control-label" for="email_message"><?=$this->transEsc('Message')?>:</label> <textarea id="email_message" class="form-control" name="message" rows="4"><?=isset($this->message) ? $this->message : ''?></textarea> </div> -<? if ($this->disableFrom && $this->userEmailInFrom): ?> +<?php if ($this->disableFrom && $this->userEmailInFrom): ?> <div class="form-group"> <div class="checkbox"> <label> @@ -33,7 +33,7 @@ </label> </div> </div> -<? endif ?> +<?php endif ?> <?=$this->recaptcha()->html($this->useRecaptcha) ?> <div class="form-group"> <input type="submit" class="btn btn-primary" name="submit" value="<?=$this->transEsc('Send')?>"/> diff --git a/themes/bootstrap3/templates/Helpers/ils-offline.phtml b/themes/bootstrap3/templates/Helpers/ils-offline.phtml index 5dfceac17b4369c73f0504f0a57bf2ea37333852..17d08f65759c9aae73b5037a085e07d6b6bdb4e7 100644 --- a/themes/bootstrap3/templates/Helpers/ils-offline.phtml +++ b/themes/bootstrap3/templates/Helpers/ils-offline.phtml @@ -2,6 +2,6 @@ <h2><?=$this->transEsc('ils_offline_title')?></h2> <p><strong><?=$this->transEsc('ils_offline_status')?></strong></p> <p><?=$this->transEsc($this->offlineModeMsg)?></p> - <? $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> + <?php $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> <p><a href="mailto:<?=$supportEmail?>"><?=$supportEmail?></a></p> </div> diff --git a/themes/bootstrap3/templates/Helpers/openurl.phtml b/themes/bootstrap3/templates/Helpers/openurl.phtml index 22d5cac3e2f76b3bebaa00f876ecdacb26d96cc3..d192d79b626ed879187b9576871bca2a3c0a3f84 100644 --- a/themes/bootstrap3/templates/Helpers/openurl.phtml +++ b/themes/bootstrap3/templates/Helpers/openurl.phtml @@ -1,4 +1,4 @@ -<? +<?php echo $this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'openurl.js', 'SET'); $classes = ''; if ($this->openUrlEmbed) { @@ -14,13 +14,13 @@ } ?> -<span class="openUrlControls<? if ($this->openUrlEmbed): ?> openUrlEmbed<? if ($this->openUrlEmbedAutoLoad): ?> openUrlEmbedAutoLoad<? endif; ?><? endif; ?>"> - <? if (!$this->openUrlImageBasedSrc || $this->openUrlImageBasedMode == 'both'): ?> +<span class="openUrlControls<?php if ($this->openUrlEmbed): ?> openUrlEmbed<?php if ($this->openUrlEmbedAutoLoad): ?> openUrlEmbedAutoLoad<?php endif; ?><?php endif; ?>"> + <?php if (!$this->openUrlImageBasedSrc || $this->openUrlImageBasedMode == 'both'): ?> <a href="<?=$this->escapeHtmlAttr($this->resolverUrl)?>"<?=$class?> data-search-class-id="<?=$this->escapeHtmlAttr($this->searchClassId) ?>"> - <? /* put the openUrl here in a span (COinS almost) so we can retrieve it later */ ?> + <?php /* put the openUrl here in a span (COinS almost) so we can retrieve it later */ ?> <span title="<?=$this->escapeHtmlAttr($this->openUrl)?>" class="openUrl"></span> - <? if ($this->openUrlGraphic): ?> - <? + <?php if ($this->openUrlGraphic): ?> + <?php $style = ''; if ($this->openUrlGraphicWidth) { $style .= 'width:' . $this->escapeHtmlAttr($this->openUrlGraphicWidth) . 'px;'; @@ -30,21 +30,21 @@ } ?> <img src="<?=$this->escapeHtmlAttr($this->openUrlGraphic)?>" alt="<?=$this->transEsc('Get full text')?>" style="<?=$style?>" /> - <? else: ?> + <?php else: ?> <?=$this->transEsc('Get full text')?> - <? endif; ?> + <?php endif; ?> </a> - <? endif; ?> + <?php endif; ?> - <? if ($this->openUrlImageBasedSrc): ?> - <? $ibOpenUrl = $this->openUrlImageBasedOverride ? $this->openUrlImageBasedOverride : $this->openUrl; ?> + <?php if ($this->openUrlImageBasedSrc): ?> + <?php $ibOpenUrl = $this->openUrlImageBasedOverride ? $this->openUrlImageBasedOverride : $this->openUrl; ?> <a href="<?=$this->escapeHtmlAttr($this->openUrlBase . '?' . $ibOpenUrl)?>"<?=$class_ib?>> <span title="<?=$this->escapeHtmlAttr($ibOpenUrl)?>" class="openUrl"></span> <img data-recordid="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" src="<?=$this->escapeHtmlAttr($this->openUrlImageBasedSrc)?>" alt="<?=$this->transEsc('Get full text')?>" /> </a> - <? endif; ?> + <?php endif; ?> </span> -<? if ($this->openUrlEmbed): ?> +<?php if ($this->openUrlEmbed): ?> <div class="resolver hidden"><?=$this->transEsc('Loading')?>...</div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Helpers/pagination.phtml b/themes/bootstrap3/templates/Helpers/pagination.phtml index 584ac0d8234abe50dbb028a4f6d5460c57b55c09..dc9125364a93c714d75566525c4d44681cecd7c7 100644 --- a/themes/bootstrap3/templates/Helpers/pagination.phtml +++ b/themes/bootstrap3/templates/Helpers/pagination.phtml @@ -1,60 +1,60 @@ -<? if ($this->pageCount): ?> +<?php if ($this->pageCount): ?> <ul class="pagination"> <!-- Previous page link --> - <li<? if (isset($this->previous)): ?>> - <? $newParams = $this->params; $newParams['page'] = $this->previous; ?> + <li<?php if (isset($this->previous)): ?>> + <?php $newParams = $this->params; $newParams['page'] = $this->previous; ?> <a href="<?= $this->currentPath() . '?' . http_build_query($newParams); ?>"> « <?=$this->translate('Prev')?> </a> - <? else: ?> + <?php else: ?> class="disabled"> <span>« <?=$this->translate('Prev')?></span> - <? endif; ?> + <?php endif; ?> </li> <!-- First page link --> - <li<? if (isset($this->first) && $this->first != $this->current): ?>> - <? $newParams = $this->params; $newParams['page'] = $this->first; ?> + <li<?php if (isset($this->first) && $this->first != $this->current): ?>> + <?php $newParams = $this->params; $newParams['page'] = $this->first; ?> <a href="<?= $this->currentPath() . '?' . http_build_query($newParams); ?>"> <?=$this->translate('First')?> </a> - <? else: ?> + <?php else: ?> class="disabled"> <span><?=$this->translate('First')?></span> - <? endif; ?> + <?php endif; ?> </li> <!-- Numbered page links --> - <? foreach ($this->pagesInRange as $page): ?> - <li<? if ($page != $this->current): ?>> - <? $newParams = $this->params; $newParams['page'] = $page; ?> + <?php foreach ($this->pagesInRange as $page): ?> + <li<?php if ($page != $this->current): ?>> + <?php $newParams = $this->params; $newParams['page'] = $page; ?> <a href="<?= $this->currentPath() . '?' . http_build_query($newParams); ?>"> - <? echo $page; ?> + <?=$page?> </a> - <? else: ?> - class="active"> <span><? echo $page; ?></span> - <? endif; ?> + <?php else: ?> + class="active"> <span><?=$page?></span> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> <!-- Last page link --> - <li<? if (isset($this->last) && $this->last != $this->current): ?>> - <? $newParams = $this->params; $newParams['page'] = $this->last; ?> + <li<?php if (isset($this->last) && $this->last != $this->current): ?>> + <?php $newParams = $this->params; $newParams['page'] = $this->last; ?> <a href="<?= $this->currentPath() . '?' . http_build_query($newParams); ?>"> <?=$this->translate('Last')?> </a> - <? else: ?> + <?php else: ?> class="disabled"> <span><?=$this->translate('Last')?></span> - <? endif; ?> + <?php endif; ?> </li> <!-- Next page link --> - <li<? if (isset($this->next)): ?>> - <? $newParams = $this->params; $newParams['page'] = $this->next; ?> + <li<?php if (isset($this->next)): ?>> + <?php $newParams = $this->params; $newParams['page'] = $this->next; ?> <a href="<?= $this->currentPath() . '?' . http_build_query($newParams); ?>"> <?=$this->translate('Next')?> > </a> - <? else: ?> + <?php else: ?> class="disabled"> <span><?=$this->translate('Next')?> »</span> - <? endif; ?> + <?php endif; ?> </li> </ul> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/AlphaBrowseLink.phtml b/themes/bootstrap3/templates/Recommend/AlphaBrowseLink.phtml index ff50e683e82fcb3ce855940cb94d8e07893f6613..e1c6819894d06df6ad074d20c645508edfa19528 100644 --- a/themes/bootstrap3/templates/Recommend/AlphaBrowseLink.phtml +++ b/themes/bootstrap3/templates/Recommend/AlphaBrowseLink.phtml @@ -1,4 +1,4 @@ -<? +<?php $index = $this->recommend->getIndex(); $from = $this->recommend->getQuery(); $link = $this->translate( @@ -7,7 +7,7 @@ '%%index%%' => $this->transEsc('browse_' . $index), '%%from%%' => $this->escapeHtml($from), '%%url%%' => $this->url('alphabrowse-home') - . '?from=' . urlencode($from) . '&source=' . urlencode($index) + . '?from=' . urlencode($from) . '&source=' . urlencode($index) ] ); ?> diff --git a/themes/bootstrap3/templates/Recommend/AuthorFacets.phtml b/themes/bootstrap3/templates/Recommend/AuthorFacets.phtml index 678d50f91117cf25236aa2f10bf4c4e62e31d68c..fb1c6016d7e722ba0ded18be31750772ac393cd3 100644 --- a/themes/bootstrap3/templates/Recommend/AuthorFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/AuthorFacets.phtml @@ -1,22 +1,22 @@ -<? if ($this->recommend->getResults()->getResultTotal() > 0): ?> - <? $similarAuthors = $this->recommend->getSimilarAuthors(); ?> - <? if (!empty($similarAuthors['list'])): ?> +<?php if ($this->recommend->getResults()->getResultTotal() > 0): ?> + <?php $similarAuthors = $this->recommend->getSimilarAuthors(); ?> + <?php if (!empty($similarAuthors['list'])): ?> <p class="author-label"><?=$this->transEsc('Author Results for')?> <strong><?=$this->escapeHtml($this->recommend->getSearchTerm()) ?></strong></p> <div class="author-facets"> <div class="author-list"> - <? foreach($similarAuthors['list'] as $i => $author): ?> - <? if ($i == 5): ?> + <?php foreach($similarAuthors['list'] as $i => $author): ?> + <?php if ($i == 5): ?> <a href="<?=$this->url('author-search') . '?lookfor=' . urlencode($this->recommend->getSearchTerm()) ?>"><strong><?=$this->transEsc("see all") ?> <?=(isset($similarAuthors['count']) && $similarAuthors['count']) ? $similarAuthors['count'] : ''?> »</strong></a> </div> <div class="author-list"> - <? endif; ?> - <a href="<?=$this->url('author-home') . '?author=' . urlencode($author['value'])?>"><?=$author['value'] ?><? /* count disabled -- uncomment to add: echo ' - ' . $author['count']; */ ?></a> - <? if ($i+1 < count($similarAuthors['list'])): ?> + <?php endif; ?> + <a href="<?=$this->url('author-home') . '?author=' . urlencode($author['value'])?>"><?=$author['value'] ?><?php /* count disabled -- uncomment to add: echo ' - ' . $author['count']; */ ?></a> + <?php if ($i + 1 < count($similarAuthors['list'])): ?> <br/> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </div> </div> <br/> - <? endif; ?> -<? endif; ?> + <?php endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/AuthorInfo.phtml b/themes/bootstrap3/templates/Recommend/AuthorInfo.phtml index 32a87e6a628d0d3b65d91606a2f500c416285dfe..d7fabadac0068b1e5c55da4c2f44424b6cf0dc13 100644 --- a/themes/bootstrap3/templates/Recommend/AuthorInfo.phtml +++ b/themes/bootstrap3/templates/Recommend/AuthorInfo.phtml @@ -1,14 +1,14 @@ -<? $this->info = $this->recommend->getAuthorInfo() ?> -<? if (!(empty($this->info['description']) || empty($this->info))): ?> +<?php $this->info = $this->recommend->getAuthorInfo() ?> +<?php if (!(empty($this->info['description']) || empty($this->info))): ?> <div class="wikipedia well clearfix"> <h2><?=$this->info['name'] ?></h2> - <? if (isset($this->info['image'])): ?> + <?php if (isset($this->info['image'])): ?> <img class="pull-left flip" src="<?=$this->info['image'] ?>" alt="<?=$this->escapeHtmlAttr($this->info['altimage']) ?>" width="150px"/> - <? endif; ?> + <?php endif; ?> <?=preg_replace('/___baseurl___/', $this->url('search-results'), $this->info['description']) ?> <a href="http://<?=$this->info['wiki_lang'] ?>.wikipedia.org/wiki/<?=$this->escapeHtmlAttr($this->info['name']/*url*/) ?>" target="new"><?=$this->transEsc('wiki_link') ?></a> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml b/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml index 6a277a43a4d17e2e75cc3c23294fe0fa3fa895cd..9a74b529767e1c665ab44e50e981cb507e25a696 100644 --- a/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml +++ b/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml @@ -1,19 +1,20 @@ -<? +<?php $data = $this->recommend->getRecommendations(); $results = $this->recommend->getResults(); + $displayQuery = $results->getParams()->getDisplayQuery(); + $that = $this; + $callback = function ($heading) use ($results, $displayQuery, $that) { + // Generate a new search URL that replaces the user's current term + // with the authority term: + $url = $that->url($results->getOptions()->getSearchAction()) + . $results->getUrlQuery()->replaceTerm($displayQuery, $heading); + return "<a href=\"$url\">" . $that->escapeHtml($heading) . '</a>'; + }; + $content = implode(', ', array_map($callback, $data)); ?> -<? if (is_array($data) && !empty($data)): ?> +<?php if (!empty($content)): ?> <div class="authoritybox"> <div><strong><?=$this->transEsc('See also')?>:</strong></div> - <div> - <? for ($i = 0; $i < count($data); $i++): ?> - <? - // Generate a new search URL that replaces the user's current term with the authority term: - $url = $this->url($results->getOptions()->getSearchAction()) - . $results->getUrlQuery()->replaceTerm($results->getParams()->getDisplayQuery(), $data[$i]['heading']); - ?> - <a href="<?=$url?>"><?=$this->escapeHtml($data[$i]['heading'])?></a><? if ($i != count($data) - 1): ?>, <? endif; ?> - <? endfor; ?> - </div> + <div><?=$content?></div> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/CatalogResults.phtml b/themes/bootstrap3/templates/Recommend/CatalogResults.phtml index 628900def89f0b58667d7e76bdf71e04f1d1a5d1..902143a3fa3a4b0dfbe9635d786102218329cbc9 100644 --- a/themes/bootstrap3/templates/Recommend/CatalogResults.phtml +++ b/themes/bootstrap3/templates/Recommend/CatalogResults.phtml @@ -1,29 +1,29 @@ -<? $searchObject = $this->recommend->getResults(); $results = $searchObject->getResults(); if (!empty($results)): ?> +<?php $searchObject = $this->recommend->getResults(); $results = $searchObject->getResults(); if (!empty($results)): ?> <h4><?=$this->transEsc('Catalog Results')?></h4> <ul class="list-group"> - <? foreach ($results as $driver): ?> + <?php foreach ($results as $driver): ?> <li class="list-group-item catalog-result"> - <? $formats = $driver->getFormats(); $format = isset($formats[0]) ? $formats[0] : ''; ?> + <?php $formats = $driver->getFormats(); $format = $formats[0] ?? ''; ?> <a href="<?=$this->recordLink()->getUrl($driver)?>" class="title <?=$this->record($driver)->getFormatClass($format)?>"> <?=$this->record($driver)->getTitleHtml()?> </a> - <? $summDate = $driver->getPublicationDates(); ?> - <? $summAuthors = $driver->getPrimaryAuthorsWithHighlighting(); ?> - <? if (!empty($summDate) || !empty($summAuthors)): ?> - <? if (!empty($summDate)): ?> + <?php $summDate = $driver->getPublicationDates(); ?> + <?php $summAuthors = $driver->getPrimaryAuthorsWithHighlighting(); ?> + <?php if (!empty($summDate) || !empty($summAuthors)): ?> + <?php if (!empty($summDate)): ?> <br/> <span class="small author"> <?=$this->transEsc('Published')?>: (<?=$this->escapeHtml($summDate[0])?>) </span> - <? endif; ?> - <? if (!empty($summAuthors)): ?> + <?php endif; ?> + <?php if (!empty($summAuthors)): ?> <br/> <span class="small"><?=$this->transEsc('By')?></span> - <a class="small date" href="<?=$this->record($driver)->getLink('author', $this->highlight($summAuthors[0], null, true, false))?>"><?=$this->highlight($summAuthors[0])?></a><? if (count($summAuthors) > 1): ?><span class="small">, <?=$this->transEsc('more_authors_abbrev')?></span><? endif; ?> - <? endif; ?> - <? endif; ?> + <a class="small date" href="<?=$this->record($driver)->getLink('author', $this->highlight($summAuthors[0], null, true, false))?>"><?=$this->highlight($summAuthors[0])?></a><?php if (count($summAuthors) > 1): ?><span class="small">, <?=$this->transEsc('more_authors_abbrev')?></span><?php endif; ?> + <?php endif; ?> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> <a class="list-group-item" href="<?=$this->url($searchObject->getOptions()->getSearchAction()) . $searchObject->getUrlQuery()->setLimit($searchObject->getOptions()->getDefaultLimit())?>"><?=$this->transEsc('More catalog results')?>...</a> </ul> -<? endif ?> +<?php endif ?> diff --git a/themes/bootstrap3/templates/Recommend/Channels.phtml b/themes/bootstrap3/templates/Recommend/Channels.phtml index 8fb018c7e03180c273006360c273fad4df645e47..082f2a71faf012c2a1797b1ee1882d28a7522d5c 100644 --- a/themes/bootstrap3/templates/Recommend/Channels.phtml +++ b/themes/bootstrap3/templates/Recommend/Channels.phtml @@ -1,4 +1,4 @@ -<? +<?php $results = $this->recommend->getResults(); $link = $this->url('channels-search') . $results->getUrlQuery()->getParams(false) diff --git a/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml b/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml index f4a0e5545309e7a6b84da043b47f61c2978a7fcc..a0fc65e58334db347ba36ae5c728353b18bbf57e 100644 --- a/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml @@ -1,8 +1,8 @@ -<? +<?php $this->overrideSideFacetCaption = 'In This Collection'; ?> -<? if ($this->recommend->keywordFilterEnabled()): ?> - <? +<?php if ($this->recommend->keywordFilterEnabled()): ?> + <?php $keywordFilter = $this->recommend->getKeywordFilter(); if (!empty($keywordFilter)) { $this->extraSideFacetFilters = [ @@ -17,7 +17,7 @@ ]; } ?> - <? ob_start() ?> + <?php ob_start() ?> <div class="panel panel-default"> <div class="panel-heading"> <h4 class="panel-title"> @@ -27,16 +27,17 @@ <div class="panel-body"> <form class="form-inline" role="form" method="get" name="keywordFilterForm" id="keywordFilterForm"> <input id="keywordFilter_lookfor" type="text" name="lookfor" value="<?=$this->escapeHtmlAttr($keywordFilter)?>" class="form-control"/> - <? foreach ($this->recommend->getResults()->getParams()->getFilterList(true) as $field => $filters): ?> - <? foreach ($filters as $filter): ?> + <?php foreach ($this->recommend->getResults()->getParams()->getFilterList(true) as $field => $filters): ?> + <?php foreach ($filters as $filter): ?> <input type="hidden" name="filter[]" value="<?=$this->escapeHtmlAttr($filter['field'])?>:"<?=$this->escapeHtmlAttr($filter['value'])?>"" /> - <? endforeach; ?> - <? endforeach; ?> + <?php endforeach; ?> + <?php endforeach; ?> <input class="btn btn-default" type="submit" name="submit" value="<?=$this->transEsc('Set')?>"/> </form> </div> </div> - <? $this->sideFacetExtraControls = ob_get_contents(); ?> - <? ob_end_clean(); ?> -<? endif; ?> + <?php $this->sideFacetExtraControls = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> +<?php $this->baseUriExtra = $this->recommend->getResults()->getParams()->getCollectionId(); ?> <?=$this->render('Recommend/SideFacets.phtml')?> diff --git a/themes/bootstrap3/templates/Recommend/DOI.phtml b/themes/bootstrap3/templates/Recommend/DOI.phtml index f15e2a6b06a064b48e25bf211f0a2c26f0b36a3c..384d32735b620b9e5f5198a459b0b0679eada0e9 100644 --- a/themes/bootstrap3/templates/Recommend/DOI.phtml +++ b/themes/bootstrap3/templates/Recommend/DOI.phtml @@ -1,10 +1,10 @@ -<? $doi = $this->recommend->getDOI(); if (!empty($doi)): ?> - <? $url = $this->recommend->getURL(); ?> +<?php $doi = $this->recommend->getDOI(); if (!empty($doi)): ?> + <?php $url = $this->recommend->getURL(); ?> <div class="alert alert-info"> <?=$this->translate('doi_detected_html', ['%%url%%' => $url, '%%doi%%' => $doi])?> </div> - <? if ($this->recommend->isFullMatch()): ?> - <? $redirect = 'document.location.href = "' . $this->escapeJs($url) . '";'; ?> + <?php if ($this->recommend->isFullMatch()): ?> + <?php $redirect = 'document.location.href = "' . $this->escapeJs($url) . '";'; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $redirect, 'SET')?> - <? endif; ?> -<? endif; ?> + <?php endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/DPLATerms.phtml b/themes/bootstrap3/templates/Recommend/DPLATerms.phtml index 267c561a9ce4191975969fee3e4d66919f1a32c1..17a72bc381883d6cafb4692ac62320dbbfb6a8a9 100644 --- a/themes/bootstrap3/templates/Recommend/DPLATerms.phtml +++ b/themes/bootstrap3/templates/Recommend/DPLATerms.phtml @@ -1,18 +1,18 @@ -<? $results = $this->recommend->getResults(); ?> -<? if(!empty($results)): ?> +<?php $results = $this->recommend->getResults(); ?> +<?php if(!empty($results)): ?> <ul class="list-group" id="side-panel-dpla"> - <li class="list-group-item title<? if($this->recommend->isCollapsed()): ?> collapsed<? endif ?>" data-toggle="collapse" href="#side-collapse-dpla"> + <li class="list-group-item title<?php if($this->recommend->isCollapsed()): ?> collapsed<?php endif ?>" data-toggle="collapse" href="#side-collapse-dpla"> DPLA </li> - <div id="side-collapse-dpla" class="collapse<? if(!$this->recommend->isCollapsed()): ?> in<? endif ?>"> - <? foreach($results as $item): ?> + <div id="side-collapse-dpla" class="collapse<?php if(!$this->recommend->isCollapsed()): ?> in<?php endif ?>"> + <?php foreach($results as $item): ?> <li class="list-group-item"> <a href="<?=$item['link'] ?>" target="new"><?=$this->escapeHtml($item['title']) ?></a><br/> - <? if(!empty($item['desc'])): ?> + <?php if(!empty($item['desc'])): ?> <span class="desc" title="<?=$item['desc'] ?>"><?=$this->escapeHtml($this->truncate($item['desc'], 50)) ?></span><br/> - <? endif; ?> + <?php endif; ?> (<span class="from"><?=$this->transEsc('Provider') ?>: <?=$this->escapeHtml($item['provider']) ?></span>) </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/Deprecated.phtml b/themes/bootstrap3/templates/Recommend/Deprecated.phtml index ff8266ef2c7dcbafc58aa400e648b37946ac45fd..69cb7c76ea6d65c9f2612077be5629547cf6f2d5 100644 --- a/themes/bootstrap3/templates/Recommend/Deprecated.phtml +++ b/themes/bootstrap3/templates/Recommend/Deprecated.phtml @@ -1,2 +1,2 @@ -<? /* do nothing -- this module is a placeholder for old deprecated features +<?php /* do nothing -- this module is a placeholder for old deprecated features to prevent legacy configurations from causing fatal errors. */ ?> diff --git a/themes/bootstrap3/templates/Recommend/EuropeanaResults.phtml b/themes/bootstrap3/templates/Recommend/EuropeanaResults.phtml index 0b23e82cfb2cdd3b2119301183cb2f7adfe13156..c52272607c9255064d0089cb9b602f1a50f643bf 100644 --- a/themes/bootstrap3/templates/Recommend/EuropeanaResults.phtml +++ b/themes/bootstrap3/templates/Recommend/EuropeanaResults.phtml @@ -1,4 +1,4 @@ -<? $data = $this->recommend->getResults(); if (is_array($data)): ?> +<?php $data = $this->recommend->getResults(); if (is_array($data)): ?> <div class="sidegroup rssResults"> <div class="suggestionHeader"> <a href="http://www.europeana.eu/portal/" title="Europeana.eu" target="_blank"> @@ -7,18 +7,18 @@ </div> <div> <ul class="list-group"> - <? $i = 0; foreach ($data['worksArray'] as $workKey => $work): ?> - <li class="list-group-item suggestedResult <? (++$i % 2) ? 'alt ' : ''?>record<?=$i?>"> + <?php $i = 0; foreach ($data['worksArray'] as $workKey => $work): ?> + <li class="list-group-item suggestedResult <?php (++$i % 2) ? 'alt ' : ''?>record<?=$i?>"> <div class="resultitem clearfix"> - <? if (isset($work['enclosure'])): ?> + <?php if (isset($work['enclosure'])): ?> <span class="europeanaImg"><img src="<?=$this->escapeHtmlAttr($work['enclosure'])?>" id="europeanaImage<?=$this->escapeHtmlAttr($workKey)?>"/></span> - <? endif; ?> + <?php endif; ?> <a href="<?=$this->escapeHtmlAttr($work['link'])?>" target="_blank"> <span><?=$this->escapeHtml($this->truncate($work['title'], 90))?></span> </a> </div> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> <p class="olSubjectMore"> <a href="<?=$this->escapeHtmlAttr($data['sourceLink'])?>" title="<?=$this->escapeHtmlAttr($data['feedTitle'])?>" target="_blank"> @@ -28,4 +28,4 @@ </div> </div> <div class="clearfix"></div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/EuropeanaResultsDeferred.phtml b/themes/bootstrap3/templates/Recommend/EuropeanaResultsDeferred.phtml index df193c4f4b71930357a14ab019746f7616565873..71299f87d89479edab2f4bc1dbc493de14e701ce 100644 --- a/themes/bootstrap3/templates/Recommend/EuropeanaResultsDeferred.phtml +++ b/themes/bootstrap3/templates/Recommend/EuropeanaResultsDeferred.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up Javascript for use below: $loadJs = 'var url = VuFind.path + "/AJAX/Recommend?' . $this->recommend->getUrlParams() . '";' . "\$('#EuropeanaDeferredRecommend').load(url);"; diff --git a/themes/bootstrap3/templates/Recommend/ExpandFacets.phtml b/themes/bootstrap3/templates/Recommend/ExpandFacets.phtml index 883fe08950120bda9d714be503542e5646683560..285e8628faf5f6b788d9081a5bba52b813f6bb65 100644 --- a/themes/bootstrap3/templates/Recommend/ExpandFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/ExpandFacets.phtml @@ -1,17 +1,17 @@ -<? +<?php $expandFacetSet = $this->recommend->getExpandedSet(); // Get empty search object to use as basis for parameter generation below: $blankResults = $this->recommend->getEmptyResults(); ?> -<? if ($expandFacetSet): ?> +<?php if ($expandFacetSet): ?> <div class="sidegroup"> - <? foreach ($expandFacetSet as $title=>$cluster): ?> + <?php foreach ($expandFacetSet as $title => $cluster): ?> <h4><?=$this->transEsc($cluster['label']) ?></h4> <div class="list-group"> - <? foreach ($cluster['list'] as $thisFacet): ?> + <?php foreach ($cluster['list'] as $thisFacet): ?> <a class="list-group-item" href="<?=$this->url('search-results') . $blankResults->getUrlQuery()->addFacet($title, $thisFacet['value'])?>"><?=$this->escapeHtml($thisFacet['displayText'])?></a> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/FacetCloud.phtml b/themes/bootstrap3/templates/Recommend/FacetCloud.phtml index e65b1bdd46c96e5a22fd712972b266d820b768c9..19581cb55a402e1d9485f291017fc37b377487f8 100644 --- a/themes/bootstrap3/templates/Recommend/FacetCloud.phtml +++ b/themes/bootstrap3/templates/Recommend/FacetCloud.phtml @@ -1,18 +1,18 @@ -<? +<?php $expandFacetSet = $this->recommend->getExpandedSet(); // Get empty search object to use as basis for parameter generation below: $blankResults = $this->recommend->getEmptyResults(); $cloudLimit = $this->recommend->getFacetLimit(); ?> -<? if ($expandFacetSet): ?> +<?php if ($expandFacetSet): ?> <div class="sidegroup"> - <? foreach ($expandFacetSet as $title=>$facets): ?> + <?php foreach ($expandFacetSet as $title => $facets): ?> <dl class="narrowList navmenu"> <dt><?=$this->transEsc($facets['label']) ?></dt> - <? + <?php foreach ($facets['list'] as $i => $facetItem) { if ($i < $cloudLimit) { - echo (($i == 0) ? '' : ', ') + echo(($i == 0) ? '' : ', ') . '<a href="' . $blankResults->getUrlQuery()->addFacet($title, $facetItem['value']) . '">' . $this->escapeHtml($facetItem['displayText']) . '</a> (' . $this->escapeHtml($facetItem['count']) . ')'; @@ -23,6 +23,6 @@ } ?> </dl> - <? endforeach; ?> + <?php endforeach; ?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/FavoriteFacets.phtml b/themes/bootstrap3/templates/Recommend/FavoriteFacets.phtml index c01e8718b409ca5a5d8ebffa249aecb0f1419cf6..7130d5b4d7ca42372ecfbf8065aa5e5aef4695ea 100644 --- a/themes/bootstrap3/templates/Recommend/FavoriteFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/FavoriteFacets.phtml @@ -1,28 +1,28 @@ -<? $results = $this->recommend->getResults(); ?> -<? $sideFacetSet = $this->recommend->getFacetSet(); ?> +<?php $results = $this->recommend->getResults(); ?> +<?php $sideFacetSet = $this->recommend->getFacetSet(); ?> -<? if (isset($sideFacetSet['tags']) && !empty($sideFacetSet['tags']['list'])): ?> +<?php if (isset($sideFacetSet['tags']) && !empty($sideFacetSet['tags']['list'])): ?> <h4 class="tag"><?=$this->transEsc($sideFacetSet['tags']['label'])?></h4> <ul class="list-group"> - <? $filterList = $results->getParams()->getFilterList(true); - $tagFilterList = isset($filterList[$sideFacetSet['tags']['label']]) ? $filterList[$sideFacetSet['tags']['label']] : null; ?> - <? if (!empty($tagFilterList)): ?> - <? $field = $sideFacetSet['tags']['label']; ?> - <? foreach ($tagFilterList as $filter): ?> - <? $removeLink = $this->currentPath().$results->getUrlQuery()->removeFacet($filter['field'], $filter['value']); ?> + <?php $filterList = $results->getParams()->getFilterList(true); + $tagFilterList = $filterList[$sideFacetSet['tags']['label']] ?? null; ?> + <?php if (!empty($tagFilterList)): ?> + <?php $field = $sideFacetSet['tags']['label']; ?> + <?php foreach ($tagFilterList as $filter): ?> + <?php $removeLink = $this->currentPath() . $results->getUrlQuery()->removeFacet($filter['field'], $filter['value']); ?> <a class="list-group-item active" href="<?=$removeLink?>"> <span class="pull-right flip"><i class="fa fa-minus-circle" aria-hidden="true"></i></span> <?=$this->escapeHtml($filter['displayText'])?> </a> - <? endforeach; ?> - <? endif; ?> - <? foreach($sideFacetSet['tags']['list'] as $thisFacet): ?> - <? if(!$thisFacet['isApplied']): ?> - <a class="list-group-item" href="<?=$this->currentPath().$results->getUrlQuery()->addFacet('tags', $thisFacet['value'])?>"> + <?php endforeach; ?> + <?php endif; ?> + <?php foreach($sideFacetSet['tags']['list'] as $thisFacet): ?> + <?php if(!$thisFacet['isApplied']): ?> + <a class="list-group-item" href="<?=$this->currentPath() . $results->getUrlQuery()->addFacet('tags', $thisFacet['value'])?>"> <span class="badge"><?=$this->escapeHtml($thisFacet['count'])?></span> <?=$this->escapeHtml($thisFacet['displayText'])?> </a> - <? endif ?> - <? endforeach; ?> + <?php endif ?> + <?php endforeach; ?> </ul> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/MapSelection.phtml b/themes/bootstrap3/templates/Recommend/MapSelection.phtml index 6ebcfbaedead9fcbd10bd7b53f22cbc38e746971..f6956786f564365b1e23afa15fb87f8a7f162e48 100644 --- a/themes/bootstrap3/templates/Recommend/MapSelection.phtml +++ b/themes/bootstrap3/templates/Recommend/MapSelection.phtml @@ -1,43 +1,60 @@ -<? if ($this->recommend->getSearchResultCoordinates()) :?> -<? - $this->headScript()->appendFile('vendor/ol/ol.js'); - $this->headScript()->appendFile('map_selection.js'); - $this->headLink()->appendStylesheet('vendor/ol/ol.css'); +<?php if ($this->recommend->getSearchResultCoordinates()) :?> + <?php + // Add translation strings + $this->jsTranslations()->addStrings([ + 'rectangle_center_message' => 'rectangle_center_message', + ]); - $resultsCoords = $this->recommend->getMapResultCoordinates(); - $baseUrl = $this->url('home'); - $geoField = $this->recommend->getGeoField(); - $urlpath = $this->url('search-results'); - $coordinates = $this->recommend->getSelectedCoordinates(); - $showSelection = true; - $popupTitle = $this->transEsc('map_results_label'); - if ($coordinates == null) { - $coordinates = $this->recommend->getDefaultCoordinates(); - $showSelection = false; - } - $height = $this->recommend->getHeight(); - $searchParams = $this->recommend->getSearchParams(); - $params = [json_encode($geoField), json_encode($coordinates), json_encode($urlpath), - json_encode($baseUrl), json_encode($searchParams), json_encode($showSelection), - json_encode($resultsCoords), json_encode($popupTitle)]; - $jsParams = implode(', ', $params); - $jsLoad = "loadMapSelection(" . $jsParams . ");"; - $addSearchOption = <<<EOF - $('.search-query-options>.advanced').after(' <a href="#" class="advanced" onclick=\\'{$jsLoad} $(this).remove(); return false;\\'>{$this->transEsc('Geographic Search')}</a>'); + $this->headScript()->appendFile('vendor/leaflet/leaflet.js'); + $this->headScript()->appendFile('vendor/leaflet/leaflet.draw.js'); + $this->headScript()->appendFile('vendor/leaflet/leaflet.markercluster.js'); + $this->headScript()->appendFile('map_selection_leaflet.js'); + $this->headLink()->appendStylesheet('vendor/leaflet/leaflet.css'); + $this->headLink()->appendStylesheet('vendor/leaflet/leaflet.draw.css'); + $this->headLink()->appendStylesheet('vendor/leaflet/MarkerCluster.css'); + $this->headLink()->appendStylesheet('vendor/leaflet/MarkerCluster.Default.css'); + $this->headLink()->appendStylesheet('geofeatures.css'); + + $basemap = $this->recommend->getBasemap(); + $geoField = $this->recommend->getGeoField(); + $height = $this->recommend->getHeight(); + $showSelection = true; + $baseUrl = $this->url('home'); + $urlpath = $this->url('search-results'); + $searchParams = $this->recommend->getSearchParams(); + $coordinates = $this->recommend->getSelectedCoordinates(); + + if ($coordinates == null) { + $coordinates = $this->recommend->getDefaultCoordinates(); + $showSelection = false; + } + $resultsCoords = $this->recommend->getMapResultCoordinates(); + $params = [ + json_encode($geoField), json_encode($coordinates), + json_encode($urlpath), json_encode($baseUrl), + json_encode($searchParams), json_encode($showSelection), + json_encode($resultsCoords), json_encode($basemap) + ]; + + $jsParams = implode(', ', $params); + $jsLoad = "loadMapSelection(" . $jsParams . ");"; + $addSearchOption = <<<EOF + $('.search-query-options>.advanced').after(' <a href="#" class="advanced" onclick=\\'{$jsLoad} $(this).remove(); return false;\\'>{$this->transEsc('Geographic Search')}</a>'); EOF; -?> -<div class="authorbox"> - <div id="geo_search" style="display: none;"> - <button id="draw_box"><?=$this->transEsc('Draw Search Box')?></button> - <span class="geo_maphelp"> <a href="<?=$this->url('help-home')?>?topic=geosearch" data-lightbox class="help-link"><?=$this->transEsc('Need Help?')?></a></span> - <div id="geo_search_map" style="height: <?=$height?>px;"> - <div id="popup"></div> + ?> + <div class="authorbox"> + <div id="geo_search" style="display: none;"> + <button id="draw_box"><?php echo $this->transEsc('Draw Search Box')?></button> + <span class="geo_maphelp"> <a href="<?php echo $this->url('help-home')?>?topic=geosearch" data-lightbox class="help-link"><?php echo $this->transEsc('Need Help?')?></a></span> + <div id="geo_search_map" style="height: <?php echo $height?>px;"></div> </div> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $showSelection ? $jsLoad : $addSearchOption, 'SET')?> </div> - <? if ($showSelection) :?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $jsLoad, 'SET');?> - <? else: ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $addSearchOption, 'SET');?> - <? endif; ?> -</div> -<? endif; ?> + <?php + // Overwrite leaflet.draw.js tooltips with international translations + $loadTranslations + = 'L.drawLocal.draw.handlers.rectangle.tooltip.start = "' . $this->transEsc('draw_searchbox_start') . '";' + . 'L.drawLocal.draw.handlers.simpleshape.tooltip.end = "' . $this->transEsc('draw_searchbox_end') . '";' + ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $loadTranslations, 'SET')?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/OpenLibrarySubjects.phtml b/themes/bootstrap3/templates/Recommend/OpenLibrarySubjects.phtml index 372687659d0c7a6614f79bb70eba10c3117f1236..d9a900ec1708f0baba4ccd47a3f1a18c21e138d5 100644 --- a/themes/bootstrap3/templates/Recommend/OpenLibrarySubjects.phtml +++ b/themes/bootstrap3/templates/Recommend/OpenLibrarySubjects.phtml @@ -1,26 +1,26 @@ -<? $data = $this->recommend->getResult(); if (is_array($data)): ?> +<?php $data = $this->recommend->getResult(); if (is_array($data)): ?> <div class="sidegroup"> - <h4>Open Library <? /* Intentionally not translated -- this is a site name, not a phrase */ ?></h4> + <h4>Open Library <?php /* Intentionally not translated -- this is a site name, not a phrase */ ?></h4> <div><?=$this->transEsc('Results for')?> <?=$this->escapeHtmlAttr($data['subject'])?> ...</div> <ul class="similar"> - <? foreach ($data['worksArray'] as $work): ?> + <?php foreach ($data['worksArray'] as $work): ?> <li> <a href="http://openlibrary.org<?=$work['key']?>" title="<?=$this->transEsc('Get full text')?>" target="_blank"> <span class="olSubjectCover"> - <? if (isset($work['cover_id']) && !empty($work['cover_id'])): ?> + <?php if (isset($work['cover_id']) && !empty($work['cover_id'])): ?> <img src="http://covers.openlibrary.org/b/<?=$this->escapeHtmlAttr($work['cover_id_type'])?>/<?=$this->escapeHtmlAttr($work['cover_id'])?>-S.jpg" class="olSubjectImage" alt="<?=$this->escapeHtmlAttr($work['title'])?>" /> - <? else: ?> + <?php else: ?> <img src="<?=$this->imageLink('noCover2.gif')?>" class="olSubjectImage" alt="<?=$this->escapeHtmlAttr($work['title'])?>" /> - <? endif; ?> + <?php endif; ?> </span> <span><?=$this->escapeHtmlAttr($this->truncate($work['title'], 50))?></span> - <? if (isset($work['mainAuthor'])): ?> + <?php if (isset($work['mainAuthor'])): ?> <span class="olSubjectAuthor"><?=$this->transEsc('by')?> <?=$this->escapeHtmlAttr($this->truncate($work['mainAuthor'], 40))?></span> - <? endif; ?> + <?php endif; ?> </a> <div class="clearfix"></div> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> <p class="olSubjectMore"> <a href="http://openlibrary.org/subjects" title="Open Library" target="_blank"> @@ -28,4 +28,4 @@ </a> </p> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/OpenLibrarySubjectsDeferred.phtml b/themes/bootstrap3/templates/Recommend/OpenLibrarySubjectsDeferred.phtml index 0e54681e66150fd7df9093e245dc3880529a54be..0ab09b0806f9d329914fe7754422a6f995fe57c2 100644 --- a/themes/bootstrap3/templates/Recommend/OpenLibrarySubjectsDeferred.phtml +++ b/themes/bootstrap3/templates/Recommend/OpenLibrarySubjectsDeferred.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up Javascript for use below: $loadJs = 'var url = VuFind.path + "/AJAX/Recommend?' . $this->recommend->getUrlParams() . '";' . "\$('#openLibraryDeferredRecommend').load(url);"; diff --git a/themes/bootstrap3/templates/Recommend/PubDateVisAjax.phtml b/themes/bootstrap3/templates/Recommend/PubDateVisAjax.phtml index 06a770e5a77bad056f657895d8ab7df8b526ad94..542e4b60e81cd5550356675b151848dd789ca646 100644 --- a/themes/bootstrap3/templates/Recommend/PubDateVisAjax.phtml +++ b/themes/bootstrap3/templates/Recommend/PubDateVisAjax.phtml @@ -1,29 +1,28 @@ -<? $visFacets = $this->recommend->getVisFacets(); ?> -<? if ($visFacets): ?> - - <? /* load jQuery flot */ ?> -<?$this->headScript()->appendFile('vendor/flot/excanvas.min.js', null, ['conditional' => 'IE']); - $this->headScript()->appendFile('vendor/flot/jquery.flot.min.js'); - $this->headScript()->appendFile('vendor/flot/jquery.flot.resize.min.js'); - $this->headScript()->appendFile('vendor/flot/jquery.flot.selection.min.js'); - $this->headScript()->appendFile('pubdate_vis.js'); ?> - - <? foreach ($visFacets as $facetField=>$facetRange): ?> +<?php if ($visFacets = $this->recommend->getVisFacets()): ?> + <?php + // load jQuery flot: + $this->headScript()->appendFile('vendor/flot/excanvas.min.js', null, ['conditional' => 'IE']); + $this->headScript()->appendFile('vendor/flot/jquery.flot.min.js'); + $this->headScript()->appendFile('vendor/flot/jquery.flot.resize.min.js'); + $this->headScript()->appendFile('vendor/flot/jquery.flot.selection.min.js'); + $this->headScript()->appendFile('pubdate_vis.js'); + ?> + <?php foreach ($visFacets as $facetField => $facetRange): ?> <div class="authorbox"> <div id="datevis<?=$this->escapeHtml($facetField)?>xWrapper" class="hidden"> <strong><?=$this->transEsc($facetRange['label']) ?></strong> - <? /* space the flot visualisation */ ?> + <?php /* space the flot visualisation */ ?> <div id="datevis<?=$facetField ?>x" style="margin:0 10px;width:auto;height:80px;cursor:crosshair;"></div> <div id="clearButtonText" style="display: none"><?=$this->transEsc('Clear') ?></div> </div> </div> - <? endforeach; ?> + <?php endforeach; ?> <div id="dateVisColorSettings"><!-- do not delete! used for passing CSS to Javascript --></div> - <? + <?php $js = "loadVis('" . $this->recommend->getFacetFields() . "', '" . $this->recommend->getSearchParams() . "', VuFind.path, " . $this->recommend->getZooming() . ");"; echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $js, 'SET'); ?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/RandomRecommend.phtml b/themes/bootstrap3/templates/Recommend/RandomRecommend.phtml index 07e745113c1ad6db7ed5047067aba7ada111a68e..636796d8eb1047d17316891cd36bcab446add540 100644 --- a/themes/bootstrap3/templates/Recommend/RandomRecommend.phtml +++ b/themes/bootstrap3/templates/Recommend/RandomRecommend.phtml @@ -1,36 +1,35 @@ -<? $recommend = $this->recommend->getResults(); if (count($recommend)> 0): ?> +<?php $recommend = $this->recommend->getResults(); if (count($recommend) > 0): ?> <ul class="list-group random <?=$this->recommend->getDisplayMode()?>"> <li class="list-group-item title" data-toggle="collapse" href="#side-collapse-randomrecommend"> <?=$this->transEsc("random_recommendation_title")?> </li> - <div id="side-collapse-randomrecommend" class="collapse<? if(!in_array('RandomRecommend', $collapsedFacets)): ?> in<? endif ?>"> - <? foreach ($recommend as $driver): ?> + <div id="side-collapse-randomrecommend" class="collapse<?php if(!in_array('RandomRecommend', $collapsedFacets)): ?> in<?php endif ?>"> + <?php foreach ($recommend as $driver): ?> <li class="list-group-item"> - <?if($this->recommend->getDisplayMode() === "images" || $this->recommend->getDisplayMode() === "mixed"):?> - - <? /* Display thumbnail if appropriate: */ ?> + <?php if ($this->recommend->getDisplayMode() === "images" || $this->recommend->getDisplayMode() === "mixed"): ?> + <?php /* Display thumbnail if appropriate: */ ?> <?=$this->record($driver)->getCover('RandomRecommend', 'small:medium', $this->recordLink()->getUrl($driver)); ?> - <?endif;?> + <?php endif; ?> - <? $formats = $driver->getFormats(); $format = isset($formats[0]) ? $formats[0] : ''; ?> + <?php $formats = $driver->getFormats(); $format = $formats[0] ?? ''; ?> <a href="<?=$this->recordLink()->getUrl($driver)?>" class="title <?=$this->record($driver)->getFormatClass($format)?> clearfix"> <?=$this->record($driver)->getTitleHtml()?> - <? $summAuthors = $driver->getPrimaryAuthors(); ?> - <span class="small<? if (!empty($summAuthors)): ?> pull-right flip<? endif; ?>"> - <? $summDate = $driver->getPublicationDates(); ?> - <? if (!empty($summDate)): ?> + <?php $summAuthors = $driver->getPrimaryAuthors(); ?> + <span class="small<?php if (!empty($summAuthors)): ?> pull-right flip<?php endif; ?>"> + <?php $summDate = $driver->getPublicationDates(); ?> + <?php if (!empty($summDate)): ?> <?=$this->transEsc('Published')?>: (<?=$this->escapeHtml($summDate[0])?>) - <? endif; ?> + <?php endif; ?> </span> </a> - <? if (!empty($summAuthors)): ?> + <?php if (!empty($summAuthors)): ?> <span class="small text-right"> <?=$this->transEsc('By')?> - <a href="<?=$this->record($driver)->getLink('author', $summAuthors[0])?>"><?=$this->escapeHtml($summAuthors[0])?></a><? if (count($summAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?> + <a href="<?=$this->record($driver)->getLink('author', $summAuthors[0])?>"><?=$this->escapeHtml($summAuthors[0])?></a><?php if (count($summAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?> </span> - <? endif; ?> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> </div> </ul> -<?endif;?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/RemoveFilters.phtml b/themes/bootstrap3/templates/Recommend/RemoveFilters.phtml index 124f3dc28a73f1d5308c0fcfc82c7d90a8d911b5..06168da8dfa4257f0f4a3257b976617329434a9c 100644 --- a/themes/bootstrap3/templates/Recommend/RemoveFilters.phtml +++ b/themes/bootstrap3/templates/Recommend/RemoveFilters.phtml @@ -1,6 +1,6 @@ -<? if ($this->recommend->hasFilters()): ?> +<?php if ($this->recommend->hasFilters()): ?> <div class="alert alert-info"> <?=$this->transEsc('nohit_active_filters')?> <a href="<?=$this->recommend->getFilterlessUrl()?>"><?=$this->transEsc('nohit_query_without_filters')?></a> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets.phtml b/themes/bootstrap3/templates/Recommend/SideFacets.phtml index c93bd6091f89a4b303399dd26c45fed921ab5fe8..1813fa75f8e13117bf6122ad8eeea096159cc146 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets.phtml @@ -1,16 +1,16 @@ -<? +<?php $this->headScript()->appendFile('facets.js'); // Save results/options to $this so they are available to sub-templates: $this->results = $results = $this->recommend->getResults(); - $this->options = $options = $this->searchOptions($this->searchClassId); + $this->options = $options = $results->getOptions(); ?> -<? if ($results->getResultTotal() > 0): ?> +<?php if ($results->getResultTotal() > 0): ?> <h4><?=$this->transEsc(isset($this->overrideSideFacetCaption) ? $this->overrideSideFacetCaption : 'Narrow Search')?></h4> -<? endif; ?> -<? $checkboxFilters = $results->getParams()->getCheckboxFacets(); ?> -<? $checkboxesShown = false; ?> -<? if (count($checkboxFilters) > 0): +<?php endif; ?> +<?php $checkboxFilters = $results->getParams()->getCheckboxFacets(); ?> +<?php $checkboxesShown = false; ?> +<?php if (count($checkboxFilters) > 0): foreach ($checkboxFilters as $current) { if ($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible']) { $checkboxesShown = true; @@ -18,58 +18,80 @@ } } ?> - <?if ($checkboxesShown):?> + <?php if ($checkboxesShown): ?> <div class="checkboxFilter"> <?=$this->context($this)->renderInContext('Recommend/SideFacets/checkbox-filters.phtml', ['checkboxFilters' => $checkboxFilters, 'results' => $results]); ?> </div> - <? endif; ?> -<? endif; ?> -<? $extraFilters = isset($this->extraSideFacetFilters) ? $this->extraSideFacetFilters : []; ?> -<? $collapsedFacets = $this->recommend->getCollapsedFacets() ?> -<? $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); ?> -<? if (!empty($filterList)): ?> + <?php endif; ?> +<?php endif; ?> +<?php $extraFilters = isset($this->extraSideFacetFilters) ? $this->extraSideFacetFilters : []; ?> +<?php $collapsedFacets = $this->recommend->getCollapsedFacets() ?> +<?php $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); ?> +<?php if (!empty($filterList)): ?> <?=$this->context($this)->renderInContext('Recommend/SideFacets/filter-list.phtml', [ 'collapsedFacets' => $collapsedFacets, 'extraFilters' => $extraFilters, 'filterList' => $filterList, ]); ?> -<? endif; ?> +<?php endif; ?> <?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?> -<? $sideFacetSet = $this->recommend->getFacetSet(); $rangeFacets = $this->recommend->getAllRangeFacets(); ?> -<? $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?> -<? $hierarchicalFacetSortOptions = $this->recommend->getHierarchicalFacetSortOptions() ?> -<? if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?> - <? foreach ($sideFacetSet as $title => $cluster): ?> +<?php $sideFacetSet = $this->recommend->getFacetSet(); $rangeFacets = $this->recommend->getAllRangeFacets(); ?> +<?php $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?> +<?php $hierarchicalFacetSortOptions = $this->recommend->getHierarchicalFacetSortOptions() ?> +<?php if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?> + <?php foreach ($sideFacetSet as $title => $cluster): ?> <div class="facet-group" id="side-panel-<?=$this->escapeHtmlAttr($title) ?>"> - <div class="title<? if(in_array($title, $collapsedFacets)): ?> collapsed<? endif ?>" data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" > + <button class="title<?php if(in_array($title, $collapsedFacets)): ?> collapsed<?php endif ?>" data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" > <?=$this->transEsc($cluster['label'])?> - </div> - <div id="side-collapse-<?=$this->escapeHtmlAttr($title) ?>" class="collapse<? if(!in_array($title, $collapsedFacets)): ?> in<? endif ?>"> - <? if (isset($rangeFacets[$title])): ?> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/range-slider.phtml', ['title' => $title, 'facet' => $rangeFacets[$title]]); ?> - <? else: ?> - <? $contextVars = [ - 'allowExclude' => $this->recommend->excludeAllowed($title), - 'title' => $title, - 'sortOptions' => isset($hierarchicalFacetSortOptions[$title]) ? $hierarchicalFacetSortOptions[$title] : '', - 'collapsedFacets' => $collapsedFacets - ]; ?> - <? if (in_array($title, $hierarchicalFacets)): ?> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/hierarchical-facet.phtml', $contextVars); ?> + </button> + <div id="side-collapse-<?=$this->escapeHtmlAttr($title) ?>" class="collapse<?php if(!in_array($title, $collapsedFacets)): ?> in<?php endif ?>"> + <?php if (isset($rangeFacets[$title])): // special display for ranges ?> + <?=$this->context($this)->renderInContext( + 'Recommend/SideFacets/range-slider.phtml', + ['title' => $title, 'facet' => $rangeFacets[$title]] + ); ?> + <?php else: ?> + <?php + // Set some variables so we can figure out which sub-template(s) to + // render. If we have a hierarchical facet, we will need to render a + // jsTree Javascript-based template. If we also support non-JS browsers, + // we will also need to render the basic default template. If we + // do NOT support non-JS browsers, we should instead display an error + // message. + $isHierarchical = in_array($title, $hierarchicalFacets); + $noJsSupport = $this->config()->nonJavascriptSupportEnabled(); + ?> + <?php if ($isHierarchical): // do we need hierarchical JS display? ?> + <?=$this->context($this)->renderInContext( + 'Recommend/SideFacets/hierarchical-facet.phtml', + [ + 'allowExclude' => $this->recommend->excludeAllowed($title), + 'title' => $title, + 'sortOptions' => $hierarchicalFacetSortOptions[$title] ?? '', + 'collapsedFacets' => $collapsedFacets + ] + ); ?> <noscript> - <? endif; ?> - <? $contextVars = [ - 'options' => $options, - 'allowExclude' => $this->recommend->excludeAllowed($title), - 'facets_before_more' => $this->recommend->getShowMoreSetting($title), - 'showMoreInLightbox' => $this->recommend->getShowInLightboxSetting($title) - ]; ?> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/cluster-list.phtml', array_merge($contextVars, ['title' => $title, 'cluster' => $cluster])); ?> - <? if (in_array($title, $hierarchicalFacets)): ?> + <?php if (!$noJsSupport): ?> + <span class="facet"><?=$this->transEsc('Please enable JavaScript.')?></span> + <?php endif; ?> + <?php endif; ?> + <?php if (!$isHierarchical || $noJsSupport): // do we need regular display? ?> + <?=$this->context($this)->renderInContext( + 'Recommend/SideFacets/cluster-list.phtml', + [ + 'options' => $options, 'title' => $title, 'cluster' => $cluster, + 'allowExclude' => $this->recommend->excludeAllowed($title), + 'facets_before_more' => $this->recommend->getShowMoreSetting($title), + 'showMoreInLightbox' => $this->recommend->getShowInLightboxSetting($title) + ] + ); ?> + <?php endif; ?> + <?php if ($isHierarchical): // close tag opened in matching if above ?> </noscript> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </div> </div> - <? endforeach; ?> -<? endif; ?> + <?php endforeach; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml index 320c479ca0c98f6e3d16770fab672ead9a76ba18..3290f56bb4cf63c927ee595f2f31d20d94308b82 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/checkbox-filters.phtml @@ -1,6 +1,9 @@ -<? foreach ($checkboxFilters as $current): ?> - <a class="checkbox-filter<? if(!($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible'])): ?> hidden<? endif; ?>" href="<?=$current['selected'] ? $results->getUrlQuery()->removeFilter($current['filter']) : $results->getUrlQuery()->addFilter($current['filter']);?>"> +<?php foreach ($checkboxFilters as $current): ?> + <a class="checkbox-filter<?php if(!($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible'])): ?> hidden<?php endif; ?>" href="<?=$current['selected'] ? $results->getUrlQuery()->removeFilter($current['filter']) : $results->getUrlQuery()->addFilter($current['filter']);?>"> + <?php if ($current['selected']): ?> + <span class="sr-only"><?=$this->transEsc('clear_tag_filter') ?></span> + <?php endif; ?> <i class="fa fa-<?=$current['selected'] ? 'check-square-o' : 'square-o' ?>"></i> <?=$this->transEsc($current['desc']) ?> </a> -<? endforeach; ?> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml index d3d6b733e1ae0991bae540d5c605f165b4fb5c89..b4badce6746d28e1b7efd770505df50a9050ce43 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/cluster-list.phtml @@ -1,73 +1,46 @@ -<? $idAndClass = 'id="more-narrowGroupHidden-'.$this->escapeHtmlAttr($this->title).'" class="facet narrow-toggle"'; ?> -<? $moreClass = 'narrowGroupHidden-'.$this->escapeHtmlAttr($this->title).' hidden'; ?> -<? foreach ($this->cluster['list'] as $i => $thisFacet): ?> - <? - if(empty($thisFacet['displayText'])) { - $thisFacet['displayText'] = "-"; - } - ?> - <? /* MORE link */ ?> - <? if ($i == $this->facets_before_more): ?> - <? if ($facetLightbox = $this->options->getFacetListAction()): ?> - <? $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&facet=' . $this->title . '&facetop=' . $thisFacet['operator'] . '&facetexclude=' . ($this->allowExclude ? 1 : 0); ?> - <? else: ?> - <? $moreUrl = '#'; ?> - <? endif; ?> - <? if (($this->showMoreInLightbox && $this->showMoreInLightbox !== 'more') && $facetLightbox): ?> - <a <?=$idAndClass ?> data-lightbox href="<?=$moreUrl ?>" rel="nofollow"><?=$this->transEsc('more')?> ...</a> - <? break; ?> - <? endif; ?> - <a <?=$idAndClass ?> href="<?=$moreUrl ?>" onclick="return moreFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>')" rel="nofollow"><?=$this->transEsc('more')?> ...</a> - <? endif; ?> - - <? - $affectiveURL = $thisFacet['isApplied'] - ? $this->currentPath() . $results->getUrlQuery()->removeFacet($this->title, $thisFacet['value'], $thisFacet['operator']) - : $this->currentPath() . $results->getUrlQuery()->addFacet($this->title, $thisFacet['value'], $thisFacet['operator']); - ?> - <? if (!$thisFacet['isApplied'] && $this->allowExclude): ?> - <div class="facet excludable facet<?=$thisFacet['operator'] ?><? if ($thisFacet['isApplied']): ?> active<? endif; ?><? if ($i >= $this->facets_before_more): ?> <?=$moreClass ?><?endif ?>"> - <? $excludeURL = $thisFacet['isApplied'] - ? $this->currentPath() . $results->getUrlQuery()->removeFacet($this->title, $thisFacet['value'], $thisFacet['operator'])->addFacet($this->title, $thisFacet['value'], 'NOT') - : $this->currentPath() . $results->getUrlQuery()->addFacet($this->title, $thisFacet['value'], 'NOT'); - ?> - <a href="<?=$excludeURL ?>" title="<?=$this->transEsc('exclude_facet') ?>" class="exclude"><i class="fa fa-times" aria-hidden="true"></i></a> - <? else: ?> - <a href="<?=$affectiveURL ?>" class="facet facet<?=$thisFacet['operator'] ?><? if ($thisFacet['isApplied']): ?> active<? endif; ?><? if ($i >= $this->facets_before_more): ?> <?=$moreClass ?><?endif ?>"> - <? endif; ?> - - <? if ($thisFacet['isApplied']): ?> - <span class="status"><i class="fa fa-check" aria-hidden="true"></i></span> - <? else: ?> - <span class="badge"><?=$this->localizedNumber($thisFacet['count'])?></span> - <? endif; ?> - - <? if (!$thisFacet['isApplied'] && $this->allowExclude): ?> - <a href="<?=$affectiveURL ?>" class="text"> - <? else: ?> - <span class="text"> - <? endif; ?> - - <? if ($thisFacet['operator'] == 'OR'): ?> - <i class="fa <?=$thisFacet['isApplied'] ? 'fa-check-square-o' : 'fa-square-o' ?>" aria-hidden="true"></i> - <? endif; ?> - - <?=$this->escapeHtml($thisFacet['displayText'])?> - - <? if (!$thisFacet['isApplied'] && $this->allowExclude): ?> +<?php $idAndClass = 'id="more-narrowGroupHidden-' . $this->escapeHtmlAttr($this->title) . '" class="facet narrow-toggle"'; ?> +<?php $moreClass = 'narrowGroupHidden-' . $this->escapeHtmlAttr($this->title) . ' hidden'; ?> +<?php foreach ($this->cluster['list'] as $i => $thisFacet): ?> + <?php if ($i == $this->facets_before_more): ?> + <?php if ($facetLightbox = $this->options->getFacetListAction()): ?> + <?php $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&facet=' . $this->title . '&facetop=' . $thisFacet['operator'] . '&facetexclude=' . ($this->allowExclude ? 1 : 0); ?> + <?php else: ?> + <?php $moreUrl = '#'; ?> + <?php endif; ?> + <?php if (($this->showMoreInLightbox && $this->showMoreInLightbox !== 'more') && $facetLightbox): ?> + <a <?=$idAndClass ?> data-lightbox href="<?=$moreUrl ?>" rel="nofollow"> + <span class="text"><?=$this->transEsc('more')?> ...</span> </a> - </div> - <? else: ?> - </span> + <?php break; ?> + <?php endif; ?> + <a <?=$idAndClass ?> href="<?=$moreUrl ?>" onclick="return moreFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>')" rel="nofollow"> + <span class="text"><?=$this->transEsc('more')?> ...</span> </a> - <? endif; ?> -<? endforeach; ?> + <?php endif; ?> + <?=$this->render('Recommend/SideFacets/single-facet.phtml', [ + 'exclude' => $this->allowExclude, + 'facet' => $thisFacet, + 'group' => $this->title, + 'moreClass' => $i >= $this->facets_before_more ? $moreClass : false, + 'url' => $this->results->getUrlQuery(), + 'urlBase' => $this->currentPath(), + ]) ?> +<?php endforeach; ?> -<? /* LESS and SEE MORE links */ ?> -<? if (isset($i) && $i >= $this->facets_before_more): ?> - <? if ($this->showMoreInLightbox === 'more' && $facetLightbox = $options->getFacetListAction()): ?> - <? $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams().'&facet='.$this->title.'&facetop='.$thisFacet['operator'].'&facetexclude='.($this->allowExclude ? 1 : 0); ?> - <a class="facet narrow-toggle <?=$moreClass ?>" data-lightbox href="<?=$moreUrl ?>" rel="nofollow"><?=$this->transEsc('see all')?> ...</a> - <? endif; ?> - <a class="facet narrow-toggle <?=$moreClass ?>" href="#" onclick="return lessFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>')"><?=$this->transEsc('less')?> ...</a> -<? endif; ?> +<?php /* LESS and SEE MORE links */ ?> +<?php if (isset($i) && $i >= $this->facets_before_more): ?> + <?php if ($this->showMoreInLightbox === 'more' && $facetLightbox = $options->getFacetListAction()): ?> + <?php + $moreUrl = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&facet=' . $this->title . '&facetop=' . $thisFacet['operator'] . '&facetexclude=' . ($this->allowExclude ? 1 : 0); + if (!empty($this->baseUriExtra)) { + $moreUrl .= '&baseUriExtra=' . urlencode($this->baseUriExtra); + } + ?> + <a class="facet narrow-toggle <?=$moreClass ?>" data-lightbox href="<?=$moreUrl ?>" rel="nofollow"> + <span class="text"><?=$this->transEsc('see all')?> ...</span> + </a> + <?php endif; ?> + <a class="facet narrow-toggle <?=$moreClass ?>" href="#" onclick="return lessFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>')"> + <span class="text"><?=$this->transEsc('less')?> ...</span> + </a> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml index 085d50cbc00e309ce3f5d7466c0cb26439873d8a..fa43b26a56ec9d54b3b1e5de1b91031a7b660878 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml @@ -1,8 +1,8 @@ <div class="facet-group active-filters"> <div class="title"><?=$this->transEsc('Remove Filters')?></div> - <? foreach ($filterList as $field => $filters): ?> - <? foreach ($filters as $i => $filter): ?> - <? + <?php foreach ($filterList as $field => $filters): ?> + <?php foreach ($filters as $i => $filter): ?> + <?php $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false; if ($index !== false) { unset($collapsedFacets[$index]); // Open if we have a match @@ -17,11 +17,14 @@ } ?> <a class="facet" href="<?=$removeLink ?>" title="<?=$this->transEsc('clear_tag_filter') ?>"> + <span class="sr-only"><?=$this->transEsc('clear_tag_filter') ?></span> + <span class="text"> + <?php if ($filter['operator'] == 'NOT'): ?><?=$this->transEsc('NOT') ?><?php endif; ?> + <?php if ($filter['operator'] == 'OR' && $i > 0): ?><?=$this->transEsc('OR') ?><?php endif; ?> + <?=$this->transEsc($field) ?>: <?=$this->escapeHtml($filter['displayText']) ?> + </span> <span class="status"><i class="fa fa-times" aria-hidden="true"></i></span> - <? if ($filter['operator'] == 'NOT'): ?><?=$this->transEsc('NOT') ?><? endif; ?> - <? if ($filter['operator'] == 'OR' && $i > 0): ?><?=$this->transEsc('OR') ?><? endif; ?> - <?=$this->transEsc($field) ?>: <?=$this->escapeHtml($filter['displayText']) ?> </a> - <? endforeach; ?> - <? endforeach; ?> + <?php endforeach; ?> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml index cf26f37cad77e7a7bd4a5e1839d080bb38261c3f..e523b82959897959698ed04191004cda101551b4 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/hierarchical-facet.phtml @@ -1,6 +1,6 @@ -<? $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); ?> -<? if (!in_array($this->title, $this->collapsedFacets)): ?> - <? +<?php $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); ?> +<?php if (!in_array($this->title, $this->collapsedFacets)): ?> + <?php $script = <<<JS $(document).ready(function() { initFacetTree($('#facet_{$this->escapeHtml($this->title)}'), true); @@ -8,8 +8,8 @@ $(document).ready(function() { JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> -<? else: ?> - <? +<?php else: ?> + <?php $script = <<<JS $('#side-collapse-{$this->escapeHtmlAttr($this->title)}').on('show.bs.collapse', function() { initFacetTree($('#facet_{$this->escapeHtml($this->title)}'), true); @@ -17,8 +17,9 @@ $('#side-collapse-{$this->escapeHtmlAttr($this->title)}').on('show.bs.collapse', JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> -<? endif; ?> +<?php endif; ?> <li id="facet_<?=$this->escapeHtml($this->title)?>" class="jstree-facet" + data-source="<?=$this->escapeHtml($this->searchClassId)?>" data-facet="<?=$this->escapeHtmlAttr($this->title)?>" data-path="<?=$this->currentPath()?>" data-exclude="<?=$this->allowExclude?>" diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml index 4dae71c02fd4102d83b25846307805530570a477..35f3de7c54311067177fdb847b5f20bb7b75e915 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml @@ -3,7 +3,7 @@ <?=$results->getUrlQuery()->asHiddenFields(['page' => "/./", 'filter' => "/^{$this->title}:.*/"])?> <input type="hidden" name="<?=$this->escapeHtmlAttr($this->facet['type'])?>range[]" value="<?=$this->escapeHtmlAttr($this->title)?>"/> <div class="date-fields"> - <? $extraInputAttribs = ($this->facet['type'] == 'date') ? 'maxlength="4" ' : ''; ?> + <?php $extraInputAttribs = ($this->facet['type'] == 'date') ? 'maxlength="4" ' : ''; ?> <div class="date-from"> <label for="<?=$this->escapeHtmlAttr($this->title)?>from"> <?=$this->transEsc('date_from')?>: @@ -17,20 +17,20 @@ <input type="text" class="form-control" name="<?=$this->escapeHtmlAttr($this->title)?>to" id="<?=$this->escapeHtmlAttr($this->title)?>to" value="<?=isset($this->facet['values'][1])?$this->escapeHtmlAttr($this->facet['values'][1]):''?>" <?=$extraInputAttribs?>/> </div> </div> - <? if ($this->facet['type'] == 'date'): ?> + <?php if ($this->facet['type'] == 'date'): ?> <div class="slider-container"><input type="text" class="hidden" id="<?=$this->escapeHtmlAttr($this->title)?><?=$this->escapeHtml($this->facet['type'])?>Slider"/></div> - <? endif; ?> + <?php endif; ?> <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Set')?>"/> </form> </div> -<? if ($this->facet['type'] == 'date'): ?> - <? $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); ?> - <? $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); ?> - <? +<?php if ($this->facet['type'] == 'date'): ?> + <?php $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); ?> + <?php $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); ?> + <?php $min = !empty($this->facet['values'][0]) ? min($this->facet['values'][0], 1400) : 1400; - $future = date('Y', time()+31536000); // next year + $future = date('Y', time() + 31536000); // next year $max = !empty($this->facet['values'][1]) ? max($future, $this->facet['values'][1]) : $future; - $low = !empty($this->facet['values'][0]) ? $this->facet['values'][0] : $min; + $low = !empty($this->facet['values'][0]) ? $this->facet['values'][0] : $min; $high = !empty($this->facet['values'][1]) ? $this->facet['values'][1] : $max; $script = <<<JS $(document).ready(function() { @@ -66,4 +66,4 @@ $('#{$this->escapeHtmlAttr($this->title)}from, #{$this->escapeHtmlAttr($this->ti JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml new file mode 100644 index 0000000000000000000000000000000000000000..719347269a11f340f3dd3df007afba9335f5e5e8 --- /dev/null +++ b/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml @@ -0,0 +1,56 @@ +<?php + $toggleUrl = $this->facet['isApplied'] + ? $this->urlBase . $this->url->removeFacet($this->group, $this->facet['value'], $this->facet['operator']) + : $this->urlBase . $this->url->addFacet($this->group, $this->facet['value'], $this->facet['operator']); + + $hasSubLinks = $this->exclude && !$this->facet['isApplied']; + + $classList = ['facet', 'js-facet-item']; + if (!empty($this->moreClass)) { + $classList[] = $this->moreClass; + } + if ($this->facet['isApplied']) { + $classList[] = 'active'; + } + + $displayText = '-'; + if (!empty($this->facet['displayText'])) { + $displayText = $this->escapeHtml($this->facet['displayText']); + } elseif (!empty($this->facet['value'])) { + $displayText = $this->escapeHtml($this->facet['value']); + } + + if ($this->facet['operator'] == 'OR') { + $displayText = + '<i class="fa ' . ($this->facet['isApplied'] ? 'fa-check-square-o' : 'fa-square-o') . '" aria-hidden="true"></i> ' + . $displayText; + } +?> +<?php if ($hasSubLinks): ?> + <li class="<?=implode(' ', $classList) ?>"> +<?php else: ?> + <a href="<?=$toggleUrl ?>" class="<?=implode(' ', $classList) ?>" data-title="<?=$this->escapeHtmlAttr($this->facet['displayText']) ?>" data-count="<?=$this->facet['count'] ?>"<?php if($this->facet['isApplied']): ?> title="<?=$this->transEsc('applied_filter') ?>"<?php endif;?> data-lightbox-ignore> +<?php endif; ?> + + <?php if ($hasSubLinks): ?> + <a class="text" href="<?=$toggleUrl ?>" data-lightbox-ignore data-title="<?=$this->escapeHtmlAttr($this->facet['displayText']) ?>" data-count="<?=$this->facet['count'] ?>"<?php if($this->facet['isApplied']): ?> title="<?=$this->transEsc('applied_filter') ?>"<?php endif;?>> + <?=$displayText ?> + </a> + <?php else: ?> + <span class="text"> + <?=$displayText ?> + </span> + <?php endif; ?> + + <?php if (!$this->facet['isApplied']): ?> + <span class="badge"> + <?=$this->localizedNumber($this->facet['count']) ?> + </span> + <?php endif; ?> + + <?php if ($this->exclude && !$this->facet['isApplied']): ?> + <?php $excludeURL = $this->urlBase . $this->url->addFacet($this->group, $this->facet['value'], 'NOT'); ?> + <a href="<?=$excludeURL ?>" data-lightbox-ignore title="<?=$this->transEsc('exclude_facet') ?>" class="exclude"><i class="fa fa-times" aria-hidden="true"></i></a> + <?php endif; ?> + +<?=$hasSubLinks ? '</li>' : '</a>'; ?> diff --git a/themes/bootstrap3/templates/Recommend/SpellingSuggestions.phtml b/themes/bootstrap3/templates/Recommend/SpellingSuggestions.phtml index 16c1c7b9a2e2ff03e5b7c7065c49234921f24ba7..5a69ac101d55a157da0ec5b23762b981c12d9e0a 100644 --- a/themes/bootstrap3/templates/Recommend/SpellingSuggestions.phtml +++ b/themes/bootstrap3/templates/Recommend/SpellingSuggestions.phtml @@ -1,10 +1,10 @@ -<? +<?php $results = $this->recommend->getResults(); $label = $results->getResultTotal() > 0 ? '<strong>' . $this->transEsc('spell_suggest') . '</strong>:' : $this->transEsc('nohit_spelling') . ':'; $suggestions = $this->search()->renderSpellingSuggestions($label, $results, $this); ?> -<? if (!empty($suggestions)): ?> +<?php if (!empty($suggestions)): ?> <?=$suggestions?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SummonBestBets.phtml b/themes/bootstrap3/templates/Recommend/SummonBestBets.phtml index 3302226bb5ab23f8d4de157b955d1deec90666a8..27733561fb5d242037c2a5ac5ba012e10306894f 100644 --- a/themes/bootstrap3/templates/Recommend/SummonBestBets.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonBestBets.phtml @@ -1,14 +1,14 @@ -<? $summonBestBets = $this->recommend->getResults(); if (!empty($summonBestBets)): ?> +<?php $summonBestBets = $this->recommend->getResults(); if (!empty($summonBestBets)): ?> <div class="authorbox"> - <? foreach ($summonBestBets as $current): ?> + <?php foreach ($summonBestBets as $current): ?> <p> - <? if (isset($current['link']) && !empty($current['link'])):?> + <?php if (isset($current['link']) && !empty($current['link'])):?> <a href="<?=$this->escapeHtmlAttr($current['link'])?>"><?=$this->escapeHtml($current['title'])?></a> - <? else: ?> + <?php else: ?> <b><?=$this->escapeHtml($current['title'])?></b> - <? endif; ?> + <?php endif; ?> <br/><?=$current['description']?> </p> - <? endforeach; ?> + <?php endforeach; ?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SummonBestBetsDeferred.phtml b/themes/bootstrap3/templates/Recommend/SummonBestBetsDeferred.phtml index 276d1da69e9c47db01d724e8d664ff27a97ecc2d..7a2e2490f3ad52a5e507f0a21316909f27ee77d5 100644 --- a/themes/bootstrap3/templates/Recommend/SummonBestBetsDeferred.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonBestBetsDeferred.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up Javascript for use below: $loadJs = 'var url = VuFind.path + "/AJAX/Recommend?' . $this->recommend->getUrlParams() . '";' . "\$('#SummonDeferredBestBets').load(url);"; diff --git a/themes/bootstrap3/templates/Recommend/SummonDatabases.phtml b/themes/bootstrap3/templates/Recommend/SummonDatabases.phtml index 2888ef723efbc220d02a78e439843c2ee84378a9..c6834fb7d7549694bd35c31f27b9971e29ae06c5 100644 --- a/themes/bootstrap3/templates/Recommend/SummonDatabases.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonDatabases.phtml @@ -1,8 +1,8 @@ -<? $summonDatabases = $this->recommend->getResults(); if (!empty($summonDatabases)): ?> +<?php $summonDatabases = $this->recommend->getResults(); if (!empty($summonDatabases)): ?> <div class="authorbox"> <p><?=$this->transEsc('summon_database_recommendations')?></p> - <? foreach ($summonDatabases as $current): ?> + <?php foreach ($summonDatabases as $current): ?> <p><a href="<?=$this->escapeHtmlAttr($current['link'])?>"><?=$this->escapeHtml($current['title'])?></a><br/><?=$this->escapeHtml($current['description'])?></p> - <? endforeach; ?> + <?php endforeach; ?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SummonDatabasesDeferred.phtml b/themes/bootstrap3/templates/Recommend/SummonDatabasesDeferred.phtml index 6d5270d39541a4d25179e8e05f2aa01881834550..2c0f150f4876907a42dbfdcb69184f6b85153a4a 100644 --- a/themes/bootstrap3/templates/Recommend/SummonDatabasesDeferred.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonDatabasesDeferred.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up Javascript for use below: $loadJs = 'var url = VuFind.path + "/AJAX/Recommend?' . $this->recommend->getUrlParams() . '";' . "\$('#SummonDeferredDatabases').load(url);"; diff --git a/themes/bootstrap3/templates/Recommend/SummonResults.phtml b/themes/bootstrap3/templates/Recommend/SummonResults.phtml index 0a539c4edf8db08fccb2a534b18e4ea1457a7431..af3c43139c8bedb25d012794d27c750d6098c06d 100644 --- a/themes/bootstrap3/templates/Recommend/SummonResults.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonResults.phtml @@ -1,22 +1,22 @@ -<? $searchObject = $this->recommend->getResults(); $results = $searchObject->getResults(); if (!empty($results)): ?> +<?php $searchObject = $this->recommend->getResults(); $results = $searchObject->getResults(); if (!empty($results)): ?> <h4><?=$this->transEsc('Summon Results')?></h4> <div class="list-group"> - <? foreach ($results as $driver): ?> + <?php foreach ($results as $driver): ?> <div class="list-group-item"> <span> - <? $formats = $driver->getFormats(); $format = isset($formats[0]) ? $formats[0] : ''; ?> + <?php $formats = $driver->getFormats(); $format = $formats[0] ?? ''; ?> <a href="<?=$this->recordLink()->getUrl($driver)?>" class="title <?=$this->record($driver)->getFormatClass($format)?>"> <?=$this->record($driver)->getTitleHtml()?> </a> - <? $summAuthors = $driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> + <?php $summAuthors = $driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> <span class="small"> <?=$this->transEsc('by')?> - <a href="<?=$this->record($driver)->getLink('author', $this->highlight($summAuthors[0], null, true, false))?>"><?=$this->highlight($summAuthors[0])?></a><? if (count($summAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?> + <a href="<?=$this->record($driver)->getLink('author', $this->highlight($summAuthors[0], null, true, false))?>"><?=$this->highlight($summAuthors[0])?></a><?php if (count($summAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?> </span> - <? endif; ?> + <?php endif; ?> </span> </div> - <? endforeach; ?> + <?php endforeach; ?> <a class="list-group-item" href="<?=$this->url($searchObject->getOptions()->getSearchAction()) . $searchObject->getUrlQuery()->setLimit($searchObject->getOptions()->getDefaultLimit())?>"><?=$this->transEsc('More Summon results')?>...</a> </div> -<? endif ?> +<?php endif ?> diff --git a/themes/bootstrap3/templates/Recommend/SummonResultsDeferred.phtml b/themes/bootstrap3/templates/Recommend/SummonResultsDeferred.phtml index 49fff6ea945810962854eace729c824937a07512..72c4ce280d352b08cc268b031c0462bc21077500 100644 --- a/themes/bootstrap3/templates/Recommend/SummonResultsDeferred.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonResultsDeferred.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up Javascript for use below: $loadJs = 'var url = VuFind.path + "/AJAX/Recommend?' . $this->recommend->getUrlParams() . '";' . "\$('#SummonDeferredRecommend').load(url);"; diff --git a/themes/bootstrap3/templates/Recommend/SummonTopics.phtml b/themes/bootstrap3/templates/Recommend/SummonTopics.phtml index de7de6adfb92d0a955260e68b313a5fa42202cd9..988dd1f76887119f2fcd076931a9969f44472447 100644 --- a/themes/bootstrap3/templates/Recommend/SummonTopics.phtml +++ b/themes/bootstrap3/templates/Recommend/SummonTopics.phtml @@ -1,18 +1,18 @@ -<? $summonTopics = $this->recommend->getResults(); if (!empty($summonTopics)): $summonTopics = current($summonTopics); ?> +<?php $summonTopics = $this->recommend->getResults(); if (!empty($summonTopics)): $summonTopics = current($summonTopics); ?> <div class="authorbox"> <p><b><?=$this->transEsc('Suggested Topics')?></b></p> - <? if (isset($summonTopics['title'])): ?> + <?php if (isset($summonTopics['title'])): ?> <p> <a href="<?=$this->url('summon-search')?>?lookfor=%22<?=urlencode($summonTopics['title'])?>%22"><?=$this->escapeHtml($summonTopics['title'])?></a><br /> - <? if (isset($summonTopics['snippet'])): ?><?=$this->escapeHtml($summonTopics['snippet'])?><? endif; ?> - <? if (isset($summonTopics['sourceLink'])): ?><a href="<?=$this->escapeHtmlAttr($summonTopics['sourceLink'])?>"><?=$this->transEsc('more')?>...</a><? endif; ?> + <?php if (isset($summonTopics['snippet'])): ?><?=$this->escapeHtml($summonTopics['snippet'])?><?php endif; ?> + <?php if (isset($summonTopics['sourceLink'])): ?><a href="<?=$this->escapeHtmlAttr($summonTopics['sourceLink'])?>"><?=$this->transEsc('more')?>...</a><?php endif; ?> </p> - <? endif; ?> - <? if (isset($summonTopics['relatedTopics']) && !empty($summonTopics['relatedTopics'])): ?> + <?php endif; ?> + <?php if (isset($summonTopics['relatedTopics']) && !empty($summonTopics['relatedTopics'])): ?> <p> <b><?=$this->transEsc('wcterms_exact')?>:</b> - <? foreach ($summonTopics['relatedTopics'] as $i => $topic): ?><? if ($i > 0): ?>, <? endif; ?><a href="<?=$this->url('summon-search')?>?lookfor=%22<?=urlencode($topic['title'])?>%22"><?=$this->escapeHtml($topic['title'])?></a><? endforeach; ?> + <?php foreach ($summonTopics['relatedTopics'] as $i => $topic): ?><?php if ($i > 0): ?>, <?php endif; ?><a href="<?=$this->url('summon-search')?>?lookfor=%22<?=urlencode($topic['title'])?>%22"><?=$this->escapeHtml($topic['title'])?></a><?php endforeach; ?> </p> - <? endif; ?> + <?php endif; ?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SwitchQuery.phtml b/themes/bootstrap3/templates/Recommend/SwitchQuery.phtml index ea7bb52c81e595c8b676dec428c601660a937880..5060ee838ace2a7175eef51496b70c0790275f97 100644 --- a/themes/bootstrap3/templates/Recommend/SwitchQuery.phtml +++ b/themes/bootstrap3/templates/Recommend/SwitchQuery.phtml @@ -1,10 +1,10 @@ -<? $suggestions = $this->recommend->getSuggestions(); if (!empty($suggestions)): ?> +<?php $suggestions = $this->recommend->getSuggestions(); if (!empty($suggestions)): ?> <div class="alert alert-info"> <p><?=$this->transEsc('switchquery_intro')?></p> <ul> - <? foreach ($suggestions as $desc => $query): ?> + <?php foreach ($suggestions as $desc => $query): ?> <li><?=$this->transEsc($desc)?>: <a href="<?=$this->recommend->getResults()->getUrlQuery()->setSearchTerms($query)?>"><?=$this->escapeHtml($query)?></a>.</li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SwitchTab.phtml b/themes/bootstrap3/templates/Recommend/SwitchTab.phtml index 8a95b293c79c2a90583d846777079019efbc56f9..a141ae55cf4c296ffeafacfb24775d5fecf618ea 100644 --- a/themes/bootstrap3/templates/Recommend/SwitchTab.phtml +++ b/themes/bootstrap3/templates/Recommend/SwitchTab.phtml @@ -1,19 +1,19 @@ -<? - $searchTabs = is_object($this->params) - ? $this->searchtabs()->getTabConfigForParams($this->params) : []; +<?php + $tabConfig = is_object($this->params) + ? $this->searchTabs()->getTabConfigForParams($this->params) : []; ?> -<? if (count($searchTabs) > 0): ?> +<?php if (count($tabConfig) > 0): ?> <div class="alert alert-info"> - <?=$this->transEsc('nohit_change_tab', ['%%activeTab%%' => $this->translate($this->recommend->getActiveTab($searchTabs)['label'])])?> + <?=$this->transEsc('nohit_change_tab', ['%%activeTab%%' => $this->translate($this->recommend->getActiveTab($tabConfig)['label'])])?> <ul> - <? $inactiveTabs = $this->recommend->getInactiveTabs($searchTabs); ?> - <? foreach ($inactiveTabs as $tab): ?> + <?php $inactiveTabs = $this->recommend->getInactiveTabs($tabConfig); ?> + <?php foreach ($inactiveTabs as $tab): ?> <li> - <? if (!$tab['selected']): ?><a href="<?=$this->escapeHtmlAttr($tab['url'])?>"><? endif; ?> + <?php if (!$tab['selected']): ?><a href="<?=$this->escapeHtmlAttr($tab['url'])?>"><?php endif; ?> <?=$this->transEsc($tab['label']); ?> - <? if (!$tab['selected']): ?></a><? endif; ?> + <?php if (!$tab['selected']): ?></a><?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/SwitchType.phtml b/themes/bootstrap3/templates/Recommend/SwitchType.phtml index 5c174ca1adc246ac81b7dab4c4aaf62d8cc074ba..d0d79784e90db51e617a4a63d051a26068182328 100644 --- a/themes/bootstrap3/templates/Recommend/SwitchType.phtml +++ b/themes/bootstrap3/templates/Recommend/SwitchType.phtml @@ -1,6 +1,6 @@ -<? if ($handler = $this->recommend->getNewHandler()): ?> +<?php if ($handler = $this->recommend->getNewHandler()): ?> <div class="alert alert-info"> <?=$this->transEsc('widen_prefix')?> <a href="<?=$this->recommend->getResults()->getUrlQuery()->setHandler($handler)?>"><?=$this->transEsc($this->recommend->getNewHandlerName())?></a>. </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/TopFacets.phtml b/themes/bootstrap3/templates/Recommend/TopFacets.phtml index 1ba9bc0022c4367b384c822758a5249f9c77f547..789ce06224a4c396c387d134f4e1ed0ff50aa616 100644 --- a/themes/bootstrap3/templates/Recommend/TopFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/TopFacets.phtml @@ -1,52 +1,60 @@ -<? +<?php // TODO: This file needs love $topFacetSet = $this->recommend->getTopFacetSet(); $topFacetSettings = $this->recommend->getTopFacetSettings(); $results = $this->recommend->getResults(); ?> -<? if (isset($topFacetSet)): ?> - <? foreach($topFacetSet as $title => $cluster): ?> - <? $moreClass = ' NarrowGroupHidden_'.$this->escapeHtml($title).' hidden'; ?> - <? $allowExclude = $this->recommend->excludeAllowed($title); ?> +<?php if (isset($topFacetSet)): ?> + <?php foreach($topFacetSet as $title => $cluster): ?> + <?php $moreClass = ' NarrowGroupHidden_' . $this->escapeHtml($title) . ' hidden'; ?> + <?php $allowExclude = $this->recommend->excludeAllowed($title); ?> <div class="top-facets"> - <strong><?=$this->transEsc('top_facet_label', ['%%label%%' => $this->translate($cluster['label'])]) ?></strong><br /> - <? $limit = $topFacetSettings['rows'] * $topFacetSettings['cols']; ?> - <? foreach($cluster['list'] as $index => $thisFacet): ?> - <span class="facet <?=$index > $limit ? $moreClass : '' ?>"> - <? if ($thisFacet['isApplied']): - if (isset($thisFacet['specialType']) && $thisFacet['specialType'] == 'keyword') { - $removeLink = $this->currentPath().$results->getUrlQuery()->replaceTerm($thisFacet['value'], ''); - } else { - $removeLink = $this->currentPath().$results->getUrlQuery()->removeFacet($title, $thisFacet['value'], $thisFacet['operator']); - } ?> - <a href="<?=$removeLink ?>" class="applied"> - <?=$this->escapeHtml($thisFacet['displayText'])?> <i class="fa fa-check" aria-hidden="true"></i><!-- - --></a> - <? else: ?> - <a href="<?=$this->currentPath().$results->getUrlQuery()->addFacet($title, $thisFacet['value'], $thisFacet['operator'])?>"> - <?=$this->escapeHtml($thisFacet['displayText'])?><!-- - --></a> - <span class="badge"><?=$this->localizedNumber($thisFacet['count']) ?> - <? if ($allowExclude): ?> - <a href="<?=$this->currentPath().$results->getUrlQuery()->addFacet($title, $thisFacet['value'], 'NOT')?>" title="<?=$this->transEsc('exclude_facet')?>"><i class="fa fa-times" aria-hidden="true"></i></a> - <? endif; ?> - </span> - <? endif; ?> - </span> - <? /* More link */ ?> - <? if ($index == $limit): ?> - <span id="more-NarrowGroupHidden_<?=$this->escapeHtml($title)?>" class="narrow-toggle"> - <a href="#" onclick="moreFacets('NarrowGroupHidden_<?=$this->escapeHtml($title)?>'); return false;"> - <?=$this->transEsc('more') ?> ... - </a> + <!--Toggle for mobile collapse --> + <?php $topLabel = $this->transEsc('top_facet_label', ['%%label%%' => $this->translate($cluster['label'])]); ?> + <a class="top-title js-toggle visible-xs" data-toggle="collapse" data-target="#top_<?=$this->escapeHtml($title) ?>"><i class="fa fa-caret-right"></i> <?=$topLabel ?></a> + <!--Regular title for non-mobile --> + <strong class="top-title hidden-xs"><?=$topLabel ?></strong> + <div class="collapse in" id="top_<?=$this->escapeHtml($title) ?>"> + <?php $limit = $topFacetSettings['rows'] * $topFacetSettings['cols']; ?> + <?php foreach($cluster['list'] as $index => $thisFacet): ?> + <span class="facet <?=$index > $limit ? $moreClass : '' ?>"> + <?php if ($thisFacet['isApplied']): + if (isset($thisFacet['specialType']) && $thisFacet['specialType'] == 'keyword') { + $removeLink = $this->currentPath() . $results->getUrlQuery()->replaceTerm($thisFacet['value'], ''); + } else { + $removeLink = $this->currentPath() . $results->getUrlQuery()->removeFacet($title, $thisFacet['value'], $thisFacet['operator']); + } ?> + <a href="<?=$removeLink ?>" class="applied"> + <?=$this->escapeHtml($thisFacet['displayText'])?> <i class="fa fa-check" aria-hidden="true"></i><!-- + --></a> + <?php else: ?> + <a href="<?=$this->currentPath() . $results->getUrlQuery()->addFacet($title, $thisFacet['value'], $thisFacet['operator'])?>"> + <?=$this->escapeHtml($thisFacet['displayText'])?><!-- + --></a> + <span class="badge"><?=$this->localizedNumber($thisFacet['count']) ?> + <?php if ($allowExclude): ?> + <a href="<?=$this->currentPath() . $results->getUrlQuery()->addFacet($title, $thisFacet['value'], 'NOT')?>" title="<?=$this->transEsc('exclude_facet')?>"><i class="fa fa-times" aria-hidden="true"></i></a> + <?php endif; ?> + </span> + <?php endif; ?> </span> - <? endif; ?> - <? endforeach; ?> - <? if (count($cluster['list']) > $limit): ?> - <a class="narrow-toggle <?=$moreClass ?>" href="#" onclick="lessFacets('NarrowGroupHidden_<?=$title ?>'); return false;"> - <?=$this->transEsc('less') ?> ... - </a> - <? endif; ?> + <?php /* More link */ ?> + <?php if ($index == $limit): ?> + <span id="more-NarrowGroupHidden_<?=$this->escapeHtml($title)?>" class="narrow-toggle"> + <a href="#" onclick="moreFacets('NarrowGroupHidden_<?=$this->escapeHtml($title)?>'); return false;"> + <?=$this->transEsc('more') ?> ... + </a> + </span> + <?php endif; ?> + <?php endforeach; ?> + <?php if (count($cluster['list']) > $limit): ?> + <a class="narrow-toggle <?=$moreClass ?>" href="#" onclick="lessFacets('NarrowGroupHidden_<?=$title ?>'); return false;"> + <?=$this->transEsc('less') ?> ... + </a> + <?php endif; ?> + </div> </div> - <? endforeach; ?> -<? endif; ?> + <?php endforeach; ?> +<?php endif; ?> + +<?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, 'collapseTopFacets()', 'SET')?> diff --git a/themes/bootstrap3/templates/Recommend/VisualFacets.phtml b/themes/bootstrap3/templates/Recommend/VisualFacets.phtml index a250ef53528f863094dac343f5eceb2bb3dd7fcc..67ecbced35c47c73a34d3da9b62ae512fb61e63d 100644 --- a/themes/bootstrap3/templates/Recommend/VisualFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/VisualFacets.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->headScript()->appendFile("vendor/d3.min.js"); $visualFacetSet = $this->recommend->getPivotFacetSet(); @@ -12,7 +12,7 @@ $toplevelinfo['name'] = $toplevelfacet['value']; $toplevelinfo['field'] = $toplevelfacet['field']; $toplevelinfo['size'] = $toplevelfacet['count']; - $pivot = isset($toplevelfacet['pivot']) ? $toplevelfacet['pivot'] : []; + $pivot = $toplevelfacet['pivot'] ?? []; foreach($pivot as $secondlevelfacet) { $secondlevelinfo = []; $secondlevelinfo['name'] = $secondlevelfacet['value']; @@ -30,7 +30,7 @@ } ?> -<? if (isset($visualFacetSet)): ?> +<?php if (isset($visualFacetSet)): ?> <script type="text/javascript"> var pivotdata = <?=json_encode($visualFacetSet);?>; jQuery(document).ready(function(data) { @@ -263,4 +263,4 @@ function settitle() { </script> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/WebResults.phtml b/themes/bootstrap3/templates/Recommend/WebResults.phtml index e72f1f5ed1c226b4f9bc45146106f83148811cc4..ba7a75bfd324c5bfa088e73a2ff1d006b6075c04 100644 --- a/themes/bootstrap3/templates/Recommend/WebResults.phtml +++ b/themes/bootstrap3/templates/Recommend/WebResults.phtml @@ -1,23 +1,23 @@ -<? $searchObject = $this->recommend->getResults(); $results = $searchObject->getResults(); if (!empty($results)): ?> +<?php $searchObject = $this->recommend->getResults(); $results = $searchObject->getResults(); if (!empty($results)): ?> <div class="sidegroup"> <h4><?=$this->transEsc('Library Web Search')?></h4> <ul class="similar"> - <? foreach ($results as $driver): ?> + <?php foreach ($results as $driver): ?> <li> <a href="<?=$this->escapeHtmlAttr($driver->getUrl())?>" class="title"> <?=$this->record($driver)->getTitleHtml()?> </a> - <? $snippet = $driver->getHighlightedSnippet(); ?> - <? $summary = $driver->getSummary(); ?> - <? if (!empty($snippet)): ?> + <?php $snippet = $driver->getHighlightedSnippet(); ?> + <?php $summary = $driver->getSummary(); ?> + <?php if (!empty($snippet)): ?> <br /><?=$this->highlight($snippet['snippet'])?> - <? elseif (!empty($summary)): ?> + <?php elseif (!empty($summary)): ?> <br /><?=$this->escapeHtml($summary[0])?> - <? endif; ?> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> <p><a href="<?=$this->url($searchObject->getOptions()->getSearchAction()) . $searchObject->getUrlQuery()->setLimit($searchObject->getOptions()->getDefaultLimit())?>"><?=$this->transEsc('Find More')?>...</a></p> </div> -<? endif ?> +<?php endif ?> diff --git a/themes/bootstrap3/templates/Recommend/WorldCatIdentities.phtml b/themes/bootstrap3/templates/Recommend/WorldCatIdentities.phtml index 934d9b7e4832a53ed3d14d427c6f9337a9f8d66b..07938465a7b7b6e82d5f4ca952bae813925ef528 100644 --- a/themes/bootstrap3/templates/Recommend/WorldCatIdentities.phtml +++ b/themes/bootstrap3/templates/Recommend/WorldCatIdentities.phtml @@ -1,29 +1,30 @@ -<? $worldCatIdentities = $this->recommend->getIdentities(); if (!empty($worldCatIdentities)): ?> +<?php $worldCatIdentities = $this->recommend->getIdentities(); if (!empty($worldCatIdentities)): ?> <div> <h3><?=$this->transEsc('Authors Related to Your Search')?></h3> <dl> - <? $i = 0; foreach ($worldCatIdentities as $author => $subjects): ?> - <? if (++$i == 4): ?> - <dd id="moreWCIdents"><a href="#" onclick="moreFacets('WCIdents'); return false;"><?=$this->transEsc('more')?> ...</a></dd> - <span class="hidden" id="narrowGroupHidden_WCIdents"> - <? endif; ?> + <?php $i = 0; foreach ($worldCatIdentities as $author => $subjects): ?> + <?php if (++$i == 4): ?> + <dd id="more-WCIdents"><a href="#" onclick="moreFacets('WCIdents'); return false;"><?=$this->transEsc('more')?> ...</a></dd> + <span class="hidden WCIdents"> + <?php endif; ?> <dd> <a href="<?=$this->url('search-results')?>?lookfor=%22<?=urlencode($author)?>%22&type=Author"><?=$this->escapeHtml($author)?></a> - <? if (count($subjects) > 0): ?> + <?php if (count($subjects) > 0): ?> <dl> <dd><?=$this->transEsc('Related Subjects')?>:</dd> - <? $j = 0; foreach ($subjects as $subj): ?> - <? if (++$j == 3): ?> - <dd id="moreWCIdents<?=$i?>"><a href="#" onclick="moreFacets('WCIdents<?=$i?>'); return false;"><?=$this->transEsc('more')?> ...</a></dd> - <? endif; ?> + <?php $j = 0; foreach ($subjects as $subj): ?> + <?php if (++$j == 3): ?> + <dd id="more-WCIdents<?=$i?>"><a href="#" onclick="moreFacets('WCIdents<?=$i?>'); return false;"><?=$this->transEsc('more')?> ...</a></dd> + <span class="hidden WCIdents<?=$i?>"> + <?php endif; ?> <dd>• <a href="<?=$this->url('search-results')?>?lookfor=%22<?=urlencode($subj)?>%22&type=Subject"><?=$this->escapeHtml($subj)?></a></dd> - <? endforeach; ?> - <? if ($j > 2): ?><dd><a href="#" onclick="lessFacets('WCIdents<?=$i?>'); return false;"><?=$this->transEsc('less')?> ...</a></dd></span><? endif; ?> + <?php endforeach; ?> + <?php if ($j > 2): ?><dd><a href="#" onclick="lessFacets('WCIdents<?=$i?>'); return false;"><?=$this->transEsc('less')?> ...</a></dd></span><?php endif; ?> </dl> - <? endif; ?> + <?php endif; ?> </dd> - <? endforeach; ?> - <? if ($i > 3): ?><dd><a href="#" onclick="lessFacets('WCIdents'); return false;"><?=$this->transEsc('less')?> ...</a></dd></span><? endif; ?> + <?php endforeach; ?> + <?php if ($i > 3): ?><dd><a href="#" onclick="lessFacets('WCIdents'); return false;"><?=$this->transEsc('less')?> ...</a></dd></span><?php endif; ?> </dl> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewdata.phtml b/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewdata.phtml index 6b9cb54ea1fd589852cfd05b738943cbd293b6b7..a360769b9c818121e976210fbd3c411de2c731ff 100644 --- a/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewdata.phtml +++ b/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewdata.phtml @@ -1,6 +1,6 @@ -<? +<?php $previews = isset($this->config->Content->previews) - ? explode(',', $this->config->Content->previews) : array(); + ? explode(',', $this->config->Content->previews) : []; if (!empty($previews)) { $idClasses = $this->record($this->driver)->getPreviewIds(); diff --git a/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewlink.phtml b/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewlink.phtml index cdc2f821901c39fd0bcb8cb733b8dfdf68bf8bb3..d77cc7ad6ce26ec76f0c9c1bf2b6fafa04c29861 100644 --- a/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewlink.phtml +++ b/themes/bootstrap3/templates/RecordDriver/AbstractBase/previewlink.phtml @@ -1,6 +1,6 @@ -<? +<?php $previews = isset($this->config->Content->previews) - ? explode(',', $this->config->Content->previews) : array(); + ? explode(',', $this->config->Content->previews) : []; if (!empty($previews)) { $idClasses = $this->record($this->driver)->getPreviewIds(); // If we found at least one identifier, we can build the placeholder HTML: diff --git a/themes/bootstrap3/templates/RecordDriver/BrowZine/result-grid.phtml b/themes/bootstrap3/templates/RecordDriver/BrowZine/result-grid.phtml new file mode 100644 index 0000000000000000000000000000000000000000..55e2215dd74e23a87ec347778c0bb8b1e398f0e2 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/BrowZine/result-grid.phtml @@ -0,0 +1,19 @@ +<?php + $urls = $this->driver->getURLs(); + $url = isset($urls[0]) ? $urls[0]['url'] : null; + $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $url); +?> + +<div class="grid-result"> + <div class="grid-body"> + <div class="browzine"><?=$coverDetails['html']?></div> + <div> + <a class="title" href="<?=$this->escapeHtmlAttr($url)?>"> + <?=$this->record($this->driver)->getTitleHtml(80)?> + </a> + </div> + </div> + + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> +</div> + diff --git a/themes/bootstrap3/templates/RecordDriver/BrowZine/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/BrowZine/result-list.phtml index 2c891b4fbb02577b04a06890044fbc9e5419d764..51180c6b566c60ab2ba6bc2198921eed2bca9889 100644 --- a/themes/bootstrap3/templates/RecordDriver/BrowZine/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/BrowZine/result-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $urls = $this->driver->getURLs(); $url = isset($urls[0]) ? $urls[0]['url'] : null; $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $url); @@ -10,28 +10,28 @@ <div class="browzine media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> -<? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> <div class="media-body"> <div class="resultItemLine1"> - <? if ($url): ?><a href="<?=$this->escapeHtmlAttr($url)?>" class="title"><? endif; ?> + <?php if ($url): ?><a href="<?=$this->escapeHtmlAttr($url)?>" class="title"><?php endif; ?> <?=$this->record($this->driver)->getTitleHtml()?> - <? if ($url): ?></a><? endif; ?> + <?php if ($url): ?></a><?php endif; ?> </div> - <? if (($sjr = $this->driver->getSjrValue()) && ($issn = $this->driver->getCleanISSN())): ?> + <?php if (($sjr = $this->driver->getSjrValue()) && ($issn = $this->driver->getCleanISSN())): ?> <div class="resultItemLine2"> <a class="label label-default" href="http://www.scimagojr.com/journalsearch.php?q=<?=urlencode($issn)?>&tip=iss"> SJR: <?=$this->escapeHtml($sjr)?> </a> </div> - <? endif; ?> + <?php endif; ?> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/collection-info.phtml similarity index 53% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/collection-info.phtml index a88aeba6e005f11cfa3f4ca4dd74dd45d4d513d5..15eeae4ae7d8e2bc28765e94403af85f3165a485 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/collection-info.phtml @@ -1,55 +1,56 @@ -<? $this->headScript()->appendFile('collection_record.js'); ?> +<?php $this->headScript()->appendFile('collection_record.js'); ?> <div class="media"> - <? + <?php $QRCode = $this->record($this->driver)->getQRCode("core"); $coverDetails = $this->record($this->driver)->getCoverDetails('collection-info', 'medium', $this->record($this->driver)->getThumbnail('large')); $cover = $coverDetails['html']; $preview = $this->record($this->driver)->getPreviews(); ?> - <? if ($QRCode || $cover || $preview): ?> + <?php if ($QRCode || $cover || $preview): ?> <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> - <? /* Display thumbnail if appropriate: */ ?> - <? if($cover): ?> + <?php /* Display thumbnail if appropriate: */ ?> + <?php if($cover): ?> <?=$cover?> - <? endif; ?> + <?php endif; ?> - <? /* Display qrcode if appropriate: */ ?> - <? if($QRCode): ?> + <?php /* Display qrcode if appropriate: */ ?> + <?php if($QRCode): ?> <span class="hidden-xs"> <br/><img alt="<?=$this->transEsc('QR Code')?>" class="qrcode" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> </span> - <? endif; ?> + <?php endif; ?> - <? if ($preview): ?><?=$preview?><? endif; ?> + <?php if ($preview): ?><?=$preview?><?php endif; ?> </div> - <? endif; ?> + <?php endif; ?> <div class="media-body"> <h2><?=$this->escapeHtml($this->driver->getShortTitle() . ' ' . $this->driver->getSubtitle() . ' ' . $this->driver->getTitleSection())?></h2> - <? $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $summary[0] : false; ?> - <? if ($summary): ?> + <?php $summary = $this->driver->getSummary(); $summary = $summary[0] ?? false; ?> + <?php if ($summary): ?> <p><?=$this->escapeHtml($summary)?></p> - <? endif; ?> + <?php endif; ?> - <? /* Display the lists that this record is saved to */ ?> + <?php /* Display the lists that this record is saved to */ ?> <div class="savedLists" id="savedLists"> <strong><?=$this->transEsc("Saved in")?>:</strong> </div> <a id="moreInfoToggle" href="#" class="hidden"><?=$this->transEsc('more_info_toggle')?></a> - <?/* Display Main Details */?> - <? + <?php /* Display Main Details */ ?> + <?php $formatter = $this->recordDataFormatter(); $fields = $formatter->getData($driver, $formatter->getDefaults('collection-info')); ?> - <? if (!empty($fields)): ?> - <table id="collectionInfo" class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> - <? foreach ($fields as $key => $current): ?> - <tr><th><?=$this->transEsc($key)?>:</th><td><?=$current['value']?></td></tr> - <? endforeach; ?> + <?php if (!empty($fields)): ?> + <table id="collectionInfo" class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> + <?php foreach ($fields as $current): ?> + <tr><th><?=$this->transEsc($current['label'])?>:</th><td><?=$current['value']?></td></tr> + <?php endforeach; ?> </table> - <? endif; ?> - <?/* End Main Details */?> + <?php endif; ?> + <?php /* End Main Details */ ?> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-record.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/collection-record.phtml similarity index 56% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-record.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/collection-record.phtml index 64ce05a7b5dcbde529a8202366bbc2d785f7ad27..1b3fb042a9046c16e809c4ce1192d28149c16f0b 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-record.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/collection-record.phtml @@ -1,14 +1,15 @@ <h2><?=$this->escapeHtml($this->driver->getShortTitle() . ' ' . $this->driver->getSubtitle() . ' ' . $this->driver->getTitleSection())?></h2> <a href="<?=$this->recordLink()->getUrl($this->driver)?>"><?=$this->transEsc('View Full ' . ($this->driver->isCollection() ? 'Collection' : 'Record'))?></a> -<? +<?php $formatter = $this->recordDataFormatter(); $fields = $formatter->getData($driver, $formatter->getDefaults('collection-record')); ?> -<? if (!empty($fields)): ?> - <table class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> - <? foreach ($fields as $key => $current): ?> - <tr><th><?=$this->transEsc($key)?>:</th><td><?=$current['value']?></td></tr> - <? endforeach; ?> +<?php if (!empty($fields)): ?> + <table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> + <?php foreach ($fields as $current): ?> + <tr><th><?=$this->transEsc($current['label'])?>:</th><td><?=$current['value']?></td></tr> + <?php endforeach; ?> </table> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core-qrcode.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core-qrcode.phtml similarity index 100% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/core-qrcode.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/core-qrcode.phtml diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml similarity index 56% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml index bb5d1cf94611a36c68f80699912ef34632265cbe..d9aa92a1f22e205815ea9ffea168f3736580b287 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml @@ -1,67 +1,68 @@ <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> - <? + <?php $QRCode = $this->record($this->driver)->getQRCode("core"); $coverDetails = $this->record($this->driver)->getCoverDetails('core', 'medium', $this->record($this->driver)->getThumbnail('large')); $cover = $coverDetails['html']; $preview = $this->record($this->driver)->getPreviews(); ?> - <? if ($QRCode || $cover || $preview): ?> + <?php if ($QRCode || $cover || $preview): ?> <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?> img-col"> - <? /* Display thumbnail if appropriate: */ ?> - <? if($cover): ?> + <?php /* Display thumbnail if appropriate: */ ?> + <?php if($cover): ?> <?=$cover?> - <? endif; ?> + <?php endif; ?> - <? /* Display qrcode if appropriate: */ ?> - <? if($QRCode): ?> + <?php /* Display qrcode if appropriate: */ ?> + <?php if($QRCode): ?> <span class="hidden-xs"> <br/><img alt="<?=$this->transEsc('QR Code')?>" class="qrcode" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> </span> - <? endif; ?> + <?php endif; ?> - <? // if you have a preview tab but want to move or remove the preview link + <?php // if you have a preview tab but want to move or remove the preview link // from this area of the record view, this can be split into // getPreviewData() (should stay here) and // getPreviewLink() (can go in your desired tab) ?> - <? if ($preview): ?> + <?php if ($preview): ?> <div class="record-previews"> <?=$preview?> </div> - <? endif; ?> + <?php endif; ?> </div> - <? endif; ?> + <?php endif; ?> <div class="media-body"> <h3 property="name"><?=$this->escapeHtml($this->driver->getShortTitle() . ' ' . $this->driver->getSubtitle() . ' ' . $this->driver->getTitleSection())?></h3> - <? $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $this->escapeHtml($summary[0]) : false; ?> - <? if ($summary): ?> + <?php $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $this->escapeHtml($summary[0]) : false; ?> + <?php if ($summary): ?> <p><?=$this->truncate($summary, 300)?></p> - <? if(strlen($summary) > 300): ?> + <?php if(strlen($summary) > 300): ?> <p><a href='<?=$this->recordLink()->getTabUrl($this->driver, 'Description')?>#tabnav'><?=$this->transEsc('Full description')?></a></p> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> - <? /* Display the lists that this record is saved to */ ?> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php /* Display the lists that this record is saved to */ ?> <div class="savedLists"> <strong><?=$this->transEsc("Saved in")?>:</strong> </div> - <? endif; ?> + <?php endif; ?> - <?/* Display Main Details */?> - <? + <?php /* Display Main Details */ ?> + <?php $formatter = $this->recordDataFormatter(); $coreFields = $formatter->getData($driver, $formatter->getDefaults('core')); ?> - <? if (!empty($coreFields)): ?> - <table class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> - <? foreach ($coreFields as $key => $current): ?> - <tr><th><?=$this->transEsc($key)?>:</th><td><?=$current['value']?></td></tr> - <? endforeach; ?> + <?php if (!empty($coreFields)): ?> + <table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> + <?php foreach ($coreFields as $current): ?> + <tr><th><?=$this->transEsc($current['label'])?>:</th><td><?=$current['value']?></td></tr> + <?php endforeach; ?> </table> - <? endif; ?> - <?/* End Main Details */?> + <?php endif; ?> + <?php /* End Main Details */ ?> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-allRecordLinks.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-allRecordLinks.phtml new file mode 100644 index 0000000000000000000000000000000000000000..b81d763c32dfa50e0a43fc054a0c76c2d563b7a9 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-allRecordLinks.phtml @@ -0,0 +1,11 @@ +<?php foreach ($data as $recordLink): ?> + <?=$this->transEsc($recordLink['title'])?>: + <a href="<?=$this->recordLink()->related($recordLink['link'], true, $this->driver->getSourceIdentifier())?>"><?=$this->escapeHtml($recordLink['value'])?></a><br /> +<?php endforeach; ?> +<?php /* if we have record links, display relevant explanatory notes */ + $related = $this->driver->getRelationshipNotes(); + if (!empty($related)): ?> + <?php foreach ($related as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <?php endforeach; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-allSubjectHeadings.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-allSubjectHeadings.phtml new file mode 100644 index 0000000000000000000000000000000000000000..0b2fd5960fa2c6f75926fe562f6cabbfdcd8d1a9 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-allSubjectHeadings.phtml @@ -0,0 +1,11 @@ +<?php foreach ($data as $field): ?> + <div class="subject-line" property="keywords"> + <?php $subject = ''; ?> + <?php if(count($field) == 1) $field = explode('--', $field[0]); ?> + <?php $i = 0; foreach ($field as $subfield): ?> + <?=($i++ == 0) ? '' : ' > '?> + <?php $subject = trim($subject . ' ' . $subfield); ?> + <a title="<?=$this->escapeHtmlAttr($subject)?>" href="<?=$this->record($this->driver)->getLink('subject', $subject)?>" rel="nofollow"><?=trim($this->escapeHtml($subfield))?></a> + <?php endforeach; ?> + </div> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-authorNotes.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-authorNotes.phtml new file mode 100644 index 0000000000000000000000000000000000000000..13ece0146629441a2c52677de55f7a09e1f94e96 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-authorNotes.phtml @@ -0,0 +1,8 @@ +<?php $isbn = $this->driver->getCleanISBN(); ?> +<?php $authorNotes = empty($isbn) ? [] : $this->authorNotes($isbn); if (!empty($authorNotes)): ?> + <?php foreach ($authorNotes as $provider => $list): ?> + <?php foreach ($list as $field): ?> + <?=$field['Content']?><br /> + <?php endforeach; ?> + <?php endforeach; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-authors.phtml similarity index 84% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-authors.phtml index bbc3a63584b902dacbc53ce54f71de7923e3f243..5a30567231ded60a56bdf1a808663558577be4e8 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-authors.phtml @@ -1,4 +1,4 @@ -<? +<?php $formatProperty = function ($datafield, $name, $label) { if (count($datafield) == 0) { return ''; @@ -11,20 +11,20 @@ $formatProperty = function ($datafield, $name, $label) { }; $formattedAuthors = []; ?> -<? if (!empty($data[$type])): ?> - <? foreach ($data[$type] as $author => $dataFields): ?> - <? ob_start(); ?> +<?php if (!empty($data[$type])): ?> + <?php foreach ($data[$type] as $author => $dataFields): ?> + <?php ob_start(); ?> <span class="author-data" property="<?=$this->escapeHtml($schemaLabel)?>"> <a href="<?=$this->record($this->driver)->getLink('author', $author)?>"> <?=$this->escapeHtml($author)?> </a> - <? + <?php // Display additional data using the appropriate translation prefix // (for example, to render author roles correctly): if (!empty($requiredDataFields)) { foreach ($requiredDataFields as $field) { $name = $field['name']; - $prefix = isset($field['prefix']) ? $field['prefix'] : ''; + $prefix = $field['prefix'] ?? ''; if (isset($dataFields[$name])) { echo $formatProperty($dataFields[$name], $name, $prefix); } @@ -32,11 +32,11 @@ $formattedAuthors = []; } ?> </span> - <? + <?php // Strip whitespace before close tags to avoid spaces in front of commas: $formattedAuthors[] = trim(preg_replace('/\s+<\//', '</', ob_get_contents())); ob_end_clean(); ?> - <? endforeach; ?> -<? endif; ?> + <?php endforeach; ?> +<?php endif; ?> <?=implode(', ', $formattedAuthors)?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-childRecords.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-childRecords.phtml new file mode 100644 index 0000000000000000000000000000000000000000..af4d6b1fd60c0b1b79dc165511f232119c962eec --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-childRecords.phtml @@ -0,0 +1 @@ +<a href="<?=$this->recordLink()->getChildRecordSearchUrl($this->driver)?>"><?=$this->transEsc('child_record_count', ['%%count%%' => $data])?></a> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-containerTitle.phtml similarity index 98% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-containerTitle.phtml index ff678db3e458bc3ac9614008e8c05996f67df079..368bbd5ecd0f19acd83d58c66ea9d5c971f2337c 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-containerTitle.phtml @@ -1,4 +1,4 @@ -<? +<?php $containerSource = $this->driver->getSourceIdentifier(); $containerID = $this->driver->getContainerRecordID(); $ref = $this->driver->getContainerReference(); diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-onlineAccess.phtml similarity index 67% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-onlineAccess.phtml index 2ddb8a1bddf8262010ebed547753f9ff47ba493d..4a545667a7a334f39747fc02532589fb69523ce8 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-onlineAccess.phtml @@ -1,14 +1,14 @@ -<? +<?php $openUrl = $this->openUrl($this->driver, 'record'); $openUrlActive = $openUrl->isActive(); // Account for replace_other_urls setting $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); ?> -<? if (!empty($urls) || $openUrlActive): ?> - <? foreach ($urls as $current): ?> +<?php if (!empty($urls) || $openUrlActive): ?> + <?php foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/> - <? endforeach; ?> - <? if ($openUrlActive): ?> + <?php endforeach; ?> + <?php if ($openUrlActive): ?> <?=$openUrl->renderTemplate()?><br/> - <? endif; ?> -<? endif; ?> + <?php endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml new file mode 100644 index 0000000000000000000000000000000000000000..782054e0da9d041d16c1e985a6cb7e7ab0f71741 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml @@ -0,0 +1,14 @@ +<?php foreach ($data as $field): ?> + <span property="publisher" typeof="Organization"> + <?php $pubPlace = $field->getPlace(); if (!empty($pubPlace)): ?> + <span property="location"><?=$this->escapeHtml($pubPlace)?></span> + <?php endif; ?> + <?php $pubName = $field->getName(); if (!empty($pubName)): ?> + <span property="name"><?=$this->escapeHtml($pubName)?></span> + <?php endif; ?> + </span> + <?php $pubDate = $field->getDate(); if (!empty($pubDate)): ?> + <span property="publicationDate"><?=$this->escapeHtml($pubDate)?></span> + <?php endif; ?> + <br/> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-series.phtml similarity index 55% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-series.phtml index b71c03ebc2fa09a215c236b743e2816f2b65a048..1d123855397d3f9ca4c8de0007eedf1fbf1eaf2f 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-series.phtml @@ -1,16 +1,16 @@ -<? foreach ($data as $field): ?> - <?/* Depending on the record driver, $field may either be an array with +<?php foreach ($data as $field): ?> + <?php /* Depending on the record driver, $field may either be an array with "name" and "number" keys or a flat string containing only the series - name. We should account for both cases to maximize compatibility. */?> - <? if (is_array($field)): ?> - <? if (!empty($field['name'])): ?> + name. We should account for both cases to maximize compatibility. */ ?> + <?php if (is_array($field)): ?> + <?php if (!empty($field['name'])): ?> <a href="<?=$this->record($this->driver)->getLink('series', $field['name'])?>"><?=$this->escapeHtml($field['name'])?></a> - <? if (!empty($field['number'])): ?> + <?php if (!empty($field['number'])): ?> <?=$this->escapeHtml($field['number'])?> - <? endif; ?> + <?php endif; ?> <br/> - <? endif; ?> - <? else: ?> + <?php endif; ?> + <?php else: ?> <a href="<?=$this->record($this->driver)->getLink('series', $field)?>"><?=$this->escapeHtml($field)?></a><br/> - <? endif; ?> -<? endforeach; ?> + <?php endif; ?> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-summary.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-summary.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4d34b160042725b3e235c9dbdfe7eaf48487ee8c --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-summary.phtml @@ -0,0 +1,10 @@ +<?php foreach ($this->driver->getSummary() as $summary): ?> + <?=$this->escapeHtml($summary) ?><br /> +<?php endforeach; ?> +<?php $isbn = $this->driver->getCleanISBN(); ?> +<?php foreach ($this->summaries($isbn) as $provider => $content): ?> + <?php foreach ($content as $summary): ?> + <?php $htmlContent = substr($summary, 0, 1) == '<' && substr($summary, -1) == '>'; ?> + <?=$htmlContent ? $summary : ($this->escapeHtml($summary) . '<br />')?> + <?php endforeach; ?> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-tags.phtml similarity index 67% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-tags.phtml index 285e4ea0d319845dc9002386936b4e7b58af630e..86e870ae58cdb646fc391890381a9da20f5a784a 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-tags.phtml @@ -1,4 +1,4 @@ -<? +<?php if($loggedin = $this->auth()->isLoggedIn()) { $user_id = $loggedin->id; $loggedin = true; @@ -6,10 +6,10 @@ $user_id = false; } ?> -<? if ($this->usertags()->getMode() !== 'disabled'): ?> - <? $tagList = $this->driver->getTags(null, null, 'count', $user_id); ?> +<?php if ($this->usertags()->getMode() !== 'disabled'): ?> + <?php $tagList = $this->driver->getTags(null, null, 'count', $user_id); ?> <a class="tag-record btn btn-link pull-right flip" href="<?=$this->recordLink()->getActionUrl($this->driver, 'AddTag')?>" data-lightbox> <i class="fa fa-plus" aria-hidden="true"></i> <?=$this->transEsc('Add Tag')?> </a> - <?=$this->context($this)->renderInContext('record/taglist', array('tagList'=>$tagList, 'loggedin'=>$loggedin)) ?> -<? endif; ?> + <?=$this->context($this)->renderInContext('record/taglist', ['tagList' => $tagList, 'loggedin' => $loggedin]) ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/format-class.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/format-class.phtml similarity index 100% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/format-class.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/format-class.phtml diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/format-list.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/format-list.phtml similarity index 60% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/format-list.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/format-list.phtml index 2b9a07e77b12d61c49a7566606b8283af9abc74d..086cd3fab1c7eb38f227a2814d72ac31ca17b7b4 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/format-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/format-list.phtml @@ -1,3 +1,3 @@ -<? foreach ($this->driver->getFormats() as $format): ?> +<?php foreach ($this->driver->getFormats() as $format): ?> <span class="format <?=$this->record($this->driver)->getFormatClass($format) ?>"><?=$this->transEsc($format) ?></span> -<? endforeach; ?> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-author.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-author.phtml new file mode 100644 index 0000000000000000000000000000000000000000..de7d0c87204b0eb30c0ecd74f74f4a02c736db4c --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-author.phtml @@ -0,0 +1,6 @@ +<?php $searchRoute = $this->searchOptions($this->driver->getSourceIdentifier())->getSearchAction(); ?> +<?php if ($searchRoute === 'search-results'): // override search-results with author module ?> + <?=$this->url('author-home')?>?author=<?=urlencode($this->lookfor)?> +<?php else: ?> + <?=$this->url($searchRoute)?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Author +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-journaltitle.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-journaltitle.phtml new file mode 100644 index 0000000000000000000000000000000000000000..d238071508ca905cf894e78e820be500d550bd95 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-journaltitle.phtml @@ -0,0 +1,2 @@ +<?php $searchRoute = $this->searchOptions($this->driver->getSourceIdentifier())->getSearchAction(); ?> +<?=$this->url($searchRoute)?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=JournalTitle diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-series.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-series.phtml new file mode 100644 index 0000000000000000000000000000000000000000..f9aefd443a787b1cc35a373348a00636271fc075 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-series.phtml @@ -0,0 +1,2 @@ +<?php $searchRoute = $this->searchOptions($this->driver->getSourceIdentifier())->getSearchAction(); ?> +<?=$this->url($searchRoute)?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Series diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-subject.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-subject.phtml new file mode 100644 index 0000000000000000000000000000000000000000..187e19bd3eb765159f4b5bac14dcfb5edcd69e2b --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-subject.phtml @@ -0,0 +1,2 @@ +<?php $searchRoute = $this->searchOptions($this->driver->getSourceIdentifier())->getSearchAction(); ?> +<?=$this->url($searchRoute)?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Subject diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-title.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-title.phtml new file mode 100644 index 0000000000000000000000000000000000000000..591cc334703a2df8c10a47900cfce9fab075a3d6 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-title.phtml @@ -0,0 +1,2 @@ +<?php $searchRoute = $this->searchOptions($this->driver->getSourceIdentifier())->getSearchAction(); ?> +<?=$this->url($searchRoute)?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Title diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml similarity index 61% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml index c4ccf7d78043c5570872edf9ca6e22a2e54bcbce..323229a2109b3ac78389c5c7ff45e71042a76ff4 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up some convenience variables: $id = $this->driver->getUniqueId(); $source = $this->driver->getSourceIdentifier(); @@ -19,61 +19,61 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> -<? endif; ?> -<div class="result<? if($this->driver->supportsAjaxStatus()): ?> ajaxItem<? endif ?>"> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> +<div class="result<?php if($this->driver->supportsAjaxStatus()): ?> ajaxItem<?php endif ?>"> <input type="hidden" value="<?=$this->escapeHtmlAttr($id) ?>" class="hiddenId"/> <input type="hidden" value="<?=$this->escapeHtmlAttr($source) ?>" class="hiddenSource"/> <?=$this->record($this->driver)->getCheckbox()?> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif; ?> + <?php endif; ?> <div class="media-body"> <div class="result-body"> <div class="resultItemLine1"> - <? $missing = $this->driver instanceof \VuFind\RecordDriver\Missing; ?> - <? if (!$missing): ?><a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="getFull" data-view="<?=$this->params->getOptions()->getListViewOption() ?>"><? endif; ?> + <?php $missing = $this->driver instanceof \VuFind\RecordDriver\Missing; ?> + <?php if (!$missing): ?><a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="getFull" data-view="<?=$this->params->getOptions()->getListViewOption() ?>"><?php endif; ?> <span class="title"><?=$this->record($this->driver)->getTitleHtml()?></span> - <? if (!$missing): ?></a><? endif; ?> + <?php if (!$missing): ?></a><?php endif; ?> </div> <div class="resultItemLine2"> - <? if($this->driver->isCollection()): ?> - <?=implode('<br>', array_map(array($this, 'escapeHtml'), $this->driver->getSummary())); ?> - <? else: ?> - <? $summAuthors = $this->driver->getPrimaryAuthors(); if (!empty($summAuthors)): ?> + <?php if($this->driver->isCollection()): ?> + <?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?> + <?php else: ?> + <?php $summAuthors = $this->driver->getPrimaryAuthors(); if (!empty($summAuthors)): ?> <?=$this->transEsc('by')?> - <? $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> + <?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> <a href="<?=$this->record($this->driver)->getLink('author', $summAuthor)?>"><?=$this->escapeHtml($summAuthor)?></a><?=($i + 1 < $authorCount ? ';' : '') ?> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> - <? if (!empty($journalTitle)): ?> + <?php $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> + <?php if (!empty($journalTitle)): ?> <?=!empty($summAuthor) ? '<br/>' : ''?> - <?=/* TODO: handle highlighting more elegantly here */ $this->transEsc('Published in') . ' <a href="' . $this->record($this->driver)->getLink('journaltitle', str_replace(array('{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'), '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?> + <?=/* TODO: handle highlighting more elegantly here */ $this->transEsc('Published in') . ' <a href="' . $this->record($this->driver)->getLink('journaltitle', str_replace(['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?> <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate[0]) . ')' : ''?> - <? elseif (!empty($summDate)): ?> + <?php elseif (!empty($summDate)): ?> <?=!empty($summAuthor) ? '<br/>' : ''?> <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate[0])?> - <? endif; ?> - <? $summInCollection = $this->driver->getContainingCollections(); if (false && !empty($summInCollection)): ?> - <? foreach ($summInCollection as $collId => $collText): ?> + <?php endif; ?> + <?php $summInCollection = $this->driver->getContainingCollections(); if (false && !empty($summInCollection)): ?> + <?php foreach ($summInCollection as $collId => $collText): ?> <div> <b><?=$this->transEsc("in_collection_label")?></b> - <a class="collectionLinkText" href="<?=$this->url('collection', array('id' => $collId))?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> <?=$this->escapeHtml($collText)?> </a> </div> - <? endforeach; ?> - <? endif; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> </div> <div class="last"> - <? if(!$this->driver->isCollection()) { + <?php if(!$this->driver->isCollection()) { if ($snippet = $this->driver->getHighlightedSnippet()) { if (!empty($snippet['caption'])) { echo '<strong>' . $this->transEsc($snippet['caption']) . ':</strong> '; @@ -84,37 +84,37 @@ } } ?> - <? $listTags = ($this->usertags()->getMode() !== 'disabled') ? $this->driver->getTags( + <?php $listTags = ($this->usertags()->getMode() !== 'disabled') ? $this->driver->getTags( null === $list_id ? true : $list_id, // get tags for all lists if no single list is selected $user_id, 'tag' - ) : array(); + ) : []; ?> - <? if (count($listTags) > 0): ?> + <?php if (count($listTags) > 0): ?> <strong><?=$this->transEsc('Your Tags')?>:</strong> - <? foreach ($listTags as $tag): ?> + <?php foreach ($listTags as $tag): ?> <a href="<?=$this->currentPath() . $results->getUrlQuery()->addFacet('tags', $tag->tag)?>"><?=$this->escapeHtml($tag->tag)?></a> - <? endforeach; ?> + <?php endforeach; ?> <br/> - <? endif; ?> - <? $listNotes = $this->driver->getListNotes($list_id, $user_id); ?> - <? if (count($listNotes) > 0): ?> + <?php endif; ?> + <?php $listNotes = $this->driver->getListNotes($list_id, $user_id); ?> + <?php if (count($listNotes) > 0): ?> <strong><?=$this->transEsc('Notes')?>:</strong> - <? if (count($listNotes) > 1): ?><br/><? endif; ?> - <? foreach ($listNotes as $note): ?> + <?php if (count($listNotes) > 1): ?><br/><?php endif; ?> + <?php foreach ($listNotes as $note): ?> <?=$this->escapeHtml($note)?><br/> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? if (count($this->lists) > 0): ?> + <?php if (count($this->lists) > 0): ?> <strong><?=$this->transEsc('Saved in')?>:</strong> - <? $i=0;foreach($this->lists as $current): ?> - <a href="<?=$this->url('userList', array('id' => $current->id))?>"><?=$this->escapeHtml($current->title)?></a><? if($i++ < count($this->lists)-1): ?>,<? endif; ?> - <? endforeach; ?> + <?php $i = 0;foreach($this->lists as $current): ?> + <a href="<?=$this->url('userList', ['id' => $current->id])?>"><?=$this->escapeHtml($current->title)?></a><?php if($i++ < count($this->lists) - 1): ?>,<?php endif; ?> + <?php endforeach; ?> <br/> - <? endif; ?> + <?php endif; ?> <div class="callnumAndLocation ajax-availability hidden"> - <? if ($this->driver->supportsAjaxStatus()): ?> + <?php if ($this->driver->supportsAjaxStatus()): ?> <strong class="hideIfDetailed"><?=$this->transEsc('Call Number')?>:</strong> <span class="callnumber ajax-availability hidden"> <?=$this->transEsc('Loading')?>...<br/> @@ -124,14 +124,14 @@ <?=$this->transEsc('Loading')?>... </span> <div class="locationDetails"></div> - <? else: ?> - <? $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> + <?php else: ?> + <?php $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </div> - <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), + <?php /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), but even if we don't plan to display the link, we still want to get the $openUrl value for use in generating a COinS (Z3988) tag -- see bottom of file. */ @@ -142,39 +142,39 @@ if ($openUrlActive || !empty($urls)): ?> - <? if ($openUrlActive): ?> + <?php if ($openUrlActive): ?> <br/> <?=$openUrl->renderTemplate()?> - <? endif;?> + <?php endif;?> - <? if (!is_array($urls)) { $urls = array(); } + <?php if (!is_array($urls)) { $urls = []; } if(!$this->driver->isCollection()): foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a> - <? endforeach; ?> - <? endif; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> <br/> <?=$this->record($this->driver)->getFormatList() ?> - <? if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> + <?php if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> <span class="status ajax-availability hidden"><?=$this->transEsc('Loading')?>...</span> <br/><br/> - <? endif; ?> + <?php endif; ?> <?=$this->record($this->driver)->getPreviews()?> </div> </div> <div class="result-links hidden-print"> - <i class="fa fa-fw fa-edit" aria-hidden="true"></i> <a href="<?=$this->url('myresearch-edit')?>?id=<?=urlencode($id)?>&source=<?=urlencode($source)?><? if (!is_null($list_id)):?>&list_id=<?=urlencode($list_id)?><? endif; ?>" class="edit tool"><?=$this->transEsc('Edit')?></a><br/> - <? /* Use a different delete URL if we're removing from a specific list or the overall favorites: */ + <i class="fa fa-fw fa-edit" aria-hidden="true"></i> <a href="<?=$this->url('myresearch-edit')?>?id=<?=urlencode($id)?>&source=<?=urlencode($source)?><?php if (null !== $list_id):?>&list_id=<?=urlencode($list_id)?><?php endif; ?>" class="edit tool"><?=$this->transEsc('Edit')?></a><br/> + <?php /* Use a different delete URL if we're removing from a specific list or the overall favorites: */ $deleteUrl = null === $list_id ? $this->url('myresearch-favorites') - : $this->url('userList', array('id' => $list_id)); + : $this->url('userList', ['id' => $list_id]); $deleteUrlGet = $deleteUrl . '?delete=' . urlencode($id) . '&source=' . urlencode($source); - $dLabel = 'delete-label-' . preg_replace('[\W]','-',$id); + $dLabel = 'delete-label-' . preg_replace('[\W]', '-', $id); ?> <div class="dropdown"> <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> <a class="dropdown-toggle" id="<?=$dLabel ?>" role="button" data-toggle="dropdown" href="<?=$deleteUrlGet ?>"> @@ -186,11 +186,11 @@ </ul> </div> - <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?> + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> </div> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif; ?> + <?php endif; ?> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-grid.phtml similarity index 74% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-grid.phtml index 2cbe7bb326ab6a17014869d8b391827c667497c0..bb091ff369f061e43bcbc47b685846ff549517b5 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-grid.phtml @@ -1,4 +1,4 @@ -<? +<?php /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), but even if we don't plan to display the link, we still want to get the $openUrl value for use in generating a COinS (Z3988) tag -- see bottom of file. @@ -11,37 +11,37 @@ $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); <div class="grid-result<?=$this->driver->supportsAjaxStatus()?' ajaxItem':''?>"> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueID())?>" class="hiddenId" /> - <? if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> + <?php if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> <label class="grid-checkbox"> <?=$this->record($this->driver)->getCheckbox('', 'search-cart-form') ?> </label> - <? endif; ?> + <?php endif; ?> <div class="grid-body"> <?=$this->record($this->driver)->getCover('result-grid', 'small', $this->recordLink()->getUrl($this->driver)); ?> - <? if (!$openUrlActive && empty($urls)): ?> - <? if ($this->driver->supportsAjaxStatus()): ?> + <?php if (!$openUrlActive && empty($urls)): ?> + <?php if ($this->driver->supportsAjaxStatus()): ?> <div class="result-formats status ajax-availability hidden"> <span class="label label-default"><?=$this->transEsc('Loading')?>...</span> </div> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> <div> <a class="title" href="<?=$this->recordLink()->getUrl($this->driver)?>"> <?=$this->record($this->driver)->getTitleHtml(80)?> </a> - <? if ($openUrlActive || !empty($urls)): ?> + <?php if ($openUrlActive || !empty($urls)): ?> <br/><br/> - <? if ($openUrlActive): ?> + <?php if ($openUrlActive): ?> <?=$openUrl->renderTemplate()?><br /> - <? endif; ?> - <? if (!is_array($urls)) $urls = array(); foreach ($urls as $current): ?> + <?php endif; ?> + <?php if (!is_array($urls)) $urls = []; foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a> <br/> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> </div> - <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?> + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml similarity index 62% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml index a28191deb95830732978a5322084a4f514372724..3f8f07cc084a0f2826baca627e8bc53a6f39fb46 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $this->recordLink()->getUrl($this->driver)); $cover = $coverDetails['html']; $thumbnail = false; @@ -8,15 +8,15 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> -<? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueID())?>" class="hiddenId" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" /> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> <div class="media-body"> <div class="result-body"> <div> @@ -26,79 +26,79 @@ </div> <div> - <? if($this->driver->isCollection()): ?> - <?=implode('<br>', array_map(array($this, 'escapeHtml'), $this->driver->getSummary())); ?> - <? else: ?> - <? $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> + <?php if($this->driver->isCollection()): ?> + <?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?> + <?php else: ?> + <?php $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> <?=$this->transEsc('by')?> - <? $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> + <?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> <a href="<?=$this->record($this->driver)->getLink('author', $this->highlight($summAuthor, null, true, false))?>"><?=$this->highlight($summAuthor)?></a><?=$i + 1 < $authorCount ? ',' : ''?> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> - <? if (!empty($journalTitle)): ?> + <?php $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> + <?php if (!empty($journalTitle)): ?> <?=!empty($summAuthor) ? '<br />' : ''?> <?=$this->transEsc('Published in')?> - <? $containerSource = $this->driver->getSourceIdentifier(); ?> - <? $containerID = $this->driver->getContainerRecordID(); ?> - <? /* TODO: handle highlighting more elegantly here: */?> - <a href="<?=($containerID ? $this->recordLink()->getUrl("$containerSource|$containerID") : $this->record($this->driver)->getLink('journaltitle', str_replace(array('{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'), '', $journalTitle)))?>"><?=$this->highlight($journalTitle) ?></a> + <?php $containerSource = $this->driver->getSourceIdentifier(); ?> + <?php $containerID = $this->driver->getContainerRecordID(); ?> + <?php /* TODO: handle highlighting more elegantly here: */?> + <a href="<?=($containerID ? $this->recordLink()->getUrl("$containerSource|$containerID") : $this->record($this->driver)->getLink('journaltitle', str_replace(['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $journalTitle)))?>"><?=$this->highlight($journalTitle) ?></a> <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate[0]) . ')' : ''?> - <? elseif (!empty($summDate)): ?> + <?php elseif (!empty($summDate)): ?> <?=!empty($summAuthor) ? '<br />' : ''?> <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate[0])?> - <? endif; ?> - <? $summInCollection = $this->driver->getContainingCollections(); if (!empty($summInCollection)): ?> - <? foreach ($summInCollection as $collId => $collText): ?> + <?php endif; ?> + <?php $summInCollection = $this->driver->getContainingCollections(); if (!empty($summInCollection)): ?> + <?php foreach ($summInCollection as $collId => $collText): ?> <div> <b><?=$this->transEsc("in_collection_label")?></b> - <a class="collectionLinkText" href="<?=$this->url('collection', array('id' => $collId))?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> <?=$this->escapeHtml($collText)?> </a> </div> - <? endforeach; ?> - <? endif; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> </div> - <? if(!$this->driver->isCollection()): ?> - <? if ($snippet = $this->driver->getHighlightedSnippet()): ?> - <? if (!empty($snippet['caption'])): ?> + <?php if(!$this->driver->isCollection()): ?> + <?php if ($snippet = $this->driver->getHighlightedSnippet()): ?> + <?php if (!empty($snippet['caption'])): ?> <strong><?=$this->transEsc($snippet['caption']) ?>:</strong> '; - <? endif; ?> - <? if (!empty($snippet['snippet'])): ?> + <?php endif; ?> + <?php if (!empty($snippet['snippet'])): ?> <span class="quotestart">“</span>...<?=$this->highlight($snippet['snippet']) ?>...<span class="quoteend">”</span><br/> - <? endif; ?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> - <? + <?php /* Display information on duplicate records if available */ if ($dedupData = $this->driver->getDedupData()): ?> <div class="dedupInformation"> - <? + <?php $i = 0; foreach ($dedupData as $source => $current) { if (++$i == 1) { - ?><span class="currentSource"><a href="<?=$this->recordLink()->getUrl($this->driver)?>"><?=$this->transEsc("source_$source", array(), $source)?></a></span><? + ?><span class="currentSource"><a href="<?=$this->recordLink()->getUrl($this->driver)?>"><?=$this->transEsc("source_$source", [], $source)?></a></span><?php } else { if ($i == 2) { - ?> <span class="otherSources">(<?=$this->transEsc('Other Sources')?>: <? + ?> <span class="otherSources">(<?=$this->transEsc('Other Sources')?>: <?php } else { - ?>, <? + ?>, <?php } - ?><a href="<?=$this->recordLink()->getUrl($current['id'])?>"><?=$this->transEsc("source_$source", array(), $source)?></a><? + ?><a href="<?=$this->recordLink()->getUrl($current['id'])?>"><?=$this->transEsc("source_$source", [], $source)?></a><?php } } if ($i > 1) { - ?>)</span><? + ?>)</span><?php }?> </div> - <? endif; ?> + <?php endif; ?> <div class="callnumAndLocation ajax-availability hidden"> - <? if ($this->driver->supportsAjaxStatus()): ?> + <?php if ($this->driver->supportsAjaxStatus()): ?> <strong class="hideIfDetailed"><?=$this->transEsc('Call Number')?>:</strong> <span class="callnumber ajax-availability hidden"> <?=$this->transEsc('Loading')?>...<br/> @@ -108,14 +108,14 @@ <?=$this->transEsc('Loading')?>... </span> <div class="locationDetails"></div> - <? else: ?> - <? $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> + <?php else: ?> + <?php $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </div> - <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), + <?php /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), but even if we don't plan to display the link, we still want to get the $openUrl value for use in generating a COinS (Z3988) tag -- see bottom of file. */ @@ -125,26 +125,26 @@ $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); if ($openUrlActive || !empty($urls)): ?> - <? if ($openUrlActive): ?> + <?php if ($openUrlActive): ?> <br/> <?=$openUrl->renderTemplate()?> - <? endif; ?> - <? if (!is_array($urls)) $urls = array(); + <?php endif; ?> + <?php if (!is_array($urls)) $urls = []; if(!$this->driver->isCollection()): foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a><br/> - <? endforeach; ?> - <? endif; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> <div class="result-formats"> <?=$this->record($this->driver)->getFormatList() ?> - <? if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> + <?php if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> <span class="status ajax-availability hidden"> <span class="label label-default"><?=$this->transEsc('Loading')?>...</span> </span> - <? endif; ?> + <?php endif; ?> </div> <div class="result-previews"> @@ -152,11 +152,11 @@ </div> </div> <div class="result-links hidden-print"> - <? /* Display qrcode if appropriate: */ ?> - <? if ($QRCode = $this->record($this->driver)->getQRCode("results")): ?> - <? + <?php /* Display qrcode if appropriate: */ ?> + <?php if ($QRCode = $this->record($this->driver)->getQRCode("results")): ?> + <?php // Add JS Variables for QrCode - $this->jsTranslations()->addStrings(array('qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show')); + $this->jsTranslations()->addStrings(['qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show']); ?> <span class="hidden-xs"> <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> @@ -166,43 +166,43 @@ </script> </div><br/> </span> - <? endif; ?> + <?php endif; ?> - <? if ($this->cart()->isActiveInSearch() && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?> + <?php if ($this->cart()->isActiveInSearch() && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?> <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?><br/> - <? endif; ?> + <?php endif; ?> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> - <? if ($this->permission()->allowDisplay('feature.Favorites')): ?> - <? /* Add to favorites */ ?> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> + <?php /* Add to favorites */ ?> <i class="result-link-icon fa fa-fw fa-star" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" data-lightbox class="save-record result-link-label" data-id="<?=$this->escapeHtmlAttr($this->driver->getUniqueId()) ?>"><?=$this->transEsc('Add to favorites')?></a><br/> - <? elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> + <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> <?=$block?> - <? endif; ?> - <? /* Saved lists */ ?> + <?php endif; ?> + <?php /* Saved lists */ ?> <div class="savedLists"> <strong><?=$this->transEsc("Saved in")?>:</strong> </div> - <? endif; ?> + <?php endif; ?> - <? /* Hierarchy tree link */ ?> - <? $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> - <? foreach ($trees as $hierarchyID => $hierarchyTitle): ?> + <?php /* Hierarchy tree link */ ?> + <?php $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> + <?php foreach ($trees as $hierarchyID => $hierarchyTitle): ?> <div class="hierarchyTreeLink"> <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> <i class="result-link-icon fa fa-fw fa-sitemap" aria-hidden="true"></i> <a class="hierarchyTreeLinkText result-link-label" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>" data-lightbox-href="<?=$this->recordLink()->getTabUrl($this->driver, 'AjaxTab')?>?hierarchy=<?=urlencode($hierarchyID)?>" data-lightbox-post="tab=hierarchytree"> - <?=$this->transEsc('hierarchy_view_context')?><? if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><? endif; ?> + <?=$this->transEsc('hierarchy_view_context')?><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> </a> </div> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?> + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> </div> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/results-qrcode.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/results-qrcode.phtml similarity index 100% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/results-qrcode.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/results-qrcode.phtml diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/toolbar.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml similarity index 62% rename from themes/bootstrap3/templates/RecordDriver/SolrDefault/toolbar.phtml rename to themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml index c90ddb37fbf02f55c340b2985077b9fee9819eac..d544352792a0952e5097d6bcbabd01f55af43256 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/toolbar.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml @@ -1,41 +1,41 @@ -<? +<?php $addThis = $this->addThis(); if (!empty($addThis)) { $this->headScript()->appendFile('https://s7.addthis.com/js/250/addthis_widget.js?pub=' . urlencode($addThis)); } ?> <ul class="record-nav nav nav-pills hidden-print"> - <? if (count($this->driver->getCitationFormats()) > 0): ?> + <?php if (count($this->driver->getCitationFormats()) > 0): ?> <li><a class="cite-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Cite')?>" rel="nofollow"><i class="fa fa-asterisk" aria-hidden="true"></i> <?=$this->transEsc('Cite this')?></a></li> - <? endif; ?> - <? if ($this->accountCapabilities()->getSmsSetting() !== 'disabled'): ?> + <?php endif; ?> + <?php if ($this->accountCapabilities()->getSmsSetting() !== 'disabled'): ?> <li><a class="sms-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'SMS')?>" rel="nofollow"><i class="fa fa-mobile" aria-hidden="true"></i> <?=$this->transEsc('Text this')?></a></li> - <? endif; ?> + <?php endif; ?> <li><a class="mail-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Email')?>" rel="nofollow"><i class="fa fa-envelope" aria-hidden="true"></i> <?=$this->transEsc('Email this')?></a></li> - <? $exportFormats = $this->export()->getFormatsForRecord($this->driver); ?> - <? if(count($exportFormats) > 0): ?> + <?php $exportFormats = $this->export()->getFormatsForRecord($this->driver); ?> + <?php if(count($exportFormats) > 0): ?> <li class="dropdown"> <a class="export-toggle dropdown-toggle" data-toggle="dropdown" href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>" rel="nofollow"><i class="fa fa-list-alt" aria-hidden="true"></i> <?=$this->transEsc('Export Record') ?></a> <ul class="dropdown-menu" role="menu"> - <? foreach ($exportFormats as $exportFormat): ?> - <li><a <? if ($this->export()->needsRedirect($exportFormat)): ?>target="<?=$this->escapeHtmlAttr($exportFormat)?>Main" <? endif; ?>href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>?style=<?=$this->escapeHtmlAttr($exportFormat)?>" rel="nofollow"><?=$this->transEsc('Export to')?> <?=$this->transEsc($this->export()->getLabelForFormat($exportFormat))?></a></li> - <? endforeach; ?> + <?php foreach ($exportFormats as $exportFormat): ?> + <li><a <?php if ($this->export()->needsRedirect($exportFormat)): ?>target="<?=$this->escapeHtmlAttr($exportFormat)?>Main" <?php endif; ?>href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>?style=<?=$this->escapeHtmlAttr($exportFormat)?>" rel="nofollow"><?=$this->transEsc('Export to')?> <?=$this->transEsc($this->export()->getLabelForFormat($exportFormat))?></a></li> + <?php endforeach; ?> </ul> </li> - <? endif; ?> + <?php endif; ?> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <li> - <? if ($this->permission()->allowDisplay('feature.Favorites')): ?> + <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> <a class="save-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" rel="nofollow"><i class="fa fa-star" aria-hidden="true"></i> <?=$this->transEsc('Add to favorites')?></a> - <? elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> + <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> <?=$block?> - <? endif; ?> + <?php endif; ?> </li> - <? endif; ?> - <? if (!empty($addThis)): ?> + <?php endif; ?> + <?php if (!empty($addThis)): ?> <li><a class="addThis addthis_button" href="https://www.addthis.com/bookmark.php?v=250&pub=<?=urlencode($addThis)?>"><i class="fa fa-bookmark" aria-hidden="true"></i> <?=$this->transEsc('Bookmark')?></a></li> - <? endif; ?> + <?php endif; ?> <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?> </ul> diff --git a/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml b/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml index c686dd3bb8e1a38465228aaab7abe6f5c8a69702..ae075e294b56d7a77a1f2ed383a1584b6e1d032c 100644 --- a/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml +++ b/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml @@ -1,5 +1,5 @@ -<? $this->headLink()->appendStylesheet('EDS.css'); ?> -<? +<?php $this->headLink()->appendStylesheet('EDS.css'); ?> +<?php $items = $this->driver->getItems(); $dbLabel = $this->driver->getDbLabel(); $thumb = $this->driver->getThumbnail('medium'); @@ -10,69 +10,70 @@ ?> <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> <div class="media-left medium img-col"> - <? if ($thumb): ?> + <?php if ($thumb): ?> <img src="<?=$this->escapeHtmlAttr($thumb)?>" class="recordcover" alt="<?=$this->transEsc('Cover Image')?>"/> - <? else: ?> + <?php else: ?> <span class="recordcover pt-icon pt-<?=$this->driver->getPubTypeId()?>"></span> - <? endif; ?> - <? if ($pubType): ?> + <?php endif; ?> + <?php if ($pubType): ?> <p><?=$this->transEsc($pubType)?></p> - <? endif; ?> + <?php endif; ?> <div class="external-links"> - <? $pLink = $this->driver->getPLink(); + <?php $pLink = $this->driver->getPLink(); if($pLink): ?> <span> <a href="<?=$this->escapeHtmlAttr($pLink)?>"> <?=$this->transEsc('View in EDS')?> </a> </span><br /> - <? endif; ?> - <? $pdfLink = $this->driver->getPdfLink(); + <?php endif; ?> + <?php $pdfLink = $this->driver->getPdfLink(); if ($pdfLink): ?> <span> <a href="<?=$pdfLink?>" class="icon pdf fulltext"> <?=$this->transEsc('PDF Full Text')?> </a> </span><br /> - <? endif; ?> - <? if ($this->driver->hasHTMLFullTextAvailable()): ?> + <?php endif; ?> + <?php if ($this->driver->hasHTMLFullTextAvailable()): ?> <span> <a href="<?=$this->recordLink()->getUrl($this->driver, 'fulltext')?>#html" class="icon html fulltext"> <?=$this->transEsc('HTML Full Text')?> </a> </span><br /> - <? endif; ?> + <?php endif; ?> </div> </div> <div class="media-body info-col"> <h3 property="name"><?=$this->driver->getTitle()?></h3> - <table class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> - <? foreach ($items as $key => $item): ?> - <? if (!empty($item['Data'])): ?> + <table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> + <?php foreach ($items as $key => $item): ?> + <?php if (!empty($item['Data'])): ?> <tr> <th><?=$this->transEsc($item['Label'])?>:</th> <td><?=$this->driver->linkUrls($item['Data'])?></td> </tr> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> - <? if ($dbLabel): ?> + <?php if ($dbLabel): ?> <tr> <th><?=$this->transEsc('Database')?>:</th> <td><?=$this->escapeHtml($dbLabel)?></td> </tr> - <? endif; ?> + <?php endif; ?> - <?if ($this->driver->hasHTMLFullTextAvailable() && !$restrictedView): - $fullText = $this->driver->getHtmlFullText();?> + <?php if ($this->driver->hasHTMLFullTextAvailable() && !$restrictedView): ?> + <?php $fullText = $this->driver->getHtmlFullText(); ?> <tr id="html"> <td colspan="2"> <?=$fullText?> </td> </tr> - <? elseif ($this->driver->hasHTMLFullTextAvailable() && $restrictedView): ?> + <?php elseif ($this->driver->hasHTMLFullTextAvailable() && $restrictedView): ?> <tr id="html"> <td> <?=$this->transEsc('Full text is not displayed to guests')?> @@ -83,26 +84,27 @@ </a> </td> </tr> - <? endif; ?> + <?php endif; ?> </table> <div class="resultItemLine4 custom-links"> - <? $customLinks = array_merge($this->driver->getFTCustomLinks(), $this->driver->getCustomLinks()); + <?php $customLinks = array_merge($this->driver->getFTCustomLinks(), $this->driver->getCustomLinks()); if (!empty($customLinks)): ?> - <? foreach ($customLinks as $customLink): ?> - <? $url = isset($customLink['Url']) ? $customLink['Url'] : ''; - $mot = isset($customLink['MouseOverText'])? $customLink['MouseOverText'] : ''; - $icon = isset ($customLink['Icon']) ? $customLink['Icon'] : ''; - $name = isset($customLink['Text']) ? $customLink['Text'] : ''; - ?> - <span> - <a href="<?=$this->escapeHtmlAttr($url)?>" target="_blank" title="<?=$this->escapeHtmlAttr($mot)?>" class="custom-link"> - <? if ($icon): ?><img src="<?=$this->escapeHtmlAttr($icon)?>" /> <? endif; ?><?=$this->escapeHtml($name)?> - </a> - </span> - <? endforeach; ?> - <? endif; ?> + <?php foreach ($customLinks as $customLink): ?> + <?php + $url = $customLink['Url'] ?? ''; + $mot = $customLink['MouseOverText'] ?? ''; + $icon = $customLink['Icon'] ?? ''; + $name = $customLink['Text'] ?? ''; + ?> + <span> + <a href="<?=$this->escapeHtmlAttr($url)?>" target="_blank" title="<?=$this->escapeHtmlAttr($mot)?>" class="custom-link"> + <?php if ($icon): ?><img src="<?=$this->escapeHtmlAttr($icon)?>" /> <?php endif; ?><?=$this->escapeHtml($name)?> + </a> + </span> + <?php endforeach; ?> + <?php endif; ?> </div> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml index 65337cb47df032a9a6c5506eae11fbedf357a6e5..a8855831777b1e3e0c69f093e6296ee53c0a4ed7 100644 --- a/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml @@ -1,49 +1,49 @@ -<? +<?php $this->headLink()->appendStylesheet('EDS.css'); $accessLevel = $this->driver->getAccessLevel(); $restrictedView = empty($accessLevel) ? false : true; $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $this->recordLink()->getUrl($this->driver)); ?> -<? +<?php $thumbnail = false; $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('result'); ob_start(); ?> <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtml($coverDetails['size'])?>"> - <? if ($coverDetails['cover']): ?> + <?php if ($coverDetails['cover']): ?> <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="_record_link"> <img src="<?=$this->escapeHtmlAttr($coverDetails['cover'])?>" class="recordcover" alt="<?=$this->transEsc('Cover Image')?>"/> </a> - <? else: ?> + <?php else: ?> <span class="recordcover pt-icon pt-<?=$this->driver->getPubTypeId()?>"></span> <div><?=$this->driver->getPubType()?></div> - <? endif; ?> + <?php endif; ?> </div> -<? $thumbnail = ob_get_contents(); ?> -<? ob_end_clean(); ?> +<?php $thumbnail = ob_get_contents(); ?> +<?php ob_end_clean(); ?> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueID())?>" class="hiddenId" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" /> <div class="media<?=$this->driver->supportsAjaxStatus()?' ajaxItem':''?>"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif; ?> + <?php endif; ?> <div class="media-body"> <div class="result-body"> - <? $items = $this->driver->getItems(); + <?php $items = $this->driver->getItems(); if (isset($items) && !empty($items)): foreach ($items as $item): if (!empty($item)): ?> <div class="resultItemLine1"> - <? if('Ti' == $item['Group']): ?> + <?php if('Ti' == $item['Group']): ?> <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title getFull _record_link" data-view="<?=$this->params->getOptions()->getListViewOption()?>"> <?=$item['Data']?> </a> - <? else:?> + <?php else:?> <p> <b><?=$this->transEsc($item['Label'])?>:</b> - <?=$item['Data']?> + <?=$this->driver->linkUrls($item['Data'])?> </p> - <? endif;?> + <?php endif;?> </div> - <? endif; + <?php endif; endforeach; elseif ($restrictedView): ?> <div class="resultItemLine1"> @@ -55,45 +55,46 @@ </a> </p> </div> - <? endif; ?> + <?php endif; ?> <div class="resultItemLine4 custom-links"> - <? $customLinks = array_merge($this->driver->getFTCustomLinks(), $this->driver->getCustomLinks()); + <?php $customLinks = array_merge($this->driver->getFTCustomLinks(), $this->driver->getCustomLinks()); if (!empty($customLinks)): ?> - <? foreach ($customLinks as $customLink): ?> - <? $url = isset($customLink['Url']) ? $customLink['Url'] : ''; - $mot = isset($customLink['MouseOverText'])? $customLink['MouseOverText'] : ''; - $icon = isset ($customLink['Icon']) ? $customLink['Icon'] : ''; - $name = isset($customLink['Text']) ? $customLink['Text'] : ''; - ?> - <span> - <a href="<?=$this->escapeHtmlAttr($url)?>" target="_blank" title="<?=$this->escapeHtmlAttr($mot)?>" class="custom-link"> - <? if ($icon): ?><img src="<?=$this->escapeHtmlAttr($icon)?>" /> <? endif; ?><?=$this->escapeHtml($name)?> - </a> - </span> - <? endforeach; ?> - <? endif; ?> + <?php foreach ($customLinks as $customLink): ?> + <?php + $url = $customLink['Url'] ?? ''; + $mot = $customLink['MouseOverText'] ?? ''; + $icon = $customLink['Icon'] ?? ''; + $name = $customLink['Text'] ?? ''; + ?> + <span> + <a href="<?=$this->escapeHtmlAttr($url)?>" target="_blank" title="<?=$this->escapeHtmlAttr($mot)?>" class="custom-link"> + <?php if ($icon): ?><img src="<?=$this->escapeHtmlAttr($icon)?>" /> <?php endif; ?><?=$this->escapeHtml($name)?> + </a> + </span> + <?php endforeach; ?> + <?php endif; ?> </div> - <? if ($this->driver->hasHTMLFullTextAvailable()): ?> + <?php if ($this->driver->hasHTMLFullTextAvailable()): ?> <a href="<?= $this->recordLink()->getUrl($this->driver, 'fulltext') ?>#html" class="icon html fulltext _record_link" target="_blank"> <?=$this->transEsc('HTML Full Text')?> </a> - <? endif; ?> + <?php endif; ?> - <? if ($this->driver->hasPdfAvailable()): ?> - <a href="<?= $this->recordLink()->getUrl($this->driver).'/PDF'; ?>" class="icon pdf fulltext" target="_blank"> + <?php if ($this->driver->hasPdfAvailable()): ?> + <a href="<?= $this->recordLink()->getUrl($this->driver) . '/PDF'; ?>" class="icon pdf fulltext" target="_blank"> <?=$this->transEsc('PDF Full Text')?> </a> - <? endif; ?> + <?php endif; ?> </div> <div class="result-links hidden-print"> - <? /* Display qrcode if appropriate: */ ?> - <? if ($QRCode = $this->record($this->driver)->getQRCode("results")): ?> - <? + <?php /* Display qrcode if appropriate: */ ?> + <?php if ($QRCode = $this->record($this->driver)->getQRCode("results")): ?> + <?php // Add JS Variables for QrCode - $this->jsTranslations()->addStrings(array('qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show')); + $this->jsTranslations()->addStrings(['qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show']); ?> <span class="hidden-xs"> <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> @@ -103,37 +104,37 @@ </script> </div><br/> </span> - <? endif; ?> + <?php endif; ?> - <? if ($this->cart()->isActiveInSearch() && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?> + <?php if ($this->cart()->isActiveInSearch() && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?> <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?><br/> - <? endif; ?> + <?php endif; ?> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> - <? /* Add to favorites */ ?> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php /* Add to favorites */ ?> <i class="fa fa-fw fa-star" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" class="save-record" data-lightbox id="<?=$this->driver->getUniqueId() ?>" title="<?=$this->transEsc('Add to favorites')?>"><?=$this->transEsc('Add to favorites')?></a><br/> - <? /* Saved lists */ ?> + <?php /* Saved lists */ ?> <div class="savedLists alert alert-info hidden"> <strong><?=$this->transEsc("Saved in")?>:</strong> </div> - <? endif; ?> + <?php endif; ?> - <? /* Hierarchy tree link */ ?> - <? $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> - <? foreach ($trees as $hierarchyID => $hierarchyTitle): ?> + <?php /* Hierarchy tree link */ ?> + <?php $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> + <?php foreach ($trees as $hierarchyID => $hierarchyTitle): ?> <div class="hierarchyTreeLink"> <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> <i class="fa fa-fw fa-sitemap" aria-hidden="true"></i> <a class="hierarchyTreeLinkText" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>"> - <?=$this->transEsc('hierarchy_view_context')?><? if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><? endif; ?> + <?=$this->transEsc('hierarchy_view_context')?><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> </a> </div> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/LibGuides/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/LibGuides/result-list.phtml index a422c928ad85f07472410cdeee6d8c50aae889b8..7952029a6eba27894ec0045c41a09a5175215539 100644 --- a/themes/bootstrap3/templates/RecordDriver/LibGuides/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/LibGuides/result-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $url = $this->driver->getUniqueId(); ?> <div class="media"> diff --git a/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml index c11935a4fca558dee7043aa1911ea260b027c3be..a836656fef657d44f406b7250b43c66662173e4a 100644 --- a/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $this->recordLink()->getUrl($this->driver)); $cover = $coverDetails['html']; $thumbnail = false; @@ -8,15 +8,15 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> -<? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueID())?>" class="hiddenId" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" /> <div class="media<?=$this->driver->supportsAjaxStatus()?' ajaxItem':''?>"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif; ?> + <?php endif; ?> <div class="media-body"> <div> <b> @@ -25,38 +25,38 @@ </div> <div> - <? $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> + <?php $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> <?=$this->transEsc('by')?> - <? $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> + <?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> <a href="<?=$this->record($this->driver)->getLink('author', $this->highlight($summAuthor, null, true, false))?>"><?=$this->highlight($summAuthor)?></a><?=$i + 1 < $authorCount ? ',' : ''?> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> - <? if (!empty($journalTitle)): ?> + <?php $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> + <?php if (!empty($journalTitle)): ?> <?=!empty($summAuthor) ? '<br />' : ''?> - <?=/* TODO: handle highlighting more elegantly here */ $this->transEsc('Published in') . ' <a href="' . $this->record($this->driver)->getLink('journaltitle', str_replace(array('{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'), '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?> + <?=/* TODO: handle highlighting more elegantly here */ $this->transEsc('Published in') . ' <a href="' . $this->record($this->driver)->getLink('journaltitle', str_replace(['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?> <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate[0]) . ')' : ''?> - <? elseif (!empty($summDate)): ?> + <?php elseif (!empty($summDate)): ?> <?=!empty($summAuthor) ? '<br />' : ''?> <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate[0])?> - <? endif; ?> - <? $summInCollection = $this->driver->getContainingCollections(); + <?php endif; ?> + <?php $summInCollection = $this->driver->getContainingCollections(); if (!empty($summInCollection)): ?> - <? foreach ($summInCollection as $collId => $collText): ?> + <?php foreach ($summInCollection as $collId => $collText): ?> <div> <b><?=$this->transEsc("in_collection_label")?></b> - <a class="collectionLinkText" href="<?=$this->url('collection', array('id' => $collId))?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> <?=$this->escapeHtml($collText)?> </a> </div> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> <div> <div class="callnumAndLocation ajax-availability hidden"> - <? if ($this->driver->supportsAjaxStatus()): ?> + <?php if ($this->driver->supportsAjaxStatus()): ?> <strong class="hideIfDetailed"><?=$this->transEsc('Call Number')?>:</strong> <span class="callnumber ajax-availability hidden"> <?=$this->transEsc('Loading')?>... @@ -66,14 +66,14 @@ <?=$this->transEsc('Loading')?>... </span> <div class="locationDetails"></div> - <? else: ?> - <? $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> + <?php else: ?> + <?php $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </div> - <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), + <?php /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), but even if we don't plan to display the link, we still want to get the $openUrl value for use in generating a COinS (Z3988) tag -- see bottom of file. */ @@ -82,36 +82,36 @@ // Account for replace_other_urls setting $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); if ($openUrlActive || !empty($urls)): ?> - <? if ($openUrlActive): ?> + <?php if ($openUrlActive): ?> <br/> <?=$openUrl->renderTemplate()?> - <? endif; ?> - <? if (!is_array($urls)) $urls = array(); + <?php endif; ?> + <?php if (!is_array($urls)) $urls = []; if(!$this->driver->isCollection()): foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a><br/> - <? endforeach; ?> - <? endif; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> <div class="result-formats"> <?=$this->record($this->driver)->getFormatList() ?> - <? if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> + <?php if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> <span class="status ajax-availability small"> <span class="label label-default"><?=$this->transEsc('Loading')?>...</span> </span> - <? endif; ?> + <?php endif; ?> </div> <div class="result-previews"> <?=$this->record($this->driver)->getPreviews()?> </div> - <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?> + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> </div> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/Primo/format-class.phtml b/themes/bootstrap3/templates/RecordDriver/Primo/format-class.phtml index a035c93fc8fb4cb01000f71449640b679aa417ba..12be46ed61115e3154dad984d0e8ce909c0fc717 100644 --- a/themes/bootstrap3/templates/RecordDriver/Primo/format-class.phtml +++ b/themes/bootstrap3/templates/RecordDriver/Primo/format-class.phtml @@ -1,4 +1,4 @@ -<? +<?php // Convert Primo formats to VuFind formats so icons display correctly: switch ($this->format) { case 'Audio Recording': diff --git a/themes/bootstrap3/templates/RecordDriver/SolrAuth/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/SolrAuthDefault/result-list.phtml similarity index 75% rename from themes/bootstrap3/templates/RecordDriver/SolrAuth/result-list.phtml rename to themes/bootstrap3/templates/RecordDriver/SolrAuthDefault/result-list.phtml index 274d8cb07ef5e55f1616c2702e0167d49a79549d..f3491bb9f3689ed48b09184c5a904668b2763830 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrAuth/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/SolrAuthDefault/result-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $heading = $this->driver->getTitle(); if (empty($heading)) { $heading = $this->translate('Heading unavailable.'); @@ -13,21 +13,21 @@ </div> <div class="resultItemLine2"> - <? if (!empty($seeAlso)): ?> + <?php if (!empty($seeAlso)): ?> <?=$this->transEsc("See also")?>:<br/> - <? foreach ($seeAlso as $current): ?> + <?php foreach ($seeAlso as $current): ?> <a href="<?=$this->url('authority-search')?>?lookfor=%22<?=urlencode($current)?>%22&type=MainHeading"><?=$this->escapeHtml($current)?></a><br/> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> <div class="resultItemLine3"> - <? if (!empty($useFor)): ?> + <?php if (!empty($useFor)): ?> <?=$this->transEsc("Use for")?>:<br/> - <? foreach ($useFor as $current): ?> + <?php foreach ($useFor as $current): ?> <?=$this->escapeHtml($current)?><br/> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml deleted file mode 100644 index f2a8dfd40071bd23a629c0da0f371ec89e9bbf75..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml +++ /dev/null @@ -1,11 +0,0 @@ -<? foreach ($data as $recordLink): ?> - <?=$this->transEsc($recordLink['title'])?>: - <a href="<?=$this->recordLink()->related($recordLink['link'])?>"><?=$this->escapeHtml($recordLink['value'])?></a><br /> -<? endforeach; ?> -<? /* if we have record links, display relevant explanatory notes */ - $related = $this->driver->getRelationshipNotes(); - if (!empty($related)): ?> - <? foreach ($related as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> -<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml deleted file mode 100644 index 2ab86fa7b88170ea16733f059a1e751582338470..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml +++ /dev/null @@ -1,11 +0,0 @@ -<? foreach ($data as $field): ?> - <div class="subject-line" property="keywords"> - <? $subject = ''; ?> - <? if(count($field) == 1) $field = explode('--', $field[0]); ?> - <? $i = 0; foreach ($field as $subfield): ?> - <?=($i++ == 0) ? '' : ' > '?> - <? $subject = trim($subject . ' ' . $subfield); ?> - <a title="<?=$this->escapeHtmlAttr($subject)?>" href="<?=$this->record($this->driver)->getLink('subject', $subject)?>" rel="nofollow"><?=trim($this->escapeHtml($subfield))?></a> - <? endforeach; ?> - </div> -<? endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml deleted file mode 100644 index 1d58594cea1c5d24e940fc9c794ce6738613d710..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml +++ /dev/null @@ -1,8 +0,0 @@ -<? $isbn = $this->driver->getCleanISBN(); ?> -<? $authorNotes = empty($isbn) ? [] : $this->authorNotes($isbn); if (!empty($authorNotes)): ?> - <? foreach ($authorNotes as $provider => $list): ?> - <? foreach ($list as $field): ?> - <?=$field['Content']?><br /> - <? endforeach; ?> - <? endforeach; ?> -<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml deleted file mode 100644 index cb6ad882fa2384d79a1bc135aa78646efd0c3f45..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml +++ /dev/null @@ -1 +0,0 @@ -<a href="<?=$this->recordLink()->getChildRecordSearchUrl($this->driver)?>"><?=$this->transEsc('child_record_count', array('%%count%%' => $data))?></a> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml deleted file mode 100644 index c8b7a78a2eb308d3b131cdd829b8b56626947762..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml +++ /dev/null @@ -1,14 +0,0 @@ -<? foreach ($data as $field): ?> - <span property="publisher" typeof="Organization"> - <? $pubPlace = $field->getPlace(); if (!empty($pubPlace)): ?> - <span property="location"><?=$this->escapeHtml($pubPlace)?></span> - <? endif; ?> - <? $pubName = $field->getName(); if (!empty($pubName)): ?> - <span property="name"><?=$this->escapeHtml($pubName)?></span> - <? endif; ?> - </span> - <? $pubDate = $field->getDate(); if (!empty($pubDate)): ?> - <span property="publicationDate"><?=$this->escapeHtml($pubDate)?></span> - <? endif; ?> - <br/> -<? endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-author.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-author.phtml deleted file mode 100644 index fdd7fe966a2d7d2667fe9236afbe083b3c190a32..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-author.phtml +++ /dev/null @@ -1 +0,0 @@ -<?=$this->url('author-home')?>?author=<?=urlencode($this->lookfor)?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-journaltitle.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-journaltitle.phtml deleted file mode 100644 index 154b38cbbb1d9899e131d99f74d19c5be8477e60..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-journaltitle.phtml +++ /dev/null @@ -1 +0,0 @@ -<?=$this->url('search-results')?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=JournalTitle diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-series.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-series.phtml deleted file mode 100644 index 3b0406eeff76aa9ae5e463083713810713b7e265..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-series.phtml +++ /dev/null @@ -1 +0,0 @@ -<?=$this->url('search-results')?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Series diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-subject.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-subject.phtml deleted file mode 100644 index 6424a15c8860a6df7df0bb882ebbf82093c1f078..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-subject.phtml +++ /dev/null @@ -1 +0,0 @@ -<?=$this->url('search-results')?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Subject diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-title.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-title.phtml deleted file mode 100644 index 163ccdcb55b7fe8e9a02c4eceff950af447096e7..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/link-title.phtml +++ /dev/null @@ -1 +0,0 @@ -<?=$this->url('search-results')?>?lookfor=%22<?=urlencode($this->lookfor)?>%22&type=Title diff --git a/themes/bootstrap3/templates/RecordDriver/SolrWeb/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/SolrWeb/result-list.phtml index 3cf9f42b6fc0474eebaaf7211016dbccf8415e14..19b1f0e736b32b4d46cf53b6755f4b232bea4623 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrWeb/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/SolrWeb/result-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $url = $this->driver->getUrl(); ?> <div class="media"> @@ -10,20 +10,20 @@ </div> <div class="resultItemLine2"> - <? $snippet = $this->driver->getHighlightedSnippet(); ?> - <? $summary = $this->driver->getSummary(); ?> - <? if (!empty($snippet)): ?> + <?php $snippet = $this->driver->getHighlightedSnippet(); ?> + <?php $summary = $this->driver->getSummary(); ?> + <?php if (!empty($snippet)): ?> <?=$this->highlight($snippet['snippet'])?> - <? elseif (!empty($summary)): ?> + <?php elseif (!empty($summary)): ?> <?=$this->escapeHtml($summary[0])?> - <? endif; ?> + <?php endif; ?> </div> <div class="resultItemLine3"> <?=$this->escapeHtml($url)?> - <? $lastMod = $this->driver->getLastModified(); if (!empty($lastMod)): ?> - <br /><?=$this->transEsc('Last Modified')?>: <?=$this->escapeHtml(trim(str_replace(array('T', 'Z'), ' ', $lastMod)))?> - <? endif; ?> + <?php $lastMod = $this->driver->getLastModified(); if (!empty($lastMod)): ?> + <br /><?=$this->transEsc('Last Modified')?>: <?=$this->escapeHtml(trim(str_replace(['T', 'Z'], ' ', $lastMod)))?> + <?php endif; ?> </div> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/Summon/format-class.phtml b/themes/bootstrap3/templates/RecordDriver/Summon/format-class.phtml index ee5e6148494c74efc4a9ff2690f34ef0b525c1c4..4b9f0d2a3bfe200a9503eb1dbda4103b7a4217c4 100644 --- a/themes/bootstrap3/templates/RecordDriver/Summon/format-class.phtml +++ b/themes/bootstrap3/templates/RecordDriver/Summon/format-class.phtml @@ -1,4 +1,4 @@ -<? +<?php // Convert Summon formats to VuFind formats so icons display correctly: switch ($this->format) { case 'Audio Recording': diff --git a/themes/bootstrap3/templates/RecordTab/collectionhierarchytree.phtml b/themes/bootstrap3/templates/RecordTab/collectionhierarchytree.phtml index a03b1fc56c9b98263128caee84e5ea33a4eea3ef..eefcc2b4ac5fd0553d8e153083ea9ee64631a366 100644 --- a/themes/bootstrap3/templates/RecordTab/collectionhierarchytree.phtml +++ b/themes/bootstrap3/templates/RecordTab/collectionhierarchytree.phtml @@ -1,16 +1,16 @@ -<? $this->treeContext = 'Collection'; ?> +<?php $this->treeContext = 'Collection'; ?> <div class="collection-hierarchytree"> <div class="tree-panel"> <?=$this->render('RecordTab/hierarchytree.phtml')?> </div> <div id="tree-preview"> - <? if (($collectionRecord = $this->tab->getActiveRecord()) !== false): ?> - <? if ($collectionRecord === null): ?> + <?php if (($collectionRecord = $this->tab->getActiveRecord()) !== false): ?> + <?php if ($collectionRecord === null): ?> <?=$this->render('collection/collection-record-error.phtml')?> - <? else: ?> + <?php else: ?> <?=html_entity_decode($this->record($collectionRecord)->getCollectionBriefRecord())?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </div> </div> diff --git a/themes/bootstrap3/templates/RecordTab/collectionlist.phtml b/themes/bootstrap3/templates/RecordTab/collectionlist.phtml index 6d7b52855b637dcac464e0f1356cc40265f1206d..67721ad320f44d26288fc201600eb72f06763916 100644 --- a/themes/bootstrap3/templates/RecordTab/collectionlist.phtml +++ b/themes/bootstrap3/templates/RecordTab/collectionlist.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Collection Items') . ': ' . $this->driver->getBreadcrumb()); @@ -7,24 +7,24 @@ $params = $this->tab->getParams(); $searchDetails = ['results' => $results, 'params' => $params, 'indexStart' => 1]; ?> -<? if (($recordTotal = $results->getResultTotal()) > 0): // only display these at very top if we have results ?> - <? foreach ($results->getRecommendations('top') as $current): ?> +<?php if (($recordTotal = $results->getResultTotal()) > 0): // only display these at very top if we have results ?> + <?php foreach ($results->getRecommendations('top') as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> <div class="clearfix hidden-print"> <div class="pull-left flip"> - <? + <?php $transParams = [ '%%start%%' => $this->localizedNumber($results->getStartRecord()), - '%%end%%' => $this->localizedNumber($results->getEndRecord()), + '%%end%%' => $this->localizedNumber($results->getEndRecord()), '%%total%%' => $this->localizedNumber($recordTotal) ]; ?> - <? if (!isset($this->skipTotalCount)): ?> + <?php if (!isset($this->skipTotalCount)): ?> <?=$this->translate('showing_items_of_html', $transParams); ?> - <? else: ?> + <?php else: ?> <?=$this->translate('showing_items_html', $transParams); ?> - <? endif; ?> + <?php endif; ?> </div> <div class="pull-right flip"> <?=$this->render('search/controls/limit.phtml', $searchDetails)?> @@ -36,6 +36,6 @@ <?=$this->render('search/list-' . $results->getParams()->getView() . '.phtml', $searchDetails)?> <?=$this->paginationControl($results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $results])?> </form> -<? else: ?> +<?php else: ?> <?=$this->transEsc('collection_empty')?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/description.phtml b/themes/bootstrap3/templates/RecordTab/description.phtml index 3d73373ffc50f983c38e8046f959e60cd5c6e6fc..dc203a6a8e017e654acb49ba903d6e8851c9a8bf 100644 --- a/themes/bootstrap3/templates/RecordTab/description.phtml +++ b/themes/bootstrap3/templates/RecordTab/description.phtml @@ -1,16 +1,17 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Description') . ': ' . $this->driver->getBreadcrumb()); $formatter = $this->recordDataFormatter(); $mainFields = $formatter->getData($driver, $formatter->getDefaults('description')); ?> -<table class="table table-striped" summary="<?=$this->transEsc('Description')?>"> - <? if (!empty($mainFields)): ?> - <? foreach ($mainFields as $key => $current): ?> - <tr><th><?=$this->transEsc($key)?>:</th><td><?=$current['value']?></td></tr> - <? endforeach; ?> - <? else: ?> +<table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Description')?></caption> + <?php if (!empty($mainFields)): ?> + <?php foreach ($mainFields as $current): ?> + <tr><th><?=$this->transEsc($current['label'])?>:</th><td><?=$current['value']?></td></tr> + <?php endforeach; ?> + <?php else: ?> <tr><td><?=$this->transEsc('no_description')?></td></tr> - <? endif; ?> + <?php endif; ?> </table> diff --git a/themes/bootstrap3/templates/RecordTab/excerpt.phtml b/themes/bootstrap3/templates/RecordTab/excerpt.phtml index 625e470d79a4c296c1fc06e686eb8913ba632974..46f695587993f5afd5f49466b79837c23b2671dd 100644 --- a/themes/bootstrap3/templates/RecordTab/excerpt.phtml +++ b/themes/bootstrap3/templates/RecordTab/excerpt.phtml @@ -1,18 +1,18 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Excerpt') . ': ' . $this->driver->getBreadcrumb()); // Grab excerpt data: $excerpts = $this->tab->getContent(); ?> -<? if (count($excerpts) > 0): ?> - <? foreach ($excerpts as $provider => $list): ?> - <? foreach ($list as $excerpt): ?> +<?php if (count($excerpts) > 0): ?> + <?php foreach ($excerpts as $provider => $list): ?> + <?php foreach ($list as $excerpt): ?> <p class="summary"><?=$excerpt['Content']?></p> - <?=isset($excerpt['Copyright']) ? $excerpt['Copyright'] : ''?> + <?=$excerpt['Copyright'] ?? ''?> <hr/> - <? endforeach; ?> - <? endforeach; ?> -<? else: ?> + <?php endforeach; ?> + <?php endforeach; ?> +<?php else: ?> <?=$this->transEsc('No excerpts were found for this record.')?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml b/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml index 754171e34193e55b1449ad8f977b5fae57b64ab5..630d4be321e1b4059a8d2380fb9a694afdc5427c 100644 --- a/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml +++ b/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('hierarchy_tree') . ': ' . $this->driver->getBreadcrumb()); $hierarchyTreeList = $this->tab->getTreeList(); @@ -20,20 +20,20 @@ $this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'hierarchyTree.js'); echo $this->inlineScript(); ?> -<? if (count($hierarchyTreeList) > 1): ?> +<?php if (count($hierarchyTreeList) > 1): ?> <div id="treeSelector"> - <? foreach ($hierarchyTreeList as $hierarchy => $hierarchyTitle): ?> - <? if($activeTree == $hierarchy): ?> + <?php foreach ($hierarchyTreeList as $hierarchy => $hierarchyTitle): ?> + <?php if($activeTree == $hierarchy): ?> <i class="fa fa-sitemap" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?> - <? else: ?> + <?php else: ?> <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </div> -<? endif; ?> -<? if ($activeTree): ?> +<?php endif; ?> +<?php if ($activeTree): ?> <div id="hierarchyTreeHolder"> - <? if ($this->tab->searchActive()): ?> + <?php if ($this->tab->searchActive()): ?> <div id="treeSearch" class="form-inline hidden"> <input type="text" id="treeSearchText" class="form-control search-query" value=""> <select class="form-control" id="treeSearchType" name="type"> @@ -45,19 +45,23 @@ </div> <div id="treeSearchNoResults" class="alert alert-danger hidden"><?=$this->translate('nohit_heading')?></div> <div id="treeSearchLimitReached" class="alert alert-danger hidden"><?=$this->translate('tree_search_limit_reached_html', ['%%url%%' => $this->url('search-results'), '%%limit%%' => $this->tab->getSearchLimit()])?></div> - <? endif; ?> + <?php endif; ?> <div id="hierarchyLoading" class="hide"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i> <?=$this->transEsc("Loading")?>...</div> <div id="hierarchyTree" class="hierarchy-tree"> <input type="hidden" value="<?=$this->escapeHtml($this->driver->getUniqueId())?>" class="hiddenRecordId" /> <input type="hidden" value="<?=$this->escapeHtml($activeTree)?>" class="hiddenHierarchyId" /> <input type="hidden" value="<?=isset($this->treeContext) ? $this->treeContext : 'Record'?>" class="hiddenContext" /> - <? if ($this->layout()->getTemplate() != 'layout/lightbox'): ?> + <?php if ($this->layout()->getTemplate() != 'layout/lightbox'): ?> <noscript> - <ul class="fa-ul"> - <?=$this->tab->renderTree($this->url('home'))?> - </ul> + <?php if ($this->config()->nonJavascriptSupportEnabled()): ?> + <ul class="fa-ul"> + <?=$this->tab->renderTree($this->url('home'))?> + </ul> + <?php else: ?> + <?=$this->transEsc('Please enable JavaScript.')?> + <?php endif; ?> </noscript> - <? endif; ?> + <?php endif; ?> </div> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml index 2ef3ceeb56e13cb368ced08596e2b0df3146e113..3d4f71b9831d329283d71192b4d14c6befdceeb3 100644 --- a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml +++ b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up convenience variables: $account = $this->auth()->getManager(); $user = $account->isLoggedIn(); @@ -19,165 +19,109 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> -<? if (!empty($holdings['blocks'])):?> +<?php if (!empty($holdings['blocks'])):?> <div id="account-block-msg" class="alert alert-danger"> <?=$this->transEsc('account_block_options_missing', ['%%details%%' => implode('; ', $holdings['blocks'])]) ?> </div> -<? endif; ?> +<?php endif; ?> <?=($offlineMode == "ils-offline") ? $this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message']) : ''?> -<? if (($this->ils()->getHoldsMode() == 'driver' && !empty($holdings['holdings'])) || $this->ils()->getTitleHoldsMode() == 'driver'): ?> - <? if ($account->loginEnabled() && $offlineMode != 'ils-offline'): ?> - <? if (!$user): ?> +<?php if (($this->ils()->getHoldsMode() == 'driver' && !empty($holdings['holdings'])) || $this->ils()->getTitleHoldsMode() == 'driver'): ?> + <?php if ($account->loginEnabled() && $offlineMode != 'ils-offline'): ?> + <?php if (!$user): ?> <div class="alert alert-info"> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'Holdings')?>?login=true&catalogLogin=true" data-lightbox><?=$this->transEsc("hold_login")?></a> </div> - <? elseif (!$user->cat_username): ?> + <?php elseif (!$user->cat_username): ?> <div class="alert alert-info"> <?=$this->translate("hold_profile_html", ['%%url%%' => $this->recordLink()->getTabUrl($this->driver, 'Holdings') . '?catalogLogin=true'])?> </div> - <? endif; ?> - <? endif; ?> -<? endif; ?> -<? $holdingTitleHold = $this->driver->tryMethod('getRealTimeTitleHold'); if (!empty($holdingTitleHold)): ?> + <?php endif; ?> + <?php endif; ?> +<?php endif; ?> +<?php $holdingTitleHold = $this->driver->tryMethod('getRealTimeTitleHold'); if (!empty($holdingTitleHold)): ?> <a class="placehold" data-lightbox title="<?=$this->transEsc('request_place_text')?>" href="<?=$this->recordLink()->getRequestUrl($holdingTitleHold)?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc('title_hold_place')?></a> -<? endif; ?> -<? if (!empty($urls) || $openUrlActive): ?> +<?php endif; ?> +<?php if (!empty($urls) || $openUrlActive): ?> <h3><?=$this->transEsc("Internet")?></h3> - <? if (!empty($urls)): ?> - <? foreach ($urls as $current): ?> + <?php if (!empty($urls)): ?> + <?php foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/> - <? endforeach; ?> - <? endif; ?> - <? if ($openUrlActive): ?><?=$openUrl->renderTemplate()?><? endif; ?> -<? endif; ?> -<? foreach ($holdings['holdings'] as $holding): ?> + <?php endforeach; ?> + <?php endif; ?> + <?php if ($openUrlActive): ?><?=$openUrl->renderTemplate()?><?php endif; ?> +<?php endif; ?> +<?php foreach ($holdings['holdings'] ?? [] as $holding): ?> <h3> - <? $locationText = $this->transEsc('location_' . $holding['location'], [], $holding['location']); ?> - <? if (isset($holding['locationhref']) && $holding['locationhref']): ?> + <?php $locationText = $this->transEsc('location_' . $holding['location'], [], $holding['location']); ?> + <?php if (isset($holding['locationhref']) && $holding['locationhref']): ?> <a href="<?=$holding['locationhref']?>" target="_blank"><?=$locationText?></a> - <? else: ?> + <?php else: ?> <?=$locationText?> - <? endif; ?> + <?php endif; ?> </h3> -<table class="table table-striped" summary="<?=$this->transEsc('holdings_details_from', ['%%location%%' => $this->transEsc($holding['location'])]) ?>"> - <? $callNos = $this->tab->getUniqueCallNumbers($holding['items']); if (!empty($callNos)): ?> +<table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('holdings_details_from', ['%%location%%' => $this->transEsc($holding['location'])]) ?></caption> + <?php $callNos = $this->tab->getUniqueCallNumbers($holding['items']); if (!empty($callNos)): ?> <tr> <th><?=$this->transEsc("Call Number")?>: </th> <td width="50%"> - <? foreach ($callNos as $callNo): ?> - <? if ($this->callnumberHandler): ?> + <?php foreach ($callNos as $callNo): ?> + <?php if ($this->callnumberHandler): ?> <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($callNo) ?>"><?=$this->escapeHtml($callNo)?></a> - <? else: ?> + <?php else: ?> <?=$this->escapeHtml($callNo)?> - <? endif; ?> + <?php endif; ?> <br /> - <? endforeach; ?> + <?php endforeach; ?> </td> </tr> - <? endif; ?> - <? if (isset($holding['textfields'])): foreach ($holding['textfields'] as $textFieldName => $textFields): ?> + <?php endif; ?> + <?php if (isset($holding['textfields'])): foreach ($holding['textfields'] as $textFieldName => $textFields): ?> <tr> - <? // Translation for summary is a special case for backwards-compatibility ?> + <?php // Translation for summary is a special case for backwards-compatibility ?> <th><?=$textFieldName == 'summary' ? $this->transEsc("Volume Holdings") : $this->transEsc(ucfirst($textFieldName))?>:</th> <td> - <? foreach ($textFields as $current): ?> + <?php foreach ($textFields as $current): ?> <?=$this->escapeHtml($current)?><br/> - <? endforeach; ?> + <?php endforeach; ?> </td> </tr> - <? endforeach; endif; ?> - <? foreach ($holding['items'] as $row): ?> - <? - // AJAX Check record? - $check = isset($row['check']) && $row['check']; - $checkStorageRetrievalRequest = isset($row['checkStorageRetrievalRequest']) && $row['checkStorageRetrievalRequest']; - $checkILLRequest = isset($row['checkILLRequest']) && $row['checkILLRequest']; + <?php endforeach; endif; ?> + <?php foreach ($holding['items'] as $row): ?> + <?php + try { + echo $this->context($this)->renderInContext( + 'RecordTab/holdingsils/' . $this->tab->getTemplate() . '.phtml', + ['holding' => $row] + ); + } catch (Exception $e) { + echo $this->context($this)->renderInContext( + 'RecordTab/holdingsils/standard.phtml', + ['holding' => $row] + ); + } ?> - <? if (isset($row['barcode']) && $row['barcode'] != ""): ?> - <tr vocab="http://schema.org/" typeof="Offer"> - <th><?=$this->transEsc("Copy")?> <?=$this->escapeHtml($row['number'])?></th> - <td> - <? if ($row['reserve'] == "Y"): ?> - <link property="availability" href="http://schema.org/InStoreOnly" /> - <?=$this->transEsc("On Reserve - Ask at Circulation Desk")?><br /> - <? endif; ?> - <? if (isset($row['use_unknown_message']) && $row['use_unknown_message']): ?> - <span class="text-muted"><?=$this->transEsc("status_unknown_message")?></span> - <? else: ?> - <? if ($row['availability']): ?> - <? /* Begin Available Items (Holds) */ ?> - <span class="text-success"><?=$this->transEsc("Available")?><link property="availability" href="http://schema.org/InStock" /></span> - <? if (isset($row['link']) && $row['link']): ?> - <a class="<?=$check ? 'checkRequest ' : ''?>placehold" <? if (!empty($row['linkLightbox'])): ?>data-lightbox <? endif; ?>href="<?=$this->recordLink()->getRequestUrl($row['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Hold" : "Place a Hold")?></a> - <? endif; ?> - <? if (isset($row['storageRetrievalRequestLink']) && $row['storageRetrievalRequestLink']): ?> - <a class="<?=$checkStorageRetrievalRequest ? 'checkStorageRetrievalRequest ' : ''?> placeStorageRetrievalRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($row['storageRetrievalRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkStorageRetrievalRequest ? "storage_retrieval_request_check_text" : "storage_retrieval_request_place_text")?></a> - <? endif; ?> - <? if (isset($row['ILLRequestLink']) && $row['ILLRequestLink']): ?> - <a class="<?=$checkILLRequest ? 'checkILLRequest ' : ''?>placeILLRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($row['ILLRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkILLRequest ? "ill_request_check_text" : "ill_request_place_text")?></a> - <? endif; ?> - <? else: ?> - <? /* Begin Unavailable Items (Recalls) */ ?> - <span class="text-danger"><?=$this->transEsc($row['status'])?><link property="availability" href="http://schema.org/OutOfStock" /></span> - <? if (isset($row['returnDate']) && $row['returnDate']): ?>– <span class="small"><?=$this->escapeHtml($row['returnDate'])?></span><? endif; ?> - <? if (isset($row['duedate']) && $row['duedate']): ?> - – <span class="small"><?=$this->transEsc("Due")?>: <?=$this->escapeHtml($row['duedate'])?></span> - <? endif; ?> - <? if (isset($row['requests_placed']) && $row['requests_placed'] > 0): ?> - <span><?=$this->transEsc("Requests")?>: <?=$this->escapeHtml($row['requests_placed'])?></span> - <? endif; ?> - <? if (isset($row['link']) && $row['link']): ?> - <a class="<?=$check ? 'checkRequest' : ''?> placehold" <? if (!empty($row['linkLightbox'])): ?>data-lightbox <? endif; ?>href="<?=$this->recordLink()->getRequestUrl($row['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Recall" : "Recall This")?></a> - <? endif; ?> - <? endif; ?> - <? if (isset($row['item_notes'])): ?> - <div class="item-notes"> - <b><?=$this->transEsc("Item Notes")?>:</b> - <ul> - <? foreach ($row['item_notes'] as $item_note): ?> - <li><?=$this->escapeHtml($item_note) ?></li> - <? endforeach; ?> - </ul> - </div> - <? endif; ?> - <? endif; ?> - <? /* Embed item structured data: library, barcode, call number */ ?> - <? if ($row['location']): ?> - <meta property="seller" content="<?=$this->escapeHtmlAttr($row['location'])?>" /> - <? endif; ?> - <? if ($row['barcode']): ?> - <meta property="serialNumber" content="<?=$this->escapeHtmlAttr($row['barcode'])?>" /> - <? endif; ?> - <? if ($row['callnumber']): ?> - <meta property="sku" content="<?=$this->escapeHtmlAttr($row['callnumber'])?>" /> - <? endif; ?> - <? /* Declare that the item is to be borrowed, not for sale */ ?> - <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut" /> - <link property="itemOffered" href="#record" /> - </td> - </tr> - <? endif; ?> - <? endforeach; ?> - <? if (!empty($holding['purchase_history'])): ?> + <?php endforeach; ?> + <?php if (!empty($holding['purchase_history'])): ?> <tr> <th><?=$this->transEsc("Most Recent Received Issues")?>:</th> <td> - <? foreach ($holding['purchase_history'] as $current): ?> + <?php foreach ($holding['purchase_history'] as $current): ?> <?=$this->escapeHtml($current['issue'])?><br/> - <? endforeach; ?> + <?php endforeach; ?> </td> </tr> - <? endif; ?> + <?php endif; ?> </table> -<? endforeach; ?> +<?php endforeach; ?> -<? $history = $this->driver->getRealTimeHistory(); ?> -<? if (is_array($history) && !empty($history)): ?> -<h3><?=$this->transEsc("Most Recent Received Issues")?></h3> -<table class="table table-striped"> - <? foreach ($history as $row): ?> - <tr><td><?=$this->escapeHtml($row['issue'])?></td></tr> - <? endforeach; ?> -</table> -<? endif; ?> +<?php $history = $this->driver->getRealTimeHistory(); ?> +<?php if (is_array($history) && !empty($history)): ?> + <h3><?=$this->transEsc("Most Recent Received Issues")?></h3> + <table class="table table-striped"> + <?php foreach ($history as $row): ?> + <tr><td><?=$this->escapeHtml($row['issue'])?></td></tr> + <?php endforeach; ?> + </table> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils/extended.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils/extended.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4b5dc2120b3feaa808d26bfc29e428096f04faf5 --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/holdingsils/extended.phtml @@ -0,0 +1,101 @@ +<?php if (strlen($holding['barcode'] ?? '') > 0): ?> + <?php + $check = $holding['check'] ?? false; + $checkStorageRetrievalRequest = $holding['checkStorageRetrievalRequest'] ?? false; + $checkILLRequest = $holding['checkILLRequest'] ?? false; + ?> + <tr vocab="http://schema.org/" typeof="Offer"> + <td colspan="2" class="holding-<?=$holding['availability'] ? "available" : "unavailable" ?>"> + <div class="holding-details"> + <span class="holding-field copy-number"> + <?=$this->transEsc("Copy")?> <?=$this->escapeHtml($holding['number'])?> + </span> + <span class="holding-field enumchron"> + <?php if ($holding['enumchron'] ?? false): ?> + (<?=$this->escapeHtml($holding['enumchron'])?>) + <?php endif; ?> + </span> + <span class="holding-field barcode"> + <?=$this->escapeHtml($holding['barcode'])?> + </span> + <span class="holding-field availability"> + <?php if ($holding['reserve'] == "Y"): ?> + <link property="availability" href="http://schema.org/InStoreOnly" /> + <?=$this->transEsc("On Reserve - Ask at Circulation Desk")?><br /> + <?php endif; ?> + + <?php if ($holding['use_unknown_message'] ?? false): ?> + <span class="text-muted"><?=$this->transEsc("status_unknown_message")?></span> + <?php else: ?> + <?php if ($holding['availability']): ?> + <?php /* Begin Available Items (Holds) */ ?> + <span class="text-success"><?=$this->transEsc("Available")?><link property="availability" href="http://schema.org/InStock" /></span> + <?php else: ?> + <?php /* Begin Unavailable Items (Recalls) */ ?> + <span class="text-danger"><?=$this->transEsc($holding['status'])?><link property="availability" href="http://schema.org/OutOfStock" /></span> + <?php if ($holding['returnDate'] ?? false): ?> + <span class="small return-date"><?=$this->escapeHtml($holding['returnDate'])?></span> + <?php endif; ?> + <?php if ($holding['duedate'] ?? false): ?> + <span class="small due-date"><?=$this->transEsc("Due")?>: <?=$this->escapeHtml($holding['duedate'])?></span> + <?php endif; ?> + <?php if (($holding['requests_placed'] ?? 0) > 0): ?> + <span class="request-count"><?=$this->transEsc("Requests")?>: <?=$this->escapeHtml($holding['requests_placed'])?></span> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> + </span> + <span class="holding-field storage-retrieval"> + <?php if ($holding['storageRetrievalRequestLink'] ?? false): ?> + <a class="<?=$checkStorageRetrievalRequest ? 'checkStorageRetrievalRequest ' : ''?> placeStorageRetrievalRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($holding['storageRetrievalRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkStorageRetrievalRequest ? "storage_retrieval_request_check_text" : "storage_retrieval_request_place_text")?></a> + <?php endif; ?> + </span> + <span class="holding-field ill-request"> + <?php if ($holding['ILLRequestLink'] ?? false): ?> + <a class="<?=$checkILLRequest ? 'checkILLRequest ' : ''?>placeILLRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($holding['ILLRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkILLRequest ? "ill_request_check_text" : "ill_request_place_text")?></a> + <?php endif; ?> + </span> + <?php if ($holding['availability'] ?? false): ?> + <span class="holding-field place-hold"> + <?php if ($holding['link'] ?? false): ?> + <a class="<?=$check ? 'checkRequest ' : ''?>placehold" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?=$this->recordLink()->getRequestUrl($holding['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Hold" : "Place a Hold")?></a> + <?php endif; ?> + </span> + <?php else: ?> + <span class="holding-field recall"> + <?php if ($holding['link'] ?? false): ?> + <a class="<?=$check ? 'checkRequest' : ''?> placehold" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?=$this->recordLink()->getRequestUrl($holding['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Recall" : "Recall This")?></a> + <?php endif; ?> + <?=$this->relais()->renderButtonIfActive($this->driver ?? null)?> + </span> + <?php endif; ?> + </div> + + <?php if (isset($holding['item_notes'])): ?> + <div class="holding-notes"> + <div class="item-notes"> + <b><?=$this->transEsc("Item Notes")?>:</b> + <ul> + <?php foreach ($holding['item_notes'] as $item_note): ?> + <li><?=$this->escapeHtml($item_note) ?></li> + <?php endforeach; ?> + </ul> + </div> + </div> + <?php endif; ?> + <?php /* Embed item structured data: library, barcode, call number */ ?> + <?php if ($holding['location'] ?? false): ?> + <meta property="seller" content="<?=$this->escapeHtmlAttr($holding['location'])?>" /> + <?php endif; ?> + <?php if ($holding['barcode'] ?? false): ?> + <meta property="serialNumber" content="<?=$this->escapeHtmlAttr($holding['barcode'])?>" /> + <?php endif; ?> + <?php if ($holding['callnumber'] ?? false): ?> + <meta property="sku" content="<?=$this->escapeHtmlAttr($holding['callnumber'])?>" /> + <?php endif; ?> + <?php /* Declare that the item is to be borrowed, not for sale */ ?> + <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut" /> + <link property="itemOffered" href="#record" /> + </td> + </tr> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils/standard.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils/standard.phtml new file mode 100644 index 0000000000000000000000000000000000000000..ed677dc85576594f53058de00f321d303e42b44a --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/holdingsils/standard.phtml @@ -0,0 +1,74 @@ +<?php if (strlen($holding['barcode'] ?? '') > 0): ?> + <?php + $check = $holding['check'] ?? false; + $checkStorageRetrievalRequest = $holding['checkStorageRetrievalRequest'] ?? false; + $checkILLRequest = $holding['checkILLRequest'] ?? false; + ?> + <tr vocab="http://schema.org/" typeof="Offer"> + <th class="copy-number"><?=$this->transEsc("Copy")?> <?=$this->escapeHtml($holding['number'])?> + <?php if ($holding['enumchron'] ?? false): ?> + <span class="enumchron">(<?=$this->escapeHtml($holding['enumchron'])?>)</span> + <?php endif; ?> + </th> + <td> + <?php if ($holding['reserve'] == "Y"): ?> + <link property="availability" href="http://schema.org/InStoreOnly" /> + <?=$this->transEsc("On Reserve - Ask at Circulation Desk")?><br /> + <?php endif; ?> + <?php if ($holding['use_unknown_message'] ?? false): ?> + <span class="text-muted"><?=$this->transEsc("status_unknown_message")?></span> + <?php else: ?> + <?php if ($holding['availability'] ?? false): ?> + <?php /* Begin Available Items (Holds) */ ?> + <span class="text-success"><?=$this->transEsc("Available")?><link property="availability" href="http://schema.org/InStock" /></span> + <?php if ($holding['link'] ?? false): ?> + <a class="<?=$check ? 'checkRequest ' : ''?>placehold" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?=$this->recordLink()->getRequestUrl($holding['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Hold" : "Place a Hold")?></a> + <?php endif; ?> + <?php if ($holding['storageRetrievalRequestLink'] ?? false): ?> + <a class="<?=$checkStorageRetrievalRequest ? 'checkStorageRetrievalRequest ' : ''?> placeStorageRetrievalRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($holding['storageRetrievalRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkStorageRetrievalRequest ? "storage_retrieval_request_check_text" : "storage_retrieval_request_place_text")?></a> + <?php endif; ?> + <?php if ($holding['ILLRequestLink'] ?? false): ?> + <a class="<?=$checkILLRequest ? 'checkILLRequest ' : ''?>placeILLRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($holding['ILLRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkILLRequest ? "ill_request_check_text" : "ill_request_place_text")?></a> + <?php endif; ?> + <?php else: ?> + <?php /* Begin Unavailable Items (Recalls) */ ?> + <span class="text-danger"><?=$this->transEsc($holding['status'])?><link property="availability" href="http://schema.org/OutOfStock" /></span> + <?php if ($holding['returnDate'] ?? false): ?>– <span class="small"><?=$this->escapeHtml($holding['returnDate'])?></span><?php endif; ?> + <?php if ($holding['duedate'] ?? false): ?> + – <span class="small"><?=$this->transEsc("Due")?>: <?=$this->escapeHtml($holding['duedate'])?></span> + <?php endif; ?> + <?php if (($holding['requests_placed'] ?? 0) > 0): ?> + <span><?=$this->transEsc("Requests")?>: <?=$this->escapeHtml($holding['requests_placed'])?></span> + <?php endif; ?> + <?php if ($holding['link'] ?? false): ?> + <a class="<?=$check ? 'checkRequest' : ''?> placehold" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?=$this->recordLink()->getRequestUrl($holding['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Recall" : "Recall This")?></a> + <?php endif; ?> + <?=$this->relais()->renderButtonIfActive($this->driver ?? null)?> + <?php endif; ?> + <?php if (isset($holding['item_notes'])): ?> + <div class="item-notes"> + <b><?=$this->transEsc("Item Notes")?>:</b> + <ul> + <?php foreach ($holding['item_notes'] as $item_note): ?> + <li><?=$this->escapeHtml($item_note) ?></li> + <?php endforeach; ?> + </ul> + </div> + <?php endif; ?> + <?php endif; ?> + <?php /* Embed item structured data: library, barcode, call number */ ?> + <?php if ($holding['location'] ?? false): ?> + <meta property="seller" content="<?=$this->escapeHtmlAttr($holding['location'])?>" /> + <?php endif; ?> + <?php if ($holding['barcode'] ?? false): ?> + <meta property="serialNumber" content="<?=$this->escapeHtmlAttr($holding['barcode'])?>" /> + <?php endif; ?> + <?php if ($holding['callnumber'] ?? false): ?> + <meta property="sku" content="<?=$this->escapeHtmlAttr($holding['callnumber'])?>" /> + <?php endif; ?> + <?php /* Declare that the item is to be borrowed, not for sale */ ?> + <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut" /> + <link property="itemOffered" href="#record" /> + </td> + </tr> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/holdingsworldcat.phtml b/themes/bootstrap3/templates/RecordTab/holdingsworldcat.phtml index 00292694f6e99fa088587415f70f7719be278372..e25df3d8d0fb2fecd14c720942e83305182950de 100644 --- a/themes/bootstrap3/templates/RecordTab/holdingsworldcat.phtml +++ b/themes/bootstrap3/templates/RecordTab/holdingsworldcat.phtml @@ -1,31 +1,31 @@ -<? $holdings = $this->tab->getHoldings(); ?> -<? if (isset($holdings->diagnostic->message)): ?> +<?php $holdings = $this->tab->getHoldings(); ?> +<?php if (isset($holdings->diagnostic->message)): ?> <p><?=$this->escapeHtml($holdings->diagnostic->message)?></p> -<? elseif ($holdings && count($holdings) > 0): ?> +<?php elseif ($holdings && count($holdings) > 0): ?> <h3><?=$this->transEsc('Holdings at Other Libraries')?></h3> <table class="table table-striped"> - <? foreach ($holdings as $holding): ?> + <?php foreach ($holdings as $holding): ?> <tr> <th colspan="2"> - <? if (isset($holding->electronicAddress->text) && !empty($holding->electronicAddress->text)): ?> + <?php if (isset($holding->electronicAddress->text) && !empty($holding->electronicAddress->text)): ?> <a href="<?=$this->escapeHtmlAttr($holding->electronicAddress->text)?>"><?=$this->escapeHtml($holding->physicalLocation)?></a> - <? else: ?> + <?php else: ?> <?=$this->escapeHtml($holding->physicalLocation)?> - <? endif; ?> + <?php endif; ?> </th> </tr> - <? if (!empty($holding->physicalAddress->text)): ?> + <?php if (!empty($holding->physicalAddress->text)): ?> <tr> <th><?=$this->transEsc('Address')?>: </th> <td><?=$this->escapeHtml($holding->physicalAddress->text)?></td> </tr> - <? endif; ?> - <? if (isset($holding->holdingSimple->copiesSummary->copiesCount)): ?> + <?php endif; ?> + <?php if (isset($holding->holdingSimple->copiesSummary->copiesCount)): ?> <tr> <th><?=$this->transEsc('Copies')?>: </th> <td><?=$this->escapeHtml($holding->holdingSimple->copiesSummary->copiesCount)?></td> </tr> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </table> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/map.phtml b/themes/bootstrap3/templates/RecordTab/map.phtml index f80df20e0f2c1e2aed794f15c08d95bd20890804..1126a4eedd547da7df3d2275c7c10d4f830cc515 100644 --- a/themes/bootstrap3/templates/RecordTab/map.phtml +++ b/themes/bootstrap3/templates/RecordTab/map.phtml @@ -1,16 +1,21 @@ -<? $mapType = $this->tab->getMapType(); ?> -<? if (isset($mapType) && ($mapType == 'openlayers')) : ?> - <? $this->headScript()->appendFile('vendor/ol/ol.js'); - $this->headScript()->appendFile('map_tab_ol.js'); - $this->headLink()->appendStylesheet('vendor/ol/ol.css'); +<?php + $this->headScript()->appendFile('vendor/leaflet/leaflet.js'); + $this->headScript()->appendFile('vendor/leaflet/leaflet.latlng-graticule.js'); + $this->headScript()->appendFile('map_tab_leaflet.js'); + $this->headLink()->appendStylesheet('vendor/leaflet/leaflet.css'); + $this->jsTranslations()->addStrings( + ['Coordinates' => 'Coordinates', 'no_description' => 'no_description'] + ); $mapTabData = $this->tab->getMapTabData(); $mapGraticule = $this->tab->getMapGraticule(); - $popupTitle = $this->transEsc('map_results_label'); - $params = [json_encode($mapTabData), json_encode($popupTitle), json_encode($mapGraticule)]; + $basemap = $this->tab->getBasemap(); + $params = [ + json_encode($mapTabData), json_encode($mapGraticule), json_encode($basemap) + ]; $jsParams = implode(', ', $params); - $jsLoad = "loadMapTab(" . $jsParams . ");"; ?> - <div id="wrap" style="width: inherit; height: 479px"> - <div id="map-canvas" style="width: 100%; height: 100%"><div id="popup"></div></div> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $jsLoad, 'SET');?> - </div> -<? endif; ?> + $jsLoad = "loadMapTab(" . $jsParams . ");"; +?> +<div id="wrap" style="width: inherit; height: 479px"> + <div id="map-canvas" style="width: 100%; height: 100%"></div> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $jsLoad, 'SET')?> +</div> diff --git a/themes/bootstrap3/templates/RecordTab/preview.phtml b/themes/bootstrap3/templates/RecordTab/preview.phtml index 06bb49936f2a473bcfe4b4fd35860613d98ad5f9..b151b30ad9334e521566a28775d4187ce349a4b4 100644 --- a/themes/bootstrap3/templates/RecordTab/preview.phtml +++ b/themes/bootstrap3/templates/RecordTab/preview.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Preview') . ': ' . $this->driver->getBreadcrumb()); diff --git a/themes/bootstrap3/templates/RecordTab/reviews.phtml b/themes/bootstrap3/templates/RecordTab/reviews.phtml index a39b2352c57405bf9d1b54a2bc9033f531da686d..d95e06775e8737a016c06b5be94e2a752370845b 100644 --- a/themes/bootstrap3/templates/RecordTab/reviews.phtml +++ b/themes/bootstrap3/templates/RecordTab/reviews.phtml @@ -1,32 +1,32 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Reviews') . ': ' . $this->driver->getBreadcrumb()); // Grab review data: $reviews = $this->tab->getContent(); ?> -<? if (count($reviews) > 0): ?> - <? foreach ($reviews as $provider => $list): ?> - <? foreach ($list as $review): ?> - <? if (isset($review['Summary']) && !empty($review['Summary'])): ?> +<?php if (count($reviews) > 0): ?> + <?php foreach ($reviews as $provider => $list): ?> + <?php foreach ($list as $review): ?> + <?php if (isset($review['Summary']) && !empty($review['Summary'])): ?> <p> - <? if (isset($review['Rating'])): ?> + <?php if (isset($review['Rating'])): ?> <img src="<?=$this->imageLink($review['Rating'] . '.gif')?>" alt="<?=$review['Rating']?>/5 Stars"/> - <? endif; ?> + <?php endif; ?> <strong><?=$review['Summary']?></strong> <?=isset($review['Date']) ? strftime('%B %e, %Y', strtotime($review['Date'])) : ''?> </p> - <? endif; ?> - <? if (isset($review['Source'])): ?><strong><?=$this->transEsc('Review by')?> <?=$review['Source']?></strong><? endif; ?> + <?php endif; ?> + <?php if (isset($review['Source'])): ?><strong><?=$this->transEsc('Review by')?> <?=$review['Source']?></strong><?php endif; ?> <p class="summary"> - <?=isset($review['Content']) ? $review['Content'] : ''?> - <? if ((!isset($review['Content']) || empty($review['Content'])) && isset($review['ReviewURL'])): ?> + <?=$review['Content'] ?? ''?> + <?php if ((!isset($review['Content']) || empty($review['Content'])) && isset($review['ReviewURL'])): ?> <a target="new" href="<?=$this->escapeHtmlAttr($review['ReviewURL'])?>"><?=$this->transEsc('Read the full review online...')?></a> - <? endif; ?> + <?php endif; ?> </p> - <?=isset($review['Copyright']) ? $review['Copyright'] : ''?> + <?=$review['Copyright'] ?? ''?> <hr/> - <? endforeach; ?> - <? endforeach; ?> -<? else: ?> + <?php endforeach; ?> + <?php endforeach; ?> +<?php else: ?> <?=$this->transEsc('No reviews were found for this record')?>. -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/similaritemscarousel.phtml b/themes/bootstrap3/templates/RecordTab/similaritemscarousel.phtml index c2e591b20bc4d1a281dbdf08e255133d68820dc8..0c6e3d562771e5b64c3512292d4cf7b236c82121 100644 --- a/themes/bootstrap3/templates/RecordTab/similaritemscarousel.phtml +++ b/themes/bootstrap3/templates/RecordTab/similaritemscarousel.phtml @@ -1,42 +1,42 @@ <h4><?=$this->transEsc('Similar Items')?></h4> -<? $similarRecords = $this->tab->getResults(); ?> -<? if (!empty($similarRecords)): ?> - <? $perPage = 4 ?> +<?php $similarRecords = $this->tab->getResults(); ?> +<?php if (!empty($similarRecords)): ?> + <?php $perPage = 4 ?> <div id="similar-items-carousel" class="carousel slide" data-ride="carousel"> <!-- Indicators --> <ol class="carousel-indicators"> <li data-target="#similar-items-carousel" data-slide-to="0" class="active"></li> - <? for($i=1;$i<count($similarRecords)/$perPage;$i++): ?> + <?php for($i = 1;$i < count($similarRecords) / $perPage;$i++): ?> <li data-target="#similar-items-carousel" data-slide-to="<?=$i ?>"></li> - <? endfor; ?> + <?php endfor; ?> </ol> <!-- Wrapper for slides --> <div class="carousel-inner"> <div class="item active"> - <? foreach ($similarRecords as $index=>$data): ?> + <?php foreach ($similarRecords as $index => $data): ?> <div class="carousel-item"> <a class="hover-overlay" href="<?=$this->recordLink()->getUrl($data)?>"> - <? $thumb = $this->record($data)->getThumbnail('large'); ?> + <?php $thumb = $this->record($data)->getThumbnail('large'); ?> <img src="<?=$thumb ?>" title="<?=$data->getTitle() ?>"/> <div class="content"> - <? $formats = $data->getFormats(); ?> - <i class="fa fa-x<? if (count($formats) > 0): ?> fa-<?=preg_replace('/[^a-z0-9]/', '', strtolower($formats[0]))?>" title="<?=$formats[0] ?><? endif; ?>"></i> + <?php $formats = $data->getFormats(); ?> + <i class="fa fa-x<?php if (count($formats) > 0): ?> fa-<?=preg_replace('/[^a-z0-9]/', '', strtolower($formats[0]))?>" title="<?=$formats[0] ?><?php endif; ?>"></i> <b><?=$this->escapeHtml($data->getTitle())?></b> - <? $authors = $data->getPrimaryAuthors(); if (!empty($authors)): ?> - <br/><?=$this->transEsc('by')?>: <?=$this->escapeHtml($authors[0]);?><? if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?> - <? endif; ?> - <? $pubDates = $data->getPublicationDates(); if (!empty($pubDates)): ?> + <?php $authors = $data->getPrimaryAuthors(); if (!empty($authors)): ?> + <br/><?=$this->transEsc('by')?>: <?=$this->escapeHtml($authors[0]);?><?php if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?> + <?php endif; ?> + <?php $pubDates = $data->getPublicationDates(); if (!empty($pubDates)): ?> <br/><?=$this->transEsc('Published')?>: (<?=$this->escapeHtml($pubDates[0])?>) - <? endif; ?> + <?php endif; ?> </div> </a> </div> - <? if(($index+1) % $perPage == 0 && $index < count($similarRecords)-1): ?> + <?php if(($index + 1) % $perPage == 0 && $index < count($similarRecords) - 1): ?> </div> <div class="item"> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </div> </div> @@ -48,10 +48,10 @@ <span class="fa fa-chevron-right glyphicon-chevron-right" title="<?=$this->transEsc('Next') ?>"></span> </a> </div> -<? else: ?> +<?php else: ?> <p><?=$this->transEsc('Cannot find similar records')?></p> -<? endif; ?> -<? +<?php endif; ?> +<?php $script = <<<JS var normalizeHeightCount = $('#similar-items-carousel img').length; function normalizeHeights() { diff --git a/themes/bootstrap3/templates/RecordTab/staffviewarray.phtml b/themes/bootstrap3/templates/RecordTab/staffviewarray.phtml index e4e80bd770d851cb2b8907a07d459a717075ff03..86ece285924519387d7ec034346d8915719159b3 100644 --- a/themes/bootstrap3/templates/RecordTab/staffviewarray.phtml +++ b/themes/bootstrap3/templates/RecordTab/staffviewarray.phtml @@ -1,17 +1,16 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); ?> <table class="citation table table-striped"> - <? foreach ($this->driver->getRawData() as $field => $values): ?> + <?php foreach ($this->driver->getRawData() as $field => $values): ?> <tr> <th><?=$this->escapeHtml($field)?></th> <td> - <? if (!is_array($values)) { $values = [$values]; } ?> - <? foreach ($values as $value): ?> + <?php foreach ((array)$values as $value): ?> <?=$this->escapeHtml(is_array($value) ? print_r($value, true) : $value)?><br /> - <? endforeach; ?> + <?php endforeach; ?> </td> </tr> - <? endforeach; ?> + <?php endforeach; ?> </table> diff --git a/themes/bootstrap3/templates/RecordTab/staffviewmarc.phtml b/themes/bootstrap3/templates/RecordTab/staffviewmarc.phtml index e00ee857b49c8868f643efd9c2be7eed9341a7e3..d41b84a73e6af073fbe6c178bd29da95d5507407 100644 --- a/themes/bootstrap3/templates/RecordTab/staffviewmarc.phtml +++ b/themes/bootstrap3/templates/RecordTab/staffviewmarc.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); ?> diff --git a/themes/bootstrap3/templates/RecordTab/toc.phtml b/themes/bootstrap3/templates/RecordTab/toc.phtml index e0fa6787a9b4db7b15e66018a7db2e98c8655eca..3538894eb6a7c0543c5812c80570c14975af0245 100644 --- a/themes/bootstrap3/templates/RecordTab/toc.phtml +++ b/themes/bootstrap3/templates/RecordTab/toc.phtml @@ -1,16 +1,28 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Table of Contents') . ': ' . $this->driver->getBreadcrumb()); - $toc = $this->driver->getTOC(); + $toc = $this->tab->getContent(); + if (empty($toc)) { + $driverToc = $this->driver->getTOC(); + if (!empty($driverToc)) { + $toc['RecordDriver'] = $driverToc; + } + } ?> -<? if (!empty($toc)): ?> +<?php if (!empty($toc)): ?> <strong><?=$this->transEsc('Table of Contents')?>: </strong> - <ul class="toc"> - <? foreach ($toc as $line): ?> - <li><?=$this->escapeHtml($line)?></li> - <? endforeach; ?> - </ul> -<? else: ?> - <?=$this->transEsc("Table of Contents unavailable")?>. -<? endif; ?> + <?php foreach ($toc as $provider => $content): ?> + <?php if (!is_array($content)): // treat non-array content as raw HTML ?> + <?=$content?> + <?php else: ?> + <ul class="toc"> + <?php foreach ($content as $line): ?> + <li><?=$this->escapeHtml($line)?></li> + <?php endforeach; ?> + </ul> + <?php endif; ?> + <?php endforeach; ?> +<?php else: ?> + <?=$this->transEsc('Table of Contents unavailable')?>. +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/usercomments.phtml b/themes/bootstrap3/templates/RecordTab/usercomments.phtml index 7fe6ab303d31668cf50c191f647fe1c5ac3de368..f3b2887135c68270a9fe056e46bf58eadd711f1d 100644 --- a/themes/bootstrap3/templates/RecordTab/usercomments.phtml +++ b/themes/bootstrap3/templates/RecordTab/usercomments.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Comments') . ': ' . $this->driver->getBreadcrumb()); ?> @@ -10,16 +10,16 @@ <input type="hidden" name="id" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>"/> <input type="hidden" name="source" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>"/> <label class="comment-label"><?=$this->transEsc("Your Comment")?></label> - <? $user = $this->auth()->isLoggedIn() ?> - <? if($user): ?> + <?php $user = $this->auth()->isLoggedIn() ?> + <?php if($user): ?> <div class="text-form"> <textarea name="comment" class="form-control" rows="3" required></textarea> - <? if ($this->tab->isRecaptchaActive()): ?> + <?php if ($this->tab->isRecaptchaActive()): ?> <?=$this->recaptcha()->html(true, false) ?> - <? endif; ?> + <?php endif; ?> <input class="btn btn-primary" data-loading-text="<?=$this->transEsc('Submitting') ?>..." type="submit" value="<?=$this->transEsc("Add your comment")?>"/> </div> - <? else: ?> + <?php else: ?> <a href="<?=$this->url('myresearch-userlogin') ?>" class="btn btn-primary" data-lightbox title="Login"><i class="fa fa-sign-in" aria-hidden="true"></i> <?=$this->transEsc("You must be logged in first") ?></a> - <? endif; ?> + <?php endif; ?> </form> diff --git a/themes/bootstrap3/templates/Related/Channels.phtml b/themes/bootstrap3/templates/Related/Channels.phtml index a4a3b046c8ee5441044705ab5c26b0aba0d8188b..4ae2c885659076e82e17a2d9beecd2b2a42b9c1c 100644 --- a/themes/bootstrap3/templates/Related/Channels.phtml +++ b/themes/bootstrap3/templates/Related/Channels.phtml @@ -1,4 +1,4 @@ -<? +<?php $driver = $this->related->getDriver(); $link = $this->url('channels-record') . '?id=' . urlencode($driver->getUniqueId()) diff --git a/themes/bootstrap3/templates/Related/Deprecated.phtml b/themes/bootstrap3/templates/Related/Deprecated.phtml index ff8266ef2c7dcbafc58aa400e648b37946ac45fd..69cb7c76ea6d65c9f2612077be5629547cf6f2d5 100644 --- a/themes/bootstrap3/templates/Related/Deprecated.phtml +++ b/themes/bootstrap3/templates/Related/Deprecated.phtml @@ -1,2 +1,2 @@ -<? /* do nothing -- this module is a placeholder for old deprecated features +<?php /* do nothing -- this module is a placeholder for old deprecated features to prevent legacy configurations from causing fatal errors. */ ?> diff --git a/themes/bootstrap3/templates/Related/Similar.phtml b/themes/bootstrap3/templates/Related/Similar.phtml index 94047e13130fbc35777d7574d90555dfa83ad2a9..feeab41e8e84fd4acd4a94b41e46fdcecb68d545 100644 --- a/themes/bootstrap3/templates/Related/Similar.phtml +++ b/themes/bootstrap3/templates/Related/Similar.phtml @@ -1,23 +1,23 @@ <h4><?=$this->transEsc('Similar Items')?></h4> -<? $similarRecords = $this->related->getResults(); ?> -<? if (!empty($similarRecords)): ?> +<?php $similarRecords = $this->related->getResults(); ?> +<?php if (!empty($similarRecords)): ?> <ul class="list-group"> - <? foreach ($similarRecords as $data): ?> + <?php foreach ($similarRecords as $data): ?> <li class="list-group-item"> - <? $formats = $data->getFormats(); ?> - <i class="fa fa-x<? if (count($formats) > 0): ?> fa-<?=preg_replace('/[^a-z0-9]/', '', strtolower($formats[0]))?>" title="<?=$formats[0] ?><? endif; ?>"></i> + <?php $formats = $data->getFormats(); ?> + <i class="fa fa-x<?php if (count($formats) > 0): ?> fa-<?=preg_replace('/[^a-z0-9]/', '', strtolower($formats[0]))?>" title="<?=$formats[0] ?><?php endif; ?>"></i> <a href="<?=$this->recordLink()->getUrl($data)?>"> <?=$this->escapeHtml($data->getTitle())?> </a> - <? $authors = $data->getPrimaryAuthors(); if (!empty($authors)): ?> - <br/><?=$this->transEsc('by')?>: <?=$this->escapeHtml($authors[0]);?><? if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?> - <? endif; ?> - <? $pubDates = $data->getPublicationDates(); if (!empty($pubDates)): ?> + <?php $authors = $data->getPrimaryAuthors(); if (!empty($authors)): ?> + <br/><?=$this->transEsc('by')?>: <?=$this->escapeHtml($authors[0]);?><?php if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?> + <?php endif; ?> + <?php $pubDates = $data->getPublicationDates(); if (!empty($pubDates)): ?> <br/><?=$this->transEsc('Published')?>: (<?=$this->escapeHtml($pubDates[0])?>) - <? endif; ?> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> -<? else: ?> +<?php else: ?> <p><?=$this->transEsc('Cannot find similar records')?></p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/admin/config/home.phtml b/themes/bootstrap3/templates/admin/config/home.phtml index 8a97b35c69eae28d2aab7ceac0124498187c0f0b..8a9e84a5b08f5f63b3e33b7c463d48d0b4c9ee9f 100644 --- a/themes/bootstrap3/templates/admin/config/home.phtml +++ b/themes/bootstrap3/templates/admin/config/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('VuFind Administration - Configuration')); ?> @@ -7,12 +7,12 @@ <?=$this->flashmessages()?> <p>Most VuFind configuration is handled by editing the configuration files in <strong><?=$this->escapeHtml($this->baseConfigPath)?></strong>.</p> <p>Some basic settings can also be adjusted through the auto-configuration tool.</p> - <? if (!$this->showInstallLink): ?> + <?php if (!$this->showInstallLink): ?> <p><?=$this->transEsc('Auto configuration is currently disabled') ?>.</p> - <p><a href="<?=$this->url('admin/config', array('action' => 'EnableAutoConfig'))?>"><?=$this->transEsc('Enable Auto Config')?></a></p> - <? else: ?> + <p><a href="<?=$this->url('admin/config', ['action' => 'EnableAutoConfig'])?>"><?=$this->transEsc('Enable Auto Config')?></a></p> + <?php else: ?> <p><a href="<?=$this->url('install-home')?>"><?=$this->transEsc('auto_configure_title')?></a></p> - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/admin/home.phtml b/themes/bootstrap3/templates/admin/home.phtml index 59e43f64b71c4412dcda022c57e7127c2ef44918..36de2e5b41be303461e8efa270caaabb509fa293 100644 --- a/themes/bootstrap3/templates/admin/home.phtml +++ b/themes/bootstrap3/templates/admin/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('VuFind Administration - Home')); @@ -6,39 +6,38 @@ $coreLabels = [ 'biblio' => $this->translate('Bibliographic Index'), 'authority' => $this->translate('Authority Index'), - 'stats' => $this->translate('Usage Statistics Index') ]; ?> <div class="<?=$this->layoutClass('mainbody')?>"> <h2><?=$this->transEsc('VuFind Administration')?></h2> - <? $cores = is_object($this->xml) ? $this->xml->xpath('/response/lst[@name="status"]/lst') : []; ?> - <? foreach ($cores as $core): ?> - <? $coreName = (string)$core['name']; ?> - <? $coreLabel = isset($coreLabels[$coreName]) ? $coreLabels[$coreName] : ucwords($coreName) . ' Index'; ?> + <?php $cores = is_object($this->xml) ? $this->xml->xpath('/response/lst[@name="status"]/lst') : []; ?> + <?php foreach ($cores as $core): ?> + <?php $coreName = (string)$core['name']; ?> + <?php $coreLabel = $coreLabels[$coreName] ?? ucwords($coreName) . ' Index'; ?> <h3><?=$this->transEsc($coreLabel)?></h3> <table class="table table-striped"> <tr> <th><?=$this->transEsc('Record Count')?>: </th> - <? $recordCount = $core->xpath('//lst[@name="' . $coreName . '"]/lst/int[@name="numDocs"]') ?> + <?php $recordCount = $core->xpath('//lst[@name="' . $coreName . '"]/lst/int[@name="numDocs"]') ?> <td><?=$this->escapeHtml((string)array_pop($recordCount))?></td> </tr> <tr> <th><?=$this->transEsc('Start Time')?>: </th> - <? $startTime = $core->xpath('//lst[@name="' . $coreName . '"]/date[@name="startTime"]') ?> + <?php $startTime = $core->xpath('//lst[@name="' . $coreName . '"]/date[@name="startTime"]') ?> <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($startTime))))?></td> </tr> <tr> <th><?=$this->transEsc('Last Modified')?>: </th> - <? $lastModified = $core->xpath('//lst[@name="' . $coreName . '"]/lst/date[@name="lastModified"]') ?> + <?php $lastModified = $core->xpath('//lst[@name="' . $coreName . '"]/lst/date[@name="lastModified"]') ?> <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($lastModified))))?></td> </tr> <tr> <th><?=$this->transEsc('Uptime')?>: </th> - <? $uptime = $core->xpath('//lst[@name="' . $coreName . '"]/long[@name="uptime"]') ?> + <?php $uptime = $core->xpath('//lst[@name="' . $coreName . '"]/long[@name="uptime"]') ?> <td><?=$this->printms((string)array_pop($uptime))?></td> </tr> </table> - <? endforeach; ?> + <?php endforeach; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/admin/maintenance/home.phtml b/themes/bootstrap3/templates/admin/maintenance/home.phtml index e5c98577a04499897f3149ca42dc6f44a4a5adb9..16d86ef679c503b102e8351f43757fd89175d352 100644 --- a/themes/bootstrap3/templates/admin/maintenance/home.phtml +++ b/themes/bootstrap3/templates/admin/maintenance/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('VuFind Administration - System Maintenance')); ?> @@ -7,26 +7,26 @@ <h3>Utilities</h3> <?=$this->flashmessages()?> - <form class="form-admin-maintenance expired-searches" method="get" action="<?=$this->url('admin/maintenance', array('action' => 'DeleteExpiredSearches'))?>"> + <form class="form-admin-maintenance expired-searches" method="get" action="<?=$this->url('admin/maintenance', ['action' => 'DeleteExpiredSearches'])?>"> <label for="del_daysOld">Delete unsaved user search histories older than</label> <input id="del_daysOld" type="number" name="daysOld" size="5" value="2" class="form-control"/> days. <input type="submit" name="submit" value="<?=$this->transEsc('Submit')?>" class="btn btn-danger"/> </form> <hr /> - <form class="form-admin-maintenance expired-sessions" method="get" action="<?=$this->url('admin/maintenance', array('action' => 'DeleteExpiredSessions'))?>"> + <form class="form-admin-maintenance expired-sessions" method="get" action="<?=$this->url('admin/maintenance', ['action' => 'DeleteExpiredSessions'])?>"> <label for="delsess_daysOld">Delete user sessions older than</label> <input id="delsess_daysOld" type="number" name="daysOld" size="5" value="2" class="form-control"/> days. <input type="submit" name="submit" value="<?=$this->transEsc('Submit')?>" class="btn btn-danger"/> </form> <hr /> - <form class="form-admin-maintenance clear-cache" method="get" action="<?=$this->url('admin/maintenance', array('action' => 'ClearCache'))?>"> + <form class="form-admin-maintenance clear-cache" method="get" action="<?=$this->url('admin/maintenance', ['action' => 'ClearCache'])?>"> Clear cache(s): - <? foreach ($caches as $cache): ?> + <?php foreach ($caches as $cache): ?> <label> <input type="checkbox" checked="checked" name="cache[]" value="<?=$this->escapeHtmlAttr($cache)?>" /> <?=$this->escapeHtml($cache) ?> </label> - <? endforeach; ?> + <?php endforeach; ?> <input type="submit" name="submit" value="<?=$this->transEsc('Submit')?>" class="btn btn-danger"/> </form> </div> diff --git a/themes/bootstrap3/templates/admin/socialstats/home.phtml b/themes/bootstrap3/templates/admin/socialstats/home.phtml index 7fb0f9c2d799276cc1235ac091bc6b85c253685e..c22b4fd3ddcafac52896540a8864325e572c2a82 100644 --- a/themes/bootstrap3/templates/admin/socialstats/home.phtml +++ b/themes/bootstrap3/templates/admin/socialstats/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('VuFind Administration - Social Statistics')); ?> diff --git a/themes/bootstrap3/templates/admin/tags/home.phtml b/themes/bootstrap3/templates/admin/tags/home.phtml index 0a8f646b427c180ed4615e6603b1171a666175d4..c857f4da48b3049f35f27c3a0252c4fd04f44827 100644 --- a/themes/bootstrap3/templates/admin/tags/home.phtml +++ b/themes/bootstrap3/templates/admin/tags/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('VuFind Administration - Tag Management')); ?> diff --git a/themes/bootstrap3/templates/admin/tags/list.phtml b/themes/bootstrap3/templates/admin/tags/list.phtml index edf5daabbef6187a2740a346a1ba4aa647835a02..5715be6465a92e8b8457983c5c975d905d89ed4a 100644 --- a/themes/bootstrap3/templates/admin/tags/list.phtml +++ b/themes/bootstrap3/templates/admin/tags/list.phtml @@ -11,53 +11,53 @@ <?=$this->flashmessages()?> - <form class="form-tags-list" action="<?= $this->url('admin/tags', array('action' => 'List'))?>" method="get"> + <form class="form-tags-list" action="<?= $this->url('admin/tags', ['action' => 'List'])?>" method="get"> <h3><?=$this->translate('filter_tags')?></h3> <div class="tag-controls"> <label for="user_id"> <?=$this->translate('Username')?> <select name="user_id" id="user_id" class="form-control"> <option value="ALL"><?=$this->translate('All')?></option> - <? foreach($this->uniqueUsers as $user):?> - <option value="<?= $user['user_id'] ?>"<? if(isset($this->params['user_id']) && $user['user_id'] == $this->params['user_id']): ?> selected="selected"<? endif;?>> + <?php foreach($this->uniqueUsers as $user):?> + <option value="<?= $user['user_id'] ?>"<?php if(isset($this->params['user_id']) && $user['user_id'] == $this->params['user_id']): ?> selected="selected"<?php endif;?>> <?=$user['username'] ?> </option> - <? endforeach;?> + <?php endforeach;?> </select> </label> <label for="tag_id"> <?=$this->translate('Tag')?> <select name="tag_id" id="tag_id" class="form-control"> <option value="ALL"><?=$this->translate('All')?></option> - <? foreach($this->uniqueTags as $tag):?> - <option value="<?= $tag['tag_id'] ?>"<? if(isset($this->params['tag_id']) && $tag['tag_id'] == $this->params['tag_id']): ?> selected="selected"<? endif;?>> + <?php foreach($this->uniqueTags as $tag):?> + <option value="<?= $tag['tag_id'] ?>"<?php if(isset($this->params['tag_id']) && $tag['tag_id'] == $this->params['tag_id']): ?> selected="selected"<?php endif;?>> <?=$tag['tag'] ?> </option> - <? endforeach;?> + <?php endforeach;?> </select> </label> <label for="resource_id"> <?=$this->translate('Title')?> <select name="resource_id" id="resource_id" class="form-control"> <option value="ALL"><?=$this->translate('All')?></option> - <? foreach($this->uniqueResources as $resource):?> - <option value="<?= $resource['resource_id']; ?>" title="<?=$resource['title'] ?>"<? if(isset($this->params['resource_id']) && $resource['resource_id'] == $this->params['resource_id']): ?> selected="selected"<? endif;?>> + <?php foreach($this->uniqueResources as $resource):?> + <option value="<?= $resource['resource_id']; ?>" title="<?=$resource['title'] ?>"<?php if(isset($this->params['resource_id']) && $resource['resource_id'] == $this->params['resource_id']): ?> selected="selected"<?php endif;?>> <?=$this->truncate($resource['title'], 80) ?> (<?=$resource['resource_id'] ?>) </option> - <? endforeach;?> + <?php endforeach;?> </select> </label> <label for="taglistsubmit"> <input type="submit" id="taglistsubmit" value="<?=$this->transEsc('Filter')?>" class="btn btn-primary"> - <? if((isset($this->params['user_id']) && !is_null($this->params['user_id'])) || (isset($this->params['tag_id']) && !is_null($this->params['tag_id'])) || (isset($this->params['resource_id']) && !is_null($this->params['resource_id']))):?> - <a href="<?= $this->url('admin/tags', array('action' => 'List')); ?>"><?=$this->translate('clear_tag_filter')?></a> - <? endif;?> + <?php if((isset($this->params['user_id']) && null !== $this->params['user_id']) || (isset($this->params['tag_id']) && null !== $this->params['tag_id']) || (isset($this->params['resource_id']) && null !== $this->params['resource_id'])):?> + <a href="<?= $this->url('admin/tags', ['action' => 'List']); ?>"><?=$this->translate('clear_tag_filter')?></a> + <?php endif;?> </label> </div> </form> - <? if(count($this->results) > 0):?> - <form action="<?= $this->url('admin/tags', array('action' => 'Delete'))?>" method="post"> + <?php if(count($this->results) > 0):?> + <form action="<?= $this->url('admin/tags', ['action' => 'Delete'])?>" method="post"> <input type="hidden" name="user_id" value="<?=isset($this->params['user_id']) ? $this->params['user_id'] : '' ?>" /> <input type="hidden" name="tag_id" value="<?=isset($this->params['tag_id']) ? $this->params['tag_id'] : '' ?>" /> <input type="hidden" name="resource_id" value="<?=isset($this->params['resource_id']) ? $this->params['resource_id'] : '' ?>" /> @@ -70,7 +70,7 @@ <th><?=$this->translate('Title')?></th> </tr> - <? foreach ($this->results as $tag): ?> + <?php foreach ($this->results as $tag): ?> <tr> <td> <label for="<?=$this->prefix?>checkbox_<?=$tag['id']?>"> @@ -82,7 +82,7 @@ <td><?=$tag->username ?> (<?= $tag->user_id?>)</td> <td><?=$tag->title?> (<?= $tag->resource_id?>)</td> </tr> - <? endforeach;?> + <?php endforeach;?> </table> <input type="submit" name="deleteSelected" value="<?=$this->transEsc('delete_selected')?>" class="btn btn-default"> @@ -90,10 +90,10 @@ <input type="submit" name="deleteFilter" value="<?=$this->transEsc('delete_all')?>" class="btn btn-danger"> </form> - <?=$this->paginationControl($this->results, 'Sliding', 'Helpers/pagination.phtml', array('params' => $this->params))?> - <? else:?> + <?=$this->paginationControl($this->results, 'Sliding', 'Helpers/pagination.phtml', ['params' => $this->params])?> + <?php else:?> <p><?=$this->translate('tag_filter_empty')?></p> - <? endif;?> + <?php endif;?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/admin/tags/manage.phtml b/themes/bootstrap3/templates/admin/tags/manage.phtml index 426b2d51398bb38117c92d05798c787d33724179..cfe5bed4cec253294174ba41726b1f456eca9c3a 100644 --- a/themes/bootstrap3/templates/admin/tags/manage.phtml +++ b/themes/bootstrap3/templates/admin/tags/manage.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('VuFind Administration - Tag Maintenance')); ?> @@ -11,61 +11,61 @@ <?=$this->flashmessages()?> - <form class="form-tags-manage" action="<?= $this->url('admin/tags', array('action' => 'Manage'));?>" method="post" role="form"> + <form class="form-tags-manage" action="<?= $this->url('admin/tags', ['action' => 'Manage']);?>" method="post" role="form"> <div class="form-group"> <label for="type" class="control-label"><?=$this->translate('delete_tags_by')?>:</label> <select id="type" name="type" class="form-control"> - <option value="user" <? if("user" == $this->type) echo " selected=selected";?>><?=$this->translate('Username')?></option> - <option value="tag" <? if("tag" == $this->type) echo " selected=selected";?>><?=$this->translate('Tag')?></option> - <option value="resource" <? if("resource" == $this->type) echo " selected=selected";?>><?=$this->translate('Title')?></option> + <option value="user" <?=("user" == $this->type) ? ' selected=selected' : ''?>><?=$this->translate('Username')?></option> + <option value="tag" <?=("tag" == $this->type) ? ' selected=selected' : ''?>><?=$this->translate('Tag')?></option> + <option value="resource" <?=("resource" == $this->type) ? ' selected=selected' : ''?>><?=$this->translate('Title')?></option> </select> <input type="submit" value="<?=$this->translate('Submit')?>" class="btn btn-primary"/> </div> </form> - <? if(false !== $this->type):?> - <form class="form-tags-manage" action="<?= $this->url('admin/tags', array('action' => 'Delete'))?>" method="post"> + <?php if(false !== $this->type):?> + <form class="form-tags-manage" action="<?= $this->url('admin/tags', ['action' => 'Delete'])?>" method="post"> <input type="hidden" name="origin" value="manage" /> <input type="hidden" name="type" value="<?= $this->type; ?>" /> - <? if("user" == $type):?> + <?php if("user" == $type):?> <div class="form-group"> <label for="user_id" class="control-label"><?=$this->translate('Username')?></label> <select name="user_id" id="user_id" class="form-control"> - <? foreach($this->uniqueUsers as $user):?> + <?php foreach($this->uniqueUsers as $user):?> <option value="<?= $user['user_id'] ?>"> <?= $user['username'] ?> </option> - <? endforeach;?> + <?php endforeach;?> </select> <input type="submit" name="deleteFilter" value="<?=$this->translate('delete_tags')?>" class="btn btn-primary"/> </div> - <? elseif("tag" == $type):?> + <?php elseif("tag" == $type):?> <div class="form-group"> <label for="tag_id" class="control-label"><?=$this->translate('Tag')?></label> <select name="tag_id" id="tag_id" class="form-control"> - <? foreach($this->uniqueTags as $tag):?> + <?php foreach($this->uniqueTags as $tag):?> <option value="<?= $tag['tag_id'] ?>"> <?= $tag['tag'] ?> </option> - <? endforeach;?> + <?php endforeach;?> </select> <input type="submit" name="deleteFilter" value="<?=$this->translate('delete_tags')?>" class="btn btn-primary"/> </div> - <? elseif("resource" == $type):?> + <?php elseif("resource" == $type):?> <div class="form-group"> <label for="resource_id" class="control-label"><?=$this->translate('Title')?></label> <select name="resource_id" id="resource_id" class="form-control"> - <? foreach($this->uniqueResources as $resource):?> + <?php foreach($this->uniqueResources as $resource):?> <option value="<?=$resource['resource_id'] ?>" title="<?=$resource['title'] ?>"> <?=$this->truncate($resource['title'], 80) ?> (<?= $resource['resource_id'] ?>) </option> - <? endforeach;?> + <?php endforeach;?> </select> <input type="submit" name="deleteFilter" value="<?=$this->translate('delete_tags')?>" class="btn btn-primary"/> </div> - <? endif;?> + <?php endif;?> </form> - <? endif;?> + <?php endif;?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/admin/tags/menu.phtml b/themes/bootstrap3/templates/admin/tags/menu.phtml index ad57f45fa96eeae9c490b245a9e9953e30c29d7d..c1f4c4eb517600ef14a12d4bb5e9c228d67ae9aa 100644 --- a/themes/bootstrap3/templates/admin/tags/menu.phtml +++ b/themes/bootstrap3/templates/admin/tags/menu.phtml @@ -1,6 +1,6 @@ <div class="nav navbar"> <ul class="nav nav-pills"> - <li<?=strtolower($this->active) == "list" ? ' class="active"' : ''?>><a href="<?=$this->url('admin/tags', array('action' => 'List'))?>"><?=$this->transEsc('List Tags')?></a></li> - <li<?=strtolower($this->active) == "manage" ? ' class="active"' : ''?>><a href="<?=$this->url('admin/tags', array('action' => 'Manage'))?>"><?=$this->transEsc('Manage Tags')?></a></li> + <li<?=strtolower($this->active) == "list" ? ' class="active"' : ''?>><a href="<?=$this->url('admin/tags', ['action' => 'List'])?>"><?=$this->transEsc('List Tags')?></a></li> + <li<?=strtolower($this->active) == "manage" ? ' class="active"' : ''?>><a href="<?=$this->url('admin/tags', ['action' => 'Manage'])?>"><?=$this->transEsc('Manage Tags')?></a></li> </ul> </div> diff --git a/themes/bootstrap3/templates/ajax/export-favorites.phtml b/themes/bootstrap3/templates/ajax/export-favorites.phtml deleted file mode 100644 index 8caa049b958ff241a5d8aa40e1bf82265b1b0dde..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/ajax/export-favorites.phtml +++ /dev/null @@ -1,10 +0,0 @@ -<div class="alert alert-info"> - <div class="text-center"> - <?=$this->transEsc('export_success'); ?> — - <a class="btn btn-primary" href="<?=$this->escapeHtmlAttr($this->url)?>"<?=$this->export()->needsRedirect($this->format) ? ' target="_blank"' : ''?>><?= - $this->export()->needsRedirect($this->format) - ? $this->transEsc('export_redirect', ['%%service%%' => $this->translate($this->export()->getLabelForFormat($this->format))]) - : $this->transEsc('export_download') - ?></a> - </div> -</div> diff --git a/themes/bootstrap3/templates/ajax/resolverLinks.phtml b/themes/bootstrap3/templates/ajax/resolverLinks.phtml index 2a602e55378d41c14fe27fe6820b37445041bbcf..44992801ee405d5edecbcbff9a7bb5faecd5b73b 100644 --- a/themes/bootstrap3/templates/ajax/resolverLinks.phtml +++ b/themes/bootstrap3/templates/ajax/resolverLinks.phtml @@ -1,48 +1,48 @@ <div> - <? if (!empty($this->electronic)): ?> + <?php if (!empty($this->electronic)): ?> <div class="openurls"> <strong><?=$this->transEsc('Electronic')?></strong> <ul> - <? foreach ($this->electronic as $link): ?> + <?php foreach ($this->electronic as $link): ?> <li> - <? if (!empty($link['href'])): ?> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-'.$link['access'].'"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <? else: ?> + <?php if (!empty($link['href'])): ?> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-' . $link['access'] . '"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> + <?php else: ?> <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <? endif; ?> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? endif; ?> - <? if (!empty($this->print)): ?> + <?php endif; ?> + <?php if (!empty($this->print)): ?> <div class="openurls"> <strong><?=$this->transEsc('Holdings')?></strong> <ul> - <? foreach ($this->print as $link): ?> + <?php foreach ($this->print as $link): ?> <li> - <? if (!empty($link['href'])): ?> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-'.$link['access'].'"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <? else: ?> + <?php if (!empty($link['href'])): ?> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-' . $link['access'] . '"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> + <?php else: ?> <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <? endif; ?> + <?php endif; ?> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? endif; ?> + <?php endif; ?> <div class="openurls"> - <? if (!empty($this->moreOptionsLink)): ?><strong><a href="<?=$this->escapeHtmlAttr($this->moreOptionsLink)?>"><?=$this->transEsc('More options')?></a></strong><?endif; ?> - <? if (!empty($this->services)): ?> + <?php if (!empty($this->moreOptionsLink)): ?><strong><a href="<?=$this->escapeHtmlAttr($this->moreOptionsLink)?>"><?=$this->transEsc('More options')?></a></strong><?php endif; ?> + <?php if (!empty($this->services)): ?> <ul> - <? foreach ($this->services as $link): ?> - <? if (!empty($link['href'])): ?> + <?php foreach ($this->services as $link): ?> + <?php if (!empty($link['href'])): ?> <li> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-'.$link['access'].'"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-' . $link['access'] . '"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> </li> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </ul> - <? endif; ?> + <?php endif; ?> </div> </div> diff --git a/themes/bootstrap3/templates/ajax/resultgooglemapinfo.phtml b/themes/bootstrap3/templates/ajax/resultgooglemapinfo.phtml index 193957ee40b7d30f74364498400efe76411a768f..e8fa5661e94ed0bc241a251b2fe3d5d895719a00 100644 --- a/themes/bootstrap3/templates/ajax/resultgooglemapinfo.phtml +++ b/themes/bootstrap3/templates/ajax/resultgooglemapinfo.phtml @@ -1,29 +1,29 @@ <div class="mapInfoWrapper"> <h4><?=$this->transEsc('map_results_label')?></h4> <div class="mapInfoResults"> - <? $i = 0; ?> - <? foreach($this->recordSet as $record): ?> - <? $i++; ?> - <div class="mapInfoResult <? if ($i % 2 == 0): ?>alt <? endif; ?>record<?=$i ?>"> + <?php $i = 0; ?> + <?php foreach($this->recordSet as $record): ?> + <?php $i++; ?> + <div class="mapInfoResult <?php if ($i % 2 == 0): ?>alt <?php endif; ?>record<?=$i ?>"> <div class="mapInfoResultThumb"> - <? if ($thumb = $this->record($record)->getThumbnail()): ?><img class="mapInfoResultThumbImg" src="<?=$this->escapeHtmlAttr($thumb) ?>"/><? endif; ?> + <?php if ($thumb = $this->record($record)->getThumbnail()): ?><img class="mapInfoResultThumbImg" src="<?=$this->escapeHtmlAttr($thumb) ?>"/><?php endif; ?> </div> • <a href="<?=$this->recordLink()->getUrl($record)?>"><?=($title = $record->getTitle()) ? $title : $this->transEsc('Title not available') ?></a> - <? $authors = $record->getPrimaryAuthors(); if (!empty($authors)): ?> + <?php $authors = $record->getPrimaryAuthors(); if (!empty($authors)): ?> <span class="small"> - <?=$this->transEsc('by')?> <a href="<?=$this->record($record)->getLink('author', $authors[0])?>"><?=$this->escapeHtml($authors[0]);?></a><? if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?> + <?=$this->transEsc('by')?> <a href="<?=$this->record($record)->getLink('author', $authors[0])?>"><?=$this->escapeHtml($authors[0]);?></a><?php if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?> </span><br/> - <? endif; ?> + <?php endif; ?> </div> <div class="clearfix"></div> - <? if ($i == 5) break; ?> - <? endforeach; ?> + <?php if ($i == 5) break; ?> + <?php endforeach; ?> </div> - <? if ($this->recordCount > 5): ?> + <?php if ($this->recordCount > 5): ?> <div class="mapSeeAllDiv"> <a href="<?=$this->url('search-results') ?><?=$this->results->getUrlQuery()->getParams() ?>"><?=$this->transEsc('see all') ?> <?=$this->escapeHtml($this->recordCount) ?>...</a> </div> - <? endif; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/ajax/status-available-services.phtml b/themes/bootstrap3/templates/ajax/status-available-services.phtml index 46752eb03cf9ee5b56390828b2ca01dc580d6675..e3e3499ad64157764e10b97f080906e1b7828cfe 100644 --- a/themes/bootstrap3/templates/ajax/status-available-services.phtml +++ b/themes/bootstrap3/templates/ajax/status-available-services.phtml @@ -1,4 +1,4 @@ -<? +<?php // defaultServiceStatusMessage for multiple status and as fallback for missing translations $defaultServiceStatusMessage = 'HoldingStatus::services_available_html'; @@ -6,7 +6,7 @@ $defaultServiceStatusMessage = 'HoldingStatus::services_available_html'; $serviceStatusMessage = count($services) == 1 ? $this->transEsc('HoldingStatus::service_available_' . $services[0], [], $defaultServiceStatusMessage) - : $defaultServiceStatusMessage ; + : $defaultServiceStatusMessage; // build the translated serviceList for usage in defaultServiceStatusMessage $that = $this; diff --git a/themes/bootstrap3/templates/ajax/status-full.phtml b/themes/bootstrap3/templates/ajax/status-full.phtml index 4c72e03f9377a7c2b21e2eb7e8807773cb1db42b..0c0a4588df1b5e03029a377aa95ad6d6b240a861 100644 --- a/themes/bootstrap3/templates/ajax/status-full.phtml +++ b/themes/bootstrap3/templates/ajax/status-full.phtml @@ -4,36 +4,36 @@ <th><?=$this->transEsc('Call Number')?></th> <th><?=$this->transEsc('Status')?></th> </tr> - <? $i = 0; foreach ($this->statusItems as $item): ?> - <? if (++$i == 5) break; // Show no more than 5 items ?> + <?php $i = 0; foreach ($this->statusItems as $item): ?> + <?php if (++$i == 5) break; // Show no more than 5 items ?> <tr> <td class="fullLocation"> - <? $locationText = $this->transEsc('location_' . $item['location'], [], $item['location']); ?> - <? if (isset($item['locationhref']) && $item['locationhref']): ?> + <?php $locationText = $this->transEsc('location_' . $item['location'], [], $item['location']); ?> + <?php if (isset($item['locationhref']) && $item['locationhref']): ?> <a href="<?=$item['locationhref']?>" target="_blank"><?=$locationText?></a> - <? else: ?> + <?php else: ?> <?=$locationText?> - <? endif; ?> + <?php endif; ?> </td> <td class="fullCallnumber"> - <? if ($this->callnumberHandler): ?> + <?php if ($this->callnumberHandler): ?> <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($item['callnumber']) ?>"><?=$this->escapeHtml($item['callnumber'])?></a> - <? else: ?> + <?php else: ?> <?=$this->escapeHtml($item['callnumber'])?> - <? endif; ?> + <?php endif; ?> </td> <td class="fullAvailability"> - <? if (isset($item['use_unknown_message']) && $item['use_unknown_message']): ?> + <?php if (isset($item['use_unknown_message']) && $item['use_unknown_message']): ?> <span><?=$this->transEsc("status_unknown_message")?></span> - <? elseif ($item['availability']): ?> + <?php elseif ($item['availability']): ?> <span class="text-success"><?=($item['reserve'] == 'Y') ? $this->transEsc("On Reserve") : $this->transEsc("Available")?></span> - <? else: ?> + <?php else: ?> <span class="text-danger"><?=$this->transEsc($item['status'])?></span> - <? endif; ?> + <?php endif; ?> </td> </tr> - <? endforeach; ?> -<? if (count($this->statusItems) > 5): ?> + <?php endforeach; ?> +<?php if (count($this->statusItems) > 5): ?> <tr><td colspan="3"><a href="<?=$this->url('record', ['id' => $this->statusItems[0]['id']])?>"><?=count($this->statusItems) - 5?> <?=$this->transEsc('more')?> ...</a></td></tr> -<? endif; ?> +<?php endif; ?> </table> diff --git a/themes/bootstrap3/templates/alphabrowse/home.phtml b/themes/bootstrap3/templates/alphabrowse/home.phtml index 45190f6d2c41d3090318319be6ce2a0fe36a274d..bcb034b18cd4bb864835461aaaeb28b3440e0f52 100644 --- a/themes/bootstrap3/templates/alphabrowse/home.phtml +++ b/themes/bootstrap3/templates/alphabrowse/home.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->headTitle($this->translate('Browse the Collection Alphabetically')); $this->layout()->breadcrumbs = '<a href="' . $this->url('alphabrowse-home') . '">' . $this->transEsc('Browse Alphabetically') . '</a>'; $baseQuery = ['source' => $this->source, 'from' => $this->from]; @@ -15,41 +15,41 @@ } ?> -<? /* LOAD THE LINK INFORMATION INTO $pageLinks, similar to smarty's {capture} */ ?> -<? ob_start(); ?> +<?php /* LOAD THE LINK INFORMATION INTO $pageLinks, similar to smarty's {capture} */ ?> +<?php ob_start(); ?> <ul class="pager"> - <? if (isset($this->prevpage)): ?> + <?php if (isset($this->prevpage)): ?> <li><a href="<?=$this->escapeHtmlAttr($this->url('alphabrowse-home', [], ['query' => $baseQuery + ['page' => $this->prevpage]]))?>">« <?=$this->transEsc('Prev')?></a></li> - <? else: ?> + <?php else: ?> <li class="disabled"><a href="#">« <?=$this->transEsc('Prev')?></a></li> - <? endif; ?> + <?php endif; ?> - <? if (isset($this->nextpage)): ?> + <?php if (isset($this->nextpage)): ?> <li><a href="<?=$this->escapeHtmlAttr($this->url('alphabrowse-home', [], ['query' => $baseQuery + ['page' => $this->nextpage]]))?>"><?=$this->transEsc('Next')?> »</a></li> - <? else: ?> + <?php else: ?> <li class="disabled"><a href="#"><?=$this->transEsc('Next')?> »</a></li> - <? endif; ?> + <?php endif; ?> </ul> -<? $pageLinks = ob_get_contents(); ?> -<? ob_end_clean(); ?> +<?php $pageLinks = ob_get_contents(); ?> +<?php ob_end_clean(); ?> -<? /* If the top search box is not configured to show alphabrowse, or if no option +<?php /* If the top search box is not configured to show alphabrowse, or if no option is selected yet, set up a separate form: */ ?> -<? if (!$this->searchbox()->alphaBrowseOptionsEnabled() || empty($this->source)): ?> +<?php if (!$this->searchbox()->alphaBrowseOptionsEnabled() || empty($this->source)): ?> <form class="form-inline" method="get" action="<?=$this->url('alphabrowse-home')?>" name="alphaBrowseForm" id="alphaBrowseForm"> <label for="alphaBrowseForm_source"><?=$this->transEsc('Browse Alphabetically') ?></label> <select id="alphaBrowseForm_source" name="source" class="form-control"> - <? foreach ($this->alphaBrowseTypes as $key => $item): ?> - <option value="<?=$this->escapeHtmlAttr($key) ?>"<? if ($this->source == $key): ?> selected="selected"<? endif; ?>><?=$this->transEsc($item) ?></option> - <? endforeach; ?> + <?php foreach ($this->alphaBrowseTypes as $key => $item): ?> + <option value="<?=$this->escapeHtmlAttr($key) ?>"<?php if ($this->source == $key): ?> selected="selected"<?php endif; ?>><?=$this->transEsc($item) ?></option> + <?php endforeach; ?> </select> <label for="alphaBrowseForm_from"><?=$this->transEsc('starting from') ?></label> <input type="text" name="from" id="alphaBrowseForm_from" value="<?=$this->escapeHtmlAttr($this->from) ?>" class="form-control"/> <input class="btn btn-primary" type="submit" value="<?=$this->transEsc('Browse') ?>"/> </form> -<? endif; ?> +<?php endif; ?> -<? if ($this->result): ?> +<?php if ($this->result): ?> <?=$pageLinks ?> <table class="alphabrowse table table-striped"> <thead> @@ -57,70 +57,70 @@ <th class="<?=$this->source ?>"> <?=$this->transEsc("browse_" . $this->source) ?> </th> - <? foreach ($this->extras as $e): ?> + <?php foreach ($this->extras as $e): ?> <th><?=$this->transEsc("browse_" . $e) ?></th> - <? endforeach; ?> + <?php endforeach; ?> <th class="titles"><?=$this->transEsc("alphabrowse_matches") ?></th> </tr> </thead> <tbody> - <? $row = 0; ?> - <? foreach ($this->result['Browse']['items'] as $item): ?> - <? if (isset($this->highlight_row) && $row == $this->highlight_row): ?> + <?php $row = 0; ?> + <?php foreach ($this->result['Browse']['items'] as $item): ?> + <?php if (isset($this->highlight_row) && $row == $this->highlight_row): ?> <tr class="browse-match"> - <? if (isset($this->match_type) && ($this->match_type == "NONE")): ?> - <?// this is the right row but query doesn't match value ?> + <?php if (isset($this->match_type) && ($this->match_type == "NONE")): ?> + <?php // this is the right row but query doesn't match value ?> <td colspan="<?=count($this->extras) + 2;?>"><?=$this->transEsc('your_match_would_be_here')?></td> </tr> <tr> - <? endif; ?> - <? else: ?> + <?php endif; ?> + <?php else: ?> <tr> - <? endif; ?> + <?php endif; ?> <td class="<?=$this->source ?>"> <b> - <? if ($url = $this->alphabrowse()->getUrl($this->source, $item)): ?> + <?php if ($url = $this->alphabrowse()->getUrl($this->source, $item)): ?> <a href="<?=$this->escapeHtmlAttr($url)?>"><?=$this->escapeHtml($item['heading'])?></a> - <? else: ?> + <?php else: ?> <?=$this->escapeHtml($item['heading'])?> - <? endif; ?> + <?php endif; ?> </b> - <? if (count($item['useInstead']) > 0): ?> + <?php if (count($item['useInstead']) > 0): ?> <div> <?=$this->transEsc('Use instead') ?>: <ul> - <? foreach ($item['useInstead'] as $heading): ?> + <?php foreach ($item['useInstead'] as $heading): ?> <li><a href="<?=$this->escapeHtmlAttr($this->url('alphabrowse-home', [], ['query' => ['from' => $heading] + $baseQuery]))?>"><?=$this->escapeHtml($heading)?></a></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? endif; ?> + <?php endif; ?> - <? if (count($item['seeAlso']) > 0): ?> + <?php if (count($item['seeAlso']) > 0): ?> <div> <?=$this->transEsc('See also') ?>: <ul> - <? foreach ($item['seeAlso'] as $heading): ?> + <?php foreach ($item['seeAlso'] as $heading): ?> <li><a href="<?=$this->escapeHtmlAttr($this->url('alphabrowse-home', [], ['query' => ['from' => $heading] + $baseQuery]))?>"><?=$this->escapeHtml($heading)?></a></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? endif; ?> + <?php endif; ?> - <? if ($item['note']): ?> + <?php if ($item['note']): ?> <div> <?=$this->transEsc('Note') ?>: <ul> <li><?=$this->escapeHtml($item['note'])?></li> </ul> </div> - <? endif; ?> + <?php endif; ?> </td> - <? foreach ($this->extras as $extraName): ?> + <?php foreach ($this->extras as $extraName): ?> <td> - <? + <?php $extraDisplayArray = []; foreach ($item['extras'][$extraName] as $j => $e): $extraDisplayArray = array_unique(array_merge($extraDisplayArray, $e)); @@ -128,22 +128,22 @@ echo empty($extraDisplayArray) ? ' ' : implode('<br />', $extraDisplayArray); ?> </td> - <? endforeach; ?> + <?php endforeach; ?> <td class="titles"> - <? if ($item['count'] > 0): ?> + <?php if ($item['count'] > 0): ?> <?=$item['count']; ?> - <? endif; ?> + <?php endif; ?> </td> </tr> - <? $row++; ?> - <? endforeach; ?> - <? if (isset($this->highlight_end)): ?> + <?php $row++; ?> + <?php endforeach; ?> + <?php if (isset($this->highlight_end)): ?> <tr class="browse-match"> <td colspan="<?=count($this->extras) + 2;?>"><?=$this->transEsc('your_match_would_be_here')?></td> </tr> - <? endif; ?> + <?php endif; ?> </tbody> </table> <?= $pageLinks ?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/author/home.phtml b/themes/bootstrap3/templates/author/home.phtml index 8f633380032565d703bcb04d832e841862a9b473..8081f946cf87e3c76fb12f7d662c13f7fcc8c903 100644 --- a/themes/bootstrap3/templates/author/home.phtml +++ b/themes/bootstrap3/templates/author/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Author')); diff --git a/themes/bootstrap3/templates/author/results.phtml b/themes/bootstrap3/templates/author/results.phtml index d0859ea035a51c1210476a4c6e9ada7cb6245c44..591867f13a3f7d8eafa89e804a526cad1f73f276 100644 --- a/themes/bootstrap3/templates/author/results.phtml +++ b/themes/bootstrap3/templates/author/results.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); diff --git a/themes/bootstrap3/templates/author/search.phtml b/themes/bootstrap3/templates/author/search.phtml index a26bf7846c41845fd6d1e5b2461c8337832a173c..c23271fafc9f05438a26c6b6fb28250ab9ff9f05 100644 --- a/themes/bootstrap3/templates/author/search.phtml +++ b/themes/bootstrap3/templates/author/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Hide the total result count -- because of limitations in the way facet // paging works, we can't actually determine an accurate total count. (Note // that this setting simply modifies the behavior of search/results.phtml below). diff --git a/themes/bootstrap3/templates/authority/record.phtml b/themes/bootstrap3/templates/authority/record.phtml index b13625d56ea1fdc48a3b7680c9d667c34f3ee6e0..2d4f4b71b06cbdf5e7815d494f999907ccb57146 100644 --- a/themes/bootstrap3/templates/authority/record.phtml +++ b/themes/bootstrap3/templates/authority/record.phtml @@ -1,2 +1,2 @@ -<? $this->layout()->breadcrumbs = false; ?> +<?php $this->layout()->breadcrumbs = false; ?> <?=$this->record($this->driver)->getTab($this->tabs['Details'])?> diff --git a/themes/bootstrap3/templates/authority/search.phtml b/themes/bootstrap3/templates/authority/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/authority/search.phtml +++ b/themes/bootstrap3/templates/authority/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/breadcrumbs/default.phtml b/themes/bootstrap3/templates/breadcrumbs/default.phtml index d3ae32c8f4449ac2c972f82cb97967fd045ab58e..715f23a1b158f65de1894ac38e949d8068444682 100644 --- a/themes/bootstrap3/templates/breadcrumbs/default.phtml +++ b/themes/bootstrap3/templates/breadcrumbs/default.phtml @@ -1,11 +1,11 @@ <li><a href="<?=$this->url('home')?>"><?=$this->transEsc('Home')?></a></li> -<? $current = $this->layout()->breadcrumbs; $current = current($current); ?> -<? foreach($current as $id=>$parent): ?> - <li><a href="<?=$this->url('collection', ['id'=>$id]) ?>"><?=$parent ?></a></li> -<? endforeach; ?> -<? if(isset($this->layout()->end)): ?> +<?php $current = $this->layout()->breadcrumbs; $current = current($current); ?> +<?php foreach($current as $id => $parent): ?> + <li><a href="<?=$this->url('collection', ['id' => $id]) ?>"><?=$parent ?></a></li> +<?php endforeach; ?> +<?php if(isset($this->layout()->end)): ?> <li title="<?=$this->layout()->title ?>"><?=$this->truncate($this->layout()->title, 100) ?></li> <li class="active"><?=$this->layout()->end ?></li> -<? else: ?> +<?php else: ?> <li class="active" title="<?=$this->layout()->title ?>"><?=$this->truncate($this->layout()->title, 100) ?></li> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/breadcrumbs/multi.phtml b/themes/bootstrap3/templates/breadcrumbs/multi.phtml index b1b633e9371b74ebd5cda213defdfbd811375944..a5921e7a5f7ad94866660208656d228f9f05ad52 100644 --- a/themes/bootstrap3/templates/breadcrumbs/multi.phtml +++ b/themes/bootstrap3/templates/breadcrumbs/multi.phtml @@ -2,19 +2,19 @@ <li class="dropdown"> <a data-toggle="dropdown" href="#">In <?=count($this->layout()->breadcrumbs) ?> Collections</a> <div class="dropdown-menu" role="menu" aria-labelledby="dLabel"> - <? foreach ($this->layout()->breadcrumbs as $trail): ?> + <?php foreach ($this->layout()->breadcrumbs as $trail): ?> <ul class="sub-breadcrumb"> - <? foreach ($trail as $id=>$title): ?> - <li><a href="<?=$this->url('record', ['id'=>$id])?>"><?=$title ?></a></li> - <? endforeach; ?> + <?php foreach ($trail as $id => $title): ?> + <li><a href="<?=$this->url('record', ['id' => $id])?>"><?=$title ?></a></li> + <?php endforeach; ?> </ul> - <? endforeach; ?> + <?php endforeach; ?> </div> </li> -<? if(isset($this->layout()->end)): ?> +<?php if(isset($this->layout()->end)): ?> <li title="<?=$this->layout()->title ?>"><?=$this->truncate($this->layout()->title, 100) ?></li> <li class="active"><?=$this->layout()->end ?></li> -<? else: ?> +<?php else: ?> <li class="active" title="<?=$this->layout()->title ?>"><?=$this->truncate($this->layout()->title, 100) ?></li> -<? endif; ?> +<?php endif; ?> <script>$('.dropdown-toggle').dropdown()</script> diff --git a/themes/bootstrap3/templates/browse/home.phtml b/themes/bootstrap3/templates/browse/home.phtml index 10881e1d0b80813d99bd5a4909f9b85432e23982..6669d6e318dc304b0e82b6a6f1cd61c19a4cff2f 100644 --- a/themes/bootstrap3/templates/browse/home.phtml +++ b/themes/bootstrap3/templates/browse/home.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->headTitle($this->translate('Browse the Catalog')); $this->layout()->breadcrumbs = '<a href="' . $this->url('browse-home') . '">' . $this->transEsc('Browse') . '</a>'; @@ -6,80 +6,80 @@ $SEARCH_BASE = $this->url($this->currentAction == 'Tag' ? 'tag-home' : 'search-results'); ?> -<? if (!isset($this->currentAction)): ?> +<?php if (!isset($this->currentAction)): ?> <h2><?=$this->transEsc('Choose a Category to Begin Browsing') ?>:</h2> -<? endif; ?> +<?php endif; ?> <div class="browse-container"> - <div class="browse-list<? if (!empty($this->categoryList) || !empty($this->secondaryList)): ?> hidden-xs<? endif ?>" id="list1"> - <? foreach ($this->browseOptions as $item=>$currentOption): ?> - <a href="<?=$this->url('browse-' . strtolower($currentOption['action'])); ?>" class="browse-item<? if($currentOption['action'] == $this->currentAction): ?> active<? endif; ?>"> + <div class="browse-list<?php if (!empty($this->categoryList) || !empty($this->secondaryList)): ?> hidden-xs<?php endif ?>" id="list1"> + <?php foreach ($this->browseOptions as $item => $currentOption): ?> + <a href="<?=$this->url('browse-' . strtolower($currentOption['action'])); ?>" class="browse-item<?php if($currentOption['action'] == $this->currentAction): ?> active<?php endif; ?>"> <?=$this->transEsc($currentOption['description']) ?> <span class="pull-right flip"><i class="fa fa-angle-right" title="<?=$this->transEsc('more') ?>"></i></span> </a> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? if (!empty($this->categoryList)): ?> - <div class="browse-list<? if (!empty($this->secondaryList) || !empty($this->resultList)): ?> hidden-xs<? endif ?>" id="list2"> - <? foreach($this->categoryList as $findby=>$category): ?> - <a href="<?=$BROWSE_BASE ?>?findby=<?=urlencode($findby) ?>&query_field=<?=$this->browse()->getSolrField($findby, $this->currentAction) ?>" class="browse-item<? if (!is_string($category)): ?> with-badge<? endif; ?><? if ($this->findby == $findby): ?> active<? endif; ?>"> - <? if(is_string($category)): ?> + <?php if (!empty($this->categoryList)): ?> + <div class="browse-list<?php if (!empty($this->secondaryList) || !empty($this->resultList)): ?> hidden-xs<?php endif ?>" id="list2"> + <?php foreach($this->categoryList as $findby => $category): ?> + <a href="<?=$BROWSE_BASE ?>?findby=<?=urlencode($findby) ?>&query_field=<?=$this->browse()->getSolrField($findby, $this->currentAction) ?>" class="browse-item<?php if (!is_string($category)): ?> with-badge<?php endif; ?><?php if ($this->findby == $findby): ?> active<?php endif; ?>"> + <?php if(is_string($category)): ?> <?=$this->transEsc($category)?> <span class="pull-right flip"><i class="fa fa-angle-right" title="<?=$this->transEsc('more') ?>"></i></span> - <? else: ?> + <?php else: ?> <?=$this->transEsc($category['text'])?> <span class="badge"><?=number_format($category['count'])?></span> - <? endif; ?> + <?php endif; ?> </a> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> + <?php endif; ?> - <? if (!empty($this->secondaryList)): ?> - <div class="browse-list<? if (!empty($this->resultList)): ?> hidden-xs<? endif ?>" id="list3"> - <? foreach($this->secondaryList as $secondary): ?> - <? $url = $BROWSE_BASE . '?findby=' . urlencode($this->findby) + <?php if (!empty($this->secondaryList)): ?> + <div class="browse-list<?php if (!empty($this->resultList)): ?> hidden-xs<?php endif ?>" id="list3"> + <?php foreach($this->secondaryList as $secondary): ?> + <?php $url = $BROWSE_BASE . '?findby=' . urlencode($this->findby) . '&category=' . urlencode($this->category) . '&query=' . urlencode($secondary['value']); if ($this->facetPrefix) { $url .= '&facet_prefix=' . urlencode($secondary['displayText']); } if ($this->secondaryParams) { - foreach($this->secondaryParams as $var=>$val) { - $url .= '&' . $var .'=' . urlencode($val); + foreach($this->secondaryParams as $var => $val) { + $url .= '&' . $var . '=' . urlencode($val); } } $viewRecord = !empty($this->categoryList) && $this->currentAction != 'Tag' && $this->findby != 'alphabetical'; $hasBadge = $this->findby != 'alphabetical' && isset($secondary['count']); ?> - <a href="<?=$url ?>" class="browse-item<? if ($hasBadge): ?> with-badge<? endif; ?><? if ($this->query == $secondary['value'].'' || $this->query == $secondary['value'].'*'): ?> active<? endif; ?>"> + <a href="<?=$url ?>" class="browse-item<?php if ($hasBadge): ?> with-badge<?php endif; ?><?php if ($this->query == $secondary['value'] . '' || $this->query == $secondary['value'] . '*'): ?> active<?php endif; ?>"> <?=$this->escapeHtml($secondary['displayText']) ?> - <? if ($hasBadge): ?> + <?php if ($hasBadge): ?> <span class="badge"><?=number_format($secondary['count']) ?></span> - <? else: ?> + <?php else: ?> <span class="pull-right flip"><i class="fa fa-angle-right" title="<?=$this->transEsc('more') ?>"></i></span> - <? endif; ?> + <?php endif; ?> </a> - <? if($viewRecord): ?> - <a class="browse-item view-record" href="<?=$SEARCH_BASE ?>?lookfor=<? if ($this->filter): ?>&filter[]=<?=urlencode($this->filter) ?>%3A<?=str_replace('+AND+','&filter[]=', urlencode($secondary['value'])) ?><? endif; ?>&filter[]=<?=$this->browse()->getSolrField($this->currentAction) ?>%3A[* TO *]<? if($this->dewey_flag):?>&sort=dewey-sort<?endif;?>"><?=$this->transEsc('View Records') ?></a> - <? endif; ?> - <? endforeach; ?> + <?php if($viewRecord): ?> + <a class="browse-item view-record" href="<?=$SEARCH_BASE ?>?lookfor=<?php if ($this->filter): ?>&filter[]=<?=urlencode($this->filter) ?>%3A<?=str_replace('+AND+', '&filter[]=', urlencode($secondary['value'])) ?><?php endif; ?>&filter[]=<?=$this->browse()->getSolrField($this->currentAction) ?>%3A[* TO *]<?php if($this->dewey_flag): ?>&sort=dewey-sort<?php endif; ?>"><?=$this->transEsc('View Records') ?></a> + <?php endif; ?> + <?php endforeach; ?> </div> - <? endif; ?> + <?php endif; ?> - <? if (!empty($this->resultList)): ?> + <?php if (!empty($this->resultList)): ?> <div class="browse-list" id="list4"> - <? foreach($this->resultList as $result): ?> - <a class="browse-item with-badge" href="<?=$SEARCH_BASE ?>?<?=$this->paramTitle ?><?=urlencode($result['value']) ?><? if ($this->searchParams): foreach($this->searchParams as $var=>$val): ?>&<?=$var ?>=<?=urlencode($val) ?><? endforeach;endif; ?>"> + <?php foreach($this->resultList as $result): ?> + <a class="browse-item with-badge" href="<?=$SEARCH_BASE ?>?<?=$this->paramTitle ?><?=urlencode($result['value']) ?><?php if ($this->searchParams): foreach($this->searchParams as $var => $val): ?>&<?=$var ?>=<?=urlencode($val) ?><?php endforeach;endif; ?>"> <?=$this->escapeHtml($result['displayText'])?> <span class="badge"><?=number_format($result['count']) ?></span> </a> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? elseif (isset($this->query)): ?> + <?php elseif (isset($this->query)): ?> <div class="browse-list" id="list4"> <span class="browse-item"><?=$this->transEsc('nohit_heading') ?></li> </div> - <? endif; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/browzine/search.phtml b/themes/bootstrap3/templates/browzine/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/browzine/search.phtml +++ b/themes/bootstrap3/templates/browzine/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/cart/cart.phtml b/themes/bootstrap3/templates/cart/cart.phtml index d0ae574863b28e79267935218617e0e4946957c3..31090b61f518a6b9a853dd0586e507df18cbdd81 100644 --- a/themes/bootstrap3/templates/cart/cart.phtml +++ b/themes/bootstrap3/templates/cart/cart.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Book Bag')); @@ -9,7 +9,7 @@ <?=$this->flashmessages()?> <form class="form-inline" action="<?=$this->url('cart-processor')?>" method="post" name="cartForm" data-lightbox-onsubmit="cartFormHandler"> <input type="hidden" id="dropdown_value"/> - <? if (!$this->cart()->isEmpty()): ?> + <?php if (!$this->cart()->isEmpty()): ?> <div class="cart-controls clearfix"> <div class="checkbox pull-left flip"> <label> @@ -17,22 +17,22 @@ <?=$this->transEsc('select_page')?> </label> </div> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <button type="submit" class="btn btn-default" name="saveCart" title="<?=$this->transEsc('bookbag_save')?>" value="1"> <i class="fa fa-save" aria-hidden="true"></i> <?=$this->transEsc('Save')?> </button> - <? endif; ?> + <?php endif; ?> <button type="submit" class="btn btn-default" name="email" title="<?=$this->transEsc('bookbag_email')?>" value="1"> <i class="fa fa-envelope-o" aria-hidden="true"></i> <?=$this->transEsc('Email')?> </button> - <? $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> + <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> <button type="submit" class="btn btn-default" name="export" title="<?=$this->transEsc('bookbag_export')?>" value="1"> <i class="fa fa-list-alt" aria-hidden="true"></i> <?=$this->transEsc('Export')?> </button> - <? endif; ?> + <?php endif; ?> <button type="submit" class="btn btn-default dropdown-toggle" name="print" title="<?=$this->transEsc('print_selected')?>" value="1"> <i class="fa fa-printer" aria-hidden="true"></i> <?=$this->transEsc('Print')?> @@ -58,11 +58,11 @@ </ul> </div> </div> - <? endif; ?> + <?php endif; ?> <?=$this->render('cart/contents.phtml')?> </form> -<? +<?php $script = <<<JS function submitFormWithButton(link, name) { $('#dropdown_value').attr('name', name).val(1); diff --git a/themes/bootstrap3/templates/cart/contents.phtml b/themes/bootstrap3/templates/cart/contents.phtml index 40fefe4ce7461bf375c01ec2905945ea2d6526b1..f47e97cbb28d98f453f78c1c2d9ad5ebd0a1b914 100644 --- a/themes/bootstrap3/templates/cart/contents.phtml +++ b/themes/bootstrap3/templates/cart/contents.phtml @@ -1,7 +1,7 @@ -<? $records = $this->cart()->getRecordDetails(); if (!empty($records)): ?> +<?php $records = $this->cart()->getRecordDetails(); if (!empty($records)): ?> <hr/> <ul class="list-unstyled"> - <? foreach ($records as $i => $record): ?> + <?php foreach ($records as $i => $record): ?> <li> <div class="checkbox"> <label> @@ -10,8 +10,8 @@ </label> </div> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> -<? else: ?> +<?php else: ?> <p class="alert alert-info"><?=$this->transEsc('bookbag_is_empty')?>.</p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/cart/email.phtml b/themes/bootstrap3/templates/cart/email.phtml index bbea929f5ae29eb424e7c8dbc5a4ed27f64d004e..cf502474aac713f560c92b22b079033707ba7f69 100644 --- a/themes/bootstrap3/templates/cart/email.phtml +++ b/themes/bootstrap3/templates/cart/email.phtml @@ -1,38 +1,38 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('email_selected_favorites')); // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li>' . $this->searchMemory()->getLastSearchLink($this->transEsc('Search'), '', '</li> ') - . '<li><a href="' .$this->url('cart-home'). '">' .$this->transEsc('Cart'). '</a></li> ' + . '<li><a href="' . $this->url('cart-home') . '">' . $this->transEsc('Cart') . '</a></li> ' . '<li class="active">' . $this->transEsc('email_selected_favorites') . '</li>'; ?> <h2><?=$this->transEsc('bookbag_email_selected') ?></h2> <?=$this->flashmessages()?> <form class="form-cart-email" action="<?=$this->url('cart-email')?>" method="post" name="bulkEmail"> - <? foreach ($this->records as $current): ?> + <?php foreach ($this->records as $current): ?> <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($current->getSourceIdentifier() . '|' . $current->getUniqueId())?>" /> - <? endforeach; ?> + <?php endforeach; ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('Title')?>:</label> - <? if(count($this->records) > 1): ?> + <?php if(count($this->records) > 1): ?> <button type="button" class="btn btn-default hidden" data-toggle="collapse" data-target="#itemhide"> - <?=count($this->records).' '.$this->transEsc('items') ?> + <?=count($this->records) . ' ' . $this->transEsc('items') ?> </button> <div id="itemhide" class="collapse in"> <ul> - <? foreach ($this->records as $current): ?> + <?php foreach ($this->records as $current): ?> <li><?=$this->escapeHtml($current->getBreadcrumb())?></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? else: ?> + <?php else: ?> <p class="form-control-static"><?=$this->records[0]->getBreadcrumb() ?></p> - <? endif; ?> + <?php endif; ?> </div> <?=$this->render('Helpers/email-form-fields.phtml')?> </form> -<? +<?php $script = <<<JS $('button.btn.hidden').removeClass('hidden'); $('#itemhide').removeClass('in'); diff --git a/themes/bootstrap3/templates/cart/export-success.phtml b/themes/bootstrap3/templates/cart/export-success.phtml index d3b57410399d54d82099b62798abc2ad4dcdd8f2..f9fa75b8e83111f35d2195605ebdaca912943068 100644 --- a/themes/bootstrap3/templates/cart/export-success.phtml +++ b/themes/bootstrap3/templates/cart/export-success.phtml @@ -1,4 +1,14 @@ <div class="text-center"> - <?=$this->transEsc('export_success')?> — - <a class="btn btn-primary <?=$this->exportType?>" href="<?=$this->escapeHtmlAttr($this->url)?>"><?=$this->transEsc('export_download')?></a> + <?php if ('post' === $this->exportType): ?> + <form id="export-form" method="POST" action="<?=$this->escapeHtmlAttr($this->url)?>" target="<?=$this->escapeHtmlAttr($this->targetWindow)?>"> + <input type="hidden" name="<?=$this->escapeHtmlAttr($this->postField)?>" value="<?=$this->escapeHtmlAttr($this->postData)?>"> + <?=$this->transEsc('export_success')?> — + <input class="btn btn-primary" type="submit" name="submit" + value="<?=$this->escapeHtmlAttr($this->translate('export_send', ['%%service%%' => $this->translate($this->export()->getLabelForFormat($this->format))]))?>" + > + </form> + <?php else: ?> + <?=$this->transEsc('export_success')?> — + <a class="btn btn-primary <?=$this->exportType?>" href="<?=$this->escapeHtmlAttr($this->url)?>"><?=$this->transEsc('export_download')?></a> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/cart/export.phtml b/themes/bootstrap3/templates/cart/export.phtml index 9a218882ca453a2a358ef568a7ce148c9dd8eb3f..038e28fb5ad1ff961ac2a059d9bdeb00e193fd6f 100644 --- a/themes/bootstrap3/templates/cart/export.phtml +++ b/themes/bootstrap3/templates/cart/export.phtml @@ -1,54 +1,54 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Export Favorites')); // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li>' . $this->searchMemory()->getLastSearchLink($this->transEsc('Search'), '', '</li> ') - . '<li><a href="' .$this->url('cart-home'). '">' .$this->transEsc('Cart'). '</a></li> ' + . '<li><a href="' . $this->url('cart-home') . '">' . $this->transEsc('Cart') . '</a></li> ' . '<li class="active">' . $this->transEsc('Export Favorites') . '</li>'; ?> <h2><?=$this->transEsc('Export Favorites')?></h2> <?=$this->flashmessages()?> -<? if (!empty($this->exportOptions)): ?> +<?php if (!empty($this->exportOptions)): ?> <form class="form-cart-export" method="post" action="<?=$this->url('cart-export')?>" name="exportForm" title="<?=$this->transEsc('Export Items')?>"> - <? foreach ($this->records as $current): ?> + <?php foreach ($this->records as $current): ?> <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($current->getSourceIdentifier() . '|' . $current->getUniqueId())?>" /> - <? endforeach; ?> + <?php endforeach; ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('Title')?>:</label> - <? if(count($this->records) > 1): ?> + <?php if(count($this->records) > 1): ?> <button type="button" class="btn btn-default hidden" data-toggle="collapse" data-target="#itemhide"> - <?=count($this->records).' '.$this->transEsc('items') ?> + <?=count($this->records) . ' ' . $this->transEsc('items') ?> </button> <div id="itemhide" class="collapse in"> <ul> - <? foreach ($this->records as $current): ?> + <?php foreach ($this->records as $current): ?> <li><?=$this->escapeHtml($current->getBreadcrumb())?></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? else: ?> + <?php else: ?> <p class="form-control-static"><?=$this->records[0]->getBreadcrumb() ?></p> - <? endif; ?> + <?php endif; ?> </div> <div class="form-group"> <label for="format" class="control-label"><?=$this->transEsc('Format')?>:</label> <select name="format" id="format" class="form-control"> - <? $firstOption = null; ?> - <? foreach ($this->exportOptions as $exportOption): ?> - <? if ($firstOption == null) $firstOption = $exportOption; ?> - <option value="<?=$this->escapeHtmlAttr($exportOption)?>"<? if($this->export()->needsRedirect($exportOption)): ?> data-redirect<? endif; ?>><?=$this->transEsc($this->export()->getLabelForFormat($exportOption))?></option> - <? endforeach; ?> + <?php $firstOption = null; ?> + <?php foreach ($this->exportOptions as $exportOption): ?> + <?php if ($firstOption == null) $firstOption = $exportOption; ?> + <option value="<?=$this->escapeHtmlAttr($exportOption)?>"<?php if($this->export()->needsRedirect($exportOption)): ?> data-redirect<?php endif; ?>><?=$this->transEsc($this->export()->getLabelForFormat($exportOption))?></option> + <?php endforeach; ?> </select> </div> <div class="form-group"> - <input class="export btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Export')?>"<? if($this->export()->needsRedirect($firstOption)): ?> data-lightbox-ignore<? endif; ?>/> + <input class="export btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Export')?>"<?php if($this->export()->needsRedirect($firstOption)): ?> data-lightbox-ignore<?php endif; ?>/> </div> </form> -<? endif; ?> -<? +<?php endif; ?> +<?php $script = <<<JS $('button.btn.hidden').removeClass('hidden'); $('#itemhide').removeClass('in'); diff --git a/themes/bootstrap3/templates/cart/save.phtml b/themes/bootstrap3/templates/cart/save.phtml index 80a149db085604475a81bf0c65c43f0141e8af45..9922b5936bfef62163a61cb88eeca7878c6aa0ad 100644 --- a/themes/bootstrap3/templates/cart/save.phtml +++ b/themes/bootstrap3/templates/cart/save.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('bookbag_save_selected')); @@ -11,56 +11,56 @@ <?=$this->flashmessages()?> <form class="form-cart-save" method="post" action="<?=$this->url('cart-save')?>" name="bulkSave"> - <? $idParams = []; ?> - <? foreach ($this->records as $current): ?> - <? $idParams[] = urlencode('ids[]') . '=' . urlencode($current->getSourceIdentifier() . '|' . $current->getUniqueId()) ?> + <?php $idParams = []; ?> + <?php foreach ($this->records as $current): ?> + <?php $idParams[] = urlencode('ids[]') . '=' . urlencode($current->getSourceIdentifier() . '|' . $current->getUniqueId()) ?> <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($current->getSourceIdentifier() . '|' . $current->getUniqueId())?>" /> - <? endforeach; ?> + <?php endforeach; ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('Title')?>:</label> - <? if(count($this->records) > 1): ?> + <?php if(count($this->records) > 1): ?> <button type="button" class="btn btn-default hidden" data-toggle="collapse" data-target="#itemhide"> - <?=count($this->records).' '.$this->transEsc('items') ?> + <?=count($this->records) . ' ' . $this->transEsc('items') ?> </button> <div id="itemhide" class="collapse in"> <ul> - <? foreach ($this->records as $current): ?> + <?php foreach ($this->records as $current): ?> <li><?=$this->escapeHtml($current->getBreadcrumb())?></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? else: ?> + <?php else: ?> <p class="form-control-static"><?=$this->records[0]->getBreadcrumb() ?></p> - <? endif; ?> + <?php endif; ?> </div> <div class="form-group"> <label class="control-label" for="save_list"><?=$this->transEsc('Choose a List') ?></label> <select id="save_list" name="list" class="form-control"> - <? if (count($this->lists) > 0): ?> - <? foreach ($this->lists as $list): ?> - <option value="<?=$list['id'] ?>"<? if ($list['id']==$this->userList()->lastUsed()): ?> selected="selected"<? endif; ?>><?=$this->escapeHtml($list['title'])?></option> - <? endforeach; ?> - <? else: ?> + <?php if (count($this->lists) > 0): ?> + <?php foreach ($this->lists as $list): ?> + <option value="<?=$list['id'] ?>"<?php if ($list['id'] == $this->userlist()->lastUsed()): ?> selected="selected"<?php endif; ?>><?=$this->escapeHtml($list['title'])?></option> + <?php endforeach; ?> + <?php else: ?> <option value=""><?=$this->transEsc('My Favorites') ?></option> - <? endif; ?> + <?php endif; ?> </select> <a class="btn btn-link" id="make-list" href="<?=$this->url('editList', ['id' => 'NEW']) . '?' . implode('&', $idParams) ?>"><?=$this->transEsc('or create a new list'); ?></a> </div> - <? if ($this->usertags()->getMode() !== 'disabled'): ?> + <?php if ($this->usertags()->getMode() !== 'disabled'): ?> <div class="form-group"> <label class="control-label" for="add_mytags"><?=$this->transEsc('Add Tags') ?></label> <input id="add_mytags" type="text" name="mytags" value="" class="form-control"/> <span class="help-block"><?=$this->transEsc("add_tag_note") ?></span> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>"/> </div> </form> -<? +<?php $script = <<<JS $('button.btn.hidden').removeClass('hidden'); $('#itemhide').removeClass('in'); diff --git a/themes/bootstrap3/templates/channels/channelList.phtml b/themes/bootstrap3/templates/channels/channelList.phtml index abdfc3acaebf833eaf77841ab1044c1d30803e20..db34ec3ad09e2baf9c6ba7232c044ad88d00ec45 100644 --- a/themes/bootstrap3/templates/channels/channelList.phtml +++ b/themes/bootstrap3/templates/channels/channelList.phtml @@ -1,8 +1,10 @@ -<? $this->headLink()->appendStylesheet('lib/channel-slider.css'); ?> -<? $this->headScript()->appendFile('lib/channel-slider.js'); ?> -<? $this->headScript()->appendFile('channels.js'); ?> -<? $this->headScript()->appendFile('vendor/jquery.dotdotdot.min.js'); ?> -<? +<?php + $this->headLink()->appendStylesheet('vendor/slick.css'); + $this->headLink()->appendStylesheet('vendor/slick-theme.css'); + $this->headScript()->appendFile('vendor/slick.min.js'); + $this->headScript()->appendFile('channels.js'); + $this->headScript()->appendFile('lib/accessible-carousels.js'); + $this->headScript()->appendFile('vendor/jquery.dotdotdot.min.js'); $this->jsTranslations()->addStrings([ 'channel_browse' => 'channel_browse', 'channel_expand' => 'channel_expand', @@ -10,53 +12,63 @@ 'nohit_heading' => 'nohit_heading', 'View Record' => 'View Record', ]); + + $finalChannels = []; + $finalCount = 0; + foreach ($channels as $current) { + if (isset($current['contents'])) { + $finalChannels[] = $current; + $finalCount++; + } elseif ($finalCount > 0 && isset($current['token'])) { + $finalChannels[$finalCount - 1]['relatedTokens'][] = $current; + } + } ?> -<? if (empty($token)): ?> - <form action="<?=$this->url('channels-search')?>" class="form-inline"> +<?php if (empty($token)): ?> + <form action="<?=$this->url('channels-search')?>" class="channel-search form-inline"> <?=$this->transEsc('channel_searchbox_label')?> <input type="text" name="lookfor" class="form-control" value="<?=$this->escapeHtmlAttr($this->lookfor) ?>"/> <input type="submit" value="<?=$this->escapeHtmlAttr($this->translate('Submit'))?>" class="btn btn-default" /> </form> -<? endif; ?> +<?php endif; ?> -<? $insideButton = false; ?> -<? foreach ($channels as $channel): ?> - <? - $groupId = isset($channel['groupId']) - ? $channel['groupId'] - : $channel['providerId']; +<?php $insideButton = false; ?> +<?php foreach ($finalChannels as $channel): ?> + <?php + $groupId = $channel['groupId'] ?? $channel['providerId']; + $channelID = 'channel-' . md5(serialize($channel)); ?> - <? $channelID = 'channel-' . md5(serialize($channel)); ?> - <? if (isset($channel['contents'])): ?> - <? if ($insideButton): ?> - </ul> - </div> - <? $insideButton = false; ?> - <? endif; ?> + <div class="channel-wrapper"> <div class="channel-title"> <h2><?=$this->escapeHtml($channel['title'])?></h2> </div> + + <?php if (count($channel['relatedTokens'] ?? []) > 0): ?> + <div class="channel-add-menu hidden" data-group="<?=$groupId ?>"> + <button type="button" class="add-btn btn btn-link"><?=$this->transEsc('channel_add_more') ?></button><!-- + --><button type="button" class="btn btn-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <span class="caret"></span> + <span class="sr-only">Toggle Dropdown</span> + </button> + <ul class="dropdown-menu"> + <?php foreach ($channel['relatedTokens'] as $related): ?> + <li><a href="<?=current(explode('?', $this->serverUrl(true)))?>?<?=empty($queryParams) ? '' : $this->escapeHtmlAttr($queryParams . '&')?>channelProvider=<?=urlencode($related['providerId']) ?>&channelToken=<?=urlencode($related['token']) ?>&layout=lightbox" data-token="<?=$related['token'] ?>"><?=$this->escapeHtml($this->truncate($related['title'], 100)) ?></a></li> + <?php endforeach; ?> + </ul> + </div> + <?php endif; ?> + <div id="<?=$channelID ?>" class="channel" data-group="<?=$groupId ?>" data-link-json='<?=empty($channel['links']) ? '[]' : json_encode($channel['links']) ?>'> <!-- Wrapper for slides --> - <? foreach ($channel['contents'] as $item): ?> - <a href="<?=$this->recordLink()->getUrl("{$item['source']}|{$item['id']}")?>" class="channel-record slide" data-record-id="<?=$this->escapeHtmlAttr($item['id']) ?>" data-record-source="<?=$item['source'] ?>"> - <div class="thumb"><img src="<?=$this->escapeHtmlAttr($item['thumbnail'] ? $item['thumbnail'] : $this->url('cover-unavailable'))?>"/></div> + <?php foreach ($channel['contents'] as $index => $item): ?> + <a href="<?=empty($item['routeDetails']) ? $this->recordLink()->getUrl("{$item['source']}|{$item['id']}") : $this->url($item['routeDetails']['route'], $item['routeDetails']['params'])?>" class="channel-record slide hidden" data-record-id="<?=$this->escapeHtmlAttr($item['id']) ?>" data-record-source="<?=$item['source'] ?>" title="<?=$this->escapeHtml($item['title'])?>"> + <div class="thumb"> + <img <?=$index < 6 ? "src" : "data-lazy" ?>="<?=$this->escapeHtmlAttr($item['thumbnail'] ? $item['thumbnail'] : $this->url('cover-unavailable'))?>"/> + </div> <?=$this->escapeHtml($item['title'])?> </a> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? elseif (isset($channel['token'])): ?> - <? if (!$insideButton): ?> - <div class="channel-add-menu btn-group hidden" data-group="<?=$groupId ?>"> - <button type="button" class="add-btn btn btn-default"><?=$this->transEsc('channel_add_more') ?></button> - <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - <span class="caret"></span> - <span class="sr-only">Toggle Dropdown</span> - </button> - <ul class="dropdown-menu"> - <? $insideButton = true; ?> - <? endif; ?> - <li><a href="<?=current(explode('?', $this->serverUrl(true)))?>?<?=empty($queryParams) ? '' : $this->escapeHtmlAttr($queryParams . '&')?>channelProvider=<?=urlencode($channel['providerId']) ?>&channelToken=<?=urlencode($channel['token']) ?>&layout=lightbox" data-token="<?=$channel['token'] ?>"><?=$this->escapeHtml($channel['title'])?></a></li> - <? endif; ?> -<? endforeach; ?> + </div> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/channels/home.phtml b/themes/bootstrap3/templates/channels/home.phtml index 67eff2710ce43f166dde4189456297de78512a08..9f67b75c37b6899d07d35748cdffe0331ae5d8a1 100644 --- a/themes/bootstrap3/templates/channels/home.phtml +++ b/themes/bootstrap3/templates/channels/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Channels')); diff --git a/themes/bootstrap3/templates/channels/record.phtml b/themes/bootstrap3/templates/channels/record.phtml index c3239862c5733f8533bea57e49e14a45a0143b22..67c930fce481a30569e9c058ce83c8f48307fe15 100644 --- a/themes/bootstrap3/templates/channels/record.phtml +++ b/themes/bootstrap3/templates/channels/record.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Channels') . ' - ' . $this->driver->getBreadcrumb()); diff --git a/themes/bootstrap3/templates/channels/search.phtml b/themes/bootstrap3/templates/channels/search.phtml index b57213cd070ffcf625b5c2ff18984d10e981b91f..81cd7a9a227e132bbbaca8438b52c1cafe1355ec 100644 --- a/themes/bootstrap3/templates/channels/search.phtml +++ b/themes/bootstrap3/templates/channels/search.phtml @@ -1,4 +1,4 @@ -<? +<?php $lookfor = $this->results->getUrlQuery()->isQuerySuppressed() ? '' : $this->results->getParams()->getDisplayQuery(); // Set up page title: diff --git a/themes/bootstrap3/templates/collection/view.phtml b/themes/bootstrap3/templates/collection/view.phtml index ac0f1fad772ca7ac3a30fdf312e8c3e979e25503..1e8112baef67d12efae089b7e01727d212a77589 100644 --- a/themes/bootstrap3/templates/collection/view.phtml +++ b/themes/bootstrap3/templates/collection/view.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up standard record scripts: $this->headScript()->appendFile("record.js"); $this->headScript()->appendFile("check_save_statuses.js"); @@ -24,60 +24,60 @@ . '<li class="active">' . $this->recordLink()->getBreadcrumb($this->driver) . '</li>'; ?> -<? if (isset($this->scrollData) && ($this->scrollData['previousRecord'] || $this->scrollData['nextRecord'])): ?> +<?php if (isset($this->scrollData) && ($this->scrollData['previousRecord'] || $this->scrollData['nextRecord'])): ?> <ul class="pager"> - <? if ($this->scrollData['previousRecord']): ?> - <? if ($this->scrollData['firstRecord']): ?> + <?php if ($this->scrollData['previousRecord']): ?> + <?php if ($this->scrollData['firstRecord']): ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['firstRecord'])?>" title="<?=$this->transEsc('First Search Result')?>" rel="nofollow">« <?=$this->transEsc('First')?></a> </li> - <? endif; ?> + <?php endif; ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['previousRecord'])?>" title="<?=$this->transEsc('Previous Search Result')?>" rel="nofollow">« <?=$this->transEsc('Prev')?></a> </li> - <? else: ?> - <? if ($this->scrollData['firstRecord']): ?> + <?php else: ?> + <?php if ($this->scrollData['firstRecord']): ?> <li class="disabled"><a href="#">« <?=$this->transEsc('First')?></a></li> - <? endif; ?> + <?php endif; ?> <li class="disabled"><a href="#">« <?=$this->transEsc('Prev')?></a></li> - <? endif; ?> + <?php endif; ?> <?=$this->transEsc('of_num_results', [ '%%position%%' => $this->localizedNumber($this->scrollData['currentPosition']), '%%total%%' => $this->localizedNumber($this->scrollData['resultTotal']) ]) ?> - <? if ($this->scrollData['nextRecord']): ?> + <?php if ($this->scrollData['nextRecord']): ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['nextRecord'])?>" title="<?=$this->transEsc('Next Search Result')?>" rel="nofollow"><?=$this->transEsc('Next')?> »</a> </li> - <? if ($this->scrollData['lastRecord']): ?> + <?php if ($this->scrollData['lastRecord']): ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['lastRecord'])?>" title="<?=$this->transEsc('Last Search Result')?>" rel="nofollow"><?=$this->transEsc('Last')?> »</a> </li> - <? endif; ?> - <? else: ?> + <?php endif; ?> + <?php else: ?> <li class="disabled"><a href="#"><?=$this->transEsc('Next')?> »</a></li> - <? if ($this->scrollData['lastRecord']): ?> + <?php if ($this->scrollData['lastRecord']): ?> <li class="disabled"><a href="#"><?=$this->transEsc('Last')?> »</a></li> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </ul> -<? endif; ?> +<?php endif; ?> <?=$this->record($this->driver)->getToolbar()?> <div class="record"> - <div<? if (!$tree): /* in tree mode, do not constrain width with a class */ ?> class="<?=$this->layoutClass('mainbody') ?>"<? endif; ?>> + <div<?php if (!$tree): /* in tree mode, do not constrain width with a class */ ?> class="<?=$this->layoutClass('mainbody') ?>"<?php endif; ?>> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" class="hiddenId" id="record_id" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" /> <?=$this->flashmessages()?> <?=$this->record($this->driver)->getCollectionMetadata()?> - <? if (count($this->tabs) > 0): ?> + <?php if (count($this->tabs) > 0): ?> <a name="tabnav"></a> <div class="record-tabs"> <ul class="nav nav-tabs"> - <? foreach ($this->tabs as $tab => $obj): ?> - <? // add current tab to breadcrumbs if applicable: + <?php foreach ($this->tabs as $tab => $obj): ?> + <?php // add current tab to breadcrumbs if applicable: $desc = $obj->getDescription(); $tab_classes = []; if (0 === strcasecmp($this->activeTab, $tab)) { @@ -92,30 +92,30 @@ if (!$obj->supportsAjax()) { $tab_classes[] = 'noajax'; } ?> <li<?=count($tab_classes) > 0 ? ' class="' . implode(' ', $tab_classes) . '"' : ''?>> - <a class="<?=strtolower($tab) ?>" href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"<? if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<? endif ?>><?=$this->transEsc($desc)?></a> + <a class="<?=strtolower($tab) ?>" href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>><?=$this->transEsc($desc)?></a> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> <div class="tab-content collectionDetails<?=$tree ? 'Tree' : ''?>"> - <? if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> + <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> <div class="tab-pane active <?=$this->activeTab ?>-tab"> <?=isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?> </div> - <? endif; ?> + <?php endif; ?> </div> </div> - <? endif; ?> + <?php endif; ?> - <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenURL()).'"></span>':''?> + <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>':''?> </div> - <? if (isset($activeTabObj) && is_callable([$activeTabObj, 'getSideRecommendations'])): ?> + <?php if (isset($activeTabObj) && is_callable([$activeTabObj, 'getSideRecommendations'])): ?> <div class="<?=$this->layoutClass('sidebar')?>"> - <? foreach ($activeTabObj->getSideRecommendations() as $current): ?> + <?php foreach ($activeTabObj->getSideRecommendations() as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> + <?php endif; ?> </div> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$(document).ready(recordDocReady);', 'SET'); ?> diff --git a/themes/bootstrap3/templates/collections/bytitle.phtml b/themes/bootstrap3/templates/collections/bytitle.phtml index 83d144557745bd2253fc670bb7cffe2ab05b9a76..3157683e8ef335cb41159a63bb72edeee7c1ef6a 100644 --- a/themes/bootstrap3/templates/collections/bytitle.phtml +++ b/themes/bootstrap3/templates/collections/bytitle.phtml @@ -1,22 +1,22 @@ -<? $this->layout()->breadcrumbs = '<a href="' . $this->url('collections-home') . '">' . $this->transEsc('Collections') . '</a>'; ?> +<?php $this->layout()->breadcrumbs = '<a href="' . $this->url('collections-home') . '">' . $this->transEsc('Collections') . '</a>'; ?> <div id="bd"> <div id="yui-main" class="content"> <div class="disambiguationDiv" > - <? if (empty($collections)): ?> + <?php if (empty($collections)): ?> <h2><?=$this->transEsc('collection_empty')?></h2> - <? $this->headTitle($this->translate('collection_empty')); ?> - <? else: ?> + <?php $this->headTitle($this->translate('collection_empty')); ?> + <?php else: ?> <h2><?=$this->transEsc('collection_disambiguation')?></h2> - <? $this->headTitle($this->translate('collection_disambiguation')); ?> + <?php $this->headTitle($this->translate('collection_disambiguation')); ?> <div id="disambiguationItemsDiv"> - <? foreach ($collections as $i => $collection): ?> + <?php foreach ($collections as $i => $collection): ?> <div class="disambiguationItem <?=$i % 2 ? 'alt ' : ''?>record<?=$i?>"> <a href="<?=$this->url('collection', ['id' => $collection->getUniqueId()])?>"><?=$this->escapeHtml($collection->getTitle())?></a> <p><?=$this->escapeHtml(implode(' ', $collection->getSummary()))?></p> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> + <?php endif; ?> </div> </div> </div> diff --git a/themes/bootstrap3/templates/collections/home.phtml b/themes/bootstrap3/templates/collections/home.phtml index 059504c236ac796f8b32d541b11efeb97f10b9da..e5bea94fa2360d837cd7e50bfee38069ac600f7c 100644 --- a/themes/bootstrap3/templates/collections/home.phtml +++ b/themes/bootstrap3/templates/collections/home.phtml @@ -1,62 +1,62 @@ -<? +<?php $this->headTitle($this->translate('Collection Browse')); $this->layout()->breadcrumbs = '<a href="' . $this->url('collections-home') . '">' . $this->transEsc('Collections') . '</a>'; $filterList = []; $filterString = ''; - foreach (isset($filters['Other']) ? $filters['Other'] : [] as $filter) { + foreach ($filters['Other'] ?? [] as $filter) { $filter['urlPart'] = $filter['field'] . ':' . $filter['value']; $filterList[] = $filter; - $filterString .= '&' . urlencode('filter[]') . '=' . urlencode($filter['urlPart']); + $filterString .= '&' . urlencode('filter[]') . '=' . urlencode($filter['urlPart']); } ?> -<? /* LOAD THE LINK INFORMATION INTO $pageLinks, similar to smarty's {capture} */ ?> -<? ob_start(); ?> +<?php /* LOAD THE LINK INFORMATION INTO $pageLinks, similar to smarty's {capture} */ ?> +<?php ob_start(); ?> <form class="form-inline" method="GET" action="<?=$this->url('collections-home')?>"> <ul class="pager"> - <? if (isset($prevpage)): ?> + <?php if (isset($prevpage)): ?> <li><a href="<?=$this->url('collections-home')?>?from=<?=urlencode($from)?>&page=<?=urlencode($prevpage)?><?=$this->escapeHtmlAttr($filterString)?>">« <?=$this->transEsc('Prev')?></a></li> - <? else: ?> + <?php else: ?> <li class="disabled"><a href="#">« <?=$this->transEsc('Prev')?></a></li> - <? endif; ?> - <? if (isset($nextpage)): ?> + <?php endif; ?> + <?php if (isset($nextpage)): ?> <li><a href="<?=$this->url('collections-home')?>?from=<?=urlencode($from)?>&page=<?=urlencode($nextpage)?><?=$this->escapeHtmlAttr($filterString)?>"><?=$this->transEsc('Next')?> »</a></li> - <? else: ?> + <?php else: ?> <li class="disabled"><a href="#"><?=$this->transEsc('Next')?> »</a></li> - <? endif; ?> + <?php endif; ?> <input type="submit" class="btn btn-default" value="<?=$this->transEsc('Jump to')?>" /> <input type="text" name="from" value="<?=$this->escapeHtmlAttr($from)?>" class="form-control" /> </ul> </form> -<? $pageLinks = ob_get_contents(); ?> -<? ob_end_clean(); ?> +<?php $pageLinks = ob_get_contents(); ?> +<?php ob_end_clean(); ?> <h2><?=$this->transEsc('Collection Browse')?></h2> -<? if (!empty($filterList)): ?> +<?php if (!empty($filterList)): ?> <strong><?=$this->transEsc('Remove Filters')?></strong> <ul class="filters"> - <? foreach ($filterList as $filter): ?> + <?php foreach ($filterList as $filter): ?> <li> - <? + <?php $removalUrl = $this->url('collections-home') . '?from=' . urlencode($from); foreach ($filterList as $current) { if ($current['urlPart'] != $filter['urlPart']) { - $removalUrl .= '&' . urlencode('filter[]') . '=' . urlencode($current['urlPart']); + $removalUrl .= '&' . urlencode('filter[]') . '=' . urlencode($current['urlPart']); } } ?> <a href="<?=$this->escapeHtmlAttr($removalUrl)?>"><img src="<?=$this->imageLink('silk/delete.png')?>" alt="Delete"/></a> <a href="<?=$this->escapeHtmlAttr($removalUrl)?>"><?=$this->escapeHtml($filter['displayText'])?></a> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> -<? endif; ?> +<?php endif; ?> <ul class="pagination"> - <? foreach ($letters as $letter): ?> - <li<? if($letter === $from): ?> class="active"<?endif?>><a href="<?=$this->url('collections-home')?>?from=<?=urlencode($letter)?><?=$this->escapeHtmlAttr($filterString)?>"><?=$this->escapeHtml($letter)?></a></li> - <? endforeach; ?> + <?php foreach ($letters as $letter): ?> + <li<?php if (strcasecmp($letter, $from) == 0): ?> class="active"<?php endif; ?>><a href="<?=$this->url('collections-home')?>?from=<?=urlencode($letter)?><?=$this->escapeHtmlAttr($filterString)?>"><?=$this->escapeHtml($letter)?></a></li> + <?php endforeach; ?> </ul> <?=$pageLinks ?> <div class="clearfix"> diff --git a/themes/bootstrap3/templates/collections/list.phtml b/themes/bootstrap3/templates/collections/list.phtml index 98923184192daba07008a050c126ef2f772c52dc..b9b080f31aca980a09079025817be17b5e7e46b3 100644 --- a/themes/bootstrap3/templates/collections/list.phtml +++ b/themes/bootstrap3/templates/collections/list.phtml @@ -1,5 +1,5 @@ <div class="list-group"> - <? foreach ($result as $i => $item): ?> + <?php foreach ($result as $i => $item): ?> <a class="list-group-item" href="<?=$this->url('collection', ['id' => $item['value']])?>"> <strong><?=$this->escapeHtml($item['displayText'])?></strong> <span class="badge"> @@ -7,5 +7,5 @@ <i class="fa fa-angle-right" title="<?=$this->transEsc('Find More') ?>"></i> </span> </a> - <? endforeach; ?> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/combined/results-ajax.phtml b/themes/bootstrap3/templates/combined/results-ajax.phtml index d2df4e75ff1933e2c756d4cd5a93001030a2bc51..f6a80e5f6d51f29638b6ca4532ead33a6f261cc7 100644 --- a/themes/bootstrap3/templates/combined/results-ajax.phtml +++ b/themes/bootstrap3/templates/combined/results-ajax.phtml @@ -1,4 +1,4 @@ -<? +<?php // Make sure OpenURL support is loaded $this->headScript()->appendFile("openurl.js"); diff --git a/themes/bootstrap3/templates/combined/results-list.phtml b/themes/bootstrap3/templates/combined/results-list.phtml index f72e251ddaa68d7d00836a229f3dfefbad989aff..f53707e5a2eeda4125bdc123bc4ed7e11592a397 100644 --- a/themes/bootstrap3/templates/combined/results-list.phtml +++ b/themes/bootstrap3/templates/combined/results-list.phtml @@ -1,4 +1,4 @@ -<? +<?php $view = $currentSearch['view']; $results = $view->results; $params = $results->getParams(); @@ -8,54 +8,54 @@ // More link should use default limit, not custom limit: $moreUrl = $this->url($params->getOptions()->getSearchAction()) . $results->getUrlQuery()->setPage(1)->setLimit($params->getOptions()->getDefaultLimit()); ?> -<? if (isset($currentSearch['more_link']) && $currentSearch['more_link']): ?> +<?php if (isset($currentSearch['more_link']) && $currentSearch['more_link']): ?> <div class="pull-right flip"> <a href="<?=$moreUrl?>" class="btn btn-link"><i class="fa fa-gears" aria-hidden="true"></i> <?=$this->transEsc('More options')?></a> </div> <h2><a href="<?=$moreUrl?>"><?=$this->transEsc($currentSearch['label'])?></a></h2> -<? else: ?> +<?php else: ?> <h2><?=$this->transEsc($currentSearch['label'])?></h2> -<? endif; ?> -<? if (isset($currentSearch['sublabel'])): ?> +<?php endif; ?> +<?php if (isset($currentSearch['sublabel'])): ?> <p><i><?=$this->transEsc($currentSearch['sublabel'])?></i></p> -<? endif; ?> +<?php endif; ?> <div class="clearfix"> <div class="pull-left flip help-block"> - <? if ($recordTotal > 0): ?> - <? foreach (($top = $results->getRecommendations('top')) as $current): ?> + <?php if ($recordTotal > 0): ?> + <?php foreach (($top = $results->getRecommendations('top')) as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> <?=$this->context()->renderInContext('search/controls/showing.phtml', ['lookfor' => $lookfor, 'recordTotal' => $recordTotal, - 'params' => $params, 'results' => $results] ) ?> - <? else: ?> + 'params' => $params, 'results' => $results]) ?> + <?php else: ?> <h3><?=$this->transEsc('nohit_heading')?></h3> - <? endif; ?> + <?php endif; ?> </div> </div> -<? /* End Listing Options */ ?> +<?php /* End Listing Options */ ?> -<? if ($recordTotal < 1): ?> +<?php if ($recordTotal < 1): ?> <p class="alert alert-danger"> - <? if (isset($view->overrideEmptyMessage)): ?> + <?php if (isset($view->overrideEmptyMessage)): ?> <?=$view->overrideEmptyMessage?> - <? else: ?> + <?php else: ?> <?=$this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($lookfor)]) ?> - <? endif; ?> + <?php endif; ?> </p> - <? if (isset($view->parseError)): ?> + <?php if (isset($view->parseError)): ?> <p class="alert alert-danger"><?=$this->transEsc('nohit_parse_error')?></p> - <? endif; ?> - <? foreach (($top = $results->getRecommendations('top')) as $current): ?> + <?php endif; ?> + <?php foreach (($top = $results->getRecommendations('top')) as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> - <? foreach ($results->getRecommendations('noresults') as $current): ?> - <? if (!in_array($current, $top)): ?> + <?php endforeach; ?> + <?php foreach ($results->getRecommendations('noresults') as $current): ?> + <?php if (!in_array($current, $top)): ?> <?=$this->recommend($current)?> - <? endif; ?> - <? endforeach; ?> -<? else: ?> - <? + <?php endif; ?> + <?php endforeach; ?> +<?php else: ?> + <?php $viewType = in_array('list', array_keys($params->getViewList())) ? 'list' : $params->getView(); $viewParams = [ @@ -65,7 +65,7 @@ ]; ?> <?=$this->render('search/list-' . $viewType . '.phtml', $viewParams)?> - <? if (isset($currentSearch['more_link']) && $currentSearch['more_link']): ?> + <?php if (isset($currentSearch['more_link']) && $currentSearch['more_link']): ?> <p><a href="<?=$moreUrl?>"><?=$this->transEsc($currentSearch['more_link'])?> <i class="fa fa-long-arrow-right" aria-hidden="true"></i></a></p> - <? endif; ?> -<? endif; ?> + <?php endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/combined/results.phtml b/themes/bootstrap3/templates/combined/results.phtml index 35b484a7449036d2d25223a2d5cec26a0819274e..0a552a622f9d99bea7036bf4166c2a2320ac8114 100644 --- a/themes/bootstrap3/templates/combined/results.phtml +++ b/themes/bootstrap3/templates/combined/results.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $lookfor = $this->params->getDisplayQuery(); if (isset($this->overrideTitle)) { @@ -50,15 +50,15 @@ ?> <?=$this->flashmessages()?> <form id="search-cart-form" class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>"> - <? $recs = $combinedResults->getRecommendations('top'); if (!empty($recs)): ?> + <?php $recs = $combinedResults->getRecommendations('top'); if (!empty($recs)): ?> <div> - <? foreach ($recs as $current): ?> + <?php foreach ($recs as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> + <?php endif; ?> <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => ''])?> - <? + <?php $viewParams = [ 'searchClassId' => $searchClassId, 'combinedResults' => $this->combinedResults, @@ -68,12 +68,12 @@ ]; ?> <?=$this->context($this)->renderInContext('combined/stack-' . $placement . '.phtml', $viewParams); ?> - <? $recs = $combinedResults->getRecommendations('bottom'); ?> - <? if (!empty($recs)): ?> + <?php $recs = $combinedResults->getRecommendations('bottom'); ?> + <?php if (!empty($recs)): ?> <div> - <? foreach ($recs as $current): ?> + <?php foreach ($recs as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> + <?php endif; ?> </form> diff --git a/themes/bootstrap3/templates/combined/stack-distributed.phtml b/themes/bootstrap3/templates/combined/stack-distributed.phtml index 82c985d349ba8adc0f8f4c0567699869376c2803..3f954dd9a52054d73088dfd7158d49d62b97b192 100644 --- a/themes/bootstrap3/templates/combined/stack-distributed.phtml +++ b/themes/bootstrap3/templates/combined/stack-distributed.phtml @@ -1,8 +1,8 @@ <div class="combined-search-container"> - <? $columnedParams = []; ?> - <? $columnIndex = 0; ?> - <? foreach ($this->combinedResults as $searchClassId => $currentSearch): ?> - <? + <?php $columnedParams = []; ?> + <?php $columnIndex = 0; ?> + <?php foreach ($this->combinedResults as $searchClassId => $currentSearch): ?> + <?php if ( (!isset($currentSearch['ajax']) || !$currentSearch['ajax']) // AJAX column && (isset($currentSearch['hide_if_empty']) && $currentSearch['hide_if_empty']) // set to hide when empty @@ -31,15 +31,15 @@ $columnedParams[$col][] = $colParams; $columnIndex ++; ?> - <? endforeach; ?> - <? foreach ($columnedParams as $currColumn): ?> + <?php endforeach; ?> + <?php foreach ($columnedParams as $currColumn): ?> <div class="combined-column"> - <? foreach ($currColumn as $colParams): ?> + <?php foreach ($currColumn as $colParams): ?> <div id="<?=$this->escapeHtmlAttr($colParams['domId'])?>" class="combined-list"> - <? $templateSuffix = (isset($colParams['ajax']) && $colParams['ajax']) ? 'ajax' : 'list'; ?> + <?php $templateSuffix = (isset($colParams['ajax']) && $colParams['ajax']) ? 'ajax' : 'list'; ?> <?=$this->render('combined/results-' . $templateSuffix . '.phtml', $colParams)?> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/combined/stack-left.phtml b/themes/bootstrap3/templates/combined/stack-left.phtml index 0df1873f50f65b99a45574343c80a4890138f5dd..f164a047f7de622c236b4b9fcc29e4882ebb6889 100644 --- a/themes/bootstrap3/templates/combined/stack-left.phtml +++ b/themes/bootstrap3/templates/combined/stack-left.phtml @@ -1,9 +1,9 @@ <div class="combined-search-container"> - <? $columnIndex = 0; ?> - <? $rightColumns = []; ?> + <?php $columnIndex = 0; ?> + <?php $rightColumns = []; ?> <div class="combined-column"> - <? foreach ($this->combinedResults as $searchClassId => $currentSearch): ?> - <? + <?php foreach ($this->combinedResults as $searchClassId => $currentSearch): ?> + <?php $viewParams = [ 'searchClassId' => $searchClassId, 'currentSearch' => $currentSearch, @@ -13,7 +13,7 @@ 'showBulkOptions' => $this->supportsCartOptions[$columnIndex] && $this->showBulkOptions ]; ?> - <? + <?php if ( (!isset($currentSearch['ajax']) || !$currentSearch['ajax']) // AJAX column && (isset($currentSearch['hide_if_empty']) && $currentSearch['hide_if_empty']) // set to hide when empty @@ -22,8 +22,8 @@ continue; } ?> - <? $columnIndex ++; ?> - <? + <?php $columnIndex ++; ?> + <?php if ($columnIndex > 0 && $columnIndex < $columns) { $viewParams['domId'] = $currentSearch['domId']; if (isset($currentSearch['ajax'])) { @@ -34,17 +34,17 @@ } ?> <div id="<?=$this->escapeHtmlAttr($currentSearch['domId'])?>" class="combined-list"> - <? $templateSuffix = (isset($currentSearch['ajax']) && $currentSearch['ajax']) ? 'ajax' : 'list'; ?> + <?php $templateSuffix = (isset($currentSearch['ajax']) && $currentSearch['ajax']) ? 'ajax' : 'list'; ?> <?=$this->render('combined/results-' . $templateSuffix . '.phtml', $viewParams)?> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? foreach ($rightColumns as $colParams): ?> + <?php foreach ($rightColumns as $colParams): ?> <div class="combined-column"> <div id="<?=$this->escapeHtmlAttr($colParams['domId'])?>" class="combined-list"> - <? $templateSuffix = (isset($colParams['ajax']) && $colParams['ajax']) ? 'ajax' : 'list'; ?> + <?php $templateSuffix = (isset($colParams['ajax']) && $colParams['ajax']) ? 'ajax' : 'list'; ?> <?=$this->render('combined/results-' . $templateSuffix . '.phtml', $colParams)?> </div> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/combined/stack-right.phtml b/themes/bootstrap3/templates/combined/stack-right.phtml index dfca691f257076fd707e21c34981e8b9c4f75f90..43124006a8401c3191acc1c1d43b77d0113fb890 100644 --- a/themes/bootstrap3/templates/combined/stack-right.phtml +++ b/themes/bootstrap3/templates/combined/stack-right.phtml @@ -1,8 +1,8 @@ <div class="combined-search-container"> - <? $columnIndex = 0; ?> + <?php $columnIndex = 0; ?> <div class="combined-column"> - <? foreach ($this->combinedResults as $searchClassId => $currentSearch): ?> - <? + <?php foreach ($this->combinedResults as $searchClassId => $currentSearch): ?> + <?php $viewParams = [ 'searchClassId' => $searchClassId, 'currentSearch' => $currentSearch, @@ -12,7 +12,7 @@ 'showBulkOptions' => $this->supportsCartOptions[$columnIndex] && $this->showBulkOptions ]; ?> - <? + <?php if ( (!isset($currentSearch['ajax']) || !$currentSearch['ajax']) // AJAX column && (isset($currentSearch['hide_if_empty']) && $currentSearch['hide_if_empty']) // set to hide when empty @@ -22,14 +22,14 @@ } ?> <div id="<?=$this->escapeHtmlAttr($currentSearch['domId'])?>" class="combined-list"> - <? $templateSuffix = (isset($currentSearch['ajax']) && $currentSearch['ajax']) ? 'ajax' : 'list'; ?> + <?php $templateSuffix = (isset($currentSearch['ajax']) && $currentSearch['ajax']) ? 'ajax' : 'list'; ?> <?=$this->render('combined/results-' . $templateSuffix . '.phtml', $viewParams)?> </div> - <? $columnIndex ++; ?> - <? if ($columnIndex < $columns): ?> + <?php $columnIndex ++; ?> + <?php if ($columnIndex < $columns): ?> </div> <div class="combined-column"> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </div> </div> diff --git a/themes/bootstrap3/templates/confirm/confirm.phtml b/themes/bootstrap3/templates/confirm/confirm.phtml index 66d461969a499dcbcc48b11bae79c4c9a0685aba..910fa886991fdd0921ad6a03d8b628b24f87fe6d 100644 --- a/themes/bootstrap3/templates/confirm/confirm.phtml +++ b/themes/bootstrap3/templates/confirm/confirm.phtml @@ -1,27 +1,27 @@ -<? $this->headTitle($this->title) ?> +<?php $this->headTitle($this->title) ?> <div class="alignleft"> <h3><?=$this->transEsc($this->title) ?></h3> <?=$this->flashmessages();?> <div id="popupDetails" class="confirmDialog"> - <form class="pull-left flip" action="<?=$this->escapeHtmlAttr($this->confirm)?>" method="post"> - <? if (isset($this->extras)): ?> - <? foreach ($this->extras as $extra=>$value): ?> - <? if (is_array($value)): ?> - <? foreach ($value as $current): ?> + <form class="pull-left" action="<?=$this->escapeHtmlAttr($this->confirm)?>" method="post"> + <?php if (isset($this->extras)): ?> + <?php foreach ($this->extras as $extra => $value): ?> + <?php if (is_array($value)): ?> + <?php foreach ($value as $current): ?> <input type="hidden" name="<?=$this->escapeHtmlAttr($extra) ?>[]" value="<?=$this->escapeHtmlAttr($current) ?>" /> - <? endforeach; ?> - <? else: ?> + <?php endforeach; ?> + <?php else: ?> <input type="hidden" name="<?=$this->escapeHtmlAttr($extra) ?>" value="<?=$this->escapeHtmlAttr($value) ?>" /> - <? endif; ?> - <? endforeach; ?> - <? endif;?> + <?php endif; ?> + <?php endforeach; ?> + <?php endif;?> <input class="btn btn-primary" type="submit" name="confirm" value="<?=$this->transEsc('confirm_dialog_yes') ?>" /> </form> - <form action="<?=$this->escapeHtmlAttr($this->cancel) ?>" method="post"> - <input class="btn btn-default" type="submit" name="cancel" value="<?=$this->transEsc('confirm_dialog_no') ?>" /> + <form class="pull-left" action="<?=$this->escapeHtmlAttr($this->cancel) ?>" method="post"> + <input class="btn btn-default" data-lightbox-close type="submit" name="cancel" value="<?=$this->transEsc('confirm_dialog_no') ?>" /> </form> - <div class="clearer"></div> + <div class="clearfix"></div> </div> </div> diff --git a/themes/bootstrap3/templates/content/asklibrary.phtml b/themes/bootstrap3/templates/content/asklibrary.phtml index b8e493b62584820ad0285d992ec92e58b0e721d1..45902e38cc261f1991f3155e7549d580774a9855 100644 --- a/themes/bootstrap3/templates/content/asklibrary.phtml +++ b/themes/bootstrap3/templates/content/asklibrary.phtml @@ -1,4 +1,4 @@ -<? $this->headTitle($this->translate('Ask a Librarian')); ?> +<?php $this->headTitle($this->translate('Ask a Librarian')); ?> <h1><?=$this->transEsc('Ask a Librarian') ?></h1> diff --git a/themes/bootstrap3/templates/content/asklibrary_en.phtml b/themes/bootstrap3/templates/content/asklibrary_en.phtml index 6a3d03efdbb184f5a3179f9134f4e6b42e752907..eb73d48ac989ab7d52302e8f711d136c1cff3342 100644 --- a/themes/bootstrap3/templates/content/asklibrary_en.phtml +++ b/themes/bootstrap3/templates/content/asklibrary_en.phtml @@ -1,4 +1,4 @@ -<? $this->headTitle($this->translate('Ask a Librarian')); ?> +<?php $this->headTitle($this->translate('Ask a Librarian')); ?> <h1><?=$this->transEsc('Ask a Librarian') ?></h1> diff --git a/themes/bootstrap3/templates/content/faq.phtml b/themes/bootstrap3/templates/content/faq.phtml index 60bd0e069c58994a55478b6af73f6b5bf5be2f46..f97d91a625be0a41899bc994d4efaa2841f3fa3d 100644 --- a/themes/bootstrap3/templates/content/faq.phtml +++ b/themes/bootstrap3/templates/content/faq.phtml @@ -1,13 +1,13 @@ -<? $this->headTitle($this->translate('FAQs')); ?> +<?php $this->headTitle($this->translate('FAQs')); ?> <h1><?=$this->transEsc('FAQs') ?></h1> -<? if ($this->auth()->getManager()->supportsRecovery()): ?> +<?php if ($this->auth()->getManager()->supportsRecovery()): ?> <p> Q: How do I reset my password?<br/> A: Go to the login screen and click the "Forgot password" link. Enter either your user name or email address, and you will get an email containing a link to reset your password. </p> -<? endif; ?> +<?php endif; ?> <p> Q: Why does the service use cookies?<br/> A: Cookies are used to manage sessions, which include selected language, last search, book bag contents, any login information, etc. diff --git a/themes/bootstrap3/templates/devtools/deminify.phtml b/themes/bootstrap3/templates/devtools/deminify.phtml index 4874e7b00e1ca30030bf28e8b0682c08958cd534..035084c192b8fd0efbf3ac8663c3f8904a427c11 100644 --- a/themes/bootstrap3/templates/devtools/deminify.phtml +++ b/themes/bootstrap3/templates/devtools/deminify.phtml @@ -1,39 +1,39 @@ -<? +<?php $this->headTitle('Deminifier'); ?> <h2>Deminifier</h2> -<? if (empty($min)): ?> +<?php if (empty($min)): ?> <form method='POST'> Minified text: <textarea name="min"></textarea> <input type="submit" /> </form> -<? else: ?> +<?php else: ?> <h3>Minified Object</h3> - <pre><? print_r($min) ?></pre> -<? endif; ?> + <pre><?php print_r($min) ?></pre> +<?php endif; ?> -<? if (isset($results)): ?> +<?php if (isset($results)): ?> <h3>Results Object</h3> <p>Class: <?=get_class($results)?></p> -<? endif; ?> +<?php endif; ?> -<? if (isset($query)): ?> +<?php if (isset($query)): ?> <h3>Query Object</h3> - <pre><? print_r($query) ?></pre> -<? endif; ?> + <pre><?php print_r($query) ?></pre> +<?php endif; ?> -<? if (isset($backendParams) || isset($queryParams)): ?> +<?php if (isset($backendParams) || isset($queryParams)): ?> <h3>Backend Parameters</h3> - <? if (isset($queryParams)): ?> + <?php if (isset($queryParams)): ?> <h4>Query Parameters</h4> - <pre><? print_r($queryParams) ?></pre> - <? endif ; ?> - <? if (isset($backendParams)): ?> + <pre><?php print_r($queryParams) ?></pre> + <?php endif; ?> + <?php if (isset($backendParams)): ?> <h4>Non-query Parameters</h4> - <pre><? print_r($backendParams) ?></pre> - <? endif ; ?> -<? endif; ?> + <pre><?php print_r($backendParams) ?></pre> + <?php endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/devtools/home.phtml b/themes/bootstrap3/templates/devtools/home.phtml index 81e7038fe271a850f0b4e662eba92eec4704006a..807de4046819b7e573a5223e7f61989114af5a69 100644 --- a/themes/bootstrap3/templates/devtools/home.phtml +++ b/themes/bootstrap3/templates/devtools/home.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->headTitle('Development Tools'); ?> diff --git a/themes/bootstrap3/templates/devtools/language.phtml b/themes/bootstrap3/templates/devtools/language.phtml index 1ac76e9c2c1c657b0adaf5fc1dc0f3d7f692321b..f03705aee1695f76031f5cf62663b8e4ec1b7c8f 100644 --- a/themes/bootstrap3/templates/devtools/language.phtml +++ b/themes/bootstrap3/templates/devtools/language.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->headTitle($this->translate('Language')); ?> @@ -8,21 +8,21 @@ <table class="table table-striped"> <tr><th>Language</th><th>Missing Lines</th><th>Extra Lines</th><th>Percent Translated</th><th>Extra Help Files</th></tr> - <? foreach ($details as $langCode => $diffs): ?> - <? + <?php foreach ($details as $langCode => $diffs): ?> + <?php $extraCount = count($diffs['notInL1']); $missingCount = count($diffs['notInL2']); ?> <tr> <td><?=$this->escapeHtml($langCode . ' (' . $diffs['name'] . ')')?></td> - <td><?=$missingCount ?><? if($missingCount > 0): ?> (<a href="#" onclick="return diffManager.showMissing('<?=$langCode ?>')"><?=$this->transEsc('show') ?></a>)<? endif; ?></td> - <td><?=$extraCount ?><? if($extraCount > 0): ?> (<a href="#" onclick="return diffManager.showExtra('<?=$langCode ?>')"><?=$this->transEsc('show') ?></a>)<? endif; ?></td> + <td><?=$missingCount ?><?php if($missingCount > 0): ?> (<a href="#" onclick="return diffManager.showMissing('<?=$langCode ?>')"><?=$this->transEsc('show') ?></a>)<?php endif; ?></td> + <td><?=$extraCount ?><?php if($extraCount > 0): ?> (<a href="#" onclick="return diffManager.showExtra('<?=$langCode ?>')"><?=$this->transEsc('show') ?></a>)<?php endif; ?></td> <td><?=$this->escapeHtml($diffs['l2Percent'])?></td> <td><?=count($diffs['helpFiles'])?></td> </tr> - <? endforeach; ?> + <?php endforeach; ?> </table> -<? +<?php $json = json_encode($details); $script = <<<JS function createDiffManager() { diff --git a/themes/bootstrap3/templates/eds/advanced.phtml b/themes/bootstrap3/templates/eds/advanced.phtml index 7811a785914b2c2a7dc12fbd2fe76139c88fc698..0c87a8b62e9a306e9869d59b6c4f67c2b65023d5 100644 --- a/themes/bootstrap3/templates/eds/advanced.phtml +++ b/themes/bootstrap3/templates/eds/advanced.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load the EDS-specific advanced search controls and inject them into the // standard advanced search layout: $this->extraAdvancedControls = $this->render('search/advanced/eds.phtml'); @@ -11,12 +11,12 @@ <input type="hidden" name="join" value="AND"/> <input type="hidden" name="bool0[]" value="AND"/> <label class="adv-group-label"><?=$this->transEsc("adv_search_label")?>:</label> - <? for ($search = 0; $search < 3; $search++): ?> - <? if ($search === 0): ?> + <?php for ($search = 0; $search < 3; $search++): ?> + <?php if ($search === 0): ?> <div id="new_search_template"> - <? endif; ?> + <?php endif; ?> <div class="adv-search"> - <? if ($search === 0): ?><input type="hidden" value="AND" class="first-op"/><? endif; ?> + <?php if ($search === 0): ?><input type="hidden" value="AND" class="first-op"/><?php endif; ?> <select id="search_op0_<?=$search ?>" name="op0[]" class="adv-term-op form-control"> <option value="AND"><?=$this->transEsc("AND")?></option> <option value="OR"><?=$this->transEsc("OR")?></option> @@ -25,20 +25,20 @@ <input id="search_lookfor0_<?=$search ?>" name="lookfor0[]" class="adv-term-input form-control" type="text" value=""/> <span class="help-block hidden-xs"><?=$this->transEsc("in")?></span> <select id="search_type0_<?=$search ?>" name="type0[]" class="adv-term-type form-control"> - <? foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> + <?php foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> <option value="<?=$this->escapeHtml($searchVal)?>"><?=$this->transEsc($searchDesc)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <a class="adv-term-remove">×</a> </div> - <? if ($search === 0): ?> + <?php if ($search === 0): ?> </div> - <? endif; ?> - <? endfor; ?> + <?php endif; ?> + <?php endfor; ?> <i class="fa fa-plus-circle search_place_holder hidden" aria-hidden="true"></i> <a href="#" class="add_search_link hidden"><?=$this->transEsc("add_search")?></a> </div> </div> -<? +<?php $this->formOverride = ob_get_contents(); ob_end_clean(); diff --git a/themes/bootstrap3/templates/eds/home.phtml b/themes/bootstrap3/templates/eds/home.phtml index ff9936df1db6d7be50278f25029832ddf1c5817d..db73c54bb03bb966e2805d4c61fb012cb6a2f40f 100644 --- a/themes/bootstrap3/templates/eds/home.phtml +++ b/themes/bootstrap3/templates/eds/home.phtml @@ -1,3 +1,3 @@ -<? +<?php echo $this->render('search/home.phtml'); ?> diff --git a/themes/bootstrap3/templates/eds/search.phtml b/themes/bootstrap3/templates/eds/search.phtml index 3f3e56e0a72946e6d0048b2f117c8ffd4ee382bc..772edf01d51e03b7ee3ab6b34ca91084c6d938aa 100644 --- a/themes/bootstrap3/templates/eds/search.phtml +++ b/themes/bootstrap3/templates/eds/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: $this->overrideSideFacetCaption = 'Refine Results'; $this->paginationOptions = ['disableFirst' => true, 'disableLast' => true]; diff --git a/themes/bootstrap3/templates/eit/advanced.phtml b/themes/bootstrap3/templates/eit/advanced.phtml index 6d2d837a3c7db2d7eb01978580cf3ba3fa6c7a0f..09eb9fab9f8f025b3ed7147facfa77f1709eba2f 100644 --- a/themes/bootstrap3/templates/eit/advanced.phtml +++ b/themes/bootstrap3/templates/eit/advanced.phtml @@ -1,4 +1,4 @@ -<? +<?php // There are no EIT-specific advanced search controls, so just load the // standard advanced search layout: echo $this->render('search/advanced/layout.phtml'); diff --git a/themes/bootstrap3/templates/eit/search.phtml b/themes/bootstrap3/templates/eit/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/eit/search.phtml +++ b/themes/bootstrap3/templates/eit/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/error/index.phtml b/themes/bootstrap3/templates/error/index.phtml index 36ac5fb44d0e4bce00d6a74daa3be83fa7f8071b..acaa357672e535c0dd37e05158f7f32408f4f685 100644 --- a/themes/bootstrap3/templates/error/index.phtml +++ b/themes/bootstrap3/templates/error/index.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('An error has occurred')); @@ -10,19 +10,19 @@ <p> <?=$this->transEsc('Please contact the Library Reference Department for assistance')?> <br/> - <? $supportEmail = $this->escapeHtmlAttr($this->systememail()); ?> + <?php $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> <a href="mailto:<?=$supportEmail?>"><?=$supportEmail?></a> </p> </div> -<? if ($this->showInstallLink): ?> +<?php if ($this->showInstallLink): ?> <h2><a href="<?=$this->url('install-home')?>"><?=$this->transEsc('auto_configure_title', [], 'Auto Configure')?></a></h2> <?=$this->transEsc('auto_configure_description', [], 'If this is a new installation, you may be able to fix the error using VuFind\'s Auto Configure tool.')?> <h2><a href="<?=$this->url('upgrade-home')?>"><?=$this->transEsc('Upgrade VuFind')?></a></h2> <?=$this->transEsc('upgrade_description', [], 'If you are upgrading a previous VuFind version, you can load your old settings with this tool.')?> -<? endif; ?> +<?php endif; ?> -<? if (isset($this->display_exceptions) && $this->display_exceptions): ?> +<?php if (isset($this->display_exceptions) && $this->display_exceptions): ?> <h2><?=$this->transEsc('Exception')?>:</h2> <p> <b><?=$this->transEsc('Message')?>:</b> <?=$this->escapeHtml($this->exception->getMessage())?> @@ -32,18 +32,18 @@ <pre><?=$this->exception->getTraceAsString()?> </pre> - <? if ($e = $this->exception->getPrevious()): ?> + <?php if ($e = $this->exception->getPrevious()): ?> <h3>Previous exceptions:</h3> - <? while($e): ?> - <h4><?php echo get_class($e); ?></h4> + <?php while($e): ?> + <h4><?=get_class($e)?></h4> <p><?=$e->getMessage()?></p> <pre><?=$e->getTraceAsString()?></pre> - <? $e = $e->getPrevious(); ?> - <? endwhile; ?> - <? endif; ?> + <?php $e = $e->getPrevious(); ?> + <?php endwhile; ?> + <?php endif; ?> - <? if (isset($this->request)): ?> + <?php if (isset($this->request)): ?> <h2><?=$this->transEsc('error_page_parameter_list_heading')?>:</h2> <pre><?=$this->escapeHtml(var_export($this->request->getParams(), true))?></pre> - <? endif; ?> -<? endif ?> + <?php endif; ?> +<?php endif ?> diff --git a/themes/bootstrap3/templates/error/permissiondenied.phtml b/themes/bootstrap3/templates/error/permissiondenied.phtml index 344afc5be92fc5334fd8aac1725cc98e5065c5bc..3ff0f6167e1a9fcfac3f3f48c7fc57c26f33e848 100644 --- a/themes/bootstrap3/templates/error/permissiondenied.phtml +++ b/themes/bootstrap3/templates/error/permissiondenied.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('permission_denied_title')); @@ -12,14 +12,14 @@ <p><?=$this->flashmessages()?></p> <p> <?=$this->transEsc('permission_denied')?> - <? if (!empty($msg)): ?> + <?php if (!empty($msg)): ?> <div class="alert alert-danger"><?=$this->transEsc($msg)?></div> - <? endif; ?> + <?php endif; ?> </p> <p> <?=$this->transEsc('Please contact the Library Reference Department for assistance')?> <br/> - <? $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> + <?php $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> <a href="mailto:<?=$supportEmail?>"><?=$supportEmail?></a> </p> </div> diff --git a/themes/bootstrap3/templates/error/unavailable.phtml b/themes/bootstrap3/templates/error/unavailable.phtml index 9ebaa42658ffb4cf15b59ba942df27847db0006d..93f9fca5fb6ee22b491a31bda3e438e6be2edab2 100644 --- a/themes/bootstrap3/templates/error/unavailable.phtml +++ b/themes/bootstrap3/templates/error/unavailable.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('System Unavailable')); @@ -16,7 +16,7 @@ <p> <?=$this->transEsc('Please contact the Library Reference Department for assistance')?> <br/> - <? $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> + <?php $supportEmail = $this->escapeHtmlAttr($this->systemEmail()); ?> <a href="mailto:<?=$supportEmail?>"><?=$supportEmail?></a> </p> </div> diff --git a/themes/bootstrap3/templates/externalauth/ezproxylogin.phtml b/themes/bootstrap3/templates/externalauth/ezproxylogin.phtml index 1bef7b8f1ac4aeca7c07c2693798b60e3559d89e..81b1c849257f7b9d67e752b3a1ebbf195e8223dc 100644 --- a/themes/bootstrap3/templates/externalauth/ezproxylogin.phtml +++ b/themes/bootstrap3/templates/externalauth/ezproxylogin.phtml @@ -1,16 +1,16 @@ -<? +<?php // Set page title $this->headTitle($this->translate('external_auth_heading')); ?> <div class="external-content-access"> <?=$this->flashmessages()?> - <? if ($this->unauthorized): ?> + <?php if ($this->unauthorized): ?> <div class="unauthorized-description"> <p><?=$this->transEsc('external_auth_unauthorized_desc'); ?></p> </div> <div> <a href="<?=$this->url('myresearch-logout')?>" class="logout btn btn-primary" title="<?=$this->transEsc("Log Out")?>"><strong><?=$this->transEsc("Log Out")?></strong></a> </div> - <? endif; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/feedback/email.phtml b/themes/bootstrap3/templates/feedback/email.phtml index 53109c28fd21ca0a00416681d6999865e5ba96b0..d82a2b800326cf577fae6c4bc067268d87f4f67f 100644 --- a/themes/bootstrap3/templates/feedback/email.phtml +++ b/themes/bootstrap3/templates/feedback/email.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title $this->headTitle($this->translate('Feedback')); // Get rid of the feedback tab since this uses the same variables diff --git a/themes/bootstrap3/templates/feedback/form.phtml b/themes/bootstrap3/templates/feedback/form.phtml index c2e9cc573e2251da8e40a5922e46ff31a4ca5c13..f8488ec54b648dc2b4fc284931b75c46f098b1eb 100644 --- a/themes/bootstrap3/templates/feedback/form.phtml +++ b/themes/bootstrap3/templates/feedback/form.phtml @@ -3,15 +3,15 @@ <form class="form-feedback" name="feedback" method="post" action="<?=$this->url('feedback-email')?>"> <div class="form-group"> <label class="control-label" for="name"><?=$this->transEsc("feedback_name")?></label> - <input type="text" id="name" name="name" value="<?=$this->escapeHtmlAttr(isset($name) ? $name : '')?>" class="form-control"/> + <input type="text" id="name" name="name" value="<?=$this->escapeHtmlAttr($name ?? '')?>" class="form-control"/> </div> <div class="form-group"> <label class="control-label" for="email"><?=$this->transEsc("Email")?></label> - <input type="email" id="email" name="email" value="<?=$this->escapeHtmlAttr(isset($email) ? $email : '')?>" class="form-control" required/> + <input type="email" id="email" name="email" value="<?=$this->escapeHtmlAttr($email ?? '')?>" class="form-control" required/> </div> <div class="form-group"> <label class="control-label" for="comments"><?=$this->transEsc("Comments")?></label> - <textarea id="comments" name="comments" class="form-control" required><?=$this->escapeHtml(isset($comments) ? $comments : '')?></textarea> + <textarea id="comments" name="comments" class="form-control" required><?=$this->escapeHtml($comments ?? '')?></textarea> </div> <?=$this->recaptcha()->html($this->useRecaptcha) ?> <div class="form-group"> diff --git a/themes/bootstrap3/templates/header.phtml b/themes/bootstrap3/templates/header.phtml index 457b6b07bad04fb98f94485336c21d10643cd1a8..39a3e0cd9e68585f63f482fd709ab1263b31f210 100644 --- a/themes/bootstrap3/templates/header.phtml +++ b/themes/bootstrap3/templates/header.phtml @@ -1,4 +1,4 @@ -<? $account = $this->auth()->getManager(); ?> +<?php $account = $this->auth()->getManager(); ?> <div class="banner container navbar"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#header-collapse"> @@ -7,75 +7,75 @@ </button> <a class="navbar-brand lang-<?=$this->layout()->userLang ?>" href="<?=$this->url('home')?>">VuFind</a> </div> - <? if (!isset($this->layout()->renderingError)): ?> + <?php if (!isset($this->layout()->renderingError)): ?> <div class="collapse navbar-collapse" id="header-collapse"> <nav> <ul role="navigation" class="nav navbar-nav navbar-right flip"> - <? if ($this->feedback()->tabEnabled()): ?> + <?php if ($this->feedback()->tabEnabled()): ?> <li> <a id="feedbackLink" data-lightbox href="<?=$this->url('feedback-home') ?>"><i class="fa fa-envelope" aria-hidden="true"></i> <?=$this->transEsc("Feedback")?></a> </li> - <? endif; ?> - <? $cart = $this->cart(); if ($cart->isActive()): ?> + <?php endif; ?> + <?php $cart = $this->cart(); if ($cart->isActive()): ?> <li id="cartSummary"> <a id="cartItems" data-lightbox title="<?=$this->transEsc('View Book Bag')?>" href="<?=$this->url('cart-home')?>"> <i class="fa fa-suitcase" aria-hidden="true"></i> <strong><?=count($cart->getItems())?></strong> <?=$this->transEsc('items')?> <span class="full<?=!$cart->isFull() ? ' hidden' : '' ?>">(<?=$this->transEsc('bookbag_full') ?>)</span> </a> </li> - <? endif; ?> - <? if (is_object($account) && $account->loginEnabled()): // hide login/logout if unavailable ?> - <li class="logoutOptions<? if(!$account->isLoggedIn()): ?> hidden<? endif ?>"> - <a href="<?=$this->url('myresearch-home', array(), array('query' => array('redirect' => 0)))?>"><i class="fa fa-home" aria-hidden="true"></i> <?=$this->transEsc("Your Account")?></a> + <?php endif; ?> + <?php if (is_object($account) && $account->loginEnabled()): // hide login/logout if unavailable ?> + <li class="logoutOptions<?php if(!$account->isLoggedIn()): ?> hidden<?php endif ?>"> + <a href="<?=$this->url('myresearch-home', [], ['query' => ['redirect' => 0]])?>"><i class="fa fa-home" aria-hidden="true"></i> <?=$this->transEsc("Your Account")?></a> </li> - <li class="logoutOptions<? if(!$account->isLoggedIn()): ?> hidden<? endif ?>"> + <li class="logoutOptions<?php if(!$account->isLoggedIn()): ?> hidden<?php endif ?>"> <a href="<?=$this->url('myresearch-logout')?>" class="logout"><i class="fa fa-sign-out" aria-hidden="true"></i> <?=$this->transEsc("Log Out")?></a> </li> - <li id="loginOptions"<? if($account->isLoggedIn()): ?> class="hidden"<? endif ?>> - <? if ($account->getSessionInitiator($this->serverUrl($this->url('myresearch-home')))): ?> + <li id="loginOptions"<?php if($account->isLoggedIn()): ?> class="hidden"<?php endif ?>> + <?php if ($account->getSessionInitiator($this->serverUrl($this->url('myresearch-home')))): ?> <a href="<?=$this->url('myresearch-userlogin')?>"><i class="fa fa-sign-in" aria-hidden="true"></i> <?=$this->transEsc("Institutional Login")?></a> - <? else: ?> + <?php else: ?> <a href="<?=$this->url('myresearch-userlogin')?>" data-lightbox><i class="fa fa-sign-in" aria-hidden="true"></i> <?=$this->transEsc("Login")?></a> - <? endif; ?> + <?php endif; ?> </li> - <? endif; ?> + <?php endif; ?> - <? if (isset($this->layout()->themeOptions) && count($this->layout()->themeOptions) > 1): ?> + <?php if (isset($this->layout()->themeOptions) && count($this->layout()->themeOptions) > 1): ?> <li class="theme dropdown"> <form method="post" name="themeForm" id="themeForm"> <input type="hidden" name="ui"/> </form> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=$this->transEsc("Theme")?> <b class="caret"></b></a> <ul class="dropdown-menu"> - <? foreach ($this->layout()->themeOptions as $current): ?> + <?php foreach ($this->layout()->themeOptions as $current): ?> <li<?=$current['selected'] ? ' class="active"' : ''?>><a href="#" onClick="document.themeForm.ui.value='<?=$this->escapeHtmlAttr($current['name'])?>';document.themeForm.submit()"><?=$this->transEsc($current['desc'])?></a></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </li> - <? endif; ?> + <?php endif; ?> - <? if (isset($this->layout()->allLangs) && count($this->layout()->allLangs) > 1): ?> + <?php if (isset($this->layout()->allLangs) && count($this->layout()->allLangs) > 1): ?> <li class="language dropdown"> <form method="post" name="langForm" id="langForm"> <input type="hidden" name="mylang"/> </form> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=$this->transEsc("Language")?> <b class="caret"></b></a> <ul class="dropdown-menu"> - <? foreach ($this->layout()->allLangs as $langCode => $langName): ?> + <?php foreach ($this->layout()->allLangs as $langCode => $langName): ?> <li<?=$this->layout()->userLang == $langCode ? ' class="active"' : ''?>><a href="#" onClick="document.langForm.mylang.value='<?=$langCode?>';document.langForm.submit()"><?=$this->displayLanguageOption($langName)?></a></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </li> - <? endif; ?> + <?php endif; ?> </ul> </nav> </div> - <? endif; ?> + <?php endif; ?> </div> -<? if ($this->layout()->searchbox !== false): ?> +<?php if ($this->layout()->searchbox !== false): ?> <div class="search container navbar"> <nav class="nav searchbox hidden-print"> <?=$this->layout()->searchbox ?> </nav> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/help/home.phtml b/themes/bootstrap3/templates/help/home.phtml index 82e08b7db1fb8342afbef3e9cc76c25a1aeefe18..9b159585c60e34c1f7e5354cc017b8759a9370d0 100644 --- a/themes/bootstrap3/templates/help/home.phtml +++ b/themes/bootstrap3/templates/help/home.phtml @@ -1,9 +1,9 @@ -<? $this->headTitle($this->translate('Help')); ?> -<? if ($help = $this->helpText()->render($topic)): ?> - <? foreach ($this->helpText()->getWarnings() as $warning): ?> +<?php $this->headTitle($this->translate('Help')); ?> +<?php if ($help = $this->helpText()->render($topic)): ?> + <?php foreach ($this->helpText()->getWarnings() as $warning): ?> <p class="alert alert-warning"><?=$this->transEsc($warning)?></p> - <? endforeach; ?> + <?php endforeach; ?> <?=$help?> -<? else: ?> +<?php else: ?> <p class="alert alert-danger"><?=$this->transEsc('help_page_missing')?></p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/install/disabled.phtml b/themes/bootstrap3/templates/install/disabled.phtml index 6da3c47c0a0b9b5d1c8ded29f76cc6d1f7e64d73..32f495075c8389c7414d8d369153f1fe1f6a808a 100644 --- a/themes/bootstrap3/templates/install/disabled.phtml +++ b/themes/bootstrap3/templates/install/disabled.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a><li> <li class="active">' . $this->transEsc('Disabled') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a><li> <li class="active">' . $this->transEsc('Disabled') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> diff --git a/themes/bootstrap3/templates/install/done.phtml b/themes/bootstrap3/templates/install/done.phtml index f1a18df64804499c7ea5c371851928acad7a5c2c..575aeb84bb40cc1729935f25c53c06dea71a5b76 100644 --- a/themes/bootstrap3/templates/install/done.phtml +++ b/themes/bootstrap3/templates/install/done.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a>'; + $this->layout()->breadcrumbs = '<a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> diff --git a/themes/bootstrap3/templates/install/fixbasicconfig.phtml b/themes/bootstrap3/templates/install/fixbasicconfig.phtml index e3c2783114b1e3262eacf71e67b9e5a01f22a187..f6c9f069f5a2f6339774aadce80f4dff5d6539dc 100644 --- a/themes/bootstrap3/templates/install/fixbasicconfig.phtml +++ b/themes/bootstrap3/templates/install/fixbasicconfig.phtml @@ -1,13 +1,13 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Basic Config') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Basic Config') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> -<? if (isset($this->configDir)): ?> +<?php if (isset($this->configDir)): ?> <p>VuFind cannot write to <b><?=$this->escapeHtml($this->configDir)?></b>.</p> <p>Please make sure that write permissions are available on this directory.</p> @@ -15,12 +15,12 @@ <p>In Linux, try this command (note that you may need to prefix with "sudo" on some flavors):</p> <pre> - <? if (isset($this->runningUser)): ?> + <?php if (isset($this->runningUser)): ?> chown <?=$this->escapeHtml($this->runningUser)?>:<?=$this->escapeHtml($this->runningUser)?> <?=$this->escapeHtml($this->configDir)?> - <? else: ?> + <?php else: ?> chmod 777 <?=$this->escapeHtml($this->configDir)?> - <? endif; ?> + <?php endif; ?> </pre> -<? else: ?> +<?php else: ?> <p>Your configuration has been successfully updated.</p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/install/fixcache.phtml b/themes/bootstrap3/templates/install/fixcache.phtml index 9c9dc4c9b4894789266cba7f01c5ab4008464b55..9f91069c99b3f2e43502a6dc29d1751d3cae9772 100644 --- a/themes/bootstrap3/templates/install/fixcache.phtml +++ b/themes/bootstrap3/templates/install/fixcache.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Cache') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Cache') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> @@ -14,9 +14,9 @@ <p>In Linux, try this command (note that you may need to prefix with "sudo" on some flavors):</p> <pre> - <? if (isset($this->runningUser)): ?> + <?php if (isset($this->runningUser)): ?> chown <?=$this->escapeHtml($this->runningUser)?>:<?=$this->escapeHtml($this->runningUser)?> <?=$this->escapeHtml($this->cacheDir)?> - <? else: ?> + <?php else: ?> chmod 777 <?=$this->escapeHtml($this->cacheDir)?> - <? endif; ?> + <?php endif; ?> </pre> diff --git a/themes/bootstrap3/templates/install/fixdatabase.phtml b/themes/bootstrap3/templates/install/fixdatabase.phtml index deef34c38144907c81e67dabd4c9c4b350571fa7..5696a87848d7a9cacf2226bd4cff30348cdf9ab8 100644 --- a/themes/bootstrap3/templates/install/fixdatabase.phtml +++ b/themes/bootstrap3/templates/install/fixdatabase.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Database') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Database') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> @@ -16,7 +16,7 @@ <label class="control-label" for="dbname">Select database type:</label> <select name="driver" class="form-control"> <option value="mysql">MySQL</option> - <option <? if ($driver == 'pgsql'): ?>selected="selected" <? endif; ?>value="pgsql">PostgreSQL</option> + <option <?php if ($driver == 'pgsql'): ?>selected="selected" <?php endif; ?>value="pgsql">PostgreSQL</option> </select> </div> <div class="form-group"> diff --git a/themes/bootstrap3/templates/install/fixdependencies.phtml b/themes/bootstrap3/templates/install/fixdependencies.phtml index c2872601b358f870ecd570ef8edf1e34a89fe43f..8ea6bf9343db6cd1d64e459f1450488cec094153 100644 --- a/themes/bootstrap3/templates/install/fixdependencies.phtml +++ b/themes/bootstrap3/templates/install/fixdependencies.phtml @@ -1,12 +1,12 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Dependencies') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Dependencies') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> <?=$this->flashmessages()?> -<? if ($this->problems == 0): ?><p><?=$this->transEsc('No dependency problems found') ?>.</p><? endif; ?> +<?php if ($this->problems == 0): ?><p><?=$this->transEsc('No dependency problems found') ?>.</p><?php endif; ?> diff --git a/themes/bootstrap3/templates/install/fixils.phtml b/themes/bootstrap3/templates/install/fixils.phtml index d76829301d75f0f9ad32f9b959ed27965254e2ca..9d8788b9d1629c48f2091ee5184ad1c3860af687 100644 --- a/themes/bootstrap3/templates/install/fixils.phtml +++ b/themes/bootstrap3/templates/install/fixils.phtml @@ -1,13 +1,13 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix ILS') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix ILS') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> -<? if (isset($this->demo)): ?> +<?php if (isset($this->demo)): ?> <p>You are using one of VuFind's simulated Integrated Library System (ILS) drivers, which display fake information in order to demonstrate the capabilities of the system. If you want real patron and status information to display, you should change your configuration to communicate with a real ILS.</p> @@ -16,9 +16,9 @@ <div class="form-group"> <label for="driver" class="control-label">Pick a driver:</label> <select id="driver" name="driver" class="form-control"> - <? foreach ($this->drivers as $driver): ?> + <?php foreach ($this->drivers as $driver): ?> <option value="<?=$this->escapeHtmlAttr($driver)?>"><?=$this->escapeHtml($driver)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> <div class="form-group"> @@ -28,8 +28,8 @@ <p>If your ILS is not available in this list, you may be able to write your own driver. See the <a href="https://vufind.org/wiki/development" target="_new">Developer Manual</a>.</p> -<? else: ?> +<?php else: ?> <p>VuFind is having trouble communicating with your Integrated Library System (ILS). Check your configuration. You may need to edit the file at <strong><?=$this->escapeHtml($this->configPath)?></strong> and fill in some connection details.</p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/install/fixsecurity.phtml b/themes/bootstrap3/templates/install/fixsecurity.phtml index 5d5042d401c9f53326c934bc18c166f2151369d2..cc5e0d225023ac7ed1d8d16ed05ec46d2bf6dc63 100644 --- a/themes/bootstrap3/templates/install/fixsecurity.phtml +++ b/themes/bootstrap3/templates/install/fixsecurity.phtml @@ -1,15 +1,15 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Security') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Security') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> <?=$this->flashmessages()?> -<? if (isset($this->confirmUserFix) && $this->confirmUserFix): ?> +<?php if (isset($this->confirmUserFix) && $this->confirmUserFix): ?> <p>You have existing user data in your database containing non-encrypted passwords.</p> <p>If you continue with enabling security, all of your passwords will be hashed and/or encrypted.</p> <p><b>Please make a database backup before proceeding.</b></p> @@ -22,6 +22,6 @@ <input type="submit" name="fix-user-table" value="Yes" class="btn btn-primary"/> <input type="submit" name="fix-user-table" value="No" class="btn btn-default"/> </form> -<? else: ?> +<?php else: ?> <p>No security problems found.</p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/install/fixsolr.phtml b/themes/bootstrap3/templates/install/fixsolr.phtml index 2a9121c78d13cf256540543208b4a2e41c8a71af..feac33ef5305d4b157930ce4e3c61c7afa50964f 100644 --- a/themes/bootstrap3/templates/install/fixsolr.phtml +++ b/themes/bootstrap3/templates/install/fixsolr.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Solr') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix Solr') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> diff --git a/themes/bootstrap3/templates/install/fixsslcerts.phtml b/themes/bootstrap3/templates/install/fixsslcerts.phtml index 043c60bcc2d56489873ebe11bb960a3449bf4b44..107241bad4cc3acbd0595301d3a10a763f126645 100644 --- a/themes/bootstrap3/templates/install/fixsslcerts.phtml +++ b/themes/bootstrap3/templates/install/fixsslcerts.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') .'">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix SSL Certificates') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('install-home') . '">' . $this->transEsc('auto_configure_title') . '</a></li> <li class="active">' . $this->transEsc('Fix SSL Certificates') . '</li>'; ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> diff --git a/themes/bootstrap3/templates/install/home.phtml b/themes/bootstrap3/templates/install/home.phtml index 920c45cfbb9d1e4c805b8cd7b6ac75552408ecc1..1b393338756e56f20cfd4f1964a94af49424d3ac 100644 --- a/themes/bootstrap3/templates/install/home.phtml +++ b/themes/bootstrap3/templates/install/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('auto_configure_title')); @@ -7,11 +7,11 @@ ?> <h2><?=$this->transEsc('auto_configure_title')?></h2> <?=$this->flashmessages()?> -<? $errors = 0; foreach ($this->checks as $check): ?> -<? if (!$check['status']) $errors++; ?> - <div class="alert alert-<?=$check['status'] ? 'success':'danger'?>"><?=$this->escapeHtml($check['title'])?>... <?=$check['status'] ? $this->transEsc('test_ok') : $this->transesc('test_fail') . ' <a class="btn btn-danger" href="' . $this->url('install-' . strtolower($check['fix'])) . '">' . $this->transEsc('test_fix') . '</a>' ?></div> -<? endforeach; ?> +<?php $errors = 0; foreach ($this->checks as $check): ?> +<?php if (!$check['status']) $errors++; ?> + <div class="alert alert-<?=$check['status'] ? 'success':'danger'?>"><?=$this->escapeHtml($check['title'])?>... <?=$check['status'] ? $this->transEsc('test_ok') : $this->transEsc('test_fail') . ' <a class="btn btn-danger" href="' . $this->url('install-' . strtolower($check['fix'])) . '">' . $this->transEsc('test_fix') . '</a>' ?></div> +<?php endforeach; ?> -<? if ($errors == 0): ?> +<?php if ($errors == 0): ?> <p>No problems were found. You may wish to <a href="<?=$this->url('install-done')?>">Disable Auto Configuration</a> at this time.</p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/install/showsql.phtml b/themes/bootstrap3/templates/install/showsql.phtml index 54bdc9089452ef829275f106060a401e842b81c9..6c3e73023264918176ca3ed26aa9a367f793d88d 100644 --- a/themes/bootstrap3/templates/install/showsql.phtml +++ b/themes/bootstrap3/templates/install/showsql.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Install VuFind')); diff --git a/themes/bootstrap3/templates/layout/layout.phtml b/themes/bootstrap3/templates/layout/layout.phtml index 622c5e5d976696e95a2a6b131831097e88786bd5..df434797e2a372e7aa83540d75655ca192314708 100644 --- a/themes/bootstrap3/templates/layout/layout.phtml +++ b/themes/bootstrap3/templates/layout/layout.phtml @@ -1,13 +1,13 @@ <?=$this->doctype('HTML5')?> -<html lang="<?=$this->layout()->userLang?>"> +<html lang="<?=$this->layout()->userLang?>"<?php if ($this->layout()->rtl): ?> dir="rtl"<?php endif; ?>> <head> - <?$this->headThemeResources()?> + <?php $this->headThemeResources(); ?> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width,initial-scale=1.0"/> <?=$this->headMeta()?> <?=$this->headTitle()?> - <? + <?php // Set up OpenSearch link: $this->headLink( [ @@ -19,12 +19,12 @@ ); ?> <!-- RTL styling --> - <? if ($this->layout()->rtl) { + <?php if ($this->layout()->rtl) { $this->headLink()->appendStylesheet('vendor/bootstrap-rtl.min.css'); } ?> <?=$this->headLink()?> <?=$this->headStyle()?> - <? + <?php if (!isset($this->renderingError)) { // Add translation strings $this->jsTranslations()->addStrings( @@ -102,13 +102,13 @@ } // Session keep-alive - if ($this->KeepAlive()) { + if ($this->keepAlive()) { $this->headScript()->appendScript('var keepAliveInterval = ' - . $this->KeepAlive()); + . $this->keepAlive()); $this->headScript()->appendFile("keep_alive.js"); } ?> - <? + <?php $root = rtrim($this->url('home'), '/'); $translations = $this->jsTranslations()->getJSON(); $dsb = DEFAULT_SEARCH_BACKEND; @@ -121,8 +121,8 @@ JS; ?> <?=$this->headScript()?> </head> - <body class="template-dir-<?=$this->templateDir?> template-name-<?=$this->templateName?> <?=$this->layoutClass('offcanvas-row')?><? if ($this->layout()->rtl): ?> rtl<? endif; ?>"> - <? // Set up the search box -- there are three possible cases: + <body class="template-dir-<?=$this->templateDir?> template-name-<?=$this->templateName?> <?=$this->layoutClass('offcanvas-row')?><?php if ($this->layout()->rtl): ?> rtl<?php endif; ?>"> + <?php // Set up the search box -- there are three possible cases: // 1. No search box was set; we should default to the normal box // 2. It was set to false; we should display nothing // 3. It is set to a custom string; we should display the provided version @@ -133,37 +133,37 @@ JS; } ?> <header class="hidden-print"> - <? if (isset($this->layout()->srmessage)): // message for benefit of screen-reader users ?> - <span class="sr-only"><?=$this->layout()->srmessage ?></span> - <? endif; ?> + <?php if (isset($this->layout()->srmessage)): // message for benefit of screen-reader users ?> + <span class="sr-only" role="heading" aria-level="1"><?=$this->layout()->srmessage ?></span> + <?php endif; ?> <a class="sr-only" href="#content"><?=$this->transEsc('Skip to content') ?></a> <?=$this->render('header.phtml')?> </header> <nav class="breadcrumbs"> <div class="container"> - <? if((!isset($this->layout()->showBreadcrumbs) || $this->layout()->showBreadcrumbs == true) + <?php if((!isset($this->layout()->showBreadcrumbs) || $this->layout()->showBreadcrumbs == true) && !empty($this->layout()->breadcrumbs) && $this->layout()->breadcrumbs !== false ): ?> <ul class="breadcrumb hidden-print"> - <? if(is_array($this->layout()->breadcrumbs)): ?> - <? if(count($this->layout()->breadcrumbs) > 1): ?> + <?php if(is_array($this->layout()->breadcrumbs)): ?> + <?php if(count($this->layout()->breadcrumbs) > 1): ?> <?=$this->render('breadcrumbs/multi.phtml', [ 'parents' => $this->layout()->breadcrumbs, - 'title' => $this->layout()->title, - 'from' => $this->layout()->from + 'title' => $this->layout()->title, + 'from' => $this->layout()->from ]) ?> - <? else: ?> + <?php else: ?> <?=$this->render('breadcrumbs/default.phtml', [ 'parents' => $this->layout()->breadcrumbs, - 'title' => $this->layout()->title + 'title' => $this->layout()->title ]) ?> - <? endif; ?> - <? else: ?> + <?php endif; ?> + <?php else: ?> <?=$this->layout()->breadcrumbs ?> - <? endif; ?> + <?php endif; ?> </ul> - <? endif; ?> + <?php endif; ?> </div> </nav> <div role="main" class="main"> @@ -185,8 +185,8 @@ JS; <div class="offcanvas-overlay" data-toggle="offcanvas"></div> <?=$this->googleanalytics()?> <?=$this->piwik()?> - <? if ($this->recaptcha()->active()): ?> + <?php if ($this->recaptcha()->active()): ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::FILE, "https://www.google.com/recaptcha/api.js?onload=recaptchaOnLoad&render=explicit&hl=" . $this->layout()->userLang, 'SET')?> - <? endif; ?> + <?php endif; ?> </body> </html> diff --git a/themes/bootstrap3/templates/libguides/results.phtml b/themes/bootstrap3/templates/libguides/results.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/libguides/results.phtml +++ b/themes/bootstrap3/templates/libguides/results.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/librarycards/editcard.phtml b/themes/bootstrap3/templates/librarycards/editcard.phtml index 9c2be42a724ad9876f5b657d5134c56c6e406201..1361cd6947b08682eb02ba99a8d485be9ed7ae97 100644 --- a/themes/bootstrap3/templates/librarycards/editcard.phtml +++ b/themes/bootstrap3/templates/librarycards/editcard.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $pageTitle = empty($this->card->id) ? 'Add a Library Card' : "Edit Library Card"; $this->headTitle($this->translate($pageTitle)); @@ -19,16 +19,16 @@ <label class="control-label" for="card_name"><?=$this->transEsc('Library Card Name'); ?>:</label> <input id="card_name" class="form-control" type="text" name="card_name" value="<?=$this->escapeHtmlAttr($this->cardName)?>"/> </div> - <? if ($this->targets !== null): ?> + <?php if ($this->targets !== null): ?> <div class="form-group"> <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> <select id="login_target" name="target" class="form-control"> - <? foreach ($this->targets as $target): ?> + <?php foreach ($this->targets as $target): ?> <option value="<?=$this->escapeHtmlAttr($target)?>"<?=($target == $this->target ? ' selected="selected"' : '')?>><?=$this->transEsc("source_$target", null, $target)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <label class="control-label" for="login_username"><?=$this->transEsc('Username')?>:</label> <input id="login_username" type="text" name="username" value="<?=$this->escapeHtmlAttr($this->username)?>" class="form-control"/> diff --git a/themes/bootstrap3/templates/librarycards/home.phtml b/themes/bootstrap3/templates/librarycards/home.phtml index b3cbd230362f70352b985ba8f898423e7a9d8fa2..4a2ec14b2e1208406635159a58e8b3c594da6130 100644 --- a/themes/bootstrap3/templates/librarycards/home.phtml +++ b/themes/bootstrap3/templates/librarycards/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Library Cards')); @@ -10,28 +10,29 @@ <?=$this->flashmessages()?> <h2><?=$this->transEsc('Library Cards')?></h2> - <? if ($this->libraryCards->count() == 0): ?> + <?php if ($this->libraryCards->count() == 0): ?> <div><?=$this->transEsc('You do not have any library cards')?></div> - <? else: ?> - <table class="table table-striped" summary="<?=$this->transEsc('Library Cards')?>"> + <?php else: ?> + <table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Library Cards')?></caption> <tr> <th><?=$this->transEsc('Library Card Name')?></th> - <? if ($this->multipleTargets): ?> + <?php if ($this->multipleTargets): ?> <th><?=$this->transEsc('login_target')?></th> - <? endif; ?> + <?php endif; ?> <th><?=$this->transEsc('Username')?></th> <th> </th> </tr> - <? foreach ($this->libraryCards as $record): ?> + <?php foreach ($this->libraryCards as $record): ?> <tr> <td><?=$this->escapeHtml($record['card_name'])?></td> - <? $username = $record['cat_username']; if ($this->multipleTargets): ?> - <? $target = ''; ?> - <? if (strstr($username, '.')): ?> - <? list($target, $username) = explode('.', $username, 2); ?> - <? endif; ?> + <?php $username = $record['cat_username']; if ($this->multipleTargets): ?> + <?php $target = ''; ?> + <?php if (strstr($username, '.')): ?> + <?php list($target, $username) = explode('.', $username, 2); ?> + <?php endif; ?> <td><?=$target ? $this->transEsc("source_$target", null, $target) : ' ' ?></td> - <? endif; ?> + <?php endif; ?> <td><?=$this->escapeHtml($username)?></td> <td> <div class="btn-group"> @@ -46,9 +47,9 @@ </div> </td> </tr> - <? endforeach; ?> + <?php endforeach; ?> </table> - <? endif; ?> + <?php endif; ?> <div class="btn-group"> <a href="<?=$this->url('editLibraryCard') ?>NEW" class="btn btn-link"><i class="fa fa-edit" aria-hidden="true"></i> <?=$this->transEsc('Add a Library Card')?></a> diff --git a/themes/bootstrap3/templates/librarycards/selectcard.phtml b/themes/bootstrap3/templates/librarycards/selectcard.phtml index c0ba02e53b2bda6f7246cf6852e8b3b0651e1bd6..ea295d90c995885008cd58aa6af9d4347cea1be3 100644 --- a/themes/bootstrap3/templates/librarycards/selectcard.phtml +++ b/themes/bootstrap3/templates/librarycards/selectcard.phtml @@ -1,10 +1,10 @@ -<? if ($this->user): ?> - <?$cards = $this->user->getLibraryCards(); if ($cards->count() > 1): ?> +<?php if ($this->user): ?> + <?php $cards = $this->user->getLibraryCards(); if ($cards->count() > 1): ?> <form class="form-inline" action="<?=$this->url('librarycards-selectcard')?>" method="get"> <label for="library_card"><?=$this->transEsc('Library Card')?></label> <select id="library_card" name="cardID" class="jumpMenu form-control"> - <? foreach ($cards as $card): ?> - <? + <?php foreach ($cards as $card): ?> + <?php $target = ''; $username = $card->cat_username; if (strstr($username, '.')) { @@ -16,9 +16,9 @@ } ?> <option value="<?=$this->escapeHtmlAttr($card->id)?>"<?=strcasecmp($card->cat_username, $this->user->cat_username) == 0 ? ' selected="selected"' : ''?>><?=$display ?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <noscript><input type="submit" class="btn btn-default" value="<?=$this->transEsc("Set")?>" /></noscript> </form> - <? endif; ?> -<? endif; ?> + <?php endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/account.phtml b/themes/bootstrap3/templates/myresearch/account.phtml index ac81a11f783bfeac09a82e964cbc3a35e729a47f..5137e04158411607c4e0ab573ca2deea546d40bf 100644 --- a/themes/bootstrap3/templates/myresearch/account.phtml +++ b/themes/bootstrap3/templates/myresearch/account.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('User Account')); diff --git a/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml b/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml index 26a447a4cc6491acc24dc6b363867da3a15c5942..c45b65be814f752315ead128e55978daa25fcac0 100644 --- a/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml +++ b/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml @@ -1,8 +1,8 @@ -<? if (isset($list)): ?> +<?php if (isset($list)): ?> <input type="hidden" name="listID" value="<?=$this->escapeHtmlAttr($list->id)?>" /> <input type="hidden" name="listName" value="<?=$this->escapeHtmlAttr($list->title)?>" /> -<? endif; ?> -<? $user = $this->auth()->isLoggedIn(); ?> +<?php endif; ?> +<?php $user = $this->auth()->isLoggedIn(); ?> <div class="bulkActionButtons"> <div class="bulk-checkbox"> <input type="checkbox" name="selectAll" class="checkbox-select-all" id="myresearchCheckAll"/> @@ -10,15 +10,15 @@ </div> <div class="btn-group"> <input class="btn btn-default" type="submit" name="email" value="<?=$this->transEsc('Email')?>" title="<?=$this->transEsc('email_selected')?>"/> - <? if ((!is_null($this->list) && $this->list->editAllowed($user)) || is_null($this->list) && $user): ?> - <input class="btn btn-default" id="<?=$this->idPrefix?>delete_list_items_<?=!is_null($this->list) ? $this->escapeHtmlAttr($this->list->id) : ''?>" type="submit" name="delete" value="<?=$this->transEsc('Delete')?>" title="<?=$this->transEsc('delete_selected')?>"/> - <? endif; ?> - <? $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> + <?php if ((null !== $this->list && $this->list->editAllowed($user)) || null === $this->list && $user): ?> + <input class="btn btn-default" id="<?=$this->idPrefix?>delete_list_items_<?=null !== $this->list ? $this->escapeHtmlAttr($this->list->id) : ''?>" type="submit" name="delete" value="<?=$this->transEsc('Delete')?>" title="<?=$this->transEsc('delete_selected')?>"/> + <?php endif; ?> + <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> <input class="btn btn-default" type="submit" name="export" value="<?=$this->transEsc('Export')?>" title="<?=$this->transEsc('export_selected')?>"/> - <? endif; ?> + <?php endif; ?> <input class="btn btn-default" type="submit" name="print" value="<?=$this->transEsc('Print')?>" title="<?=$this->transEsc('print_selected')?>" data-lightbox-ignore/> - <? if ($this->cart()->isActive()): ?> + <?php if ($this->cart()->isActive()): ?> <input class="btn btn-default" id="<?=$this->idPrefix?>updateCart" type="submit" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/> - <? endif; ?> + <?php endif; ?> </div> </div> diff --git a/themes/bootstrap3/templates/myresearch/cataloglogin.phtml b/themes/bootstrap3/templates/myresearch/cataloglogin.phtml index eed7393889c674adeff58e4fddec8f351cfd971f..27ad44901d244ddc98589d05143dc5a1d4585dd8 100644 --- a/themes/bootstrap3/templates/myresearch/cataloglogin.phtml +++ b/themes/bootstrap3/templates/myresearch/cataloglogin.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Login')); @@ -8,23 +8,23 @@ // Convenience variable: $offlineMode = $this->ils()->getOfflineMode(); ?> -<? if ($offlineMode == "ils-offline"): ?> +<?php if ($offlineMode == "ils-offline"): ?> <?=$this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_login_message'])?> -<? else: ?> +<?php else: ?> <h3><?=$this->transEsc('Library Catalog Profile')?></h3> <?=$this->flashmessages()?> <p><?=$this->transEsc('cat_establish_account')?></p> <form method="post" action="<?=$this->serverUrl(true)?>" class="form-catalog-login"> - <? if ($this->targets !== null): ?> + <?php if ($this->targets !== null): ?> <div class="form-group"> <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> <select id="login_target" name="target" class="form-control"> - <? foreach ($this->targets as $target): ?> + <?php foreach ($this->targets as $target): ?> <option value="<?=$this->escapeHtmlAttr($target)?>"><?=$this->transEsc("source_$target", null, $target)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <label class="control-label" for="profile_cat_username"><?=$this->transEsc('Library Catalog Username')?>:</label> <input id="profile_cat_username" type="text" name="cat_username" value="" class="form-control"/> @@ -37,4 +37,4 @@ <input class="btn btn-primary" type="submit" name="processLogin" value="<?=$this->transEsc('Login')?>"> </div> </form> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/checkedout.phtml b/themes/bootstrap3/templates/myresearch/checkedout.phtml index 87f4bdf714d7cd5c1372c29c9dde7ab03f272777..1aa5feb1e0560d45c14478d20450c24f6c6958ab 100644 --- a/themes/bootstrap3/templates/myresearch/checkedout.phtml +++ b/themes/bootstrap3/templates/myresearch/checkedout.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Checked Out Items')); @@ -12,8 +12,8 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> - <? if (!empty($this->transactions)): ?> - <? if ($this->renewForm): ?> + <?php if (!empty($this->transactions)): ?> + <?php if ($this->renewForm): ?> <form name="renewals" method="post" id="renewals"> <div class="toolbar"> <div class="checkbox"> @@ -25,45 +25,45 @@ <input type="submit" class="btn btn-default" id="renewAll" name="renewAll" value="<?=$this->transEsc('renew_all')?>" /> </div> </div> - <? endif; ?> + <?php endif; ?> - <? if ($paginator): ?> - <? + <?php if ($paginator): ?> + <?php $end = min( $paginator->getAbsoluteItemNumber($paginator->getItemCountPerPage()), $paginator->getTotalItemCount() ); $transParams = [ '%%start%%' => $this->localizedNumber($paginator->getAbsoluteItemNumber(1)), - '%%end%%' => $this->localizedNumber($end), + '%%end%%' => $this->localizedNumber($end), '%%total%%' => $this->localizedNumber($paginator->getTotalItemCount()) ]; ?> <?=$this->translate('showing_items_of_html', $transParams); ?> - <? endif; ?> + <?php endif; ?> - <? foreach ($hiddenTransactions as $ilsDetails): ?> - <? if (isset($this->renewResult[$ilsDetails['item_id']])): ?> - <? $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?> - <? $prefix = isset($ilsDetails['title']) ? $ilsDetails['title'] : $ilsDetails['item_id']; ?> - <? if (isset($renewDetails['success']) && $renewDetails['success']): ?> + <?php foreach ($hiddenTransactions as $ilsDetails): ?> + <?php if (isset($this->renewResult[$ilsDetails['item_id']])): ?> + <?php $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?> + <?php $prefix = $ilsDetails['title'] ?? $ilsDetails['item_id']; ?> + <?php if (isset($renewDetails['success']) && $renewDetails['success']): ?> <div class="alert alert-success"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_success')?></div> - <? else: ?> - <div class="alert alert-danger"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_fail')?><? if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><? endif; ?></div> - <? endif; ?> - <? endif; ?> - <? if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?> - <? $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $ilsDetails['renew_details']); ?> + <?php else: ?> + <div class="alert alert-danger"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></div> + <?php endif; ?> + <?php endif; ?> + <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $ilsDetails['renew_details']); ?> <input class="pull-left flip" type="hidden" name="renewAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" /> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> - <? $i = 0; foreach ($this->transactions as $resource): ?> - <? $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php $i = 0; foreach ($this->transactions as $resource): ?> + <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId())?>" class="result"> - <? if ($this->renewForm): ?> - <? if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?> - <? $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $ilsDetails['renew_details']); ?> + <?php if ($this->renewForm): ?> + <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $ilsDetails['renew_details']); ?> <div class="checkbox"> <label> <input class="checkbox-select-item" type="checkbox" name="renewSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" id="checkbox_<?=$safeId?>" /> @@ -71,10 +71,10 @@ <input type="hidden" name="selectAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" /> <input type="hidden" name="renewAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['renew_details'])?>" /> </div> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> - <? + <?php $coverDetails = $this->record($resource)->getCoverDetails('checkedout', 'small', $this->recordLink()->getUrl($resource)); $cover = $coverDetails['html']; $thumbnail = false; @@ -84,22 +84,22 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> - <? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> + <?php endif; ?> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> <div class="media-body"> - <? + <?php // If this is a non-missing Solr record, we should display a link: if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); echo '<a href="' . $this->recordLink()->getUrl($resource) . '" class="title">' . $title . '</a>'; - } else if (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... echo $this->escapeHtml($ilsDetails['title']); } else { @@ -107,84 +107,88 @@ echo $this->transEsc('Title not available'); } ?><br/> - <? $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> + <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> <?=$this->transEsc('by')?>: - <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><? if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?><br/> - <? endif; ?> - <? if (count($resource->getFormats()) > 0): ?> + <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/> + <?php endif; ?> + <?php if (count($resource->getFormats()) > 0): ?> <?=$this->record($resource)->getFormatList() ?> <br/> - <? endif; ?> - <? if (!empty($ilsDetails['volume'])): ?> + <?php endif; ?> + <?php if (!empty($ilsDetails['volume'])): ?> <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (!empty($ilsDetails['publication_year'])): ?> + <?php if (!empty($ilsDetails['publication_year'])): ?> <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?> <br /> - <? endif; ?> + <?php endif; ?> + <?php if ($this->displayItemBarcode && !empty($ilsDetails['barcode'])): ?> + <strong><?=$this->transEsc('Barcode')?>:</strong> <?=$this->escapeHtml($ilsDetails['barcode'])?> + <br /> + <?php endif; ?> - <? if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> + <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong> <br /> - <? endif; ?> + <?php endif; ?> - <? if (!empty($ilsDetails['borrowingLocation'])): ?> + <?php if (!empty($ilsDetails['borrowingLocation'])): ?> <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (isset($ilsDetails['renew'])): ?> + <?php if (isset($ilsDetails['renew'])): ?> <strong><?=$this->transEsc('Renewed')?>:</strong> <?=$this->transEsc($ilsDetails['renew'])?> - <? if (isset($ilsDetails['renewLimit'])): ?> + <?php if (isset($ilsDetails['renewLimit'])): ?> / <?=$this->transEsc($ilsDetails['renewLimit'])?> - <? endif; ?> + <?php endif; ?> <br /> - <? endif; ?> + <?php endif; ?> - <? $showStatus = true; ?> + <?php $showStatus = true; ?> - <? if (isset($this->renewResult[$ilsDetails['item_id']])): ?> - <? $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?> - <? if (isset($renewDetails['success']) && $renewDetails['success']): ?> - <? $showStatus = false; ?> - <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($renewDetails['new_date'])?> <? if (isset($renewDetails['new_time'])): ?><?=$this->escapeHtml($renewDetails['new_time'])?><? endif; ?></strong> + <?php if (isset($this->renewResult[$ilsDetails['item_id']])): ?> + <?php $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?> + <?php if (isset($renewDetails['success']) && $renewDetails['success']): ?> + <?php $showStatus = false; ?> + <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($renewDetails['new_date'])?> <?php if (isset($renewDetails['new_time'])): ?><?=$this->escapeHtml($renewDetails['new_time'])?><?php endif; ?></strong> <div class="alert alert-success"><?=$this->transEsc('renew_success')?></div> - <? else: ?> - <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><? if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><? endif; ?></strong> - <div class="alert alert-danger"><?=$this->transEsc('renew_fail')?><? if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><? endif; ?></div> - <? endif; ?> - <? else: ?> - <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><? if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><? endif; ?></strong> - <? if ($showStatus): ?> - <? if (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "overdue"): ?> + <?php else: ?> + <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><?php endif; ?></strong> + <div class="alert alert-danger"><?=$this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></div> + <?php endif; ?> + <?php else: ?> + <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><?php endif; ?></strong> + <?php if ($showStatus): ?> + <?php if (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "overdue"): ?> <div class="alert alert-danger"><?=$this->transEsc("renew_item_overdue")?></div> - <? elseif (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "due"): ?> + <?php elseif (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "due"): ?> <div class="alert alert-info"><?=$this->transEsc("renew_item_due")?></div> - <? endif; ?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> - <? if ($showStatus && isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?> + <?php if ($showStatus && isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?> <div class="alert alert-info"><?=$this->transEsc($ilsDetails['message'])?></div> - <? endif; ?> - <? if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_link'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_link'])): ?> <a href="<?=$this->escapeHtmlAttr($ilsDetails['renew_link'])?>"><?=$this->transEsc('renew_item')?></a> - <? endif; ?> + <?php endif; ?> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> </div> - <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="'.$this->escapeHtmlAttr($resource->getCoinsOpenUrl()).'"></span>':''?> + <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> </div> - <? endforeach; ?> - <? if ($this->renewForm): ?></form><? endif; ?> + <?php endforeach; ?> + <?php if ($this->renewForm): ?></form><?php endif; ?> <?=$paginator ? $this->paginationControl($paginator, 'Sliding', 'Helpers/pagination.phtml') : ''?> - <? else: ?> + <?php else: ?> <?=$this->transEsc('You do not have any items checked out')?>. - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/myresearch/controls/sort.phtml b/themes/bootstrap3/templates/myresearch/controls/sort.phtml new file mode 100644 index 0000000000000000000000000000000000000000..0c24ec7c97c1fae5758f89fdfc7dd820ba183617 --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/controls/sort.phtml @@ -0,0 +1,11 @@ +<div class="search-controls"> + <form class="search-sort" action="<?=$this->currentPath()?>" method="get" name="sort"> + <label for="sort_options_1"><?=$this->transEsc('Sort')?></label> + <select id="sort_options_1" name="sort" class="jumpMenu form-control"> + <?php foreach ($this->sortList as $sortType => $sortData): ?> + <option value="<?=$this->escapeHtmlAttr($sortType)?>"<?=$sortData['selected']?' selected="selected"':''?>><?=$this->transEsc($sortData['desc'])?></option> + <?php endforeach; ?> + </select> + <noscript><input type="submit" class="btn btn-default" value="<?=$this->transEsc("Set")?>" /></noscript> + </form> +</div> diff --git a/themes/bootstrap3/templates/myresearch/delete.phtml b/themes/bootstrap3/templates/myresearch/delete.phtml index 5829c92163ffab74bd47fe5848025d56172bdbcd..269608ed02d69358d1d7fd209471ccb728f3b9ed 100644 --- a/themes/bootstrap3/templates/myresearch/delete.phtml +++ b/themes/bootstrap3/templates/myresearch/delete.phtml @@ -2,21 +2,21 @@ <form action="<?=$this->url('myresearch-delete')?>" method="post" name="bulkDelete" data-lightbox-onclose="VuFind.refreshPage"> <div id="popupMessages"><?=$this->flashmessages()?></div> <div id="popupDetails"> - <? if (!$this->list): ?> + <?php if (!$this->list): ?> <div class="alert alert-info"><?=$this->transEsc("fav_delete_warn") ?></div> - <? else: ?> + <?php else: ?> <h3><?=$this->transEsc("List") ?>: <?=$this->escapeHtml($this->list->title) ?></h3> - <? endif; ?> + <?php endif; ?> - <? foreach ($this->records as $favorite): ?> + <?php foreach ($this->records as $favorite): ?> <strong><?=$this->transEsc('Title') ?>:</strong> <?=$this->escapeHtml($favorite->getBreadcrumb())?><br /> - <? endforeach; ?> + <?php endforeach; ?> <br /> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Delete')?>"/> - <? foreach ($this->deleteIDS as $deleteID): ?> + <?php foreach ($this->deleteIDS as $deleteID): ?> <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($deleteID)?>" /> - <? endforeach; ?> + <?php endforeach; ?> <input type="hidden" name="listID" value="<?=$this->list?$this->escapeHtmlAttr($this->list->id):''?>" /> </div> </form> diff --git a/themes/bootstrap3/templates/myresearch/deleteaccount.phtml b/themes/bootstrap3/templates/myresearch/deleteaccount.phtml new file mode 100644 index 0000000000000000000000000000000000000000..140d2fc16ece6ac77286c9da6dbac4a1c5d25a70 --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/deleteaccount.phtml @@ -0,0 +1,20 @@ +<h2><?=$this->transEsc('delete_account_title') ?></h2> +<?=$this->flashmessages();?> +<?php if ($this->accountDeleted): ?> + <div id="delete-account-success"> + <p><?=$this->translate('delete_account_success_message'); ?></p> + <?php + // Logout redirect with inline script to make it lightbox compatible + $script = "setTimeout(function() { window.location = '{$this->redirectUrl}'; }, 3000);"; + ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> + </div> +<?php else: ?> + <form id="delete-account" method="post" action="<?=$this->url('myresearch-deleteaccount') ?>" name="deleteAccount"> + <h3><?=$this->transEsc('delete_account_confirm'); ?></h3> + <p><?=$this->translate('delete_account_description_html'); ?></p> + <input type="hidden" name="csrf" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash())?>" /> + <input id="delete-account-cancel" class="btn btn-primary" type="submit" name="reset" data-dismiss="modal" value="<?=$this->transEsc('confirm_dialog_no'); ?>"/> + <input id="delete-account-submit" class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('confirm_dialog_yes'); ?>"/> + </form> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/edit.phtml b/themes/bootstrap3/templates/myresearch/edit.phtml index 24c42c84d58e7cc70104ed4ceaa02844c72e341a..dd5fb3e8e4a2197ef185951152dd3842a4da1a8c 100644 --- a/themes/bootstrap3/templates/myresearch/edit.phtml +++ b/themes/bootstrap3/templates/myresearch/edit.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Edit') . ' : ' . $this->driver->getBreadcrumb()); @@ -9,53 +9,53 @@ <h2><?=$this->escapeHtml($this->driver->getBreadcrumb())?></h2> <form class="form-list-edit" method="post" name="editForm"> - <? if (empty($this->savedData)): ?> + <?php if (empty($this->savedData)): ?> <p class="alert alert-info"> - <? if (isset($listFilter)): ?> + <?php if (isset($listFilter)): ?> <?=$this->transEsc('The record you selected is not part of the selected list.') ?> - <? else: ?> + <?php else: ?> <?=$this->transEsc('The record you selected is not part of any of your lists.') ?> - <? endif; ?> + <?php endif; ?> </p> - <? else: ?> + <?php else: ?> <div class="list-edit-container"> - <? foreach ($this->savedData as $i=>$current): ?> + <?php foreach ($this->savedData as $i => $current): ?> <fieldset class="list-edit-group"> <h3><?=$this->transEsc('List') ?>: <?=$this->escapeHtml($current['listTitle'])?></h3> <input type="hidden" name="lists[]" value="<?=$current['listId'] ?>"/> - <? if ($this->usertags()->getMode() !== 'disabled'): ?> + <?php if ($this->usertags()->getMode() !== 'disabled'): ?> <div class="form-group"> <label class="control-label" for="edit_tags<?=$current['listId'] ?>"><?=$this->transEsc('Tags') ?>:</label> <input type="text" name="tags<?=$current['listId'] ?>" id="edit_tags<?=$current['listId'] ?>" class="form-control" value="<?=$this->escapeHtmlAttr($current['tags'])?>"/> <span class="help-block"><?=$this->transEsc("add_tag_note") ?></span> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <label class="control-label" for="edit_notes<?=$current['listId'] ?>"><?=$this->transEsc('Notes') ?>:</label> <textarea class="form-control" id="edit_notes<?=$current['listId'] ?>" name="notes<?=$current['listId'] ?>" rows="3"><?=$this->escapeHtml($current['notes'])?></textarea> </div> <a href="<?=$this->url('userList', ['id' => $current['listId']]) ?>?delete=<?=urlencode($this->driver->getUniqueId())?>&source=<?=urlencode($this->driver->getSourceIdentifier())?>" id="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>delete<?=$current['listId'] ?>" data-lightbox> - <i class="fa fa-trash"></i> <?=$this->transEsc('delete') /* TODO: replace with better string */ ?> + <i class="fa fa-trash"></i> <?=$this->transEsc('Delete') ?> </a> </fieldset> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> - <? if (count($this->lists) > 0): ?> + <?php endif; ?> + <?php if (count($this->lists) > 0): ?> <hr/> <div class="form-group"> <select name="addToList" class="form-control"> <option value="-1">- <?=$this->transEsc('Add to another list')?> -</option> - <? foreach ($this->lists as $listID=>$listTitle): ?> + <?php foreach ($this->lists as $listID => $listTitle): ?> <option value="<?=$listID ?>"><?=$this->escapeHtml($listTitle) ?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endif; ?> - <? if (!empty($this->savedData) || count($this->lists) > 0): ?> + <?php endif; ?> + <?php if (!empty($this->savedData) || count($this->lists) > 0): ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>"/> </div> - <? endif; ?> + <?php endif; ?> </form> </div> diff --git a/themes/bootstrap3/templates/myresearch/editlist.phtml b/themes/bootstrap3/templates/myresearch/editlist.phtml index df9daf9b89fab281428b3edafae31a7a84ff1386..1bd67d0d147780e200663aa006f18fc45f45ef14 100644 --- a/themes/bootstrap3/templates/myresearch/editlist.phtml +++ b/themes/bootstrap3/templates/myresearch/editlist.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $pageTitle = empty($this->list->id) ? 'Create a List' : "edit_list"; $this->headTitle($this->translate($pageTitle)); @@ -22,25 +22,25 @@ <label class="control-label" for="list_desc"><?=$this->transEsc('Description') ?></label> <textarea id="list_desc" class="form-control" name="desc" rows="3"><?=isset($this->list['description']) ? $this->escapeHtml($this->list['description']) : ''?></textarea> </div> - <? if ($this->userlist()->getMode() === 'public_only'): ?> + <?php if ($this->userlist()->getMode() === 'public_only'): ?> <input type="hidden" name="public" value="1" /> - <? elseif ($this->userlist()->getMode() === 'private_only'): ?> + <?php elseif ($this->userlist()->getMode() === 'private_only'): ?> <input type="hidden" name="public" value="0" /> - <? else: ?> + <?php else: ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('Access') ?></label> <div class="radio inline"> <label> - <input id="list_public_1" type="radio" name="public" value="1"<? if ($this->list->isPublic()): ?> checked="checked"<? endif; ?>/> <?=$this->transEsc('Public') ?> + <input id="list_public_1" type="radio" name="public" value="1"<?php if ($this->list->isPublic()): ?> checked="checked"<?php endif; ?>/> <?=$this->transEsc('Public') ?> </label> </div> <div class="radio inline"> <label> - <input id="list_public_0" type="radio" name="public" value="0"<? if (!$this->list->isPublic()): ?> checked="checked"<? endif; ?>/> <?=$this->transEsc('Private') ?> + <input id="list_public_0" type="radio" name="public" value="0"<?php if (!$this->list->isPublic()): ?> checked="checked"<?php endif; ?>/> <?=$this->transEsc('Private') ?> </label> </div> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>"/> </div> diff --git a/themes/bootstrap3/templates/myresearch/fines.phtml b/themes/bootstrap3/templates/myresearch/fines.phtml index e42726f1b52ffe38d07ca4dd8a9261d6cd648845..bd71880b49071511400aebcff2be5c1a1e7f1887 100644 --- a/themes/bootstrap3/templates/myresearch/fines.phtml +++ b/themes/bootstrap3/templates/myresearch/fines.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('My Fines')); @@ -11,10 +11,11 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> - <? if (empty($this->fines)): ?> + <?php if (empty($this->fines)): ?> <?=$this->transEsc('You do not have any fines')?> - <? else: ?> - <table class="table table-striped" summary="<?=$this->transEsc('Your Fines')?>"> + <?php else: ?> + <table class="table table-striped"> + <caption class="sr-only"><?=$this->transEsc('Your Fines')?></caption> <tr> <th><?=$this->transEsc('Title')?></th> <th><?=$this->transEsc('Checked Out')?></th> @@ -23,29 +24,29 @@ <th><?=$this->transEsc('Fee')?></th> <th><?=$this->transEsc('Balance')?></th> </tr> - <? $totalDue = 0; ?> - <? foreach ($this->fines as $record): ?> + <?php $totalDue = 0; ?> + <?php foreach ($this->fines as $record): ?> <tr> <td> - <? if (empty($record['title'])): ?> + <?php if (empty($record['title'])): ?> <?=$this->transEsc('not_applicable')?> - <? elseif (!isset($record['driver']) || !is_object($record['driver'])): ?> + <?php elseif (!isset($record['driver']) || !is_object($record['driver'])): ?> <?=$this->escapeHtml(trim($record['title'], '/:'))?> - <? else: ?> + <?php else: ?> <a href="<?=$this->recordLink()->getUrl($record['driver'])?>"><?=$this->escapeHtml(trim($record['title'], '/:'))?></a> - <? endif; ?> + <?php endif; ?> </td> <td><?=isset($record['checkout']) ? $this->escapeHtml($record['checkout']) : ''?></td> <td><?=isset($record['duedate']) ? $this->escapeHtml($record['duedate']) : ''?></td> <td><?=isset($record['fine']) ? $this->escapeHtml($record['fine']) : ''?></td> - <td><?=isset($record['amount']) ? $this->safeMoneyFormat($record['amount']/100.00) : ''?></td> - <td><?=isset($record['balance']) ? $this->safeMoneyFormat($record['balance']/100.00) : ''?></td> + <td><?=isset($record['amount']) ? $this->safeMoneyFormat($record['amount'] / 100.00) : ''?></td> + <td><?=isset($record['balance']) ? $this->safeMoneyFormat($record['balance'] / 100.00) : ''?></td> </tr> - <? $totalDue += $record['balance']; ?> - <? endforeach; ?> - <tr style="font-weight:bold"><td colspan="5"><?=$this->transEsc('Total Balance Due')?></td><td><?=$this->safeMoneyFormat($totalDue/100.00) ?></td></tr> + <?php $totalDue += $record['balance']; ?> + <?php endforeach; ?> + <tr style="font-weight:bold"><td colspan="5"><?=$this->transEsc('Total Balance Due')?></td><td><?=$this->safeMoneyFormat($totalDue / 100.00) ?></td></tr> </table> - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/myresearch/historicloans.phtml b/themes/bootstrap3/templates/myresearch/historicloans.phtml new file mode 100644 index 0000000000000000000000000000000000000000..d6e55653e0d6ab50e686bd501bbea5420019122f --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/historicloans.phtml @@ -0,0 +1,131 @@ +<?php + // Set up page title: + $this->headTitle($this->translate('Loan History')); + + // Set up breadcrumbs: + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Loan History') . '</li>'; +?> + +<div class="<?=$this->layoutClass('mainbody')?>"> + <h2><?=$this->transEsc('Loan History')?></h2> + <?=$this->flashmessages()?> + + <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> + + <?php if (!empty($this->transactions)): ?> + <nav class="search-header hidden-print"> + <?php if ($this->paginator): ?> + <div class="search-stats"> + <?php + $end = min( + $this->paginator->getAbsoluteItemNumber($this->paginator->getItemCountPerPage()), + $this->paginator->getTotalItemCount() + ); + $transParams = [ + '%%start%%' => $this->localizedNumber($this->paginator->getAbsoluteItemNumber(1)), + '%%end%%' => $this->localizedNumber($end), + '%%total%%' => $this->localizedNumber($this->paginator->getTotalItemCount()) + ]; + ?> + <?=$this->translate('showing_items_of_html', $transParams); ?> + </div> + <?php endif; ?> + <?php if ($this->sortList): ?> + <?=$this->context($this)->renderInContext('myresearch/controls/sort.phtml', ['sortList' => $this->sortList]); ?> + <?php endif; ?> + </nav> + + <?php $i = 0; foreach ($this->transactions as $resource): ?> + <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId())?>" class="result"> + <?php + $coverDetails = $this->record($resource)->getCoverDetails('checkedout', 'small', $this->recordLink()->getUrl($resource)); + $cover = $coverDetails['html']; + $thumbnail = false; + $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account'); + if ($cover): + ob_start(); ?> + <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> + <?=$cover ?> + </div> + <?php $thumbnail = ob_get_contents(); + ob_end_clean(); + endif; ?> + <div class="media"> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?=$thumbnail ?> + <?php endif ?> + <div class="media-body"> + <?php + // If this is a non-missing Solr record, we should display a link: + if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { + $title = $resource->getTitle(); + $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); + echo '<a href="' . $this->recordLink()->getUrl($resource) . + '" class="title">' . $title . '</a>'; + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ + // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... + echo $this->escapeHtml($ilsDetails['title']); + } else { + // Last resort -- indicate that no title could be found. + echo $this->transEsc('Title not available'); + } + ?><br/> + <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> + <?=$this->transEsc('by')?>: + <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/> + <?php endif; ?> + <?php if (count($resource->getFormats()) > 0): ?> + <?=$this->record($resource)->getFormatList() ?> + <br/> + <?php endif; ?> + <?php if (!empty($ilsDetails['volume'])): ?> + <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?> + <br /> + <?php endif; ?> + + <?php if (!empty($ilsDetails['publication_year'])): ?> + <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?> + <br /> + <?php endif; ?> + + <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> + <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong> + <br /> + <?php endif; ?> + + <?php if (!empty($ilsDetails['borrowingLocation'])): ?> + <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?> + <br /> + <?php endif; ?> + + <?php if (!empty($ilsDetails['checkoutDate'])): ?> + <strong><?=$this->transEsc('Checkout Date')?>:</strong> <?=$this->escapeHtml($ilsDetails['checkoutDate'])?><?php if (isset($ilsDetails['checkoutTime'])): ?> <span class="checkout-time"><?=$this->escapeHtml($ilsDetails['checkoutTime'])?><?php endif; ?></span><br/> + <?php endif; ?> + <?php if (!empty($ilsDetails['returnDate'])): ?> + <strong><?=$this->transEsc('Return Date')?>:</strong> <?=$this->escapeHtml($ilsDetails['returnDate'])?><?php if (isset($ilsDetails['returnTime'])): ?> <span class="return-time"><?=$this->escapeHtml($ilsDetails['returnTime'])?><?php endif; ?></span><br/> + <?php endif; ?> + <?php if (!empty($ilsDetails['dueDate'])): ?> + <strong><?=$this->transEsc('Due Date')?>:</strong> <?=$this->escapeHtml($ilsDetails['dueDate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <span class="due-time"><?=$this->escapeHtml($ilsDetails['dueTime'])?></span><?php endif; ?> + <?php endif; ?> + + <?php if (isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?> + <div class="alert alert-info"><?=$this->transEsc($ilsDetails['message'])?></div> + <?php endif; ?> + </div> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?=$thumbnail ?> + <?php endif ?> + </div> + <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> + </div> + <?php endforeach; ?> + <?=$this->paginator ? $this->paginationControl($this->paginator, 'Sliding', 'Helpers/pagination.phtml', ['params' => $this->params]) : ''?> + <?php else: ?> + <?=$this->transEsc('loan_history_empty')?> + <?php endif; ?> +</div> + +<div class="<?=$this->layoutClass('sidebar')?>"> + <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'historicloans'])?> +</div> diff --git a/themes/bootstrap3/templates/myresearch/holds.phtml b/themes/bootstrap3/templates/myresearch/holds.phtml index 4dee22f685fe600f7d8421639bce4c697fa676d8..1c5fc30e6e581919378ce76ef5d1cf3e196cfe0d 100644 --- a/themes/bootstrap3/templates/myresearch/holds.phtml +++ b/themes/bootstrap3/templates/myresearch/holds.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('My Holds')); @@ -13,8 +13,8 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> - <? if (!empty($this->recordList)): ?> - <? if ($this->cancelForm): ?> + <?php if (!empty($this->recordList)): ?> + <?php if ($this->cancelForm): ?> <form name="cancelForm" class="inline" method="post" id="cancelHold"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> @@ -34,24 +34,24 @@ <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> - <? endif; ?> + <?php endif; ?> - <? $iteration = 0; ?> - <? foreach ($this->recordList as $resource): ?> - <? $iteration++; ?> - <? $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php $iteration = 0; ?> + <?php foreach ($this->recordList as $resource): ?> + <?php $iteration++; ?> + <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result"> - <? if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> - <? $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?> + <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?> <input type="hidden" name="cancelAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" /> <div class="checkbox"> <label> <input type="checkbox" name="cancelSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" id="checkbox_<?=$safeId?>" /> </label> </div> - <? endif; ?> + <?php endif; ?> - <? + <?php $coverDetails = $this->record($resource)->getCoverDetails('holds', 'small', $this->recordLink()->getUrl($resource)); $cover = $coverDetails['html']; $thumbnail = false; @@ -61,22 +61,22 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> - <? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> + <?php endif; ?> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> <div class="media-body"> - <? + <?php // If this is a non-missing Solr record, we should display a link: if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); echo '<a href="' . $this->recordLink()->getUrl($resource) . '" class="title">' . $title . '</a>'; - } else if (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... echo '<span class="title">' . $this->escapeHtml($ilsDetails['title']) . '</span>'; } else { @@ -84,92 +84,92 @@ echo $this->transEsc('Title not available'); } ?><br/> - <? $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> + <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> <?=$this->transEsc('by')?>: - <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><? if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?><br/> - <? endif; ?> + <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/> + <?php endif; ?> - <? if (count($resource->getFormats()) > 0): ?> + <?php if (count($resource->getFormats()) > 0): ?> <?=$this->record($resource)->getFormatList() ?> <br/> - <? endif; ?> - <? if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?> <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?> + <?php if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?> <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (!empty($ilsDetails['requestGroup'])): ?> + <?php if (!empty($ilsDetails['requestGroup'])): ?> <strong><?=$this->transEsc('hold_requested_group') ?>:</strong> <?=$this->transEsc('request_group_' . $ilsDetails['requestGroup'], null, $ilsDetails['requestGroup'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best + <?php /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best value to display... */ ?> - <? $pickupDisplay = ''; ?> - <? $pickupTranslate = false; ?> - <? if (isset($ilsDetails['location'])): ?> - <? if ($this->pickup): ?> - <? foreach ($this->pickup as $library): ?> - <? if ($library['locationID'] == $ilsDetails['location']): ?> - <? $pickupDisplay = $library['locationDisplay']; ?> - <? $pickupTranslate = true; ?> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> - <? if (empty($pickupDisplay)): ?> - <? $pickupDisplay = $ilsDetails['location']; ?> - <? endif; ?> - <? endif; ?> - <? if (!empty($pickupDisplay)): ?> + <?php $pickupDisplay = ''; ?> + <?php $pickupTranslate = false; ?> + <?php if (isset($ilsDetails['location'])): ?> + <?php if ($this->pickup): ?> + <?php foreach ($this->pickup as $library): ?> + <?php if ($library['locationID'] == $ilsDetails['location']): ?> + <?php $pickupDisplay = $library['locationDisplay']; ?> + <?php $pickupTranslate = true; ?> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php if (empty($pickupDisplay)): ?> + <?php $pickupDisplay = $ilsDetails['location']; ?> + <?php endif; ?> + <?php endif; ?> + <?php if (!empty($pickupDisplay)): ?> <strong><?=$this->transEsc('pick_up_location') ?>:</strong> <?=$pickupTranslate ? $this->transEsc('location_' . $pickupDisplay, null, $pickupDisplay) : $this->escapeHtml($pickupDisplay)?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (!empty($ilsDetails['create'])): ?> + <?php if (!empty($ilsDetails['create'])): ?> <strong><?=$this->transEsc('Created') ?>:</strong> <?=$this->escapeHtml($ilsDetails['create']) ?> - <? if (!empty($ilsDetails['expire'])): ?>|<? endif; ?> - <? endif; ?> - <? if (!empty($ilsDetails['expire'])): ?> + <?php if (!empty($ilsDetails['expire'])): ?>|<?php endif; ?> + <?php endif; ?> + <?php if (!empty($ilsDetails['expire'])): ?> <strong><?=$this->transEsc('Expires') ?>:</strong> <?=$this->escapeHtml($ilsDetails['expire']) ?> - <? endif; ?> + <?php endif; ?> <br /> - <? if (isset($this->cancelResults['items'])): ?> - <? foreach ($this->cancelResults['items'] as $itemId=>$cancelResult): ?> - <? if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> - <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><? if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> + <?php if (isset($this->cancelResults['items'])): ?> + <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?> + <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> + <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? if (isset($ilsDetails['available']) && $ilsDetails['available'] == true): ?> + <?php if (isset($ilsDetails['available']) && $ilsDetails['available'] == true): ?> <div class="text-success"><?=$this->transEsc("hold_available") ?></div> - <? elseif (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?> + <?php elseif (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?> <div class="text-success"><?=$this->transEsc('request_in_transit') . (is_string($ilsDetails['in_transit']) ? ': ' . $this->transEsc('institution_' . $ilsDetails['in_transit'], [], $ilsDetails['in_transit']) : '') ?></div> - <? elseif (isset($ilsDetails['position'])): ?> + <?php elseif (isset($ilsDetails['position'])): ?> <p><strong><?=$this->transEsc("hold_queue_position") ?>:</strong> <?=$this->escapeHtml($ilsDetails['position']) ?></p> - <? endif; ?> - <? if (isset($ilsDetails['cancel_link'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['cancel_link'])): ?> <p><a href="<?=$this->escapeHtmlAttr($ilsDetails['cancel_link']) ?>"><?=$this->transEsc("hold_cancel") ?></a></p> - <? endif; ?> + <?php endif; ?> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> </div> - <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="'.$this->escapeHtmlAttr($resource->getCoinsOpenUrl()).'"></span>':''?> + <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> </div> - <? endforeach; ?> - <? if ($this->cancelForm): ?></form><? endif; ?> - <? else: ?> + <?php endforeach; ?> + <?php if ($this->cancelForm): ?></form><?php endif; ?> + <?php else: ?> <?=$this->transEsc('You do not have any holds or recalls placed') ?>. - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/myresearch/illrequests.phtml b/themes/bootstrap3/templates/myresearch/illrequests.phtml index c81907188407d0649287894315b6af282e9d0996..d880f395d513626ea1cacfd5bdc87d56d44e46e8 100644 --- a/themes/bootstrap3/templates/myresearch/illrequests.phtml +++ b/themes/bootstrap3/templates/myresearch/illrequests.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Interlibrary Loan Requests')); @@ -13,8 +13,8 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> - <? if (!empty($this->recordList)): ?> - <? if ($this->cancelForm): ?> + <?php if (!empty($this->recordList)): ?> + <?php if ($this->cancelForm): ?> <form name="cancelForm" class="inline" method="post" id="cancelILLRequest"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> @@ -34,24 +34,24 @@ <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> - <? endif; ?> + <?php endif; ?> - <? $iteration = 0; ?> - <? foreach ($this->recordList as $resource): ?> - <? $iteration++; ?> - <? $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php $iteration = 0; ?> + <?php foreach ($this->recordList as $resource): ?> + <?php $iteration++; ?> + <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result"> - <? if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> - <? $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?> + <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?> <div class="checkbox"> <input type="hidden" name="cancelAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" /> <label> <input type="checkbox" name="cancelSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" id="checkbox_<?=$safeId?>" /> </label> </div> - <? endif; ?> + <?php endif; ?> - <? + <?php $coverDetails = $this->record($resource)->getCoverDetails('illrequests', 'small', $this->recordLink()->getUrl($resource)); $cover = $coverDetails['html']; $thumbnail = false; @@ -61,22 +61,22 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> - <? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> + <?php endif; ?> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> <div class="media-body"> - <? + <?php // If this is a non-missing Solr record, we should display a link: if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); echo '<a href="' . $this->recordLink()->getUrl($resource) . '" class="title">' . $title . '</a>'; - } else if (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... echo $this->escapeHtml($ilsDetails['title']); } else { @@ -84,92 +84,92 @@ echo $this->transEsc('Title not available'); } ?><br/> - <? $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> + <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> <?=$this->transEsc('by')?>: - <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><? if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?><br/> - <? endif; ?> + <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/> + <?php endif; ?> - <? if (count($resource->getFormats()) > 0): ?> + <?php if (count($resource->getFormats()) > 0): ?> <?=$this->record($resource)->getFormatList() ?> <br/> - <? endif; ?> - <? if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?> <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?> + <?php if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?> <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best + <?php /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best value to display... */ ?> - <? $pickupDisplay = ''; ?> - <? $pickupTranslate = false; ?> - <? if (isset($ilsDetails['location'])): ?> - <? if ($this->pickup): ?> - <? foreach ($this->pickup as $library): ?> - <? if ($library['locationID'] == $ilsDetails['location']): ?> - <? $pickupDisplay = $library['locationDisplay']; ?> - <? $pickupTranslate = true; ?> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> - <? if (empty($pickupDisplay)): ?> - <? $pickupDisplay = $ilsDetails['location']; ?> - <? endif; ?> - <? endif; ?> - <? if (!empty($pickupDisplay)): ?> + <?php $pickupDisplay = ''; ?> + <?php $pickupTranslate = false; ?> + <?php if (isset($ilsDetails['location'])): ?> + <?php if ($this->pickup): ?> + <?php foreach ($this->pickup as $library): ?> + <?php if ($library['locationID'] == $ilsDetails['location']): ?> + <?php $pickupDisplay = $library['locationDisplay']; ?> + <?php $pickupTranslate = true; ?> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php if (empty($pickupDisplay)): ?> + <?php $pickupDisplay = $ilsDetails['location']; ?> + <?php endif; ?> + <?php endif; ?> + <?php if (!empty($pickupDisplay)): ?> <strong><?=$this->transEsc('pick_up_location') ?>:</strong> <?=$pickupTranslate ? $this->transEsc($pickupDisplay) : $this->escapeHtml($pickupDisplay)?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (!empty($ilsDetails['create'])): ?> + <?php if (!empty($ilsDetails['create'])): ?> <strong><?=$this->transEsc('Created') ?>:</strong> <?=$this->escapeHtml($ilsDetails['create']) ?> - <? if (!empty($ilsDetails['expire'])): ?>|<? endif; ?> - <? endif; ?> - <? if (!empty($ilsDetails['expire'])): ?> + <?php if (!empty($ilsDetails['expire'])): ?>|<?php endif; ?> + <?php endif; ?> + <?php if (!empty($ilsDetails['expire'])): ?> <strong><?=$this->transEsc('Expires') ?>:</strong> <?=$this->escapeHtml($ilsDetails['expire']) ?> - <? endif; ?> + <?php endif; ?> <br /> - <? if (isset($this->cancelResults['items'])): ?> - <? foreach ($this->cancelResults['items'] as $itemId=>$cancelResult): ?> - <? if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> - <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><? if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> + <?php if (isset($this->cancelResults['items'])): ?> + <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?> + <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> + <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? if (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?> + <?php if (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?> <div class="text-success"><?=$this->transEsc("request_in_transit") . (is_string($ilsDetails['in_transit']) ? ': ' . $this->transEsc('institution_' . $ilsDetails['in_transit'], [], $ilsDetails['in_transit']) : '') ?></div> - <? endif; ?> - <? if (isset($ilsDetails['processed']) && $ilsDetails['processed']): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['processed']) && $ilsDetails['processed']): ?> <div class="text-success"><?=$this->transEsc("ill_request_processed") . (is_string($ilsDetails['processed']) ? ': ' . $ilsDetails['processed'] : '') ?></div> - <? endif; ?> - <? if (isset($ilsDetails['available']) && $ilsDetails['available']): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['available']) && $ilsDetails['available']): ?> <div class="text-success"><?=$this->transEsc("ill_request_available") ?></div> - <? endif; ?> - <? if (isset($ilsDetails['canceled']) && $ilsDetails['canceled']): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['canceled']) && $ilsDetails['canceled']): ?> <div class="text-success"><?=$this->transEsc("ill_request_canceled") . (is_string($ilsDetails['canceled']) ? ': ' . $ilsDetails['canceled'] : '') ?></div> - <? endif; ?> - <? if (isset($ilsDetails['cancel_link'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['cancel_link'])): ?> <p><a href="<?=$this->escapeHtmlAttr($ilsDetails['cancel_link']) ?>"><?=$this->transEsc("ill_request_cancel") ?></a></p> - <? endif; ?> + <?php endif; ?> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> </div> - <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="'.$this->escapeHtmlAttr($resource->getCoinsOpenUrl()).'"></span>':''?> + <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> </div> - <? endforeach; ?> - <? if ($this->cancelForm): ?></form><? endif; ?> - <? else: ?> + <?php endforeach; ?> + <?php if ($this->cancelForm): ?></form><?php endif; ?> + <?php else: ?> <?=$this->transEsc('You do not have any interlibrary loan requests placed') ?>. - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/myresearch/login.phtml b/themes/bootstrap3/templates/myresearch/login.phtml index 4f0e20bc1616cfc23f18b36a580450572c3e6aaf..6f0baad732c41e53c82d8dbc48324db8d71a0298 100644 --- a/themes/bootstrap3/templates/myresearch/login.phtml +++ b/themes/bootstrap3/templates/myresearch/login.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Login')); @@ -11,15 +11,15 @@ $offlineMode = $this->ils()->getOfflineMode(); ?> -<? if ($offlineMode == "ils-offline"): ?> +<?php if ($offlineMode == "ils-offline"): ?> <?=$this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_login_message'])?> -<? endif; ?> +<?php endif; ?> <h2 class="lightbox-header"><?=$this->transEsc('Login')?></h2> <?=$this->flashmessages()?> -<? if ($hideLogin): ?> +<?php if ($hideLogin): ?> <div class="alert alert-danger"><?=$this->transEsc('login_disabled')?></div> -<? else: ?> +<?php else: ?> <?=$this->auth()->getLogin()?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/menu.phtml b/themes/bootstrap3/templates/myresearch/menu.phtml index 21ed8cc4ab3f1274124d42810ffc3552e9ae3dd1..df38a22316de0ed776f86881eea016572adcc049 100644 --- a/themes/bootstrap3/templates/myresearch/menu.phtml +++ b/themes/bootstrap3/templates/myresearch/menu.phtml @@ -1,81 +1,81 @@ +<?php + $user = $this->auth()->isLoggedIn(); + $patron = $user ? $this->auth()->getILSPatron() : false; + $capabilityParams = $patron ? ['patron' => $patron] : []; +?> <h4><?=$this->transEsc('Your Account')?></h4> <div class="myresearch-menu"> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <a href="<?=$this->url('myresearch-favorites')?>"<?=$this->active == 'favorites' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Favorites')?> </a> - <? endif; ?> - <? if ('ils-none' !== $this->ils()->getOfflineMode()): ?> - <? if ($this->ils()->checkCapability('getMyTransactions')): ?> + <?php endif; ?> + <?php if ('ils-none' !== $this->ils()->getOfflineMode()): ?> + <?php if ($this->ils()->checkCapability('getMyTransactions', $capabilityParams)): ?> <a href="<?=$this->url('myresearch-checkedout')?>"<?=$this->active == 'checkedout' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-book" aria-hidden="true"></i> <?=$this->transEsc('Checked Out Items')?> </a> - <? endif; ?> - <? if ($this->ils()->checkCapability('getMyHolds')): ?> + <?php endif; ?> + <?php if ($this->ils()->checkFunction('getMyTransactionHistory', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-historicloans')?>"<?=$this->active == 'historicloans' ? ' class="active"' : ''?>"> + <i class="fa fa-fw fa-history" aria-hidden="true"></i> <?=$this->transEsc('Loan History')?> + </a> + <?php endif; ?> + <?php if ($this->ils()->checkCapability('getMyHolds', $capabilityParams)): ?> <a href="<?=$this->url('myresearch-holds')?>"<?=$this->active == 'holds' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-flag" aria-hidden="true"></i> <?=$this->transEsc('Holds and Recalls')?> </a> - <? endif; ?> - <? if ($this->ils()->checkFunction('StorageRetrievalRequests')): ?> + <?php endif; ?> + <?php if ($this->ils()->checkFunction('StorageRetrievalRequests', $capabilityParams)): ?> <a href="<?=$this->url('myresearch-storageretrievalrequests')?>"<?=$this->active == 'storageRetrievalRequests' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-archive" aria-hidden="true"></i> <?=$this->transEsc('Storage Retrieval Requests')?> </a> - <? endif; ?> - <? if ($this->ils()->checkFunction('ILLRequests')): ?> - <a href="<?=$this->url('myresearch-illrequests')?>"<?=$this->active == 'ILLRequests' ? ' class="active"' : ''?>> - <i class="fa fa-fw fa-exchange" aria-hidden="true"></i> <?=$this->transEsc('Interlibrary Loan Requests')?> - </a> - <? endif; ?> - <? if ($this->ils()->checkCapability('getMyFines')): ?> + <?php endif; ?> + <?php if ($this->ils()->checkFunction('ILLRequests', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-illrequests')?>"<?=$this->active == 'ILLRequests' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-exchange" aria-hidden="true"></i> <?=$this->transEsc('Interlibrary Loan Requests')?> + </a> + <?php endif; ?> + <?php if ($this->ils()->checkCapability('getMyFines', $capabilityParams)): ?> <a href="<?=$this->url('myresearch-fines')?>"<?=$this->active == 'fines' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-usd" aria-hidden="true"></i> <?=$this->transEsc('Fines')?> </a> - <? endif; ?> - <? if ($this->ils()->checkCapability('getMyProfile')): ?> - <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active"' : ''?>> - <i class="fa fa-fw fa-user" aria-hidden="true"></i> <?=$this->transEsc('Profile')?> - </a> - <? endif; ?> - <? $user = $this->auth()->isLoggedIn(); if ($user && $user->libraryCardsEnabled()): ?> + <?php endif; ?> + <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-user" aria-hidden="true"></i> <?=$this->transEsc('Profile')?> + </a> + <?php if ($user && $user->libraryCardsEnabled()): ?> <a href="<?=$this->url('librarycards-home')?>"<?=$this->active == 'librarycards' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-barcode" aria-hidden="true"></i> <?=$this->transEsc('Library Cards')?> </a> - <? endif; ?> - <? endif; ?> - <? if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> + <?php endif; ?> + <?php endif; ?> + <?php if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> <a href="<?=$this->url('search-history')?>?require_login"<?=$this->active == 'history' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-search" aria-hidden="true"></i> <?=$this->transEsc('history_saved_searches')?> </a> - <? endif; ?> - <? if ($user = $this->auth()->isLoggedIn()): ?> + <?php endif; ?> + <?php if ($user): ?> <a href="<?=$this->url('myresearch-logout')?>"> <i class="fa fa-fw fa-sign-out" aria-hidden="true"></i> <?=$this->transEsc("Log Out")?> </a> - <? endif; ?> + <?php endif; ?> </div> -<? if ($this->auth()->isLoggedIn() && $this->auth()->getManager()->supportsPasswordChange()): ?> - <h4><?=$this->transEsc('Preferences')?></h4> - <div class="myresearch-menu"> - <a href="<?=$this->url('myresearch-changepassword') ?>"<?=$this->active == 'newpassword' ? ' class="active"' : ''?>> - <i class="fa fa-fw fa-lock" aria-hidden="true"></i> <?=$this->transEsc('Change Password') ?> - </a> - </div> -<? endif; ?> -<? if ($this->userlist()->getMode() !== 'disabled' && $user = $this->auth()->isLoggedIn()): ?> +<?php if ($user && $this->userlist()->getMode() !== 'disabled'): ?> <h4><?=$this->transEsc('Your Lists')?></h4> <div class="myresearch-menu"> <a href="<?=$this->url('myresearch-favorites')?>"<?=$this->active == 'favorites' ? ' class="active"' : ''?>> <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Your Favorites')?> </a> - <? $lists = $user->getLists() ?> - <? foreach ($lists as $list): ?> + <?php $lists = $user->getLists() ?> + <?php foreach ($lists as $list): ?> <a href="<?=$this->url('userList', ['id' => $list['id']])?>"<?=$this->active == 'list' . $list['id'] ? ' class="active"' : ''?>> <?=$this->escapeHtml($list['title'])?> <span class="badge"><?=$list->cnt ?></span> </a> - <? endforeach; ?> - <a href="<?=$this->url('editList', ['id'=>'NEW'])?>"> + <?php endforeach; ?> + <a href="<?=$this->url('editList', ['id' => 'NEW'])?>"> <i class="fa fa-fw fa-plus" aria-hidden="true"></i> <?=$this->transEsc('Create a List') ?> </a> </div> -<? endif ?> +<?php endif ?> diff --git a/themes/bootstrap3/templates/myresearch/mylist.phtml b/themes/bootstrap3/templates/myresearch/mylist.phtml index 0c3ace56786c59de887c0aa615b78c8a4087030d..f0f6b56e28cb8ca6ae807884a21031d492c5b7ee 100644 --- a/themes/bootstrap3/templates/myresearch/mylist.phtml +++ b/themes/bootstrap3/templates/myresearch/mylist.phtml @@ -1,4 +1,4 @@ -<? +<?php // Grab list object from search results (if applicable): $list = $this->results->getListObject(); @@ -7,13 +7,13 @@ // Set up breadcrumbs: $currPage = isset($list) ? 'List' : 'Favorites'; - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc($currPage) . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc($currPage) . '</li>'; // Load Javascript dependencies into header: $this->headScript()->appendFile("check_item_statuses.js"); // Load Javascript only if list view parameter is NOT full: - if ($this->params->getOptions()->getListViewOption()!="full") { + if ($this->params->getOptions()->getListViewOption() != "full") { $this->headScript()->appendFile("record.js"); $this->headScript()->appendFile("embedded_record.js"); } @@ -31,20 +31,20 @@ <h2><?=$list ? $this->escapeHtml($list->title) : $this->transEsc("Your Favorites")?></h2> <nav class="search-header hidden-print"> <div class="search-stats"> - <? if ($recordTotal > 0): ?> - <? + <?php if ($recordTotal > 0): ?> + <?php $transParams = [ '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), - '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), + '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), '%%total%%' => $this->localizedNumber($recordTotal) ]; ?> <?=$this->translate('showing_items_of_html', $transParams); ?> - <? endif; ?> + <?php endif; ?> </div> <div class="search-controls"> - <? if (isset($list)): ?> - <? if ($list->editAllowed($account->isLoggedIn())): ?> + <?php if (isset($list)): ?> + <?php if ($list->editAllowed($account->isLoggedIn())): ?> <a href="<?=$this->url('editList', ['id' => $list->id]) ?>" class="btn btn-link"><i class="fa fa-edit" aria-hidden="true"></i> <?=$this->transEsc("edit_list")?></a> <div class="btn-group"> <a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="<?=$this->url('myresearch-deletelist') ?>?listID=<?=urlencode($list->id)?>"> @@ -55,33 +55,33 @@ <li><a href="#"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> - <? endif; ?> - <? endif; ?> - <? if ($recordTotal > 0): ?> + <?php endif; ?> + <?php endif; ?> + <?php if ($recordTotal > 0): ?> <?=$this->render('search/controls/limit.phtml')?> <?=$this->render('search/controls/sort.phtml')?> - <? endif; ?> + <?php endif; ?> </div> </nav> - <? if ($list && !empty($list->description)): ?> + <?php if ($list && !empty($list->description)): ?> <p><?=$this->escapeHtml($list->description)?></p> - <? endif; ?> - <? if ($recordTotal > 0): ?> + <?php endif; ?> + <?php if ($recordTotal > 0): ?> <form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-myresearchbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler"> - <?=$this->context($this)->renderInContext('myresearch/bulk-action-buttons.phtml', ['idPrefix' => '', 'list' => isset($list) ? $list : null, 'account' => $this->account])?> - <? foreach ($this->results->getResults() as $i=>$current): ?> + <?=$this->context($this)->renderInContext('myresearch/bulk-action-buttons.phtml', ['idPrefix' => '', 'list' => $list ?? null, 'account' => $this->account])?> + <?php foreach ($this->results->getResults() as $i => $current): ?> <?=$this->record($current)->getListEntry($list, $user)?> - <? endforeach; ?> + <?php endforeach; ?> </form> <?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results])?> - <? else: ?> + <?php else: ?> <p><?=$this->transEsc('You do not have any saved resources')?></p> - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => isset($list) ? 'list' . $list['id'] : 'favorites'])?> - <? foreach ($this->results->getRecommendations('side') as $current): ?> + <?php foreach ($this->results->getRecommendations('side') as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/myresearch/newpassword.phtml b/themes/bootstrap3/templates/myresearch/newpassword.phtml index efe13c906c5f563cdcc3969d416c868cf1483964..5ccd51db90ebc06aaa3019ff38a3a82ed36aa3a1 100644 --- a/themes/bootstrap3/templates/myresearch/newpassword.phtml +++ b/themes/bootstrap3/templates/myresearch/newpassword.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Create New Password')); @@ -6,20 +6,20 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>' . '<li class="active">' . $this->transEsc('Create New Password') . '</li>'; ?> -<? if ($this->auth()->isLoggedIn()): ?> +<?php if ($this->auth()->isLoggedIn()): ?> <div class="<?=$this->layoutClass('mainbody')?>"> -<? endif; ?> +<?php endif; ?> <h2><?=$this->transEsc('Create New Password') ?></h2> <?=$this->flashmessages() ?> -<? if (!$this->auth()->getManager()->supportsPasswordChange($this->auth_method)): ?> +<?php if (!$this->auth()->getManager()->supportsPasswordChange($this->auth_method)): ?> <div class="error"><?=$this->transEsc('recovery_new_disabled') ?></div> -<? elseif (!isset($this->hash)): ?> +<?php elseif (!isset($this->hash)): ?> <div class="error"><?=$this->transEsc('recovery_user_not_found') ?></div> -<? else: ?> +<?php else: ?> <form id="newpassword" class="form-new-password" action="<?=$this->url('myresearch-newpassword') ?>" method="post" data-toggle="validator" role="form"> - <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash(true))?>" name="csrf"/> + <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash())?>" name="csrf"/> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->hash) ?>" name="hash"/> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->username) ?>" name="username"/> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth_method) ?>" name="auth_method"/> @@ -29,11 +29,11 @@ <input class="btn btn-primary" name="submit" type="submit" value="<?=$this->transEsc('Submit')?>" /> </div> </form> -<? endif; ?> +<?php endif; ?> -<? if ($this->auth()->isLoggedIn()): ?> +<?php if ($this->auth()->isLoggedIn()): ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'newpassword'])?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/profile.phtml b/themes/bootstrap3/templates/myresearch/profile.phtml index ca1d2ef556f74bd7bf1717c4671956d0bd8d2db3..f5010aff87bdc948b4bb8b2aa33d06e5f6812f33 100644 --- a/themes/bootstrap3/templates/myresearch/profile.phtml +++ b/themes/bootstrap3/templates/myresearch/profile.phtml @@ -1,13 +1,10 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('My Profile')); // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Profile') . '</li>'; - // Only display home library form if we have multiple pickup locations: - $showHomeLibForm = (isset($this->pickup) && count($this->pickup) > 1); - // Template for use by the renderArray helper: $arrTemplate = '<tr><th>%%LABEL%%:</th><td> %%VALUE%%</td></tr>'; ?> @@ -15,51 +12,74 @@ <h2><?=$this->transEsc('Your Profile')?></h2> <?=$this->flashmessages();?> - <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> - <table class="table table-striped"> - <? - echo $this->renderArray( - $arrTemplate, $this->profile, - [ - $this->transEsc('First Name') => 'firstname', - $this->transEsc('Last Name') => 'lastname' - ] - ); - ?> - <? if ($showHomeLibForm): ?> + <?=$this->renderArray( + $arrTemplate, $this->user, + [ + $this->transEsc('First Name') => 'firstname', + $this->transEsc('Last Name') => 'lastname', + $this->transEsc('Email') => 'email', + ] + )?> + <?php if (count($this->pickup ?? []) > 1): // Skip form if only one location: ?> <tr><th><?=$this->transEsc('Preferred Library')?>:</th> - <? - $selected = (isset($this->profile['home_library']) && $this->profile['home_library'] != "") + <?php + $selected = (strlen($this->profile['home_library'] ?? '') > 0) ? $this->profile['home_library'] : $this->defaultPickupLocation ?> <td> <form id="profile_form" class="form-inline" method="post"> <select id="home_library" name="home_library" class="form-control"> - <? foreach ($this->pickup as $lib): ?> + <?php foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID'])?' selected="selected"':''?>><?=$this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay'])?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Save')?>" /> </form> </td> - <? endif; ?> - <? - echo $this->renderArray( + <?php endif; ?> + </table> + + <div id="account-actions"> + <?php if ($this->auth()->getManager()->supportsPasswordChange()): ?> + <a class="btn btn-default" href="<?=$this->url('myresearch-changepassword') ?>"> + <i class="fa fa-fw fa-lock" aria-hidden="true"></i> <?=$this->transEsc('Change Password') ?> + </a> + <?php endif; ?> + + <?php if ($this->accountDeletion): ?> + <a class="btn btn-default" href="<?=$this->url('myresearch-deleteaccount') ?>" data-lightbox> + <i class="fa fa-times"></i> <?=$this->transEsc('delete_account_title') ?> + </a> + <?php endif; ?> + </div> + + <?php if (is_array($this->profile)): ?> + <h3><?=$this->transEsc('Library Catalog Profile')?></h3> + <p> + <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->user]); ?> + </p> + <table class="table table-striped"> + <?=$this->renderArray( $arrTemplate, $this->profile, [ + $this->transEsc('First Name') => 'firstname', + $this->transEsc('Last Name') => 'lastname', $this->transEsc('Address') . ' 1' => 'address1', $this->transEsc('Address') . ' 2' => 'address2', $this->transEsc('Zip') => 'zip', $this->transEsc('City') => 'city', $this->transEsc('Country') => 'country', $this->transEsc('Phone Number') => 'phone', + $this->transEsc('Mobile Number') => 'mobile_phone', $this->transEsc('Group') => 'group', $this->transEsc('Expires') => 'expiration_date' ] - ); - ?> - </table> + )?> + </table> + <?php elseif ('ils-none' !== $this->ils()->getOfflineMode() && !empty($this->patronLoginView->getTemplate())): ?> + <?=$this->partial($this->patronLoginView);?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/myresearch/recover.phtml b/themes/bootstrap3/templates/myresearch/recover.phtml index 4d89d789a48ce129650dbd3288adccb4a66d99ac..704b0929cef996a82011b56d0d5a9c5c57555c30 100644 --- a/themes/bootstrap3/templates/myresearch/recover.phtml +++ b/themes/bootstrap3/templates/myresearch/recover.phtml @@ -1,9 +1,9 @@ <h2><?=$this->transEsc('recovery_title') ?></h2> <?=$this->flashmessages()?> -<? if (!$this->auth()->getManager()->supportsRecovery()): ?> +<?php if (!$this->auth()->getManager()->supportsRecovery()): ?> <div class="error"><?=$this->transEsc('recovery_disabled') ?></div> -<? else: ?> +<?php else: ?> <form class="form-password-recovery" method="post"> <?=$this->auth()->getPasswordRecoveryForm() ?> </form> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml b/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml index e4bea0ca6750bfa6061b2e5ead96c9db4d98868b..412579a4f5b5d50f6c4535d958a61f6d45bd2846 100644 --- a/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml +++ b/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Storage Retrieval Requests')); @@ -13,8 +13,8 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> - <? if (!empty($this->recordList)): ?> - <? if ($this->cancelForm): ?> + <?php if (!empty($this->recordList)): ?> + <?php if ($this->cancelForm): ?> <form name="cancelForm" class="inline" method="post" id="cancelStorageRetrievalRequest"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> @@ -34,24 +34,24 @@ <li><a href="#" onClick="return false;"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> - <? endif; ?> + <?php endif; ?> - <? $iteration = 0; ?> - <? foreach ($this->recordList as $resource): ?> - <? $iteration++; ?> - <? $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php $iteration = 0; ?> + <?php foreach ($this->recordList as $resource): ?> + <?php $iteration++; ?> + <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> <div id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result"> - <? if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> - <? $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?> + <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $resource->getUniqueId()); ?> <div class="checkbox"> <input type="hidden" name="cancelAllIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" /> <label class="pull-left flip"> <input type="checkbox" name="cancelSelectedIDS[]" value="<?=$this->escapeHtmlAttr($ilsDetails['cancel_details']) ?>" id="checkbox_<?=$safeId?>" /> </label> </div> - <? endif; ?> + <?php endif; ?> - <? + <?php $coverDetails = $this->record($resource)->getCoverDetails('storageretrievalrequests', 'small', $this->recordLink()->getUrl($resource)); $cover = $coverDetails['html']; $thumbnail = false; @@ -61,22 +61,22 @@ <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?=$cover ?> </div> - <? $thumbnail = ob_get_contents(); ?> - <? ob_end_clean(); ?> - <? endif; ?> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> + <?php endif; ?> <div class="media"> - <? if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> <div class="media-body"> - <? + <?php // If this is a non-missing Solr record, we should display a link: if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); echo '<a href="' . $this->recordLink()->getUrl($resource) . '" class="title">' . $title . '</a>'; - } else if (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... echo $this->escapeHtml($ilsDetails['title']); } else { @@ -84,89 +84,89 @@ echo $this->transEsc('Title not available'); } ?><br/> - <? $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> + <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> <?=$this->transEsc('by')?>: - <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><? if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><? endif; ?><br/> - <? endif; ?> + <a href="<?=$this->record($resource)->getLink('author', $listAuthors[0])?>"><?=$this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?><br/> + <?php endif; ?> - <? if (count($resource->getFormats()) > 0): ?> + <?php if (count($resource->getFormats()) > 0): ?> <?=$this->record($resource)->getFormatList() ?> <br/> - <? endif; ?> - <? if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['volume']) && !empty($ilsDetails['volume'])): ?> <strong><?=$this->transEsc('Volume')?>:</strong> <?=$this->escapeHtml($ilsDetails['volume'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?> + <?php if (isset($ilsDetails['publication_year']) && !empty($ilsDetails['publication_year'])): ?> <strong><?=$this->transEsc('Year of Publication')?>:</strong> <?=$this->escapeHtml($ilsDetails['publication_year'])?> <br /> - <? endif; ?> + <?php endif; ?> - <? /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best + <?php /* Depending on the ILS driver, the "location" value may be a string or an ID; figure out the best value to display... */ ?> - <? $pickupDisplay = ''; ?> - <? $pickupTranslate = false; ?> - <? if (isset($ilsDetails['location'])): ?> - <? if ($this->pickup): ?> - <? foreach ($this->pickup as $library): ?> - <? if ($library['locationID'] == $ilsDetails['location']): ?> - <? $pickupDisplay = $library['locationDisplay']; ?> - <? $pickupTranslate = true; ?> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> - <? if (empty($pickupDisplay)): ?> - <? $pickupDisplay = $ilsDetails['location']; ?> - <? endif; ?> - <? endif; ?> - <? if (!empty($pickupDisplay)): ?> + <?php $pickupDisplay = ''; ?> + <?php $pickupTranslate = false; ?> + <?php if (isset($ilsDetails['location'])): ?> + <?php if ($this->pickup): ?> + <?php foreach ($this->pickup as $library): ?> + <?php if ($library['locationID'] == $ilsDetails['location']): ?> + <?php $pickupDisplay = $library['locationDisplay']; ?> + <?php $pickupTranslate = true; ?> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> + <?php if (empty($pickupDisplay)): ?> + <?php $pickupDisplay = $ilsDetails['location']; ?> + <?php endif; ?> + <?php endif; ?> + <?php if (!empty($pickupDisplay)): ?> <strong><?=$this->transEsc('pick_up_location') ?>:</strong> <?=$pickupTranslate ? $this->transEsc($pickupDisplay) : $this->escapeHtml($pickupDisplay)?> <br /> - <? endif; ?> + <?php endif; ?> - <? if (!empty($ilsDetails['create'])): ?> + <?php if (!empty($ilsDetails['create'])): ?> <strong><?=$this->transEsc('Created') ?>:</strong> <?=$this->escapeHtml($ilsDetails['create']) ?> - <? if (!empty($ilsDetails['expire'])): ?>|<? endif; ?> - <? endif; ?> - <? if (!empty($ilsDetails['expire'])): ?> + <?php if (!empty($ilsDetails['expire'])): ?>|<?php endif; ?> + <?php endif; ?> + <?php if (!empty($ilsDetails['expire'])): ?> <strong><?=$this->transEsc('Expires') ?>:</strong> <?=$this->escapeHtml($ilsDetails['expire']) ?> - <? endif; ?> + <?php endif; ?> <br /> - <? if (isset($this->cancelResults['items'])): ?> - <? foreach ($this->cancelResults['items'] as $itemId=>$cancelResult): ?> - <? if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> - <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><? if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> + <?php if (isset($this->cancelResults['items'])): ?> + <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?> + <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> + <div class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></div> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? if (isset($ilsDetails['processed']) && $ilsDetails['processed']): ?> + <?php if (isset($ilsDetails['processed']) && $ilsDetails['processed']): ?> <div class="text-success"><?=$this->transEsc("storage_retrieval_request_processed") . (is_string($ilsDetails['processed']) ? ': ' . $ilsDetails['processed'] : '') ?></div> - <? endif; ?> - <? if (isset($ilsDetails['available']) && $ilsDetails['available']): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['available']) && $ilsDetails['available']): ?> <div class="text-success"><?=$this->transEsc("storage_retrieval_request_available") ?></div> - <? endif; ?> - <? if (isset($ilsDetails['canceled']) && $ilsDetails['canceled']): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['canceled']) && $ilsDetails['canceled']): ?> <div class="text-success"><?=$this->transEsc("storage_retrieval_request_canceled") . (is_string($ilsDetails['canceled']) ? ': ' . $ilsDetails['canceled'] : '') ?></div> - <? endif; ?> - <? if (isset($ilsDetails['cancel_link'])): ?> + <?php endif; ?> + <?php if (isset($ilsDetails['cancel_link'])): ?> <p><a href="<?=$this->escapeHtmlAttr($ilsDetails['cancel_link']) ?>"><?=$this->transEsc("storage_retrieval_request_cancel") ?></a></p> - <? endif; ?> + <?php endif; ?> </div> - <? if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> <?=$thumbnail ?> - <? endif ?> + <?php endif ?> </div> - <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="'.$this->escapeHtmlAttr($resource->getCoinsOpenUrl()).'"></span>':''?> + <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> </div> - <? endforeach; ?> - <? if ($this->cancelForm): ?></form><? endif; ?> - <? else: ?> + <?php endforeach; ?> + <?php if ($this->cancelForm): ?></form><?php endif; ?> + <?php else: ?> <?=$this->transEsc('You do not have any storage retrieval requests placed') ?>. - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> diff --git a/themes/bootstrap3/templates/oai/home.phtml b/themes/bootstrap3/templates/oai/home.phtml index c9f5c55ba29e15bef60ecfb62c2b42c128c13ce1..5fbe86d46c8a37ea991c416e101b983f47e78c6f 100644 --- a/themes/bootstrap3/templates/oai/home.phtml +++ b/themes/bootstrap3/templates/oai/home.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->headTitle($this->translate('OAI Server')); $this->layout()->breadcrumbs = $this->transEsc('OAI Server'); $baseUrl = $this->url('oai-server'); diff --git a/themes/bootstrap3/templates/pazpar2/search.phtml b/themes/bootstrap3/templates/pazpar2/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/pazpar2/search.phtml +++ b/themes/bootstrap3/templates/pazpar2/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/primo/advanced.phtml b/themes/bootstrap3/templates/primo/advanced.phtml index 586975e45f3f90fb9932542771a5df20d295275a..4aa47b36d197d314190bb3d6665cc3b613b3fcbf 100644 --- a/themes/bootstrap3/templates/primo/advanced.phtml +++ b/themes/bootstrap3/templates/primo/advanced.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Advanced Search')); @@ -27,20 +27,20 @@ <input type="hidden" name="join" value="AND" /> <div class="<?=$this->layoutClass('mainbody')?>"> <h3><?=$this->transEsc('Advanced Search')?></h3> - <? /* fallback to a fixed set of search groups/fields if JavaScript is turned off */ ?> - <? if ($groups !== false) { + <?php /* fallback to a fixed set of search groups/fields if JavaScript is turned off */ ?> + <?php if ($groups !== false) { $numGroups = count($groups); } if (!isset($numGroups) || $numGroups < 3) { $numGroups = 1; } ?> - <? for ($i = 0; $i < $numGroups; $i++): ?> + <?php for ($i = 0; $i < $numGroups; $i++): ?> <input type="hidden" name="bool<?=$i?>[]" value="AND" /> <div class="primo adv-group" id="group<?=$i?>"> <label class="primo-adv-label" id="group<?=$i?>SearchHolder"><?=$this->transEsc("adv_search_label")?>:</label> <div class="search-container"> - <? + <?php if (isset($groups[$i])) { $currentGroup = $groups[$i]->getQueries(); $numRows = count($currentGroup); @@ -51,37 +51,37 @@ $numRows = 3; } ?> - <? for ($j = 0; $j < $numRows; $j++): ?> - <? $currRow = isset($currentGroup[$j]) ? $currentGroup[$j] : false; ?> + <?php for ($j = 0; $j < $numRows; $j++): ?> + <?php $currRow = $currentGroup[$j] ?? false; ?> <div class="search"> <select id="search_type<?=$i?>_<?=$j?>" name="type<?=$i?>[]" class="form-control"> - <? foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> + <?php foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> <option value="<?=$this->escapeHtmlAttr($searchVal)?>"<?=($currRow && $currRow->getHandler() == $searchVal)?' selected="selected"':''?>><?=$this->transEsc($searchDesc)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <select name="op<?=$i?>[]" id="searchForm_op<?=$i?>_<?=$j?>" class="form-control"> - <? foreach ($this->options->getAdvancedOperators() as $searchVal => $searchDesc): ?> + <?php foreach ($this->options->getAdvancedOperators() as $searchVal => $searchDesc): ?> <option value="<?=$this->escapeHtmlAttr($searchVal)?>"<?=($currRow && $currRow->getOperator() == $searchVal)?' selected="selected"':''?>><?=$this->transEsc($searchDesc)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <input id="search_lookfor<?=$i?>_<?=$j?>" type="text" value="<?=$currRow?$this->escapeHtmlAttr($currRow->getString()):''?>" size="30" name="lookfor<?=$i?>[]" class="form-control primo-adv-input"/> </div> - <? endfor; ?> + <?php endfor; ?> </div> </div> - <? endfor; ?> - <? $lastSort = $this->searchMemory()->getLastSort($this->options->getSearchClassId()); ?> - <? if (!empty($lastSort)): ?> + <?php endfor; ?> + <?php $lastSort = $this->searchMemory()->getLastSort($this->options->getSearchClassId()); ?> + <?php if (!empty($lastSort)): ?> <input type="hidden" name="sort" value="<?=$this->escapeHtmlAttr($lastSort)?>" /> - <? endif; ?> + <?php endif; ?> <input type="submit" class="btn btn-primary" name="submit" value="<?=$this->transEsc("Find")?>"/> </div> <div class="<?=$this->layoutClass('sidebar')?>"> - <? if ($hasDefaultsApplied): ?> + <?php if ($hasDefaultsApplied): ?> <input type="hidden" name="dfApplied" value="1" /> - <? endif ?> - <? if (!empty($searchFilters)): ?> + <?php endif ?> + <?php if (!empty($searchFilters)): ?> <h4><?=$this->transEsc("adv_search_filters")?></h4> <ul class="list-group"> <li class="list-group-item"> @@ -93,11 +93,11 @@ </div> </li> </ul> - <? foreach ($searchFilters as $field => $data): ?> + <?php foreach ($searchFilters as $field => $data): ?> <div> <ul class="list-group"> <li class="list-group-item title"><?=$this->transEsc($field)?></li> - <? foreach ($data as $value): ?> + <?php foreach ($data as $value): ?> <li class="list-group-item"> <div class="checkbox"> <label> @@ -105,10 +105,10 @@ </label> </div> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> </div> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> </form> diff --git a/themes/bootstrap3/templates/primo/search.phtml b/themes/bootstrap3/templates/primo/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/primo/search.phtml +++ b/themes/bootstrap3/templates/primo/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/record/addtag.phtml b/themes/bootstrap3/templates/record/addtag.phtml index 8708ac32b11d0ef0682057558fc0a71b9179ea51..10b036b398e54a9a89cb41e375203131ed3052f5 100644 --- a/themes/bootstrap3/templates/record/addtag.phtml +++ b/themes/bootstrap3/templates/record/addtag.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Add Tag')); diff --git a/themes/bootstrap3/templates/record/ajaxtab.phtml b/themes/bootstrap3/templates/record/ajaxtab.phtml index 5ee8df818ac6aee54b28d1ab7f4cb03e1c8d4f0e..52d56d17613ec585cb1b34dd0ea5ddcb2dd6a946 100644 --- a/themes/bootstrap3/templates/record/ajaxtab.phtml +++ b/themes/bootstrap3/templates/record/ajaxtab.phtml @@ -1,4 +1,4 @@ -<? +<?php foreach ($this->tabs as $tab => $obj) { if (strtolower($this->activeTab) == strtolower($tab)) { echo $this->record($this->driver)->getTab($obj); diff --git a/themes/bootstrap3/templates/record/ajaxview-accordion.phtml b/themes/bootstrap3/templates/record/ajaxview-accordion.phtml index 7576877969517c33a2ab494c010b28dd06703ff4..eaa8bb3519a4a99078413151d3106f4cb41df156 100644 --- a/themes/bootstrap3/templates/record/ajaxview-accordion.phtml +++ b/themes/bootstrap3/templates/record/ajaxview-accordion.phtml @@ -1,11 +1,13 @@ -<? +<?=$this->piwik()?> +<?=$this->googleanalytics()?> +<?php $this->defaultTab = strtolower($this->defaultTab); $idSuffix = $this->escapeHtmlAttr(md5($this->driver->getUniqueId() . '|' . $this->driver->getSourceIdentifier())); ?> <div class="list-tabs panel-group" id="accordion_<?=$idSuffix?>"> - <? $coreMetadata = $this->record($this->driver)->getCoreMetadata(); ?> - <? if (!empty($coreMetadata)): ?> + <?php $coreMetadata = $this->record($this->driver)->getCoreMetadata(); ?> + <?php if (!empty($coreMetadata)): ?> <div class="panel panel-default"> <div id="information_<?=$idSuffix?>" class="list-tab-toggle panel-heading loaded" data-toggle="collapse" data-parent="#accordion_<?=$idSuffix?>" data-target="#information_<?=$idSuffix?>-content"> <h4 class="panel-title"> @@ -14,7 +16,7 @@ </a> </h4> </div> - <div id="information_<?=$idSuffix?>-content" class="panel-collapse collapse<? if($this->defaultTab === 'information'): ?> in<? endif; ?>"> + <div id="information_<?=$idSuffix?>-content" class="panel-collapse collapse<?php if($this->defaultTab === 'information'): ?> in<?php endif; ?>"> <div class="list-tab-content record panel-body"> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" class="hiddenId" id="record_id_<?=$idSuffix?>" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>" class="hiddenSource" /> @@ -22,10 +24,10 @@ </div> </div> </div> - <? endif; ?> + <?php endif; ?> - <? $toolbar = $this->record($this->driver)->getToolbar(); ?> - <? if (!empty($toolbar)): ?> + <?php $toolbar = $this->record($this->driver)->getToolbar(); ?> + <?php if (!empty($toolbar)): ?> <div class="panel panel-default"> <div id="tools_<?=$idSuffix?>" class="list-tab-toggle panel-heading loaded" data-toggle="collapse" data-parent="#accordion_<?=$idSuffix?>" data-target="#tools_<?=$idSuffix?>-content"> <h4 class="panel-title"> @@ -34,16 +36,16 @@ </a> </h4> </div> - <div id="tools_<?=$idSuffix?>-content" class="panel-collapse collapse<? if($this->defaultTab === 'tools'): ?> in<? endif; ?>"> + <div id="tools_<?=$idSuffix?>-content" class="panel-collapse collapse<?php if($this->defaultTab === 'tools'): ?> in<?php endif; ?>"> <div class="list-tab-content panel-body"> <?=$toolbar ?> </div> </div> </div> - <? endif; ?> + <?php endif; ?> - <? $relatedList = $this->related()->getList($this->driver); ?> - <? if ($relatedList != null): ?> + <?php $relatedList = $this->related()->getList($this->driver); ?> + <?php if ($relatedList != null): ?> <div class="panel panel-default"> <div id="related_<?=$idSuffix?>" class="list-tab-toggle panel-heading loaded" data-toggle="collapse" data-parent="#accordion_<?=$idSuffix?>" data-target="#related_<?=$idSuffix?>-content"> <h4 class="panel-title"> @@ -52,18 +54,18 @@ </a> </h4> </div> - <div id="related_<?=$idSuffix?>-content" class="panel-collapse collapse<? if($this->defaultTab === 'related'): ?> in<? endif; ?>"> + <div id="related_<?=$idSuffix?>-content" class="panel-collapse collapse<?php if($this->defaultTab === 'related'): ?> in<?php endif; ?>"> <div class="list-tab-content panel-body"> - <? foreach ($relatedList as $current): ?> + <?php foreach ($relatedList as $current): ?> <?=$this->related()->render($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> </div> </div> - <? endif; ?> - <? if (count($this->tabs) > 0): ?> - <? foreach ($this->tabs as $tab => $obj): ?> - <? // add current tab to breadcrumbs if applicable: + <?php endif; ?> + <?php if (count($this->tabs) > 0): ?> + <?php foreach ($this->tabs as $tab => $obj): ?> + <?php // add current tab to breadcrumbs if applicable: $desc = $obj->getDescription(); $tab_classes = []; if (!$obj->isVisible()) { $tab_classes[] = 'hidden'; } @@ -71,13 +73,13 @@ if ($this->defaultTab === strtolower($tab)) { $tab_classes[] = 'default'; } ?> <div class="panel panel-default <?=implode(' ', $tab_classes) ?>"> - <div id="<?=strtolower($tab)?>_<?=$idSuffix?>" class="list-tab-toggle panel-heading" data-toggle="collapse" data-parent="#accordion_<?=$idSuffix?>" data-target="#<?=strtolower($tab)?>_<?=$idSuffix?>-content"<? if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<? endif ?>> + <div id="<?=strtolower($tab)?>_<?=$idSuffix?>" class="list-tab-toggle panel-heading" data-toggle="collapse" data-parent="#accordion_<?=$idSuffix?>" data-target="#<?=strtolower($tab)?>_<?=$idSuffix?>-content"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>> <h4 class="panel-title"> <a class="accordion-toggle" data-href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"><?=$this->transEsc($desc) ?></a> </h4> </div> - <div id="<?=strtolower($tab)?>_<?=$idSuffix?>-content" class="list-tab-content panel-collapse collapse<? if($this->defaultTab === strtolower($tab)): ?> in<? endif; ?>"></div> + <div id="<?=strtolower($tab)?>_<?=$idSuffix?>-content" class="list-tab-content panel-collapse collapse<?php if($this->defaultTab === strtolower($tab)): ?> in<?php endif; ?>"></div> </div> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/record/ajaxview-tabs.phtml b/themes/bootstrap3/templates/record/ajaxview-tabs.phtml index 3f4431c76c16f7df1ac003651939bf65a23c7464..ca4c04626ae45f91a6b193b3296967572603dfb1 100644 --- a/themes/bootstrap3/templates/record/ajaxview-tabs.phtml +++ b/themes/bootstrap3/templates/record/ajaxview-tabs.phtml @@ -1,37 +1,39 @@ -<? +<?=$this->piwik()?> +<?=$this->googleanalytics()?> +<?php $this->defaultTab = strtolower($this->defaultTab); $idSuffix = $this->escapeHtmlAttr(md5($this->driver->getUniqueId() . '|' . $this->driver->getSourceIdentifier())); ?> <ul class="list-tabs nav nav-tabs"> - <? $coreMetadata = trim($this->record($this->driver)->getCoreMetadata()); ?> - <? if (!empty($coreMetadata)): ?> - <li<? if($this->defaultTab === 'information'): ?> class="active"<? endif; ?>> + <?php $coreMetadata = trim($this->record($this->driver)->getCoreMetadata()); ?> + <?php if (!empty($coreMetadata)): ?> + <li<?php if($this->defaultTab === 'information'): ?> class="active"<?php endif; ?>> <a id="information_<?=$idSuffix?>" class="list-tab-toggle loaded" data-toggle="tab" data-target="#information_<?=$idSuffix?>-content" class="noajax"> <?=$this->translate('ajaxview_label_information') ?> </a> </li> - <? endif; ?> + <?php endif; ?> - <? $toolbar = trim($this->record($this->driver)->getToolbar()); ?> - <? if (!empty($toolbar)): ?> - <li<? if($this->defaultTab === 'tools'): ?> class="active"<? endif; ?>> + <?php $toolbar = trim($this->record($this->driver)->getToolbar()); ?> + <?php if (!empty($toolbar)): ?> + <li<?php if($this->defaultTab === 'tools'): ?> class="active"<?php endif; ?>> <a id="tools_<?=$idSuffix?>" class="list-tab-toggle loaded" data-toggle="tab" data-target="#tools_<?=$idSuffix?>-content" class="noajax"> <?=$this->translate('ajaxview_label_tools') ?> </a> </li> - <? endif; ?> + <?php endif; ?> - <? $list = $this->related()->getList($this->driver); ?> - <? if (!empty($list)): ?> - <li<? if($this->defaultTab === 'related'): ?> class="active"<? endif; ?>> + <?php $list = $this->related()->getList($this->driver); ?> + <?php if (!empty($list)): ?> + <li<?php if($this->defaultTab === 'related'): ?> class="active"<?php endif; ?>> <a id="related_<?=$idSuffix?>" class="list-tab-toggle loaded" data-toggle="tab" data-target="#related_<?=$idSuffix?>-content" class="noajax"> <?=$this->transEsc("Related Items")?> </a> </li> - <? endif; ?> + <?php endif; ?> - <? foreach ($this->tabs as $tab => $obj): ?> - <? // add current tab to breadcrumbs if applicable: + <?php foreach ($this->tabs as $tab => $obj): ?> + <?php // add current tab to breadcrumbs if applicable: $desc = $obj->getDescription(); $tab_classes = []; if ($this->defaultTab === strtolower($tab)) { @@ -43,31 +45,31 @@ if (!$obj->supportsAjax()) { $tab_classes[] = 'noajax'; } ?> <li<?=count($tab_classes) > 0 ? ' class="' . implode(' ', $tab_classes) . '"' : ''?>> - <a id="<?=strtolower($tab)?>_<?=$idSuffix?>" class="list-tab-toggle" href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav" data-toggle="tab" data-target="#<?=strtolower($tab)?>_<?=$idSuffix?>-content"<? if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<? endif ?>><?=$this->transEsc($desc)?></a> + <a id="<?=strtolower($tab)?>_<?=$idSuffix?>" class="list-tab-toggle" href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav" data-toggle="tab" data-target="#<?=strtolower($tab)?>_<?=$idSuffix?>-content"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>><?=$this->transEsc($desc)?></a> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> <div class="tab-content"> - <? if (!empty($coreMetadata)): ?> - <div class="list-tab-content record tab-pane<? if($this->defaultTab === 'information'): ?> active<? endif; ?>" id="information_<?=$idSuffix?>-content"> + <?php if (!empty($coreMetadata)): ?> + <div class="list-tab-content record tab-pane<?php if($this->defaultTab === 'information'): ?> active<?php endif; ?>" id="information_<?=$idSuffix?>-content"> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" class="hiddenId" id="record_id_<?=$idSuffix?>" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>" class="hiddenSource" /> <?=$coreMetadata ?> </div> - <? endif; ?> - <? if (!empty($toolbar)): ?> - <div class="list-tab-content tab-pane<? if($this->defaultTab === 'tools'): ?> active<? endif; ?>" id="tools_<?=$idSuffix?>-content"> + <?php endif; ?> + <?php if (!empty($toolbar)): ?> + <div class="list-tab-content tab-pane<?php if($this->defaultTab === 'tools'): ?> active<?php endif; ?>" id="tools_<?=$idSuffix?>-content"> <?=$toolbar ?> </div> - <? endif; ?> - <? if (!empty($list)): ?> - <div class="list-tab-content tab-pane<? if($this->defaultTab === 'related'): ?> active<? endif; ?>" id="related_<?=$idSuffix?>-content"> - <? foreach ($list as $current): ?> + <?php endif; ?> + <?php if (!empty($list)): ?> + <div class="list-tab-content tab-pane<?php if($this->defaultTab === 'related'): ?> active<?php endif; ?>" id="related_<?=$idSuffix?>-content"> + <?php foreach ($list as $current): ?> <?=$this->related()->render($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endif; ?> - <? foreach ($this->tabs as $tab => $obj): ?> - <div class="list-tab-content tab-pane<? if($this->defaultTab === strtolower($tab)): ?> active<? endif; ?>" id="<?=strtolower($tab)?>_<?=$idSuffix?>-content"></div> - <? endforeach; ?> + <?php endif; ?> + <?php foreach ($this->tabs as $tab => $obj): ?> + <div class="list-tab-content tab-pane<?php if($this->defaultTab === strtolower($tab)): ?> active<?php endif; ?>" id="<?=strtolower($tab)?>_<?=$idSuffix?>-content"></div> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/record/cart-buttons.phtml b/themes/bootstrap3/templates/record/cart-buttons.phtml index c90799a3c45535f97de0ad9e7ed4cd0bb05b0547..b153271bcefdb872b8edf304059444315f71c44c 100644 --- a/themes/bootstrap3/templates/record/cart-buttons.phtml +++ b/themes/bootstrap3/templates/record/cart-buttons.phtml @@ -1,24 +1,24 @@ -<? $cart = $this->cart(); ?> -<? if ($cart->isActive()): ?> - <? $cartId = $this->source . '|' . $this->id; ?> +<?php $cart = $this->cart(); ?> +<?php if ($cart->isActive()): ?> + <?php $cartId = $this->source . '|' . $this->id; ?> <span class="btn-bookbag-toggle" data-cart-id="<?=$this->escapeHtmlAttr($this->id)?>" data-cart-source="<?=$this->escapeHtmlAttr($this->source)?>"> - <a class="cart-add hidden<? if(!$cart->contains($cartId)): ?> correct<? endif ?>"> + <a href="#" class="cart-add hidden<?php if(!$cart->contains($cartId)): ?> correct<?php endif ?>"> <i class="cart-link-icon fa fa-plus" title="<?=$this->transEsc('Add to Book Bag') ?>"></i><!-- --><span class="cart-link-label"><?=$this->transEsc('Add to Book Bag') ?></span><!-- --></a> - <a class="cart-remove hidden<? if($cart->contains($cartId)): ?> correct<? endif ?>"> + <a href="#" class="cart-remove hidden<?php if($cart->contains($cartId)): ?> correct<?php endif ?>"> <i class="cart-link-icon fa fa-minus-circle" title="<?=$this->transEsc('Remove from Book Bag') ?>"></i><!-- --><span class="cart-link-label"><?=$this->transEsc('Remove from Book Bag') ?></span> </a> <noscript> <form method="post" name="addForm" action="<?=$this->url('cart-processor')?>"> <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($cartId)?>" /> - <? if ($cart->contains($cartId)): ?> + <?php if ($cart->contains($cartId)): ?> <input class="btn btn-default" type="submit" name="delete" value="<?=$this->transEsc('Remove from Book Bag')?>"/> - <? else: ?> + <?php else: ?> <input class="btn btn-default" type="submit" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/> - <? endif; ?> + <?php endif; ?> </form> </noscript> </span> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/record/checkbox.phtml b/themes/bootstrap3/templates/record/checkbox.phtml index 84663f8ab13dba75c2975903b1e051446641b31c..7c37bb0ab2482b8e6e321128dd30ed6fc4f77ac6 100644 --- a/themes/bootstrap3/templates/record/checkbox.phtml +++ b/themes/bootstrap3/templates/record/checkbox.phtml @@ -1,5 +1,6 @@ <label class="record-checkbox hidden-print"> - <input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<? if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<? endif; ?>/> + <input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<?php if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<?php endif; ?>/> <span class="checkbox-icon"></span> + <?php if (strlen($this->number ?? '') > 0): ?><span class="sr-only"><?=$this->transEsc('result_checkbox_label', ['%%number%%' => $this->number]) ?></span><?php endif; ?> </label> -<input type="hidden" name="idsAll[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<? if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<? endif; ?>/> +<input type="hidden" name="idsAll[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<?php if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<?php endif; ?>/> diff --git a/themes/bootstrap3/templates/record/cite.phtml b/themes/bootstrap3/templates/record/cite.phtml index 09798ecd6701748217dc7590abc1a83982895983..5ba7d65fb008eb0f7495f7230872ed8a9a707e12 100644 --- a/themes/bootstrap3/templates/record/cite.phtml +++ b/themes/bootstrap3/templates/record/cite.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Record Citations')); @@ -14,14 +14,14 @@ $citations[$format . ' Citation'] = $helper->getCitation($format); } ?> -<? if (count($citations) == 0): ?> +<?php if (count($citations) == 0): ?> <?=$this->transEsc('No citations are available for this record')?> -<? else: ?> - <? foreach ($citations as $caption => $citation): ?> +<?php else: ?> + <?php foreach ($citations as $caption => $citation): ?> <strong><?=$this->transEsc($caption)?></strong> <p class="text-left"> <?=$citation?> </p> - <? endforeach; ?> + <?php endforeach; ?> <div class="text-muted text-center"><?=$this->transEsc('Warning: These citations may not always be 100% accurate')?>.</div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/record/comments-list.phtml b/themes/bootstrap3/templates/record/comments-list.phtml index ec9faf569a1abf16528ec96140547b5e9837cdf9..2da8d3a0a7353340071c10650bede7eeb15ba043 100644 --- a/themes/bootstrap3/templates/record/comments-list.phtml +++ b/themes/bootstrap3/templates/record/comments-list.phtml @@ -1,21 +1,21 @@ -<? $comments = $this->driver->getComments(); ?> -<? if (empty($comments) || count($comments) == 0): ?> +<?php $comments = $this->driver->getComments(); ?> +<?php if (empty($comments) || count($comments) == 0): ?> <div class="alert alert-info"><?=$this->transEsc('Be the first to leave a comment')?>!</div> -<? else: ?> - <? foreach ($comments as $comment): ?> +<?php else: ?> + <?php foreach ($comments as $comment): ?> <div class="comment"> <div class="comment-name"> - <strong><?=$this->escapeHtml(trim($comment->firstname . ' ' . $comment->lastname))?></strong><br/> + <strong><?=null === $comment->user_id ? $this->transEsc('comment_anonymous_user') : $this->escapeHtml(trim($comment->firstname . ' ' . $comment->lastname))?></strong><br/> <small> <?=$this->escapeHtml($comment->created)?> - <? if (($user = $this->auth()->isLoggedIn()) && $comment->user_id == $user->id): ?> + <?php if (($user = $this->auth()->isLoggedIn()) && $comment->user_id == $user->id): ?> <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'DeleteComment')?>?delete=<?=urlencode($comment->id)?>" id="recordComment<?=$this->escapeHtml($comment->id)?>" class="delete"><?=$this->transEsc('Delete')?></a> - <? endif; ?> + <?php endif; ?> </small> </div> <div class="comment-text"> <?=$this->escapeHtml($comment->comment)?> </div> </div> - <? endforeach; ?> -<? endif; ?> + <?php endforeach; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/record/cover.phtml b/themes/bootstrap3/templates/record/cover.phtml index 435cb53795041383fc3d79210076705b1f28c906..f19dc13d0939c47b9f4fa9cb35ab8bc3d02203fc 100644 --- a/themes/bootstrap3/templates/record/cover.phtml +++ b/themes/bootstrap3/templates/record/cover.phtml @@ -1,8 +1,8 @@ -<? /* Display thumbnail if appropriate: */ ?> -<? if ($cover): ?> - <? if ($this->link): ?><a href="<?=$this->escapeHtmlAttr($this->link)?>"><? endif; ?> - <img alt="<?=$this->transEsc('Cover Image')?>" <? if ($linkPreview): ?>data-linkpreview="true" <? endif; ?>class="recordcover" src="<?=$this->escapeHtmlAttr($cover); ?>"/> - <? if ($this->link): ?></a><? endif; ?> -<? else: ?> - <img src="<?=$this->url('cover-unavailable')?>" <? if ($linkPreview): ?>data-linkpreview="true" <? endif; ?>class="recordcover" alt="<?=$this->transEsc('No Cover Image')?>"/> -<? endif; ?> +<?php /* Display thumbnail if appropriate: */ ?> +<?php if ($cover): ?> + <?php if ($this->link): ?><a href="<?=$this->escapeHtmlAttr($this->link)?>"><?php endif; ?> + <img alt="<?=$this->transEsc('Cover Image')?>" <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?>class="recordcover" src="<?=$this->escapeHtmlAttr($cover); ?>"/> + <?php if ($this->link): ?></a><?php endif; ?> +<?php else: ?> + <img src="<?=$this->url('cover-unavailable')?>" <?php if ($linkPreview): ?>data-linkpreview="true" <?php endif; ?>class="recordcover" alt="<?=$this->transEsc('No Cover Image')?>"/> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/record/email.phtml b/themes/bootstrap3/templates/record/email.phtml index 470625daf8b0a73c77439b577c0cc5f801774886..e6f5927f60adcc64d6e256dd29c958437ed4eafb 100644 --- a/themes/bootstrap3/templates/record/email.phtml +++ b/themes/bootstrap3/templates/record/email.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Email Record')); diff --git a/themes/bootstrap3/templates/record/export-menu.phtml b/themes/bootstrap3/templates/record/export-menu.phtml index 9b81be961437b7369c970892dfc8b6fe4351d7d1..ffb04ff00db449b234515e248139bea56b40ce74 100644 --- a/themes/bootstrap3/templates/record/export-menu.phtml +++ b/themes/bootstrap3/templates/record/export-menu.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Export Record')); @@ -8,14 +8,14 @@ . '<li class="active">' . $this->transEsc('Export Record') . '</li>'; ?> <?=$this->flashmessages()?> -<? $exportFormats = $this->export()->getFormatsForRecord($this->driver); if (count($exportFormats) > 0): ?> +<?php $exportFormats = $this->export()->getFormatsForRecord($this->driver); if (count($exportFormats) > 0): ?> <?=$this->transEsc('export_choose_format')?> <ul> - <? foreach ($exportFormats as $exportFormat): ?> + <?php foreach ($exportFormats as $exportFormat): ?> <li><a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>?style=<?=$this->escapeHtml($exportFormat)?>"><?=$this->transEsc('Export to')?> <?=$this->transEsc($this->export()->getLabelForFormat($exportFormat))?></a></li> - <? endforeach; ?> + <?php endforeach; ?> </ul> -<? else: ?> +<?php else: ?> <?=$this->transEsc('export_no_formats')?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/record/hold.phtml b/themes/bootstrap3/templates/record/hold.phtml index 36f7d3af002f17abe0dc7cac36bad0e6313547ac..e47ba5a96e8222100a518265a05d61ffc00e7df3 100644 --- a/themes/bootstrap3/templates/record/hold.phtml +++ b/themes/bootstrap3/templates/record/hold.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('request_place_text') . ': ' . $this->driver->getBreadcrumb()); @@ -8,32 +8,32 @@ . '<li class="active">' . $this->transEsc('request_place_text') . '</li>'; ?> <h2><?=$this->transEsc('request_place_text')?></h2> -<? if ($this->helpText): ?> +<?php if ($this->helpText): ?> <p class="helptext"><?=$this->helpText?></p> -<? endif; ?> +<?php endif; ?> <form class="form-record-hold" method="post" name="placeHold"> <?=$this->flashmessages()?> <label class="control-label"><?=$this->transEsc('Title')?></label> <p class="form-control-static"><?=$this->driver->getBreadcrumb() ?></p> - <? if (in_array("comments", $this->extraHoldFields)): ?> + <?php if (in_array("comments", $this->extraHoldFields)): ?> <div class="form-group hold-comment"> <label class="control-label"><?=$this->transEsc("Comments")?>:</label> <textarea rows="3" cols="20" name="gatheredDetails[comment]" class="form-control"><?=isset($this->gatheredDetails['comment']) ? $this->escapeHtml($this->gatheredDetails['comment']) : ''?></textarea> </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("requiredByDate", $this->extraHoldFields)): ?> + <?php if (in_array("requiredByDate", $this->extraHoldFields)): ?> <div class="form-group hold-required-by"> <label class="control-label"><?=$this->transEsc("hold_required_by")?>:</label> <input id="requiredByDate" type="text" name="gatheredDetails[requiredBy]" value="<?=(isset($this->gatheredDetails['requiredBy']) && !empty($this->gatheredDetails['requiredBy'])) ? $this->escapeHtmlAttr($this->gatheredDetails['requiredBy']) : $this->escapeHtmlAttr($this->defaultRequiredDate)?>" size="8" class="form-control"/> (<?=$this->dateTime()->getDisplayDateFormat()?>) </div> - <? endif; ?> + <?php endif; ?> - <? if ($this->requestGroupNeeded): ?> + <?php if ($this->requestGroupNeeded): ?> <div class="form-group hold-request-group"> - <? + <?php if (isset($this->gatheredDetails['requestGroupId']) && $this->gatheredDetails['requestGroupId'] !== "") { $selected = $this->gatheredDetails['requestGroupId']; } else { @@ -42,22 +42,22 @@ ?> <label class="control-label"><?=$this->transEsc("hold_request_group")?>:</label> <select id="requestGroupId" name="gatheredDetails[requestGroupId]" class="form-control"> - <? if ($selected === false): ?> + <?php if ($selected === false): ?> <option value="" selected="selected"> <?=$this->transEsc('select_request_group')?> </option> - <? endif; ?> - <? foreach ($this->requestGroups as $group): ?> + <?php endif; ?> + <?php foreach ($this->requestGroups as $group): ?> <option value="<?=$this->escapeHtmlAttr($group['id'])?>"<?=($selected == $group['id']) ? ' selected="selected"' : ''?>> <?=$this->transEsc('request_group_' . $group['name'], null, $group['name'])?> </option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("pickUpLocation", $this->extraHoldFields)): ?> - <? + <?php if (in_array("pickUpLocation", $this->extraHoldFields)): ?> + <?php if (isset($this->gatheredDetails['pickUpLocation']) && $this->gatheredDetails['pickUpLocation'] !== "") { $selected = $this->gatheredDetails['pickUpLocation']; } elseif (isset($this->homeLibrary) && $this->homeLibrary !== "") { @@ -66,47 +66,47 @@ $selected = $this->defaultPickup; } ?> - <? if ($this->requestGroupNeeded): ?> + <?php if ($this->requestGroupNeeded): ?> <div class="form-group hold-pickup-location"> <label id="pickUpLocationLabel" class="control-label"><i></i> <?=$this->transEsc("pick_up_location")?>: - <? if (in_array("requestGroup", $this->extraHoldFields)): ?> + <?php if (in_array("requestGroup", $this->extraHoldFields)): ?> <noscript> (<?=$this->transEsc("Please enable JavaScript.")?>)</noscript> - <? endif; ?> + <?php endif; ?> </label> <select id="pickUpLocation" name="gatheredDetails[pickUpLocation]" data-default="<?=$this->escapeHtmlAttr($selected)?>" class="form-control"> - <? if ($selected === false): ?> + <?php if ($selected === false): ?> <option value="" selected="selected"> <?=$this->transEsc('select_pickup_location')?> </option> - <? endif; ?> + <?php endif; ?> </select> </div> - <? elseif ($this->pickup): ?> + <?php elseif ($this->pickup): ?> <div class="form-group hold-pickup-location"> <label class="control-label"><?=$this->transEsc("pick_up_location")?>:</label> <select id="pickUpLocation" name="gatheredDetails[pickUpLocation]" class="form-control"> - <? if ($selected === false && count($this->pickup) > 1): ?> + <?php if ($selected === false && count($this->pickup) > 1): ?> <option value="" selected="selected"> <?=$this->transEsc('select_pickup_location')?> </option> - <? endif; ?> - <? foreach ($this->pickup as $lib): ?> + <?php endif; ?> + <?php foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID']) ? ' selected="selected"' : ''?>> <?=$this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay'])?> </option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? else: ?> + <?php else: ?> <input type="hidden" name="gatheredDetails[pickUpLocation]" value="<?=$this->escapeHtmlAttr($this->defaultPickup)?>" /> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="placeHold" value="<?=$this->transEsc('request_submit_text')?>"/> </div> </form> -<? +<?php // Set up hold script; we do this inline instead of in the header for lightbox compatibility: $this->inlineScript()->appendFile('hold.js'); diff --git a/themes/bootstrap3/templates/record/illrequest.phtml b/themes/bootstrap3/templates/record/illrequest.phtml index e5cc902422000c5bbdf5f13abaead99f2b2c05bc..9fac505e2561f6f192c2ded2fc5acba5d92b808c 100644 --- a/themes/bootstrap3/templates/record/illrequest.phtml +++ b/themes/bootstrap3/templates/record/illrequest.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('ill_request_place_text') . ': ' . $this->driver->getBreadcrumb()); @@ -8,62 +8,62 @@ . '<li class="active">' . $this->transEsc('ill_request_place_text') . '</li>'; ?> <h2><?=$this->transEsc('ill_request_place_text')?></h2> -<? if ($this->helpText): ?> +<?php if ($this->helpText): ?> <p class="help-block"><?=$this->helpText?></p> -<? endif; ?> +<?php endif; ?> <form id="ILLRequestForm" name="placeILLRequest" class="form-ill-retrieval-request" method="post"> <?=$this->flashmessages()?> - <? if (in_array("itemId", $this->extraFields)): ?> + <?php if (in_array("itemId", $this->extraFields)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc('ill_request_item')?>:</label> <select id="itemId" name="gatheredDetails[itemId]" class="form-control"> - <? foreach ($this->items as $item): ?> + <?php foreach ($this->items as $item): ?> <option value="<?=$this->escapeHtmlAttr($item['id'])?>"<?=($this->gatheredDetails['itemId'] == $item['id']) ? ' selected="selected"' : ''?>> <?=$this->escapeHtml($item['name'])?> </option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("pickUpLibrary", $this->extraFields) && !empty($this->pickupLibraries)): ?> + <?php if (in_array("pickUpLibrary", $this->extraFields) && !empty($this->pickupLibraries)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc("ill_request_pick_up_library")?>:</label> - <? if (count($this->pickupLibraries) > 1): ?> + <?php if (count($this->pickupLibraries) > 1): ?> <select id="pickupLibrary" name="gatheredDetails[pickUpLibrary]" class="form-control"> - <? + <?php if (isset($this->gatheredDetails['pickUpLibrary']) && $this->gatheredDetails['pickUpLibrary'] !== "") { $selected = $this->gatheredDetails['pickUpLibrary']; } else { $selected = false; } ?> - <? foreach ($this->pickupLibraries as $lib): ?> + <?php foreach ($this->pickupLibraries as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['id'])?>"<?=(($selected === false && isset($lib['isDefault']) && $lib['isDefault']) || $selected === $lib['id']) ? ' selected="selected"' : ''?>> <?=$this->transEsc('library_' . $lib['name'], null, $lib['name'])?> </option> - <? endforeach; ?> + <?php endforeach; ?> </select> - <? else: ?> - <? $lib = $this->pickupLibraries[0]; ?> + <?php else: ?> + <?php $lib = $this->pickupLibraries[0]; ?> <input type="text" class="form-control" size="40" readonly="readonly" value="<?=$this->escapeHtmlAttr($this->translate('library_' . $lib['name'], null, $lib['name']))?>" /> <input type="hidden" id="pickupLibrary" name="gatheredDetails[pickUpLibrary]" value="<?=$this->escapeHtmlAttr($lib['id'])?>" /> - <? endif; ?> + <?php endif; ?> </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("pickUpLibraryLocation", $this->extraFields)): ?> + <?php if (in_array("pickUpLibraryLocation", $this->extraFields)): ?> <div class="form-group"> <label id="pickupLibraryLocationLabel" class="control-label"><i></i> <?=$this->transEsc("ill_request_pick_up_location")?>:<noscript> (<?=$this->transEsc("Please enable JavaScript.")?>)</noscript></label> <select id="pickupLibraryLocation" name="gatheredDetails[pickUpLibraryLocation]" class="form-control"></select> </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("pickUpLocation", $this->extraFields)): ?> - <? if (count($this->pickup) > 1): ?> + <?php if (in_array("pickUpLocation", $this->extraFields)): ?> + <?php if (count($this->pickup) > 1): ?> <div class="form-group"> - <? + <?php if (isset($this->gatheredDetails['pickUpLocation']) && $this->gatheredDetails['pickUpLocation'] !== "") { $selected = $this->gatheredDetails['pickUpLocation']; } elseif (isset($this->homeLibrary) && $this->homeLibrary !== "") { @@ -74,39 +74,39 @@ ?> <label class="control-label"><?=$this->transEsc("pick_up_location")?>:</label> <select name="gatheredDetails[pickUpLocation]" class="form-control"> - <? foreach ($this->pickup as $lib): ?> + <?php foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID']) ? ' selected="selected"' : ''?>> <?=$this->escapeHtml($lib['locationDisplay'])?> </option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? else: ?> + <?php else: ?> <input type="hidden" name="gatheredDetails[pickUpLocation]" value="<?=$this->escapeHtmlAttr($this->defaultPickup)?>" /> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> - <? if (in_array("requiredByDate", $this->extraFields)): ?> + <?php if (in_array("requiredByDate", $this->extraFields)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc("hold_required_by")?>:</label> <input id="requiredByDate" type="text" name="gatheredDetails[requiredBy]" value="<?=(isset($this->gatheredDetails['requiredBy']) && !empty($this->gatheredDetails['requiredBy'])) ? $this->escapeHtmlAttr($this->gatheredDetails['requiredBy']) : $this->escapeHtmlAttr($this->defaultRequiredDate)?>" size="8" class="form-control"/> (<?=$this->dateTime()->getDisplayDateFormat()?>) </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("comments", $this->extraFields)): ?> + <?php if (in_array("comments", $this->extraFields)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc("Comments")?>:</label> <textarea rows="3" cols="20" name="gatheredDetails[comment]" class="form-control"><?=isset($this->gatheredDetails['comment']) ? $this->escapeHtml($this->gatheredDetails['comment']) : ''?></textarea> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="placeILLRequest" value="<?=$this->transEsc('ill_request_submit_text')?>"/> </div> </form> -<? +<?php // Set up ill script; we do this inline instead of in the header for lightbox compatibility: $this->inlineScript()->appendFile('ill.js'); diff --git a/themes/bootstrap3/templates/record/save.phtml b/themes/bootstrap3/templates/record/save.phtml index 33ee765c2a4ce1f3fce007312a6926e98145a3a2..e9f10997fcd9e3f938494ec1f48a06503b56061d 100644 --- a/themes/bootstrap3/templates/record/save.phtml +++ b/themes/bootstrap3/templates/record/save.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Save')); @@ -12,44 +12,44 @@ <input type="hidden" name="submit" value="1" /> <input type="hidden" name="id" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId()) ?>" /> <input type="hidden" name="source" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" /> - <? if (!empty($this->containingLists)): ?> + <?php if (!empty($this->containingLists)): ?> <p><?=$this->transEsc('This item is already part of the following list/lists') ?>: - <? foreach ($this->containingLists as $i=>$list): ?> - <a href="<?=$this->url('userList', ['id' => $list['id']]) ?>" data-lightbox-ignore><?=$this->escapeHtml($list['title'])?></a><? if($i<count($this->containingLists)-1): ?>, <? endif; ?> - <? endforeach; ?> + <?php foreach ($this->containingLists as $i => $list): ?> + <a href="<?=$this->url('userList', ['id' => $list['id']]) ?>" data-lightbox-ignore><?=$this->escapeHtml($list['title'])?></a><?php if($i < count($this->containingLists) - 1): ?>, <?php endif; ?> + <?php endforeach; ?> </p><hr/> - <? endif; ?> + <?php endif; ?> - <?/* Only display the list drop-down if the user has lists that do not contain + <?php /* Only display the list drop-down if the user has lists that do not contain this item OR if they have no lists at all and need to create a default list */?> - <? $showLists = (!empty($this->nonContainingLists) || (empty($this->containingLists) && empty($this->nonContainingLists))); ?> + <?php $showLists = (!empty($this->nonContainingLists) || (empty($this->containingLists) && empty($this->nonContainingLists))); ?> <div class="form-group"> - <? if ($showLists): ?> + <?php if ($showLists): ?> <label class="control-label" for="save_list"><?=$this->transEsc('Choose a List') ?></label> - <? endif; ?> - <? if ($showLists): ?> + <?php endif; ?> + <?php if ($showLists): ?> <select class="form-control" id="save_list" name="list"> - <? if ($this->nonContainingLists): ?> - <? foreach ($this->nonContainingLists as $list): ?> - <option value="<?=$list['id'] ?>"<? if ($list['id']==$this->userList()->lastUsed()): ?> selected="selected"<? endif; ?>><?=$this->escapeHtml($list['title'])?></option> - <? endforeach; ?> - <? else: ?> + <?php if ($this->nonContainingLists): ?> + <?php foreach ($this->nonContainingLists as $list): ?> + <option value="<?=$list['id'] ?>"<?php if ($list['id'] == $this->userlist()->lastUsed()): ?> selected="selected"<?php endif; ?>><?=$this->escapeHtml($list['title'])?></option> + <?php endforeach; ?> + <?php else: ?> <option value=""><?=$this->transEsc('My Favorites') ?></option> - <? endif; ?> + <?php endif; ?> </select> - <? endif; ?> + <?php endif; ?> <a class="btn btn-link" id="make-list" href="<?=$this->url('editList', ['id' => 'NEW'])?>?recordId=<?=urlencode($this->driver->getUniqueId())?>&recordSource=<?=urlencode($this->driver->getSourceIdentifier())?>"><?=$showLists ? $this->transEsc('or create a new list') : $this->transEsc('Create a List'); ?></a> </div> - <? if ($showLists): ?> - <? if ($this->usertags()->getMode() !== 'disabled'): ?> + <?php if ($showLists): ?> + <?php if ($this->usertags()->getMode() !== 'disabled'): ?> <div class="form-group"> <label class="control-label" for="add_mytags"><?=$this->transEsc('Add Tags') ?></label> <input class="form-control" id="add_mytags" type="text" name="mytags" value=""/> <span class="help-block"><?=$this->transEsc("add_tag_note") ?></span> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <label class="control-label" for="add_notes"><?=$this->transEsc('Add a Note') ?></label> <textarea class="form-control" id="add_notes" name="notes" rows="3"></textarea> @@ -57,5 +57,5 @@ <div class="form-group"> <input class="btn btn-primary" type="submit" value="<?=$this->transEsc('Save') ?>"/> </div> - <? endif; ?> + <?php endif; ?> </form> diff --git a/themes/bootstrap3/templates/record/sms.phtml b/themes/bootstrap3/templates/record/sms.phtml index 58cd4b2223a23199abf858bc25193af57cdbaac3..4bb8f8a3dca797423a8840a3fa4816ac1f260a03 100644 --- a/themes/bootstrap3/templates/record/sms.phtml +++ b/themes/bootstrap3/templates/record/sms.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Text this')); echo $this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'vendor/libphonenumber.js', 'SET'); @@ -8,7 +8,7 @@ . '<li>' . $this->recordLink()->getBreadcrumb($this->driver) . '</li> ' . '<li class="active">' . $this->transEsc('Text this') . '</li>'; ?> -<? $validatorCallback = <<<JS +<?php $validatorCallback = <<<JS function phoneNumberValidation() { return phoneNumberFormHandler('sms_to', '{$this->validation}'); } @@ -16,7 +16,7 @@ JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $validatorCallback, 'SET'); ?> <h2><?=$this->transEsc('Text this') ?>: <span class="title-in-heading"><?=$this->escapeHtml($this->driver->getBreadcrumb())?></span></h2> -<form method="post" name="smsRecord" class="form-record-sms"<? if(isset($this->validation) && !empty($this->validation)):?> data-lightbox-onsubmit="phoneNumberValidation"<? endif; ?>> +<form method="post" name="smsRecord" class="form-record-sms"<?php if(isset($this->validation) && !empty($this->validation)):?> data-lightbox-onsubmit="phoneNumberValidation"<?php endif; ?>> <?=$this->flashmessages()?> <input type="hidden" name="id" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" /> <input type="hidden" name="source" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" /> @@ -25,20 +25,20 @@ JS; <input id="sms_to" type="tel" name="to" placeholder="<?=$this->transEsc('sms_phone_number')?>" class="form-control"/> <div class="help-block with-errors"></div> </div> - <? if (is_array($this->carriers) && count($this->carriers) > 1): ?> + <?php if (is_array($this->carriers) && count($this->carriers) > 1): ?> <div class="form-group"> <label class="control-label" for="sms_provider"><?=$this->transEsc('Provider')?>:</label> <select id="sms_provider" name="provider" class="form-control"> <option selected="selected" value=""><?=$this->transEsc('Select your carrier')?></option> - <? foreach ($this->carriers as $val => $details): ?> + <?php foreach ($this->carriers as $val => $details): ?> <option value="<?=$this->escapeHtmlAttr($val)?>"><?=$this->escapeHtml($details['name'])?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? else: ?> - <? $keys = is_array($this->carriers) ? array_keys($this->carriers) : []; ?> - <input type="hidden" name="provider" value="<?=isset($keys[0]) ? $keys[0] : ''?>" /> - <? endif; ?> + <?php else: ?> + <?php $keys = is_array($this->carriers) ? array_keys($this->carriers) : []; ?> + <input type="hidden" name="provider" value="<?=$keys[0] ?? ''?>" /> + <?php endif; ?> <?=$this->recaptcha()->html($this->useRecaptcha) ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Send Text')?>"/> diff --git a/themes/bootstrap3/templates/record/storageretrievalrequest.phtml b/themes/bootstrap3/templates/record/storageretrievalrequest.phtml index 315f4d77a26dd836caaade4d1c9c4994afd7da69..f6aee9a841d8303b35626c8f288885eb16842de5 100644 --- a/themes/bootstrap3/templates/record/storageretrievalrequest.phtml +++ b/themes/bootstrap3/templates/record/storageretrievalrequest.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('storage_retrieval_request_place_text') . ': ' . $this->driver->getBreadcrumb()); @@ -8,13 +8,13 @@ . '<li class="active">' . $this->transEsc('storage_retrieval_request_place_text') . '</li>'; ?> <h2><?=$this->transEsc('storage_retrieval_request_place_text')?></h2> -<? if ($this->helpText): ?> +<?php if ($this->helpText): ?> <p class="helptext"><?=$this->helpText?></p> -<? endif; ?> +<?php endif; ?> <form name="placeStorageRetrievalRequest" class="form-storage-retrieval-request" method="post"> <?=$this->flashmessages()?> - <? if (in_array("item-issue", $this->extraFields)): ?> + <?php if (in_array("item-issue", $this->extraFields)): ?> <div class="form-group controls"> <div class="radio"> <label> @@ -40,20 +40,20 @@ <input type="text" name="gatheredDetails[year]" value="<?=isset($this->gatheredDetails['year']) ? $this->escapeHtmlAttr($this->gatheredDetails['year']) : ''?>" class="form-control"/><br/> </div> </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("requiredByDate", $this->extraFields)): ?> + <?php if (in_array("requiredByDate", $this->extraFields)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc("hold_required_by")?>:</label> <input id="requiredByDate" type="text" name="gatheredDetails[requiredBy]" value="<?=(isset($this->gatheredDetails['requiredBy']) && !empty($this->gatheredDetails['requiredBy'])) ? $this->escapeHtmlAttr($this->gatheredDetails['requiredBy']) : $this->escapeHtmlAttr($this->defaultRequiredDate)?>" size="8" class="form-control"/> (<?=$this->dateTime()->getDisplayDateFormat()?>) </div> - <? endif; ?> + <?php endif; ?> - <? if (in_array("pickUpLocation", $this->extraFields)): ?> - <? if ($this->pickup): ?> + <?php if (in_array("pickUpLocation", $this->extraFields)): ?> + <?php if ($this->pickup): ?> <div class="form-group"> - <? + <?php if (isset($this->gatheredDetails['pickUpLocation']) && $this->gatheredDetails['pickUpLocation'] !== "") { $selected = $this->gatheredDetails['pickUpLocation']; } elseif (isset($this->homeLibrary) && $this->homeLibrary !== "") { @@ -64,29 +64,29 @@ ?> <label class="control-label"><?=$this->transEsc("pick_up_location")?>:</label> <select name="gatheredDetails[pickUpLocation]" class="form-control"> - <? if ($selected === false && count($this->pickup) > 1): ?> + <?php if ($selected === false && count($this->pickup) > 1): ?> <option value="" selected="selected"> <?=$this->transEsc('select_pickup_location')?> </option> - <? endif; ?> - <? foreach ($this->pickup as $lib): ?> + <?php endif; ?> + <?php foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID']) ? ' selected="selected"' : ''?>> <?=$this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay'])?> </option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? else: ?> + <?php else: ?> <input type="hidden" name="gatheredDetails[pickUpLocation]" value="<?=$this->escapeHtmlAttr($this->defaultPickup)?>" /> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> - <? if (in_array("comments", $this->extraFields)): ?> + <?php if (in_array("comments", $this->extraFields)): ?> <div class="form-group"> <label class="control-label"><?=$this->transEsc("Comments")?>:</label> <textarea rows="3" cols="20" name="gatheredDetails[comment]" class="form-control"><?=isset($this->gatheredDetails['comment']) ? $this->escapeHtml($this->gatheredDetails['comment']) : ''?></textarea> </div> - <? endif; ?> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="placeStorageRetrievalRequest" value="<?=$this->transEsc('storage_retrieval_request_submit_text')?>"/> diff --git a/themes/bootstrap3/templates/record/taglist.phtml b/themes/bootstrap3/templates/record/taglist.phtml index b0bb61475f993a8bfdc18a93ab732ff2582d8bb8..ccf84ff458aac6fee2e38e83a6eee855d4cfedb5 100644 --- a/themes/bootstrap3/templates/record/taglist.phtml +++ b/themes/bootstrap3/templates/record/taglist.phtml @@ -1,26 +1,26 @@ <div class="tagList<?=$loggedin ? ' loggedin' : ''?>"> - <? if (count($tagList) > 0): ?> - <? foreach ($tagList as $tag): ?> - <? $is_me = isset($tag['is_me']) && !is_null($tag['is_me']) ? $tag['is_me'] : false; ?> + <?php if (count($tagList) > 0): ?> + <?php foreach ($tagList as $tag): ?> + <?php $is_me = isset($tag['is_me']) && null !== $tag['is_me'] ? $tag['is_me'] : false; ?> <div class="tag<?=$is_me ? ' selected' : ''?>"> <a href="<?=$this->url('tag-home')?>?lookfor=<?=urlencode($tag['tag'])?>"><?=$this->escapeHtml($tag['tag'])?></a> - <? if($loggedin): ?> + <?php if($loggedin): ?> <form method="POST" action="<?=$this->recordLink()->getActionUrl($this->driver, $is_me ? 'DeleteTag' : 'AddTag') ?>" class="tag-form"> <input type="hidden" name="tag" value="<?=$this->escapeHtmlAttr($tag['tag'])?>"/> <button type="submit" class="badge" onClick="ajaxTagUpdate(this, '<?=$this->escapeHtmlAttr($tag['tag'])?>', <?=$is_me ? 'true' : 'false' ?>);return false;"><?=$this->escapeHtml($tag['cnt']) ?> - <? if($is_me): ?> + <?php if($is_me): ?> <i class="fa fa-close" title="<?=$this->transEsc('delete_tag') ?>"></i> - <? else: ?> + <?php else: ?> <i class="fa fa-plus" title="<?=$this->transEsc('Add Tag') ?>"></i> - <? endif; ?> + <?php endif; ?> </button> </form> - <? else: ?> + <?php else: ?> <span class="badge"><?=$this->escapeHtml($tag['cnt'])?></span> - <? endif; ?> + <?php endif; ?> </div> - <? endforeach; ?> - <? else: ?> + <?php endforeach; ?> + <?php else: ?> <?=$this->transEsc('No Tags')?>, <?=$this->transEsc('Be the first to tag this record')?>! - <? endif; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/record/view.phtml b/themes/bootstrap3/templates/record/view.phtml index b9e5994a286ffbcdb21bf5a862234547cf32b880..acb9cca27a3693e7bd99335bc87fe140ec054415 100644 --- a/themes/bootstrap3/templates/record/view.phtml +++ b/themes/bootstrap3/templates/record/view.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up standard record scripts: $this->headScript()->appendFile("record.js"); $this->headScript()->appendFile("check_save_statuses.js"); @@ -18,96 +18,98 @@ $this->layout()->title = $this->driver->getShortTitle(); ?> -<? if (isset($this->scrollData) && ($this->scrollData['previousRecord'] || $this->scrollData['nextRecord'])): ?> +<?php if (isset($this->scrollData) && ($this->scrollData['previousRecord'] || $this->scrollData['nextRecord'])): ?> <ul class="pager hidden-print"> - <? if ($this->scrollData['previousRecord']): ?> - <? if ($this->scrollData['firstRecord']): ?> + <?php if ($this->scrollData['previousRecord']): ?> + <?php if ($this->scrollData['firstRecord']): ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['firstRecord'])?>" title="<?=$this->transEsc('First Search Result')?>" rel="nofollow">« <?=$this->transEsc('First')?></a> </li> - <? endif; ?> + <?php endif; ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['previousRecord'])?>" title="<?=$this->transEsc('Previous Search Result')?>" rel="nofollow">« <?=$this->transEsc('Prev')?></a> </li> - <? else: ?> - <? if ($this->scrollData['firstRecord']): ?> + <?php else: ?> + <?php if ($this->scrollData['firstRecord']): ?> <li class="disabled"><a href="#">« <?=$this->transEsc('First')?></a></li> - <? endif; ?> + <?php endif; ?> <li class="disabled"><a href="#">« <?=$this->transEsc('Prev')?></a></li> - <? endif; ?> + <?php endif; ?> <?=$this->transEsc('of_num_results', [ '%%position%%' => $this->localizedNumber($this->scrollData['currentPosition']), '%%total%%' => $this->localizedNumber($this->scrollData['resultTotal']) ]) ?> - <? if ($this->scrollData['nextRecord']): ?> + <?php if ($this->scrollData['nextRecord']): ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['nextRecord'])?>" title="<?=$this->transEsc('Next Search Result')?>" rel="nofollow"><?=$this->transEsc('Next')?> »</a> </li> - <? if ($this->scrollData['lastRecord']): ?> + <?php if ($this->scrollData['lastRecord']): ?> <li> <a href="<?=$this->recordLink()->getUrl($this->scrollData['lastRecord'])?>" title="<?=$this->transEsc('Last Search Result')?>" rel="nofollow"><?=$this->transEsc('Last')?> »</a> </li> - <? endif; ?> - <? else: ?> + <?php endif; ?> + <?php else: ?> <li class="disabled"><a href="#"><?=$this->transEsc('Next')?> »</a></li> - <? if ($this->scrollData['lastRecord']): ?> + <?php if ($this->scrollData['lastRecord']): ?> <li class="disabled"><a href="#"><?=$this->transEsc('Last')?> »</a></li> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </ul> -<? endif; ?> +<?php endif; ?> <?=$this->record($this->driver)->getToolbar()?> <div class="record source<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>"> - <div class="<?=$this->layoutClass('mainbody')?>"> + <?php $sidebarList = $this->related()->getList($this->driver); ?> + <div class="<?=$this->layoutClass('mainbody')?><?=count($sidebarList) < 1 ? ' solo' : '' ?>"> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" class="hiddenId" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>" class="hiddenSource" /> <?=$this->flashmessages()?> <?=$this->record($this->driver)->getCoreMetadata()?> - <? if (count($this->tabs) > 0): ?> + <?php if (count($this->tabs) > 0): ?> <a name="tabnav"></a> <div class="record-tabs"> <ul class="nav nav-tabs"> - <? foreach ($this->tabs as $tab => $obj): ?> - <? // add current tab to breadcrumbs if applicable: + <?php foreach ($this->tabs as $tab => $obj): ?> + <?php // add current tab to breadcrumbs if applicable: $desc = $obj->getDescription(); - $tab_classes = []; + $tabName = preg_replace("/\W/", "-", strtolower($tab)); + $tabClasses = [ 'record-tab', $tabName ]; if (0 === strcasecmp($this->activeTab, $tab)) { if (!$this->loadInitialTabWithAjax || !$obj->supportsAjax()) { - $tab_classes[] = 'active'; + $tabClasses[] = 'active'; } - $tab_classes[] = 'initiallyActive'; + $tabClasses[] = 'initiallyActive'; $this->layout()->breadcrumbs .= '<li class="active">' . $this->transEsc($desc) . '</li>'; $activeTabObj = $obj; } - if (!$obj->isVisible()) { $tab_classes[] = 'hidden'; } - if (!$obj->supportsAjax()) { $tab_classes[] = 'noajax'; } + if (!$obj->isVisible()) { $tabClasses[] = 'hidden'; } + if (!$obj->supportsAjax()) { $tabClasses[] = 'noajax'; } ?> - <li<?=count($tab_classes) > 0 ? ' class="' . implode(' ', $tab_classes) . '"' : ''?>> - <a class="<?=strtolower($tab) ?>" href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"<? if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<? endif ?>><?=$this->transEsc($desc)?></a> + <li class="<?=implode(' ', $tabClasses)?>" data-tab="<?=$tabName?>"> + <a href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>><?=$this->transEsc($desc)?></a> </li> - <? endforeach; ?> + <?php endforeach; ?> </ul> <div class="tab-content"> - <? if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> + <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> <div class="tab-pane active <?=$this->escapeHtmlAttr($this->activeTab) ?>-tab"> <?=isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?> </div> - <? endif; ?> + <?php endif; ?> </div> </div> - <? endif; ?> + <?php endif; ?> - <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenURL()).'"></span>':''?> + <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>':''?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> - <? foreach ($this->related()->getList($this->driver) as $current): ?> + <?php foreach ($sidebarList as $current): ?> <?=$this->related()->render($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> </div> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$(document).ready(recordDocReady);', 'SET'); ?> diff --git a/themes/bootstrap3/templates/records/home.phtml b/themes/bootstrap3/templates/records/home.phtml index 2aa66836fa1c09bcc9741a1b6fbf15acfc87a167..e8e558779b409489040293cb5da3c9eda9597fd4 100644 --- a/themes/bootstrap3/templates/records/home.phtml +++ b/themes/bootstrap3/templates/records/home.phtml @@ -1,4 +1,4 @@ -<? +<?php $this->overrideTitle = $this->translate('View Records'); $this->overrideSearchHeading = ''; diff --git a/themes/bootstrap3/templates/relais/button.phtml b/themes/bootstrap3/templates/relais/button.phtml new file mode 100644 index 0000000000000000000000000000000000000000..335b09a20f1c7dde80c58aad3eef15241821c9db --- /dev/null +++ b/themes/bootstrap3/templates/relais/button.phtml @@ -0,0 +1,31 @@ +<span class="relaisLink"><i class="fa fa-spinner fa-spin"></i> <?=$this->transEsc('relais_checking')?></span> +<?php + // Only activate Relais code once, even if there are multiple items: + if (empty($this->layout()->relaisActive)) { + // We need a record driver in $driver for this to work right! + if (!isset($driver)) { + throw new \Exception('Record driver missing!'); + } + $this->layout()->relaisActive = true; + $this->jsTranslations()->addStrings( + [ + 'confirm_dialog_no' => 'confirm_dialog_no', + 'confirm_dialog_yes' => 'confirm_dialog_yes', + 'relais_available' => 'relais_available', + 'relais_checking' => 'relais_checking', + 'relais_error_html' => 'relais_error_html', + 'relais_request' => 'relais_request', + 'relais_requesting' => 'relais_requesting', + 'relais_search' => 'relais_search', + 'relais_success_label' => 'relais_success_label', + 'relais_success_message' => 'relais_success_message', + ] + ); + $this->headScript()->appendFile('relais.js'); + $addLink = $this->escapeJs($this->url('relais-request')); + $oclc = $this->escapeJs($driver->tryMethod('getCleanOCLCNum')); + $failLink = $this->escapeJs($this->relais()->getSearchLink($driver)); + $activateRelais = "$(document).ready(function() { VuFind.relais.checkAvailability('$addLink', '$oclc', '$failLink') });"; + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $activateRelais, 'SET'); + } +?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/relais/request.phtml b/themes/bootstrap3/templates/relais/request.phtml new file mode 100644 index 0000000000000000000000000000000000000000..70740d0e16ded83d586291706128fede9ad39a99 --- /dev/null +++ b/themes/bootstrap3/templates/relais/request.phtml @@ -0,0 +1,14 @@ +<h2><?=$this->transEsc('relais_request')?></h2> + +<div id="requestMessage"> + <i class="fa fa-spinner fa-spin"></i> + <?=$this->transEsc('relais_checking')?> +</div> + +<div id="requestButton"></div> + +<?php + $this->headScript()->appendFile('relais.js'); + $activateRelais = "$(document).ready(function () { VuFind.relais.addItem('$oclc', '$failLink'); });\n"; + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $activateRelais, 'SET') +?> diff --git a/themes/bootstrap3/templates/search/advanced.phtml b/themes/bootstrap3/templates/search/advanced.phtml index 779b5a8ea594c3d167e13fe118238eac1f8e66ac..5a3d71ded69bedb2262970af97bc579cc6f5e4d1 100644 --- a/themes/bootstrap3/templates/search/advanced.phtml +++ b/themes/bootstrap3/templates/search/advanced.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load the Solr-specific advanced search controls and inject them into the // standard advanced search layout: $this->extraAdvancedControls = $this->render('search/advanced/solr.phtml'); diff --git a/themes/bootstrap3/templates/search/advanced/build_page.phtml b/themes/bootstrap3/templates/search/advanced/build_page.phtml index cfb4ad36d925c51a4be2cb0202b05a29919c2747..191a02eb53428e89b19fbef5b2f48d9fe17a1038 100644 --- a/themes/bootstrap3/templates/search/advanced/build_page.phtml +++ b/themes/bootstrap3/templates/search/advanced/build_page.phtml @@ -4,26 +4,28 @@ $(document).ready(function() { $('#groupPlaceHolder').removeClass('hidden'); $('#new_search_template').addClass('hidden').detach().appendTo('[role="main"]'); $('#new_search_template').find('.adv-search').removeAttr('id'); + $('#new_search_template').find('.form-control').removeAttr('id'); $('#new_search_template').find('input').removeAttr('value'); $('#new_search_template').find('option').removeAttr('selected'); $('#new_group_template').addClass('hidden').detach().appendTo('[role="main"]'); $('#new_group_template').find('.adv-group').removeAttr('id'); + $('#new_group_template').find('.form-control').removeAttr('id'); $('#new_group_template .adv-search').remove(); $('#advSearchForm .no-js').remove(); // Build page - <? if (isset($this->searchDetails) && is_object($this->searchDetails)): ?> - <? foreach ($this->searchDetails->getQueries() as $searchGroup): ?> - <? $i = 0; foreach ($searchGroup->getQueries() as $search): ?> - <? if (++$i == 1): ?> + <?php if (isset($this->searchDetails) && is_object($this->searchDetails)): ?> + <?php foreach ($this->searchDetails->getQueries() as $searchGroup): ?> + <?php $i = 0; foreach ($searchGroup->getQueries() as $search): ?> + <?php if (++$i == 1): ?> var new_group = addGroup('<?=addslashes($search->getString())?>', '<?=addslashes($search->getHandler())?>', '<?=$searchGroup->isNegated() ? 'NOT' : $searchGroup->getOperator()?>'); - <? else: ?> + <?php else: ?> addSearch(new_group, {term:'<?=addslashes($search->getString())?>', field:'<?=addslashes($search->getHandler())?>'}); - <? endif; ?> - <? endforeach; ?> - <? endforeach; ?> - <? else: ?> + <?php endif; ?> + <?php endforeach; ?> + <?php endforeach; ?> + <?php else: ?> var group = addGroup(); addSearch(group); addSearch(group); - <? endif; ?> + <?php endif; ?> }); diff --git a/themes/bootstrap3/templates/search/advanced/build_page_eds.phtml b/themes/bootstrap3/templates/search/advanced/build_page_eds.phtml index 6fe08fcebccdf177a74620e1e029da60bb95d1bb..7207789f70539f6e2e6402c336c042fb1f32e347 100644 --- a/themes/bootstrap3/templates/search/advanced/build_page_eds.phtml +++ b/themes/bootstrap3/templates/search/advanced/build_page_eds.phtml @@ -10,27 +10,27 @@ $(document).ready(function() { $('#advSearchForm .no-js').remove(); $('#groupJoin').remove(); // Build page - <? if (isset($this->searchDetails) && is_object($this->searchDetails)): ?> - <? foreach ($this->searchDetails->getQueries() as $searchGroup): ?> - <? $i = 0; foreach ($searchGroup->getQueries() as $search): ?> - <? if (++$i == 1): ?> + <?php if (isset($this->searchDetails) && is_object($this->searchDetails)): ?> + <?php foreach ($this->searchDetails->getQueries() as $searchGroup): ?> + <?php $i = 0; foreach ($searchGroup->getQueries() as $search): ?> + <?php if (++$i == 1): ?> var new_group = addGroup( '<?=addslashes($search->getString())?>', '<?=addslashes($search->getHandler())?>', '<?=$searchGroup->isNegated() ? 'NOT' : $searchGroup->getOperator()?>' ); - <? else: ?> + <?php else: ?> addSearch(new_group, { term :'<?=addslashes($search->getString())?>', field:'<?=addslashes($search->getHandler())?>', op :'<?=addslashes($search->getOperator())?>' }); - <? endif; ?> - <? endforeach; ?> - <? endforeach; ?> - <? else: ?> + <?php endif; ?> + <?php endforeach; ?> + <?php endforeach; ?> + <?php else: ?> var new_group = addGroup(); addSearch(new_group); addSearch(new_group); - <? endif; ?> + <?php endif; ?> }); diff --git a/themes/bootstrap3/templates/search/advanced/checkbox-filters.phtml b/themes/bootstrap3/templates/search/advanced/checkbox-filters.phtml index 8d075ae4c4227d6999aa67f7db60a86d761f965c..6edb65cb72169064e348ee2f14f5bb47f979f4ed 100644 --- a/themes/bootstrap3/templates/search/advanced/checkbox-filters.phtml +++ b/themes/bootstrap3/templates/search/advanced/checkbox-filters.phtml @@ -1,12 +1,12 @@ -<? if (isset($this->checkboxFacets) && count($this->checkboxFacets) > 0): ?> +<?php if (isset($this->checkboxFacets) && count($this->checkboxFacets) > 0): ?> <div class="checkbox-filters"> - <? foreach ($this->checkboxFacets as $current): ?> + <?php foreach ($this->checkboxFacets as $current): ?> <div class="checkbox"> <label> - <input type="checkbox" name="filter[]" value="<?=$this->escapeHtmlAttr($current['filter'])?>" id="<?=$this->escapeHtmlAttr(str_replace(' ', '', $current['desc']))?>"<? if ($current['selected']): ?> checked="checked"<? endif; ?>/> + <input type="checkbox" name="filter[]" value="<?=$this->escapeHtmlAttr($current['filter'])?>" id="<?=$this->escapeHtmlAttr(str_replace(' ', '', $current['desc']))?>"<?php if ($current['selected']): ?> checked="checked"<?php endif; ?>/> <?=$this->transEsc($current['desc'])?> </label> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> -<?endif;?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/advanced/eds.phtml b/themes/bootstrap3/templates/search/advanced/eds.phtml index b2fa127af21a53502f2d42e8266d74ccfe770aeb..99913e3baaa9491722de3904dab6d4a1e2df7183 100644 --- a/themes/bootstrap3/templates/search/advanced/eds.phtml +++ b/themes/bootstrap3/templates/search/advanced/eds.phtml @@ -1,128 +1,128 @@ -<? if (!empty($this->expanderList)): ?> - <fieldset class="eds"> - <legend><?=$this->transEsc('eds_modes_and_expanders')?></legend> - <? foreach ($this->expanderList as $field => $expander): - $value = $expander['Value'] ?> - <div class="checkbox"> - <label for="expand_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>"> - <input id="expand_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>" type="checkbox" <?=(isset($expander['selected']) && $expander['selected'])?'checked="checked"':''?> name="filter[]" value="EXPAND:<?=$this->escapeHtmlAttr($value)?>"> - <?=$this->transEsc('eds_expander_' . $value, array(), $expander['Label'])?> - </label> - </div> - <? endforeach; ?> - - <label for="searchModes"><?=$this->transEsc('Search Mode')?></label> - <select id="searchMode_<?=$this->escapeHtmlAttr($field)?>" name="filter[]" class="form-control"> - <? foreach ($this->searchModes as $field => $searchMode): - $value = $searchMode['Value'] ?> - <option <?=(isset($searchMode['selected']) && $searchMode['selected'])?'selected="selected"':''?> value="SEARCHMODE:<?=$this->escapeHtmlAttr($value)?>"> - <?= /* 'Label' comes from API and is always in English; try to translate structured value before using it: */ $this->transEsc('eds_mode_' . $value, array(), $searchMode['Label']) ?> - </option> - <? endforeach; ?> - </select> - </fieldset> -<? endif; ?> - -<? if (!empty($this->limiterList)): ?> - <fieldset class="eds"> - <legend><?=$this->transEsc('Limit To')?></legend> - <? foreach ($this->limiterList as $field => $facet): ?> - <? switch($facet['Type']){ - case 'multiselectvalue': ?> - <label for="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>"><?=$this->transEsc($facet['Label'])?></label><br/> - <select id="limit_<?=$this->escapeHtmlAttr($field)?>" name="filter[]" multiple="multiple" size="10" class="form-control"> - <? foreach ($facet['LimiterValues'] as $id => $facetValue): ?> - <? $value = $facetValue['Value']; ?> - <option value="<?='LIMIT|'.$this->escapeHtmlAttr($field . ':' . $facetValue['Value'])?>"<?=(isset($facetValue['selected']) && $facetValue['selected'])?' selected="selected"':''?>><?=$this->escapeHtml($facetValue['Value'])?></option> - <? endforeach; ?> - </select> - <!-- <br/> --> - <? break; - case 'select': - $value = $facet['LimiterValues'][0]['Value'] ?> - <div class="checkbox"> - <label for="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>"> - <input id="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>" type="checkbox" <?=(isset($facet['LimiterValues'][0]['selected']) && $facet['LimiterValues'][0]['selected'])?'checked="checked"':''?> name="filter[]" value="<?=$this->escapeHtmlAttr('LIMIT|'.$field . ':' . $value)?>"> - <?=$this->transEsc('eds_limiter_' . $field, array(), $facet['Label'])?> - </label> - </div> - <? break; - case 'text': ?> - <!-- not implemented --> - <? break; - case 'numeric':?> - <!-- not implemented --> - <? break; - case 'numericrange':?> - <!-- not implemented --> - <? break; - case 'ymrange': ?> - <!-- not implemented --> - <? break; - case 'yrange': ?> - <!-- not implemented --> - <? break; - case 'historicalrange':?> - <!-- not implemented --> - <? break; - case 'singleselectvalue':?> - <!-- not implemented --> - <? break; - }; ?> - <? endforeach; ?> - </fieldset> -<? endif; ?> -<? if (isset($this->dateRangeLimit)): ?> - <fieldset class="eds"> - <legend><?=$this->transEsc('adv_search_year')?></legend> - <input type="hidden" name="daterange[]" value="PublicationDate"/> - <div class="date-fields"> - <div class="date-from"> - <label for="PublicationDatefrom"><?=$this->transEsc('date_from')?>:</label> - <input type="text" name="PublicationDatefrom" id="PublicationDatefrom" value="<?=$this->escapeHtmlAttr($this->dateRangeLimit[0])?>" class="form-control"/> - </div> - <div class="date-to"> - <label for="PublicationDateto"><?=$this->transEsc('date_to')?>:</label> - <input type="text" name="PublicationDateto" id="PublicationDateto" value="<?=$this->escapeHtmlAttr($this->dateRangeLimit[1])?>" class="form-control"/> - </div> - </div> - <div class="slider-container"> - <input type="text" id="PublicationDateSlider"> - </div> - <? - $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); - $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); - $min = !empty($current['values'][0]) ? min($current['values'][0], 1400) : 1400; - $future = date('Y', time()+31536000); - $max = !empty($current['values'][1]) ? max($future, $current['values'][1]) : $future; - $low = !empty($current['values'][0]) ? $current['values'][0] : $min; - $high = !empty($current['values'][1]) ? $current['values'][1] : $max; - $min = intval($min); - $max = intval($max); - $low = intval($low); - $high = intval($high); - $init = !empty($current['values'][0]) ? 'fillTexts()' : ''; - $script = <<<JS -$(document).ready(function() { - var fillTexts = function() { - var v = PublicationDateSlider.getValue(); - $('#PublicationDatefrom').val(v[0]); - $('#PublicationDateto').val(v[1]); - }; - var PublicationDateSlider = $('#PublicationDateSlider') - .slider({ - 'min':{$min}, - 'max':{$max}, - 'handle':"square", - 'tooltip':"hide", - 'value':[{$low},{$high}] - }) - .on('change', fillTexts) - .data('slider'); - {$init} -}); -JS; - ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> - </fieldset> -<? endif; ?> +<?php if (!empty($this->expanderList)): ?> + <fieldset class="eds"> + <legend><?=$this->transEsc('eds_modes_and_expanders')?></legend> + <?php foreach ($this->expanderList as $field => $expander): + $value = $expander['Value'] ?> + <div class="checkbox"> + <label for="expand_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>"> + <input id="expand_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>" type="checkbox" <?=(isset($expander['selected']) && $expander['selected'])?'checked="checked"':''?> name="filter[]" value="EXPAND:<?=$this->escapeHtmlAttr($value)?>"> + <?=$this->transEsc('eds_expander_' . $value, [], $expander['Label'])?> + </label> + </div> + <?php endforeach; ?> + + <label for="searchModes"><?=$this->transEsc('Search Mode')?></label> + <select id="searchMode_<?=$this->escapeHtmlAttr($field)?>" name="filter[]" class="form-control"> + <?php foreach ($this->searchModes as $field => $searchMode): + $value = $searchMode['Value'] ?> + <option <?=(isset($searchMode['selected']) && $searchMode['selected'])?'selected="selected"':''?> value="SEARCHMODE:<?=$this->escapeHtmlAttr($value)?>"> + <?= /* 'Label' comes from API and is always in English; try to translate structured value before using it: */ $this->transEsc('eds_mode_' . $value, [], $searchMode['Label']) ?> + </option> + <?php endforeach; ?> + </select> + </fieldset> +<?php endif; ?> + +<?php if (!empty($this->limiterList)): ?> + <fieldset class="eds"> + <legend><?=$this->transEsc('Limit To')?></legend> + <?php foreach ($this->limiterList as $field => $facet): ?> + <?php switch($facet['Type']){ + case 'multiselectvalue': ?> + <label for="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>"><?=$this->transEsc($facet['Label'])?></label><br/> + <select id="limit_<?=$this->escapeHtmlAttr($field)?>" name="filter[]" multiple="multiple" size="10" class="form-control"> + <?php foreach ($facet['LimiterValues'] as $id => $facetValue): ?> + <?php $value = $facetValue['Value']; ?> + <option value="<?='LIMIT|' . $this->escapeHtmlAttr($field . ':' . $facetValue['Value'])?>"<?=(isset($facetValue['selected']) && $facetValue['selected'])?' selected="selected"':''?>><?=$this->escapeHtml($facetValue['Value'])?></option> + <?php endforeach; ?> + </select> + <!-- <br/> --> + <?php break; + case 'select': + $value = $facet['LimiterValues'][0]['Value'] ?> + <div class="checkbox"> + <label for="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>"> + <input id="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '+', $field))?>" type="checkbox" <?=(isset($facet['LimiterValues'][0]['selected']) && $facet['LimiterValues'][0]['selected'])?'checked="checked"':''?> name="filter[]" value="<?=$this->escapeHtmlAttr('LIMIT|' . $field . ':' . $value)?>"> + <?=$this->transEsc('eds_limiter_' . $field, [], $facet['Label'])?> + </label> + </div> + <?php break; + case 'text': ?> + <!-- not implemented --> + <?php break; + case 'numeric':?> + <!-- not implemented --> + <?php break; + case 'numericrange':?> + <!-- not implemented --> + <?php break; + case 'ymrange': ?> + <!-- not implemented --> + <?php break; + case 'yrange': ?> + <!-- not implemented --> + <?php break; + case 'historicalrange':?> + <!-- not implemented --> + <?php break; + case 'singleselectvalue':?> + <!-- not implemented --> + <?php break; + } ?> + <?php endforeach; ?> + </fieldset> +<?php endif; ?> +<?php if (isset($this->dateRangeLimit)): ?> + <fieldset class="eds"> + <legend><?=$this->transEsc('adv_search_year')?></legend> + <input type="hidden" name="daterange[]" value="PublicationDate"/> + <div class="date-fields"> + <div class="date-from"> + <label for="PublicationDatefrom"><?=$this->transEsc('date_from')?>:</label> + <input type="text" name="PublicationDatefrom" id="PublicationDatefrom" value="<?=$this->escapeHtmlAttr($this->dateRangeLimit[0])?>" class="form-control"/> + </div> + <div class="date-to"> + <label for="PublicationDateto"><?=$this->transEsc('date_to')?>:</label> + <input type="text" name="PublicationDateto" id="PublicationDateto" value="<?=$this->escapeHtmlAttr($this->dateRangeLimit[1])?>" class="form-control"/> + </div> + </div> + <div class="slider-container"> + <input type="text" id="PublicationDateSlider"> + </div> + <?php + $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); + $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); + $min = !empty($current['values'][0]) ? min($current['values'][0], 1400) : 1400; + $future = date('Y', time() + 31536000); + $max = !empty($current['values'][1]) ? max($future, $current['values'][1]) : $future; + $low = !empty($current['values'][0]) ? $current['values'][0] : $min; + $high = !empty($current['values'][1]) ? $current['values'][1] : $max; + $min = intval($min); + $max = intval($max); + $low = intval($low); + $high = intval($high); + $init = !empty($current['values'][0]) ? 'fillTexts()' : ''; + $script = <<<JS +$(document).ready(function() { + var fillTexts = function() { + var v = PublicationDateSlider.getValue(); + $('#PublicationDatefrom').val(v[0]); + $('#PublicationDateto').val(v[1]); + }; + var PublicationDateSlider = $('#PublicationDateSlider') + .slider({ + 'min':{$min}, + 'max':{$max}, + 'handle':"square", + 'tooltip':"hide", + 'value':[{$low},{$high}] + }) + .on('change', fillTexts) + .data('slider'); + {$init} +}); +JS; + ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> + </fieldset> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/advanced/layout.phtml b/themes/bootstrap3/templates/search/advanced/layout.phtml index 0937a28a066621c8bca05b5b43836d5906902608..87476abe8cb7e29b2e421b703b8d19a6cd5ded91 100644 --- a/themes/bootstrap3/templates/search/advanced/layout.phtml +++ b/themes/bootstrap3/templates/search/advanced/layout.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Advanced Search')); @@ -26,7 +26,7 @@ $hiddenFilters = $this->saved->getParams()->getHiddenFilters(); } else { $hasDefaultsApplied = $searchDetails = $searchFilters = $groups = false; - $hiddenFilters = $this->searchtabs()->getHiddenFilters($this->searchClassId, true); + $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, true); } // Step 1: Load the javascript @@ -46,7 +46,7 @@ $setGroupCount = 0; $setQueries = []; if (isset($searchDetails) && is_object($searchDetails)) { - foreach ($searchDetails->getQueries() as $group=>$searchGroup) { + foreach ($searchDetails->getQueries() as $group => $searchGroup) { $setSearchGroups[$group] = $searchGroup->isNegated() ? 'NOT' : $searchGroup->getOperator(); if ($setGroupCount < $group) { $setGroupCount = $group; @@ -64,23 +64,23 @@ <?=$this->flashmessages()?> <div role="search"> <form name="searchForm" id="advSearchForm" method="get" action="<?=$this->url($this->options->getSearchAction())?>"> - <? foreach ($hiddenFilters as $key => $filter): ?> - <? foreach ($filter as $value): ?> + <?php foreach ($hiddenFilters as $key => $filter): ?> + <?php foreach ($filter as $value): ?> <input type="hidden" name="hiddenFilters[]" value="<?=$this->escapeHtmlAttr($key) . ':' . $this->escapeHtmlAttr($value)?>" /> - <? endforeach; ?> - <? endforeach; ?> + <?php endforeach; ?> + <?php endforeach; ?> <div class="<?=$this->layoutClass('mainbody')?>"> - <? $lastSort = $this->searchMemory()->getLastSort($this->searchClassId); ?> - <? if (!empty($lastSort)): ?> + <?php $lastSort = $this->searchMemory()->getLastSort($this->searchClassId); ?> + <?php if (!empty($lastSort)): ?> <input type="hidden" name="sort" value="<?=$this->escapeHtmlAttr($lastSort)?>" /> - <? endif; ?> + <?php endif; ?> <div class="clearfix"> <h2 class="pull-left flip"><?=$this->transEsc('Advanced Search')?></h2> <div id="groupJoin" class="form-inline pull-right flip"> <label for="groupJoinOptions"><?=$this->transEsc("search_match")?>:</label> <select id="groupJoinOptions" name="join" class="form-control"> - <option value="AND"<? if($searchDetails && $searchDetails->getOperator()=='ALL'):?> selected<?endif?>><?= $this->transEsc('group_AND') ?></option> - <option value="OR"<? if($searchDetails && $searchDetails->getOperator()=='OR'):?> selected<?endif?>><?= $this->transEsc('group_OR') ?></option> + <option value="AND"<?php if($searchDetails && $searchDetails->getOperator() == 'ALL'):?> selected<?php endif; ?>><?= $this->transEsc('group_AND') ?></option> + <option value="OR"<?php if($searchDetails && $searchDetails->getOperator() == 'OR'):?> selected<?php endif; ?>><?= $this->transEsc('group_OR') ?></option> </select> </div> </div> @@ -88,61 +88,61 @@ <i class="fa fa-plus-circle" aria-hidden="true"></i> <a href="#" onClick="addGroup();return false"><?= $this->transEsc('add_search_group') ?></a> </span> - <? /* fallback to a fixed set of search groups/fields if JavaScript is turned off */ ?> + <?php /* fallback to a fixed set of search groups/fields if JavaScript is turned off */ ?> <div class="no-js"> - <? if(!empty($this->formOverride)): ?> + <?php if(!empty($this->formOverride)): ?> <?=$this->formOverride ?> - <? else: ?> - <? for($group=0 ; $group<3 || $group<=$setGroupCount ; $group++): ?> - <? if($group == 0): ?> + <?php else: ?> + <?php for($group = 0; $group < 3 || $group <= $setGroupCount; $group++): ?> + <?php if($group == 0): ?> <div id="new_group_template"> - <? endif; ?> + <?php endif; ?> <div id="group<?=$group ?>" class="adv-group"> <div class="adv-group-terms"> <label class="adv-group-label"><?=$this->transEsc("adv_search_label")?>:</label> - <? for($search=0 ; $search<3 || (isset($setQueries[$group]) && $search<count($setQueries[$group])) ; $search++): ?> - <? if($group == 0 && $search == 0): ?> + <?php for($search = 0; $search < 3 || (isset($setQueries[$group]) && $search < count($setQueries[$group])); $search++): ?> + <?php if($group == 0 && $search == 0): ?> <div id="new_search_template"> - <? endif; ?> - <div id="search<?=$group.'_'.$search ?>" class="adv-search"> - <input name="lookfor<?=$group ?>[]" id="search_lookfor<?=$group.'_'.$search ?>" class="adv-term-input form-control" type="text"<?if(isset($setQueries[$group][$search])):?> value="<?=$this->escapeHtml($setQueries[$group][$search]->getString())?>"<?endif;?>> + <?php endif; ?> + <div id="search<?=$group . '_' . $search ?>" class="adv-search"> + <input name="lookfor<?=$group ?>[]" id="search_lookfor<?=$group . '_' . $search ?>" class="adv-term-input form-control" type="text"<?php if (isset($setQueries[$group][$search])): ?> value="<?=$this->escapeHtml($setQueries[$group][$search]->getString())?>"<?php endif; ?>> <select class="adv-term-type form-control" name="type<?=$group ?>[]"> - <? foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> - <option value="<?=$this->escapeHtml($searchVal)?>"<?if(isset($setQueries[$group][$search]) && $searchVal == $setQueries[$group][$search]->getHandler()):?> selected<?endif;?>><?=$this->transEsc($searchDesc)?></option> - <? endforeach; ?> + <?php foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> + <option value="<?=$this->escapeHtml($searchVal)?>"<?php if (isset($setQueries[$group][$search]) && $searchVal == $setQueries[$group][$search]->getHandler()): ?> selected<?php endif; ?>><?=$this->transEsc($searchDesc)?></option> + <?php endforeach; ?> </select> <a href="#" class="adv-term-remove hidden">×</a> </div> - <? if($group == 0 && $search == 0): ?> + <?php if($group == 0 && $search == 0): ?> </div> <span class="float-left"> <i class="fa fa-plus-circle search_place_holder hidden" aria-hidden="true"></i> <a href="#" class="add_search_link hidden"><?=$this->transEsc("add_search")?></a> </span> - <? endif; ?> - <? endfor; ?> + <?php endif; ?> + <?php endfor; ?> </div> <div class="adv-group-match"> <label class="search_bool"><?=$this->transEsc("search_match")?>: </label> <select name="bool<?=$group ?>[]" id="search_bool<?=$group ?>" class="form-control"> - <option value="AND"<? if(isset($setSearchGroups[$group]) && 'AND' == $setSearchGroups[$group]):?> selected<?endif;?>><?=$this->transEsc("search_AND")?></option> - <option value="OR"<? if(isset($setSearchGroups[$group]) && 'OR' == $setSearchGroups[$group]):?> selected<?endif;?>><?=$this->transEsc("search_OR")?></option> - <option value="NOT"<? if(isset($setSearchGroups[$group]) && 'NOT' == $setSearchGroups[$group]):?> selected<?endif;?>><?=$this->transEsc("search_NOT")?></option> + <option value="AND"<?php if(isset($setSearchGroups[$group]) && 'AND' == $setSearchGroups[$group]):?> selected<?php endif; ?>><?=$this->transEsc("search_AND")?></option> + <option value="OR"<?php if(isset($setSearchGroups[$group]) && 'OR' == $setSearchGroups[$group]):?> selected<?php endif; ?>><?=$this->transEsc("search_OR")?></option> + <option value="NOT"<?php if(isset($setSearchGroups[$group]) && 'NOT' == $setSearchGroups[$group]):?> selected<?php endif; ?>><?=$this->transEsc("search_NOT")?></option> </select> </div> <a href="#" class="adv-group-close hidden"><i class="fa fa-close"></i> <?=$this->transEsc("del_search")?></a> </div> - <? if($group == 0): ?> + <?php if($group == 0): ?> </div> - <? endif; ?> - <? endfor; ?> - <? endif; ?> + <?php endif; ?> + <?php endfor; ?> + <?php endif; ?> </div> <div class="adv-submit"> <input class="clear-btn btn btn-default" type="button" value="<?= $this->transEsc('Clear')?>"> <input class="btn btn-primary" type="submit" value="<?= $this->transEsc('Find')?>"> </div> - <? if (isset($this->extraAdvancedControls)): ?> + <?php if (isset($this->extraAdvancedControls)): ?> <div class="clearfix"> <?=$this->extraAdvancedControls ?> </div> @@ -150,14 +150,14 @@ <input class="clear-btn btn btn-default" type="button" value="<?= $this->transEsc('Clear')?>"> <input class="btn btn-primary" type="submit" value="<?= $this->transEsc('Find')?>"> </div> - <? endif; ?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>"> - <? if ($hasDefaultsApplied): ?> + <?php if ($hasDefaultsApplied): ?> <input type="hidden" name="dfApplied" value="1" /> - <? endif ?> - <? if (!empty($searchFilters)): ?> + <?php endif ?> + <?php if (!empty($searchFilters)): ?> <h4><?=$this->transEsc("adv_search_filters")?></h4> <div class="facet-group"> <label class="checkbox"> @@ -165,15 +165,15 @@ <?=$this->transEsc("adv_search_select_all")?> </label> </div> - <? foreach ($searchFilters as $field => $data): ?> + <?php foreach ($searchFilters as $field => $data): ?> <div class="facet-group"> <div class="title"><?=$this->transEsc($field)?></div> - <? foreach ($data as $value): ?> + <?php foreach ($data as $value): ?> <label class="facet checkbox"><input class="checkbox-select-item" type="checkbox" checked="checked" name="filter[]" value='<?=$this->escapeHtmlAttr($value['field'])?>:"<?=$this->escapeHtmlAttr($value['value'])?>"' /> <?=$this->escapeHtml($value['displayText'])?></label> - <? endforeach; ?> + <?php endforeach; ?> </div> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> <h4><?=$this->transEsc("Search Tips")?></h4> <div class="facet-group"> <a class="facet help-link" data-lightbox href="<?=$this->url('help-home')?>?topic=advsearch&_=<?=time() ?>"><?=$this->transEsc("Help with Advanced Search")?></a> diff --git a/themes/bootstrap3/templates/search/advanced/limit.phtml b/themes/bootstrap3/templates/search/advanced/limit.phtml index 29a25960d9c22a24427fb7913f303f52521cd7a9..cefe0802f3280d6b7bd857276317a887ae333806 100644 --- a/themes/bootstrap3/templates/search/advanced/limit.phtml +++ b/themes/bootstrap3/templates/search/advanced/limit.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up convenience variables: $limitList = $this->options->getLimitOptions(); @@ -6,13 +6,13 @@ $lastLimit = $this->searchMemory()->getLastLimit($this->options->getSearchClassId()); $defaultLimit = empty($lastLimit) ? $this->options->getDefaultLimit() : $lastLimit; ?> -<? if (count($limitList) > 1): ?> +<?php if (count($limitList) > 1): ?> <fieldset class="limits"> <legend><?=$this->transEsc('Results per page')?></legend> <select id="limit" name="limit" class="form-control"> - <? foreach ($limitList as $limitVal): ?> + <?php foreach ($limitList as $limitVal): ?> <option value="<?=$this->escapeHtmlAttr($limitVal)?>" <?=($limitVal == $defaultLimit) ? 'selected="selected"' : ''?>><?=$this->escapeHtml($limitVal)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </fieldset> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/advanced/ranges.phtml b/themes/bootstrap3/templates/search/advanced/ranges.phtml index ba22ac62184725654e18728fadaeca07f590b1bf..7f51cb4226234625d646db30d61487101544606b 100644 --- a/themes/bootstrap3/templates/search/advanced/ranges.phtml +++ b/themes/bootstrap3/templates/search/advanced/ranges.phtml @@ -1,7 +1,7 @@ -<? if (isset($this->ranges) && !empty($this->ranges)): ?> - <? $params = $this->searchParams($this->searchClassId); $params->activateAllFacets(); ?> - <? foreach ($this->ranges as $current): $escField = $this->escapeHtmlAttr($current['field']); ?> - <? $extraInputAttribs = ($current['type'] == 'date') ? 'maxlength="4" ' : ''; ?> +<?php if (isset($this->ranges) && !empty($this->ranges)): ?> + <?php $params = $this->searchParams($this->searchClassId); $params->activateAllFacets(); ?> + <?php foreach ($this->ranges as $current): $escField = $this->escapeHtmlAttr($current['field']); ?> + <?php $extraInputAttribs = ($current['type'] == 'date') ? 'maxlength="4" ' : ''; ?> <fieldset class="range"> <legend><?=$this->transEsc($params->getFacetLabel($current['field']))?></legend> <input type="hidden" name="<?=$this->escapeHtmlAttr($current['type'])?>range[]" value="<?=$escField?>"/> @@ -15,17 +15,17 @@ <input type="text" name="<?=$escField?>to" id="<?=$escField?>to" value="<?=isset($current['values'][1])?$this->escapeHtmlAttr($current['values'][1]):''?>" class="form-control" <?=$extraInputAttribs?>/> </div> </div> - <? if ($current['type'] == 'date'): ?> + <?php if ($current['type'] == 'date'): ?> <div class="slider-container"> <input type="text" id="<?=$escField?><?=$this->escapeHtmlAttr($current['type'])?>Slider"> </div> - <? + <?php $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); $min = !empty($current['values'][0]) ? min($current['values'][0], 1400) : 1400; - $future = date('Y', time()+31536000); + $future = date('Y', time() + 31536000); $max = !empty($current['values'][1]) ? max($future, $current['values'][1]) : $future; - $low = !empty($current['values'][0]) ? $current['values'][0] : $min; + $low = !empty($current['values'][0]) ? $current['values'][0] : $min; $high = !empty($current['values'][1]) ? $current['values'][1] : $max; $min = intval($min); $max = intval($max); @@ -67,7 +67,7 @@ $('#{$escField}from, #{$escField}to').change(function () { JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> - <? endif; ?> + <?php endif; ?> </fieldset> - <? endforeach; ?> -<? endif; ?> + <?php endforeach; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/advanced/solr.phtml b/themes/bootstrap3/templates/search/advanced/solr.phtml index d6bd4fe422b013eff38dd92690e774c7a2c369d1..a305515e0f22e130760ee1154269db8ef7605e2c 100644 --- a/themes/bootstrap3/templates/search/advanced/solr.phtml +++ b/themes/bootstrap3/templates/search/advanced/solr.phtml @@ -1,25 +1,25 @@ -<? if (!empty($this->facetList) || !empty($this->checkboxFacets)): ?> +<?php if (!empty($this->facetList) || !empty($this->checkboxFacets)): ?> <fieldset class="solr-facets"> <legend><?=$this->transEsc('Limit To')?></legend> - <? if (!empty($this->checkboxFacets)): ?> + <?php if (!empty($this->checkboxFacets)): ?> <?=$this->render('search/advanced/checkbox-filters.phtml')?> - <? endif; ?> + <?php endif; ?> <div class="solr-facet-container"> - <? foreach ($this->facetList as $field => $list): ?> + <?php foreach ($this->facetList as $field => $list): ?> <div class="solr-adv-facet"> <label for="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '', $field))?>"><?=$this->transEsc($list['label'])?>:</label> <select class="form-control" id="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '', $field))?>" name="filter[]" multiple="multiple" size="10"> - <? if (is_array($this->hierarchicalFacets) && in_array($field, $this->hierarchicalFacets)): ?> - <? foreach ($list['list'] as $value): ?> - <? $display = str_pad('', 4 * $value['level'] * 6, ' ', STR_PAD_LEFT) . $this->escapeHtml($value['displayText']); ?> + <?php if (is_array($this->hierarchicalFacets) && in_array($field, $this->hierarchicalFacets)): ?> + <?php foreach ($list['list'] as $value): ?> + <?php $display = str_pad('', 4 * $value['level'] * 6, ' ', STR_PAD_LEFT) . $this->escapeHtml($value['displayText']); ?> <option value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected'])?' selected="selected"':''?>><?=$display?></option> - <? endforeach; ?> - <? else: ?> - <? + <?php endforeach; ?> + <?php else: ?> + <?php // Sort the current facet list alphabetically; we'll use this data // along with the foreach below to display facet options in the // correct order. - $sorted = array(); + $sorted = []; foreach ($list['list'] as $i => $value) { if (!empty($value['displayText'])) { $sorted[$i] = $value['displayText']; @@ -27,25 +27,25 @@ } natcasesort($sorted); ?> - <? foreach ($sorted as $i => $display): ?> - <? $value = $list['list'][$i]; ?> + <?php foreach ($sorted as $i => $display): ?> + <?php $value = $list['list'][$i]; ?> <option value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected'])?' selected="selected"':''?>><?=$this->escapeHtml($display)?></option> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </select> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> </fieldset> -<? endif; ?> -<? if (isset($this->illustratedLimit)): ?> +<?php endif; ?> +<?php if (isset($this->illustratedLimit)): ?> <fieldset class="solr"> <legend><?=$this->transEsc("Illustrated")?>:</legend> - <? foreach ($this->illustratedLimit as $current): ?> + <?php foreach ($this->illustratedLimit as $current): ?> <input id="illustrated_<?=$this->escapeHtmlAttr($current['value'])?>" type="radio" name="illustration" value="<?=$this->escapeHtmlAttr($current['value'])?>"<?=$current['selected']?' checked="checked"':''?>/> <label for="illustrated_<?=$this->escapeHtmlAttr($current['value'])?>"><?=$this->transEsc($current['text'])?></label><br/> - <? endforeach; ?> + <?php endforeach; ?> </fieldset> -<? endif; ?> +<?php endif; ?> <?=$this->render('search/advanced/limit.phtml')?> <?=$this->render('search/advanced/ranges.phtml')?> diff --git a/themes/bootstrap3/templates/search/advanced/summon.phtml b/themes/bootstrap3/templates/search/advanced/summon.phtml index 7851ce9d99f50bd4bb91d8eb76d3e6aa91247a93..ee16f075e8f21f2188c21b46f96a09ffef173743 100644 --- a/themes/bootstrap3/templates/search/advanced/summon.phtml +++ b/themes/bootstrap3/templates/search/advanced/summon.phtml @@ -1,31 +1,31 @@ -<? if (!empty($this->facetList) || !empty($this->checkboxFacets)): ?> +<?php if (!empty($this->facetList) || !empty($this->checkboxFacets)): ?> <fieldset class="summon-facets"> <legend><?=$this->transEsc('Limit To')?></legend> - <? if (!empty($this->checkboxFacets)): ?> + <?php if (!empty($this->checkboxFacets)): ?> <?=$this->render('search/advanced/checkbox-filters.phtml')?> - <? endif; ?> - <? foreach ($this->facetList as $field => $list): ?> + <?php endif; ?> + <?php foreach ($this->facetList as $field => $list): ?> <div class="facet-fieldset"> <label for="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '', $field))?>"><?=$this->transEsc($list['label'])?>:</label> <select class="form-control" id="limit_<?=$this->escapeHtmlAttr(str_replace(' ', '', $field))?>" name="filter[]" multiple="multiple" size="10"> - <? + <?php // Sort the current facet list alphabetically; we'll use this data // along with the foreach below to display facet options in the // correct order. - $sorted = array(); + $sorted = []; foreach ($list['list'] as $i => $value) { $sorted[$i] = $value['displayText']; } natcasesort($sorted); ?> - <? foreach ($sorted as $i => $display): ?> - <? $value = $list['list'][$i]; ?> + <?php foreach ($sorted as $i => $display): ?> + <?php $value = $list['list'][$i]; ?> <option value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected'])?' selected="selected"':''?>><?=$this->escapeHtml($display)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endforeach; ?> + <?php endforeach; ?> </fieldset> -<? endif; ?> +<?php endif; ?> <?=$this->render('search/advanced/limit.phtml')?> <?=$this->render('search/advanced/ranges.phtml')?> diff --git a/themes/bootstrap3/templates/search/bulk-action-buttons.phtml b/themes/bootstrap3/templates/search/bulk-action-buttons.phtml index d2d6f0306bfb98e34eee676b43c6228b0e187446..5955b7d278267de0c6bc04b5d0f34a6ed4d26da2 100644 --- a/themes/bootstrap3/templates/search/bulk-action-buttons.phtml +++ b/themes/bootstrap3/templates/search/bulk-action-buttons.phtml @@ -1,26 +1,26 @@ -<? if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> +<?php if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> <div class="bulkActionButtons hidden-print"> <div class="bulk-checkbox"> - <input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> + <input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> <label for="<?=$this->idPrefix?>addFormCheckboxSelectAll"> <?=$this->transEsc('select_page')?> | <?=$this->transEsc('with_selected')?>: </label> </div> <div class="btn-group"> - <? if (isset($this->showBulkOptions) && $this->showBulkOptions): ?> - <input id="ribbon-email" class="btn btn-default" type="submit" name="email" title="<?=$this->transEsc('bookbag_email_selected')?>" value="<?=$this->transEsc('Email')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> - <? $exportOptions = $this->export()->getBulkOptions(); if (count($exportOptions) > 0): ?> - <input id="ribbon-export" class="btn btn-default" type="submit" name="export" title="<?=$this->transEsc('bookbag_export_selected')?>" value="<?=$this->transEsc('Export')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> - <? endif; ?> - <input id="ribbon-print" class="btn btn-default" type="submit" name="print" title="<?=$this->transEsc('bookbag_print_selected')?>" value="<?=$this->transEsc('Print')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> - <? if ($this->userlist()->getMode() !== 'disabled'): ?> - <input id="ribbon-save" class="btn btn-default" type="submit" name="saveCart" title="<?=$this->transEsc('bookbag_save_selected')?>" value="<?=$this->transEsc('Save')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> - <? endif; ?> - <? endif; ?> - <? if (isset($this->showCartControls) && $this->showCartControls): ?> - <input id="<?=$this->idPrefix?>updateCart" type="submit" class="btn btn-default" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"<?if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<? endif; ?>/> - <? endif; ?> + <?php if (isset($this->showBulkOptions) && $this->showBulkOptions): ?> + <input id="ribbon-email" class="btn btn-default" type="submit" name="email" title="<?=$this->transEsc('bookbag_email_selected')?>" value="<?=$this->transEsc('Email')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <?php $exportOptions = $this->export()->getBulkOptions(); if (count($exportOptions) > 0): ?> + <input id="ribbon-export" class="btn btn-default" type="submit" name="export" title="<?=$this->transEsc('bookbag_export_selected')?>" value="<?=$this->transEsc('Export')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <?php endif; ?> + <input id="ribbon-print" class="btn btn-default" type="submit" name="print" title="<?=$this->transEsc('bookbag_print_selected')?>" value="<?=$this->transEsc('Print')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> + <input id="ribbon-save" class="btn btn-default" type="submit" name="saveCart" title="<?=$this->transEsc('bookbag_save_selected')?>" value="<?=$this->transEsc('Save')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <?php endif; ?> + <?php endif; ?> + <?php if (isset($this->showCartControls) && $this->showCartControls): ?> + <input id="<?=$this->idPrefix?>updateCart" type="submit" class="btn btn-default" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <?php endif; ?> </div> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/controls/limit.phtml b/themes/bootstrap3/templates/search/controls/limit.phtml index 6d691ecfb46f0506c7c908bcef1f32a92f5f92dd..b322fbac571f6592be8ea5f3d90f9253085740d9 100644 --- a/themes/bootstrap3/templates/search/controls/limit.phtml +++ b/themes/bootstrap3/templates/search/controls/limit.phtml @@ -1,12 +1,12 @@ -<? $limitList = $this->params->getLimitList(); ?> -<? if (count($limitList) > 1): ?> +<?php $limitList = $this->params->getLimitList(); ?> +<?php if (count($limitList) > 1): ?> <form class="form-inline" action="<?=$this->currentPath() . $this->results->getUrlQuery()->setLimit(null)?>" method="post"> <label for="limit"><?=$this->transEsc('Results per page')?></label> <select id="limit" name="limit" class="jumpMenu form-control"> - <? foreach ($limitList as $limitVal => $limitData): ?> + <?php foreach ($limitList as $limitVal => $limitData): ?> <option value="<?=$this->escapeHtmlAttr($limitVal)?>"<?=$limitData['selected']?' selected="selected"':''?>><?=$this->escapeHtml($limitData['desc'])?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <noscript><input type="submit" value="<?=$this->transEsc("Set")?>" /></noscript> </form> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/controls/showing.phtml b/themes/bootstrap3/templates/search/controls/showing.phtml index 6461dc1260b3efa8dea7c6a745ad39eeed957085..b0ae18b0c9433f12e7a459e427635832d655552b 100644 --- a/themes/bootstrap3/templates/search/controls/showing.phtml +++ b/themes/bootstrap3/templates/search/controls/showing.phtml @@ -1,28 +1,28 @@ -<? +<?php $transParams = [ '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), - '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), + '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), '%%total%%' => $this->localizedNumber($this->recordTotal), '%%lookfor%%' => $this->escapeHtml($this->lookfor) ]; ?> -<? if (!isset($this->skipTotalCount)): ?> - <? $showingResults = $this->translate('showing_results_of_html', $transParams); ?> -<? else: ?> - <? $showingResults = $this->translate('showing_results_html', $transParams); ?> -<? endif; ?> -<? if (isset($this->overrideSearchHeading)): ?> - <? $showingResults .= ' ' . $this->overrideSearchHeading; ?> -<? elseif ($this->params->getSearchType() == 'basic'): ?> - <? if (!isset($this->skipTotalCount)): ?> - <? $showingResults = $this->translate('showing_results_of_for_html', $transParams); ?> - <? else: ?> - <? $showingResults = $this->translate('showing_results_for_html', $transParams); ?> - <? endif; ?> -<? endif; ?> -<? $this->layout()->srmessage = $showingResults; ?> -<? if ($qtime = $this->results->getQuerySpeed()): ?> - <?=$showingResults; ?>, <?=$this->transEsc('query time')?>: <?=$this->localizedNumber($qtime, 2).$this->transEsc('seconds_abbrev')?> -<? else: ?> +<?php if (!isset($this->skipTotalCount)): ?> + <?php $showingResults = $this->translate('showing_results_of_html', $transParams); ?> +<?php else: ?> + <?php $showingResults = $this->translate('showing_results_html', $transParams); ?> +<?php endif; ?> +<?php if (isset($this->overrideSearchHeading)): ?> + <?php $showingResults .= ' ' . $this->overrideSearchHeading; ?> +<?php elseif ($this->params->getSearchType() == 'basic'): ?> + <?php if (!isset($this->skipTotalCount)): ?> + <?php $showingResults = $this->translate('showing_results_of_for_html', $transParams); ?> + <?php else: ?> + <?php $showingResults = $this->translate('showing_results_for_html', $transParams); ?> + <?php endif; ?> +<?php endif; ?> +<?php $this->layout()->srmessage = $showingResults; ?> +<?php if ($qtime = $this->results->getQuerySpeed()): ?> + <?=$showingResults; ?><span class="search-query-time">, <?=$this->transEsc('query time')?>: <?=$this->localizedNumber($qtime, 2) . $this->transEsc('seconds_abbrev')?></span> +<?php else: ?> <?=$showingResults; ?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/controls/sort.phtml b/themes/bootstrap3/templates/search/controls/sort.phtml index 790b56fe92fa5aa31e84265bcf308f939e49bc52..51e0d4f4c7a5f4967854e8df21f99a9c0ea492cb 100644 --- a/themes/bootstrap3/templates/search/controls/sort.phtml +++ b/themes/bootstrap3/templates/search/controls/sort.phtml @@ -1,12 +1,12 @@ -<? $list = $this->params->getSortList(); if (!empty($list)): ?> +<?php $list = $this->params->getSortList(); if (!empty($list)): ?> <form class="search-sort" action="<?=$this->currentPath()?>" method="get" name="sort"> - <?=$this->results->getUrlQuery()->asHiddenFields(array('sort' => '/.*/'));?> + <?=$this->results->getUrlQuery()->asHiddenFields(['sort' => '/.*/']);?> <label for="sort_options_1"><?=$this->transEsc('Sort')?></label> <select id="sort_options_1" name="sort" class="jumpMenu form-control"> - <? foreach ($list as $sortType => $sortData): ?> + <?php foreach ($list as $sortType => $sortData): ?> <option value="<?=$this->escapeHtmlAttr($sortType)?>"<?=$sortData['selected']?' selected="selected"':''?>><?=$this->transEsc($sortData['desc'])?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <noscript><input type="submit" class="btn btn-default" value="<?=$this->transEsc("Set")?>" /></noscript> </form> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/controls/view.phtml b/themes/bootstrap3/templates/search/controls/view.phtml index 0d8fef0da6b2bdbe4af00211c33d472397e0fcd3..82844b9da909821c99b54549f767f7a18cda8aa6 100644 --- a/themes/bootstrap3/templates/search/controls/view.phtml +++ b/themes/bootstrap3/templates/search/controls/view.phtml @@ -1,20 +1,20 @@ <div class="view-buttons hidden-xs"> - <? $viewList = $this->params->getViewList(); if (count($viewList) > 1): ?> - <? foreach ($viewList as $viewType => $viewData): ?> - <? $viewDesc = $this->translate($viewData['desc']); ?> - <? if (!$viewData['selected']): ?> + <?php $viewList = $this->params->getViewList(); if (count($viewList) > 1): ?> + <?php foreach ($viewList as $viewType => $viewData): ?> + <?php $viewDesc = $this->translate($viewData['desc']); ?> + <?php if (!$viewData['selected']): ?> <a href="<?=$this->results->getUrlQuery()->setViewParam($viewType)?>" title="<?=$this->escapeHtmlAttr($this->translate('switch_view', ['%%view%%' => $viewDesc]))?>" > - <? else: ?> + <?php else: ?> <span title="<?=$this->escapeHtmlAttr($this->translate('view_already_selected', ['%%current%%' => $viewDesc])) ?>"> - <? endif; ?> + <?php endif; ?> <i class="fa fa-<?=$viewType ?>" alt="<?=$this->escapeHtmlAttr($viewDesc)?>"></i> <?=$viewDesc ?> - <? if (!$viewData['selected']): ?> + <?php if (!$viewData['selected']): ?> </a> - <? else: ?> + <?php else: ?> </span> - <? endif; ?> + <?php endif; ?> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> </div> diff --git a/themes/bootstrap3/templates/search/email.phtml b/themes/bootstrap3/templates/search/email.phtml index 9894222f5454f448abe32fceecbf8e2430093f85..f2f80a9fd2922e3564de6a0e1c1de7add7ab5894 100644 --- a/themes/bootstrap3/templates/search/email.phtml +++ b/themes/bootstrap3/templates/search/email.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Email this Search')); diff --git a/themes/bootstrap3/templates/search/facet-list.phtml b/themes/bootstrap3/templates/search/facet-list.phtml index 5b87e7d9872f521c2a2a2beec73c626980dae07f..b3bae437d46924450ecd9b49b5e1b8b28232a90c 100644 --- a/themes/bootstrap3/templates/search/facet-list.phtml +++ b/themes/bootstrap3/templates/search/facet-list.phtml @@ -1,75 +1,53 @@ -<? - $options = $this->searchOptions($this->searchClassId); +<?php + $options = $this->results->getParams()->getOptions(); $facetLightbox = $options->getFacetListAction(); if (empty($this->sortOptions)) { $this->sort = 'default'; $this->sortOptions = [ 'default' => 'default' ]; } $urlBase = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&facet=' . urlencode($this->facet) . '&facetexclude=' . $this->exclude . '&facetop=' . $this->operator; + $searchAction = $this->url($options->getSearchAction()); + if (!empty($this->baseUriExtra)) { + $searchAction .= urlencode($this->baseUriExtra); + $urlBase .= '&baseUriExtra=' . urlencode($this->baseUriExtra); + } ?> <h2><?=$this->transEsc($this->facetLabel) ?></h2> -<? if (count($this->sortOptions) > 1): ?> +<?php if (count($this->sortOptions) > 1): ?> <div class="full-facet-sort-options"> <label><?=$this->translate('Sort') ?></label> <div class="btn-group"> - <? foreach ($this->sortOptions as $key=>$sort): ?> - <a href="<?=$urlBase . '&facetpage=1&facetsort=' . urlencode($key) ?>" class="btn btn-default js-facet-sort<? if($this->sort == $key): ?> active<? endif; ?>" data-sort="<?=$key ?>" data-lightbox-ignore><?=$this->translate($sort) ?></a> - <? endforeach; ?> + <?php foreach ($this->sortOptions as $key => $sort): ?> + <a href="<?=$urlBase . '&facetpage=1&facetsort=' . urlencode($key) ?>" class="btn btn-default js-facet-sort<?php if($this->sort == $key): ?> active<?php endif; ?>" data-sort="<?=$key ?>" data-lightbox-ignore><?=$this->translate($sort) ?></a> + <?php endforeach; ?> </div> </div> -<? endif; ?> +<?php endif; ?> <div class="lightbox-scroll full-facets"> - <? foreach ($this->sortOptions as $key=>$sort): ?> - <? $active = $this->sort == $key; ?> - <div class="full-facet-list facet-group<? if(!$active): ?> hidden<? endif; ?>" id="facet-list-<?=$this->escapeHtmlAttr($key) ?>"> - <? if ($active): ?> - <? if ($this->page > 1): ?> - <a href="<?=$urlBase . '&facetpage=' . ($this->page-1) . '&facetsort=' . $this->sort ?>" class="facet js-facet-prev-page" data-page="<?=($this->page+1) ?>" data-sort="<?=$this->sort ?>" data-limit="<?=count($this->data) ?>" data-lightbox-ignore><?=$this->translate('prev') ?> ...</a> - <? endif; ?> - <? foreach ($this->data as $item): ?> - <? $toggleUrl = $item['isApplied'] - ? $this->url($options->getSearchAction()) . $this->results->getUrlQuery()->removeFacet($this->facet, $item['value'], $item['operator']) - : $this->url($options->getSearchAction()) . $this->results->getUrlQuery()->addFacet($this->facet, $item['value'], $item['operator']) - ?> - <? $subLinks = $this->exclude && !$item['isApplied']; ?> - <? if ($subLinks): ?> - <li class="facet js-facet-item"> - <a href="<?=$toggleUrl ?>" data-lightbox-ignore data-title="<?=$this->escapeHtmlAttr($item['displayText']) ?>" data-count="<?=$item['count'] ?>"<? if($item['isApplied']): ?> title="<?=$this->transEsc('applied_filter') ?>"<? endif;?>> - <? else: ?> - <a href="<?=$toggleUrl ?>" data-lightbox-ignore class="facet js-facet-item<? if($item['isApplied']): ?> active<?endif;?>" data-title="<?=$this->escapeHtmlAttr($item['displayText']) ?>" data-count="<?=$item['count'] ?>"<? if($item['isApplied']): ?> title="<?=$this->transEsc('applied_filter') ?>"<? endif;?>> - <? endif; ?> - <? if (!empty($item['displayText'])): ?> - <?=$this->escapeHtml($item['displayText']) ?> - <? elseif (!empty($item['value'])): ?> - <?=$this->escapeHtml($item['value']) ?> - <? else: // prevent empty value inside <a> tag ?> - - - <? endif; ?> - <? if ($subLinks): ?> - </a> - <? endif; ?> - <? if($item['isApplied']): ?> - <? if ($item['operator'] == 'OR'): ?> - <i class="fa fa-check-square-o" aria-hidden="true"></i> - <? else: ?> - <i class="fa fa-check" aria-hidden="true"></i> - <? endif; ?> - <? else: ?> - <span class="badge"> - <?=$this->localizedNumber($item['count']) ?> - <? if ($this->exclude): ?> - <a href="<?=$this->url($options->getSearchAction()) . $this->results->getUrlQuery()->addFacet($this->facet, $item['value'], 'NOT') ?>" title="<?=$this->transEsc('exclude_facet') ?>" data-lightbox-ignore><i class="fa fa-times" aria-hidden="true"></i></a> - <? endif; ?> - </span> - <? endif; ?> - <?=$subLinks ? '</li>' : '</a>'; ?> - <? endforeach; ?> - <? endif; ?> - <? if ($this->anotherPage): ?> - <a href="<?=$urlBase . '&facetpage=' . ($this->page+1) . '&facetsort=' . urlencode($key) ?>" class="facet js-facet-next-page" data-page="<?=($this->page+1) ?>" data-sort="<?=$this->escapeHtmlAttr($key) ?>" data-lightbox-ignore><?=$this->translate('more') ?> ...</a> - <? endif; ?> + <?php foreach ($this->sortOptions as $key => $sort): ?> + <?php $active = $this->sort == $key; ?> + <div class="full-facet-list facet-group<?php if(!$active): ?> hidden<?php endif; ?>" id="facet-list-<?=$this->escapeHtmlAttr($key) ?>"> + <?php if ($active): ?> + <?php if ($this->page > 1): ?> + <a href="<?=$urlBase . '&facetpage=' . ($this->page - 1) . '&facetsort=' . $this->sort ?>" class="facet js-facet-prev-page" data-page="<?=($this->page + 1) ?>" data-sort="<?=$this->sort ?>" data-limit="<?=count($this->data) ?>" data-lightbox-ignore><?=$this->translate('prev') ?> ...</a> + <?php endif; ?> + <?php foreach ($this->data as $item): ?> + <?=$this->render('Recommend/SideFacets/single-facet.phtml', [ + 'exclude' => $this->exclude, + 'facet' => $item, + 'group' => $this->facet, + 'url' => $this->results->getUrlQuery(), + 'urlBase' => $searchAction + ]) ?> + <?php endforeach; ?> + <?php endif; ?> + <?php if ($this->anotherPage): ?> + <a href="<?=$urlBase . '&facetpage=' . ($this->page + 1) . '&facetsort=' . urlencode($key) ?>" class="facet js-facet-next-page" data-page="<?=($this->page + 1) ?>" data-sort="<?=$this->escapeHtmlAttr($key) ?>" data-lightbox-ignore> + <span class="text"><?=$this->translate('more') ?> ...</span> + </a> + <?php endif; ?> </div> - <? endforeach; ?> + <?php endforeach; ?> </div> <button class="btn btn-default lightbox-only" data-dismiss="modal"><?=$this->translate('close') ?></button> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '(typeof VuFind.lightbox_facets !== "undefined") && VuFind.lightbox_facets.setup();', 'SET'); ?> diff --git a/themes/bootstrap3/templates/search/history-table.phtml b/themes/bootstrap3/templates/search/history-table.phtml index a916c37c7c99dccb7af0a18cd8af6f9da0e4adfb..fcb620133173d446cbbfbb605a867dcd4dfd75cd 100644 --- a/themes/bootstrap3/templates/search/history-table.phtml +++ b/themes/bootstrap3/templates/search/history-table.phtml @@ -1,45 +1,45 @@ -<? $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; ?> +<?php $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; ?> <table class="table table-striped"> <tr> <th width="20%"><?=$this->transEsc("history_time")?></th> <th><?=$this->transEsc("history_search")?></th> <th><?=$this->transEsc("history_limits")?></th> <th><?=$this->transEsc("history_results")?></th> - <? if ($saveSupported): ?><th><?=$this->transEsc($this->showSaved ? "history_delete" : "history_save")?></th><? endif; ?> + <?php if ($saveSupported): ?><th><?=$this->transEsc($this->showSaved ? "history_delete" : "history_save")?></th><?php endif; ?> </tr> - <? foreach (($this->showSaved ? array_reverse($this->saved) : array_reverse($this->unsaved)) as $iteration => $info): ?> + <?php foreach (($this->showSaved ? array_reverse($this->saved) : array_reverse($this->unsaved)) as $iteration => $info): ?> <tr class="<?=$iteration % 2 == 1 ? 'even' : 'odd'?>row"> <td><?=$this->escapeHtml($this->dateTime()->convertToDisplayDateAndTime("U", $info->getStartTime()))?></td> <td> <?=$this->historylabel($info->getParams()->getSearchClassId())?> - <a href="<?=$this->url($info->getOptions()->getSearchAction()) . $info->getUrlQuery()->getParams()?>"><? + <a href="<?=$this->url($info->getOptions()->getSearchAction()) . $info->getUrlQuery()->getParams()?>"><?php $desc = $info->getParams()->getDisplayQuery(); echo empty($desc) ? $this->transEsc("history_empty_search") : $this->escapeHtml($desc); ?></a> </td> <td> - <? $info->getParams()->activateAllFacets(); foreach ($info->getParams()->getFilterList(true) as $field => $filters): ?> - <? foreach ($filters as $i => $filter): ?> - <? if ($filter['operator'] == 'NOT') echo $this->transEsc('NOT') . ' '; if ($filter['operator'] == 'OR' && $i > 0) echo $this->transEsc('OR') . ' '; ?> + <?php $info->getParams()->activateAllFacets(); foreach ($info->getParams()->getFilterList(true) as $field => $filters): ?> + <?php foreach ($filters as $i => $filter): ?> + <?php if ($filter['operator'] == 'NOT') echo $this->transEsc('NOT') . ' '; if ($filter['operator'] == 'OR' && $i > 0) echo $this->transEsc('OR') . ' '; ?> <strong><?=$this->transEsc($field)?></strong>: <?=$this->escapeHtml($filter['displayText'])?><br/> - <? endforeach; ?> - <? endforeach; ?> - <? foreach($info->getParams()->getCheckboxFacets() as $facet): ?> - <? if ($facet['selected']): ?> + <?php endforeach; ?> + <?php endforeach; ?> + <?php foreach($info->getParams()->getCheckboxFacets() as $facet): ?> + <?php if ($facet['selected']): ?> <strong><?=$this->transEsc($facet['desc'])?></strong><br/> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </td> <td><?=$this->escapeHtml($this->localizedNumber($info->getResultTotal()))?></td> - <? if ($saveSupported): ?> + <?php if ($saveSupported): ?> <td> - <? if ($this->showSaved): ?> + <?php if ($this->showSaved): ?> <a href="<?=$this->url('myresearch-savesearch')?>?delete=<?=urlencode($info->getSearchId())?>&mode=history"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_delete_link")?></a> - <? else: ?> + <?php else: ?> <a href="<?=$this->url('myresearch-savesearch')?>?save=<?=urlencode($info->getSearchId())?>&mode=history"><i class="fa fa-save" aria-hidden="true"></i> <?=$this->transEsc("history_save_link")?></a> - <? endif; ?> + <?php endif; ?> </td> - <? endif; ?> + <?php endif; ?> </tr> - <? endforeach; ?> + <?php endforeach; ?> </table> diff --git a/themes/bootstrap3/templates/search/history.phtml b/themes/bootstrap3/templates/search/history.phtml index 698b505a2b5937f0b39854d69e2db3753f3eb646..73c2b7953900b8012465abb6fde19a369d53544e 100644 --- a/themes/bootstrap3/templates/search/history.phtml +++ b/themes/bootstrap3/templates/search/history.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Search History')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>' + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>' . '<li class="active">' . $this->transEsc('History') . '</li>'; $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; @@ -11,21 +11,21 @@ <div class="<?=$this->layoutClass('mainbody')?>"> <?=$this->flashmessages()?> - <? if ($saveSupported && !empty($this->saved)): ?> + <?php if ($saveSupported && !empty($this->saved)): ?> <h2><?=$this->transEsc("history_saved_searches")?></h2> <?=$this->context()->renderInContext('search/history-table.phtml', ['showSaved' => true]);?> - <? endif; ?> + <?php endif; ?> <h2><?=$this->transEsc("history_recent_searches")?></h2> - <? if (!empty($this->unsaved)): ?> + <?php if (!empty($this->unsaved)): ?> <?=$this->context()->renderInContext('search/history-table.phtml', ['showSaved' => false]);?> <a href="?purge=true"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_purge")?></a> - <? else: ?> + <?php else: ?> <?=$this->transEsc("history_no_searches")?> - <? endif; ?> + <?php endif; ?> </div> -<? if ($saveSupported): ?> +<?php if ($saveSupported): ?> <div class="<?=$this->layoutClass('sidebar')?>"> <?=$this->context($this)->renderInContext( "myresearch/menu.phtml", @@ -34,4 +34,4 @@ ); ?> </div> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/home.phtml b/themes/bootstrap3/templates/search/home.phtml index 3f108689e0e34b80156e60ea9b80e5f6fec1f821..f9c8cff4cbbf84656d1cfb3b3f379416af2276a1 100644 --- a/themes/bootstrap3/templates/search/home.phtml +++ b/themes/bootstrap3/templates/search/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Search Home')); @@ -10,106 +10,12 @@ $this->searchClassId = 'Solr'; } - // Load search actions and settings (if any): - $options = $this->searchOptions($this->searchClassId); - $basicSearch = $options->getSearchAction(); - $advSearch = $options->getAdvancedSearchAction(); - $this->layout()->breadcrumbs = false; ?> <div class="searchHomeContent"> - <? - $ilsStatusScript = <<<JS -$(document).ready(function() { - $.ajax({ - dataType: 'json', - method: 'GET', - data: {'offlineModeMsg':'ils_offline_home_message'}, - url: VuFind.path + '/AJAX/JSON?method=getIlsStatus', - success: function(response) { - $('.searchHomeContent').prepend(response.data); - } - }); -}); -JS; - ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $ilsStatusScript, 'SET'); ?> - <?=$this->context($this)->renderInContext("search/searchbox.phtml", ['ignoreHiddenFilterMemory' => true])?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$("#searchForm_lookfor").focus();', 'SET'); ?> </div> -<? if (isset($facetList) && is_array($facetList)): ?> - <div class="search-home-facets"> - <? foreach ($facetList as $field => $details): ?> - <? if (isset($this->hierarchicalFacets) && in_array($field, $this->hierarchicalFacets)): ?> - <? $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); ?> - <? $this->headScript()->appendFile('facets.js'); ?> - <? $sort = isset($this->hierarchicalFacetSortOptions[$field]) ? $this->hierarchicalFacetSortOptions[$field] : ''; ?> - <? - $script = <<<JS -$(document).ready(function() { - initFacetTree($('#facet_{$this->escapeHtml($field)}'), false); -}); -JS; - ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> - <div class="home-facet <?=$this->escapeHtmlAttr($field) ?>"> - <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> - <div id="facet_<?=$this->escapeHtml($field)?>" class="jstree-facet" - data-facet="<?=$this->escapeHtml($field)?>" - data-path="<?=$this->url($basicSearch)?>" - data-exclude="0" - data-operator="AND" - data-exclude-title="<?=$this->transEsc('exclude_facet')?>" - data-sort="all"> - </div> - </div> - <noscript> - <? endif; ?> - <? $sortedList = $this->sortFacetList($this->results, $field, $details['list'], $basicSearch); ?> - <div class="home-facet <?=$this->escapeHtmlAttr($field) ?>"> - <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> - <div class="home-facet-container"> - <ul class="home-facet-list"> - <? /* Special case: two columns for LC call numbers... */ ?> - <? if ($field == "callnumber-first"): ?> - <? $i = 0; foreach ($sortedList as $url => $value): ?> - <? if (!empty($value)): ?> - <li><a href="<?=$url?>"><?=$this->escapeHtml($value)?></a></li> - <? else: $i--; ?> - <? endif; ?> - <? if (++$i == 10): ?> - </ul><ul class="home-facet-list"> - <? endif; ?> - <? endforeach; ?> - <? /* Special case: collections */ ?> - <? elseif ($field == 'hierarchy_top_title'): ?> - <? $i = 0; foreach ($sortedList as $url => $value): ?> - <? if (++$i > 10): ?> - <li><a href="<?=$this->url('collections-home')?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li> - <? break; ?> - <? endif; ?> - <li><a href="<?=$this->url('collections-bytitle')?>?title=<?=urlencode($value)?>"><?=$this->escapeHtml($value)?></a></li> - <? endforeach; ?> - <? else: ?> - <? $i = 0; foreach ($sortedList as $url => $value): ?> - <? if (++$i > 10): ?> - <li><a href="<?=$this->url($advSearch)?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li> - <? break; ?> - <? elseif (!empty($value)): ?> - <li><a href="<?=$url?>"><?=$this->escapeHtml($value)?></a></li> - <? else: $i--; ?> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> - </ul> - </div> - </div> - <? if (isset($this->hierarchicalFacets) && in_array($field, $this->hierarchicalFacets)): ?> - </noscript> - <? endif; ?> - <? endforeach; ?> - </div> -<? endif; ?> +<?=implode('', array_map([$this, 'contentBlock'], $blocks ?? []))?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/search/list-authorfacets.phtml b/themes/bootstrap3/templates/search/list-authorfacets.phtml index 822ba7ac4cdeec757f1894a7cd6a2f9b3d93c320..7c190fb1fffb91353c4e0a6c235d26a36bfd4150 100644 --- a/themes/bootstrap3/templates/search/list-authorfacets.phtml +++ b/themes/bootstrap3/templates/search/list-authorfacets.phtml @@ -3,11 +3,11 @@ <tr> <th><?=$this->transEsc("Author")?></th><th><?=$this->transEsc("sort_author_relevance")?></th> </tr> - <? foreach ($this->results->getResults() as $record): ?> + <?php foreach ($this->results->getResults() as $record): ?> <tr> <td><a href="<?=$this->url('author-home')?>?author=<?=urlencode($record['value'])?>"><?=$this->escapeHtml($record['value'])?></a></td> <td><?=$this->escapeHtml($record['count'])?></td> </tr> - <? endforeach; ?> + <?php endforeach; ?> </tbody> </table> diff --git a/themes/bootstrap3/templates/search/list-grid.phtml b/themes/bootstrap3/templates/search/list-grid.phtml index 141a04894addf5cafee9c6c6e1b4a637b76ee96d..256e0077601b4c205914c0b845cc5d69698f287d 100644 --- a/themes/bootstrap3/templates/search/list-grid.phtml +++ b/themes/bootstrap3/templates/search/list-grid.phtml @@ -1,12 +1,12 @@ <div class="search-grid"> - <? $it = 0; ?> - <? foreach ($this->results->getResults() as $current): ?> + <?php $it = 0; ?> + <?php foreach ($this->results->getResults() as $current): ?> <?=$this->record($current)->getSearchResult('grid')?> - <? $it++; ?> - <? if ($it % 4 === 0): ?> + <?php $it++; ?> + <?php if ($it % 4 === 0): ?> <br class="grid-large-break"/> - <? elseif ($it % 2 === 0): ?> + <?php elseif ($it % 2 === 0): ?> <br class="grid-small-break"/> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/search/list-list.phtml b/themes/bootstrap3/templates/search/list-list.phtml index a3d3673e14d7651612c7399fe6763371395324f4..af656e815b8e9236284def57a20d7229efe5ec8d 100644 --- a/themes/bootstrap3/templates/search/list-list.phtml +++ b/themes/bootstrap3/templates/search/list-list.phtml @@ -1,14 +1,14 @@ -<? if (!isset($this->indexStart)) $this->indexStart = 0; ?> -<? $i = $this->indexStart; ?> -<? foreach ($this->results->getResults() as $current): ?> - <? $recordNumber = $this->results->getStartRecord() + $i-$this->indexStart; ?> +<?php if (!isset($this->indexStart)) $this->indexStart = 0; ?> +<?php $i = $this->indexStart; ?> +<?php foreach ($this->results->getResults() as $current): ?> + <?php $recordNumber = $this->results->getStartRecord() + $i - $this->indexStart; ?> <div id="result<?=$i++ ?>" class="result<?=$current->supportsAjaxStatus()?' ajaxItem':''?>"> - <? if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> - <?=$this->record($current)->getCheckbox('', 'search-cart-form')?> - <? endif; ?> + <?php if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> + <?=$this->record($current)->getCheckbox('', 'search-cart-form', $recordNumber)?> + <?php endif; ?> <div class="record-number"> <?=$recordNumber ?> </div> <?=$this->record($current)->getSearchResult('list')?> </div> -<? endforeach; ?> +<?php endforeach; ?> diff --git a/themes/bootstrap3/templates/search/newitem.phtml b/themes/bootstrap3/templates/search/newitem.phtml index 5f874fddaa1d7a7ea9250895f613518f664db188..995a4e2a0bfc4f83ff7e8405b24d26b72702d3e3 100644 --- a/themes/bootstrap3/templates/search/newitem.phtml +++ b/themes/bootstrap3/templates/search/newitem.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('New Item Search')); @@ -9,30 +9,30 @@ $offlineMode = $this->ils()->getOfflineMode(); ?> <h2><?=$this->transEsc('Find New Items')?></h2> -<? if ($offlineMode == "ils-offline"): ?> +<?php if ($offlineMode == "ils-offline"): ?> <?=$this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message'])?> -<? endif; ?> +<?php endif; ?> <form method="get" class="form-search-newitem"> <div class="form-group"> <label class="control-label"><?=$this->transEsc('Range')?>:</label> <div class="btn-group" data-toggle="buttons"> - <? foreach ($this->ranges as $key => $range): ?> - <label class="btn btn-primary<? if($key == 0): ?> active<? endif ?>"> + <?php foreach ($this->ranges as $key => $range): ?> + <label class="btn btn-primary<?php if($key == 0): ?> active<?php endif ?>"> <input type="radio" name="range" id="newitem_range_<?=$this->escapeHtmlAttr($key)?>" value="<?=$this->escapeHtmlAttr($range)?>"<?=($key == 0) ? ' checked="checked"' : ''?>/> <?=($range == 1) ? $this->transEsc('Yesterday') : $this->transEsc('past_days', ['%%range%%' => $this->escapeHtml($range)])?> </label> - <? endforeach; ?> + <?php endforeach; ?> </div> </div> - <? if (is_array($this->fundList) && !empty($this->fundList)): ?> + <?php if (is_array($this->fundList) && !empty($this->fundList)): ?> <div class="form-group"> <label class="control-label" for="newitem_department"><?=$this->transEsc('Department')?>:</label> <select id="newitem_department" name="department" size="10" class="form-control"> - <? foreach ($this->fundList as $fundId => $fund): ?> + <?php foreach ($this->fundList as $fundId => $fund): ?> <option value="<?=$this->escapeHtmlAttr($fundId)?>"><?=$this->transEsc($fund)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> </div> - <? endif; ?> + <?php endif; ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>"/> </form> diff --git a/themes/bootstrap3/templates/search/newitemresults.phtml b/themes/bootstrap3/templates/search/newitemresults.phtml index 0115a979ea7c4ca6c2a0e28a28ac1483d0042f5c..8eb629497fdf64f32de588a06e0818afa046cd21 100644 --- a/themes/bootstrap3/templates/search/newitemresults.phtml +++ b/themes/bootstrap3/templates/search/newitemresults.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set some overrides, then call the standard search results action: $this->overrideTitle = $this->translate('New Items'); $this->overrideSearchHeading = $this->transEsc('New Items'); diff --git a/themes/bootstrap3/templates/search/pagination.phtml b/themes/bootstrap3/templates/search/pagination.phtml index 6eda62a3bfa321b46a2bbf6288b7eedd0a298794..ed0993063bd45fad85e0845e317a35b20e60c16e 100644 --- a/themes/bootstrap3/templates/search/pagination.phtml +++ b/themes/bootstrap3/templates/search/pagination.phtml @@ -1,27 +1,27 @@ -<? if ($this->pageCount): ?> +<?php if ($this->pageCount): ?> <ul class="pagination"> - <? if (isset($this->previous)): ?> - <? if (!isset($this->options['disableFirst']) || !$this->options['disableFirst']): ?> + <?php if (isset($this->previous)): ?> + <?php if (!isset($this->options['disableFirst']) || !$this->options['disableFirst']): ?> <li><a href="<?=$this->currentPath() . $this->results->getUrlQuery()->setPage(1)?>">[1]</a></li> - <? endif; ?> + <?php endif; ?> <li><a class="page-prev" href="<?=$this->currentPath() . $this->results->getUrlQuery()->setPage($this->previous)?>">« <?=$this->transEsc('Prev')?></a></li> - <? endif; ?> + <?php endif; ?> - <? if (count($this->pagesInRange) > 1): ?> - <? foreach ($this->pagesInRange as $page): ?> - <? if ($page != $this->current): ?> + <?php if (count($this->pagesInRange) > 1): ?> + <?php foreach ($this->pagesInRange as $page): ?> + <?php if ($page != $this->current): ?> <li><a href="<?=$this->currentPath() . $this->results->getUrlQuery()->setPage($page)?>"><?=$page?></a></li> - <? else: ?> + <?php else: ?> <li class="active"><span><?=$page?></span></li> - <? endif; ?> - <? endforeach; ?> - <? endif; ?> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> - <? if (isset($this->next)): ?> + <?php if (isset($this->next)): ?> <li><a class="page-next" href="<?=$this->currentPath() . $this->results->getUrlQuery()->setPage($this->next)?>"><?=$this->transEsc('Next');?> »</a></li> - <? if (!isset($this->options['disableLast']) || !$this->options['disableLast']): ?> + <?php if (!isset($this->options['disableLast']) || !$this->options['disableLast']): ?> <li><a href="<?=$this->currentPath() . $this->results->getUrlQuery()->setPage($this->pageCount)?>">[<?=$this->pageCount?>]</a></li> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> </ul> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/reserves.phtml b/themes/bootstrap3/templates/search/reserves.phtml index 1ed5be825d11b01202e69451e535c20574a54ce4..38c30eb2f1f49d7f1dc55771618c09620175af25 100644 --- a/themes/bootstrap3/templates/search/reserves.phtml +++ b/themes/bootstrap3/templates/search/reserves.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Reserves Search')); @@ -8,48 +8,48 @@ // Convenience variable: $offlineMode = $this->ils()->getOfflineMode(); ?> -<? if ($offlineMode == "ils-offline"): ?> +<?php if ($offlineMode == "ils-offline"): ?> <?=$this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message'])?> -<? else: ?> +<?php else: ?> <h2><?=$this->transEsc('Search For Items on Reserve')?></h2> <form method="get" name="searchForm" class="form-search-reserves"> - <? if (is_array($this->courseList)): ?> + <?php if (is_array($this->courseList)): ?> <div class="form-group"> <label for="reserves_by_course" class="control-label"><?=$this->transEsc('By Course')?>:</label> <select name="course" id="reserves_by_course" class="form-control"> <option></option> - <? foreach ($this->courseList as $courseId => $courseName): ?> + <?php foreach ($this->courseList as $courseId => $courseName): ?> <option value="<?=$this->escapeHtmlAttr($courseId)?>"><?=$this->escapeHtml($courseName)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>"/> </div> - <? endif; ?> + <?php endif; ?> - <? if (is_array($this->instList)): ?> + <?php if (is_array($this->instList)): ?> <div class="form-group"> <label for="reserves_by_inst" class="control-label"><?=$this->transEsc('By Instructor')?>:</label> <select name="inst" id="reserves_by_inst" class="form-control"> <option></option> - <? foreach ($this->instList as $instId => $instName): ?> + <?php foreach ($this->instList as $instId => $instName): ?> <option value="<?=$this->escapeHtmlAttr($instId)?>"><?=$this->escapeHtml($instName)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>"/> </div> - <? endif; ?> + <?php endif; ?> - <? if (is_array($this->deptList)): ?> + <?php if (is_array($this->deptList)): ?> <div class="form-group"> <label for="reserves_by_dept" class="control-label"><?=$this->transEsc('By Department')?>:</label> <select name="dept" id="reserves_by_dept" class="form-control"> <option></option> - <? foreach ($this->deptList as $deptId => $deptName): ?> + <?php foreach ($this->deptList as $deptId => $deptName): ?> <option value="<?=$this->escapeHtmlAttr($deptId)?>"><?=$this->escapeHtml($deptName)?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>"/> </div> - <? endif; ?> + <?php endif; ?> </form> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/reservesresults.phtml b/themes/bootstrap3/templates/search/reservesresults.phtml index cbf99ffddda3896e503f9cdd61df95cca3f9baf9..ed683a0c0d01ca80d2bb59d07c98ccd58bca1f9b 100644 --- a/themes/bootstrap3/templates/search/reservesresults.phtml +++ b/themes/bootstrap3/templates/search/reservesresults.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set some overrides, then call the standard search results action: $this->overrideTitle = $this->translate('Reserves Search Results'); $this->overrideSearchHeading = $this->transEsc('Reserves'); diff --git a/themes/bootstrap3/templates/search/reservessearch.phtml b/themes/bootstrap3/templates/search/reservessearch.phtml index d6219e06f30cb70b4b2759a13c3c9dc5bc213314..19f5354c80466a75e0327aa90b2c6709d6eda8fb 100644 --- a/themes/bootstrap3/templates/search/reservessearch.phtml +++ b/themes/bootstrap3/templates/search/reservessearch.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $this->headTitle($this->translate('Reserves Search')); @@ -22,35 +22,35 @@ <div class="resulthead"> <div class="pull-left flip"> - <? $qtime = $this->results->getQuerySpeed(); ?> - <? if (($recordTotal = $this->results->getResultTotal()) > 0): ?> - <? + <?php $qtime = $this->results->getQuerySpeed(); ?> + <?php if (($recordTotal = $this->results->getResultTotal()) > 0): ?> + <?php $transParams = [ '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), - '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), + '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), '%%total%%' => $this->localizedNumber($recordTotal), '%%lookfor%%' => $this->escapeHtml($reservesLookfor) ]; ?> - <?=$this->translate('showing_results_of_for_html', $transParams); ?><? if($qtime): ?>,<? endif; ?> - <? endif; ?> - <? if ($qtime): ?> - <?=$this->transEsc('query time')?>: <?=$this->localizedNumber($qtime, 2).$this->transEsc('seconds_abbrev')?> - <? endif; ?> + <?=$this->translate('showing_results_of_for_html', $transParams); ?><?php if($qtime): ?>,<?php endif; ?> + <?php endif; ?> + <?php if ($qtime): ?> + <?=$this->transEsc('query time')?>: <?=$this->localizedNumber($qtime, 2) . $this->transEsc('seconds_abbrev')?> + <?php endif; ?> </div> - <? if ($recordTotal > 0): ?> + <?php if ($recordTotal > 0): ?> <div class="pull-right flip"> <?=$this->render('search/controls/sort.phtml')?> </div> - <? endif; ?> + <?php endif; ?> </div> - <? if ($recordTotal < 1): ?> + <?php if ($recordTotal < 1): ?> <p class="error"><?=$this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($reservesLookfor)]) ?></p> - <? if (isset($this->parseError)): ?> + <?php if (isset($this->parseError)): ?> <p class="error"><?=$this->transEsc('nohit_parse_error')?></p> - <? endif; ?> - <? else: ?> + <?php endif; ?> + <?php else: ?> <table class="table table-striped"> <tr> <th class="department"><?=$this->transEsc('Department')?></th> @@ -58,8 +58,8 @@ <th class="instructor"><?=$this->transEsc('Instructor')?></th> <th class="items"><?=$this->transEsc('Items')?></th> </tr> - <? foreach ($this->results->getResults() as $record): ?> - <? + <?php foreach ($this->results->getResults() as $record): ?> + <?php $url = $this->currentPath() . $this->escapeHtmlAttr( '?inst=' . urlencode($record->getInstructorId()) . '&course=' . urlencode($record->getCourseId()) @@ -72,16 +72,16 @@ <td class="instructor"><a href="<?=$url?>"><?=$this->escapeHtml($record->getInstructor())?></a></td> <td class="items"><?=$this->escapeHtml($record->getItemCount())?></td> </tr> - <? endforeach; ?> + <?php endforeach; ?> </table> <?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results])?> - <? endif; ?> + <?php endif; ?> </div> -<? /* Narrow Search Options */ ?> +<?php /* Narrow Search Options */ ?> <div class="<?=$this->layoutClass('sidebar')?>"> - <? foreach ($this->results->getRecommendations('side') as $current): ?> + <?php foreach ($this->results->getRecommendations('side') as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> -<? /* End Narrow Search Options */ ?> +<?php /* End Narrow Search Options */ ?> diff --git a/themes/bootstrap3/templates/search/results.phtml b/themes/bootstrap3/templates/search/results.phtml index 9922b8390c3a2dec14d24a96f5ef7f5e1e967c44..02f1f87d4175aa3bc42766443e7b6a05619fe41d 100644 --- a/themes/bootstrap3/templates/search/results.phtml +++ b/themes/bootstrap3/templates/search/results.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set up page title: $lookfor = $this->results->getUrlQuery()->isQuerySuppressed() ? '' : $this->params->getDisplayQuery(); if (isset($this->overrideTitle)) { @@ -53,52 +53,52 @@ ?> <div class="<?=$this->layoutClass('mainbody')?>"> - <? if (($recordTotal = $this->results->getResultTotal()) > 0): // only display these at very top if we have results ?> - <? foreach ($this->results->getRecommendations('top') as $current): ?> + <?php if (($recordTotal = $this->results->getResultTotal()) > 0): // only display these at very top if we have results ?> + <?php foreach ($this->results->getRecommendations('top') as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> - <? endif; ?> + <?php endforeach; ?> + <?php endif; ?> <?=$this->flashmessages()?> <nav class="search-header hidden-print"> <div class="search-stats"> - <? if ($recordTotal > 0): ?> + <?php if ($recordTotal > 0): ?> <?=$this->context()->renderInContext('search/controls/showing.phtml', ['lookfor' => $lookfor, 'recordTotal' => $recordTotal]) ?> - <? else: ?> + <?php else: ?> <h2><?=$this->transEsc('nohit_heading')?></h2> - <? endif; ?> + <?php endif; ?> </div> - <? if ($recordTotal > 0): ?> + <?php if ($recordTotal > 0): ?> <div class="search-controls"> <?=$this->render('search/controls/limit.phtml')?> <?=$this->render('search/controls/sort.phtml')?> <?=$this->render('search/controls/view.phtml')?> </div> - <? endif; ?> + <?php endif; ?> </nav> - <? /* End Listing Options */ ?> + <?php /* End Listing Options */ ?> - <? if ($recordTotal < 1): ?> + <?php if ($recordTotal < 1): ?> <p> - <? if (isset($this->overrideEmptyMessage)): ?> + <?php if (isset($this->overrideEmptyMessage)): ?> <?=$this->overrideEmptyMessage?> - <? else: ?> - <? $this->layout()->srmessage = $this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($lookfor)]); ?> + <?php else: ?> + <?php $this->layout()->srmessage = $this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($lookfor)]); ?> <?=$this->layout()->srmessage ?> - <? endif; ?> + <?php endif; ?> </p> - <? if (isset($this->parseError)): ?> + <?php if (isset($this->parseError)): ?> <p class="alert alert-danger"><?=$this->transEsc('nohit_parse_error')?></p> - <? endif; ?> - <? foreach (($top = $this->results->getRecommendations('top')) as $current): ?> + <?php endif; ?> + <?php foreach (($top = $this->results->getRecommendations('top')) as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> - <? foreach ($this->results->getRecommendations('noresults') as $current): ?> - <? if (!in_array($current, $top)): ?> + <?php endforeach; ?> + <?php foreach ($this->results->getRecommendations('noresults') as $current): ?> + <?php if (!in_array($current, $top)): ?> <?=$this->recommend($current)?> - <? endif; ?> - <? endforeach; ?> - <? else: ?> + <?php endif; ?> + <?php endforeach; ?> + <?php else: ?> <form id="search-cart-form" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler"> <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => ''])?> </form> @@ -113,25 +113,25 @@ <a href="<?=$this->url('search-email')?>" class="mailSearch" data-lightbox id="mailSearch<?=$this->escapeHtmlAttr($this->results->getSearchId())?>"> <i class="fa fa-envelope" aria-hidden="true"></i> <?=$this->transEsc('Email this Search')?> </a> - <? if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> + <?php if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> — - <? if (is_numeric($this->results->getSearchId())): ?> - <? if ($this->results->isSavedSearch()): ?> + <?php if (is_numeric($this->results->getSearchId())): ?> + <?php if ($this->results->isSavedSearch()): ?> <a href="<?=$this->url('myresearch-savesearch')?>?delete=<?=urlencode($this->results->getSearchId())?>"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc('save_search_remove')?></a> - <? else: ?> + <?php else: ?> <a href="<?=$this->url('myresearch-savesearch')?>?save=<?=urlencode($this->results->getSearchId())?>"><i class="fa fa-save" aria-hidden="true"></i> <?=$this->transEsc('save_search')?></a> - <? endif; ?> - <? endif; ?> - <? endif; ?> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> </div> - <? endif; ?> + <?php endif; ?> </div> -<? /* End Main Listing */ ?> +<?php /* End Main Listing */ ?> -<? /* Narrow Search Options */ ?> +<?php /* Narrow Search Options */ ?> <div class="<?=$this->layoutClass('sidebar')?>"> - <? foreach ($this->results->getRecommendations('side') as $current): ?> + <?php foreach ($this->results->getRecommendations('side') as $current): ?> <?=$this->recommend($current)?> - <? endforeach; ?> + <?php endforeach; ?> </div> -<? /* End Narrow Search Options */ ?> +<?php /* End Narrow Search Options */ ?> diff --git a/themes/bootstrap3/templates/search/searchTabs.phtml b/themes/bootstrap3/templates/search/searchTabs.phtml index aa9d8aeae7c588ab04646e1d561930b2dfe40c6e..2072bf4cb98b98171301ce8bb2f1df120da6ea97 100644 --- a/themes/bootstrap3/templates/search/searchTabs.phtml +++ b/themes/bootstrap3/templates/search/searchTabs.phtml @@ -1,13 +1,13 @@ -<? if (count($searchTabs) > 0): ?> +<?php if (count($searchTabs) > 0): ?> <ul class="nav nav-tabs"> - <? foreach ($searchTabs as $tab): ?> - <? if ($this->permission()->allowDisplay($tab['permission'])): ?> + <?php foreach ($searchTabs as $tab): ?> + <?php if ($this->permission()->allowDisplay($tab['permission'])): ?> <li<?=$tab['selected'] ? ' class="active"' : ''?>> <a <?=$tab['selected'] ? '' : 'href="' . $this->escapeHtmlAttr($tab['url']) . '"' ?>><?=$this->transEsc($tab['label']); ?></a> </li> - <? elseif ($block = $this->permission()->getAlternateContent($tab['permission'])): ?> + <?php elseif ($block = $this->permission()->getAlternateContent($tab['permission'])): ?> <?=$block?> - <? endif; ?> - <? endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </ul> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/searchbox.phtml b/themes/bootstrap3/templates/search/searchbox.phtml index 17e808017076b13e847b53db79dfd0d490e57fba..4d4d4ca1fed76b88afa334efb3a814f533f6a70f 100644 --- a/themes/bootstrap3/templates/search/searchbox.phtml +++ b/themes/bootstrap3/templates/search/searchbox.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set default value if necessary: if (!isset($this->searchClassId)) { $this->searchClassId = 'Solr'; @@ -18,69 +18,69 @@ $lastLimit = $this->searchMemory()->getLastLimit($this->searchClassId); $ignoreHiddenFilterMemory = isset($this->ignoreHiddenFilterMemory) && $this->ignoreHiddenFilterMemory; $ignoreHiddenFiltersInRequest = isset($this->ignoreHiddenFiltersInRequest) && $this->ignoreHiddenFiltersInRequest; - $hiddenFilters = $this->searchtabs()->getHiddenFilters($this->searchClassId, $ignoreHiddenFilterMemory, $ignoreHiddenFiltersInRequest); + $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, $ignoreHiddenFilterMemory, $ignoreHiddenFiltersInRequest); if (empty($hiddenFilters) && !$ignoreHiddenFilterMemory) { $hiddenFilters = $this->searchMemory()->getLastHiddenFilters($this->searchClassId); if (empty($hiddenFilters)) { - $hiddenFilters = $this->searchtabs()->getHiddenFilters($this->searchClassId); + $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId); } } - $hiddenFilterParams = $this->searchtabs()->getCurrentHiddenFilterParams($this->searchClassId, $ignoreHiddenFilterMemory, '?'); + $hiddenFilterParams = $this->searchTabs()->getCurrentHiddenFilterParams($this->searchClassId, $ignoreHiddenFilterMemory, '?'); ?> -<? $searchTabs = $this->searchtabs()->getTabConfig($this->searchClassId, $this->lookfor, $this->searchIndex, $this->searchType, $hiddenFilters); ?> -<? if ($this->searchType == 'advanced'): ?> +<?php $tabConfig = $this->searchTabs()->getTabConfig($this->searchClassId, $this->lookfor, $this->searchIndex, $this->searchType, $hiddenFilters); ?> +<?php if ($this->searchType == 'advanced'): ?> <div class="navbar-form navbar-left flip"> - <? $tabs = $this->context($this)->renderInContext('search/searchTabs', ['searchTabs' => $searchTabs['tabs']]); ?> - <? if (!empty($tabs)): ?><?=$tabs ?><div class="tab-content clearfix"><? endif; ?> + <?php $tabs = $this->context($this)->renderInContext('search/searchTabs', ['searchTabs' => $tabConfig['tabs']]); ?> + <?php if (!empty($tabs)): ?><?=$tabs ?><div class="tab-content clearfix"><?php endif; ?> <p class="adv_search_terms"><?=$this->transEsc("Your search terms")?> : "<strong><?=$this->escapeHtml($this->lookfor)?></strong>"</p> <p class="adv_search_links"> <a href="<?=$this->url($advSearch)?>?edit=<?=$this->escapeHtmlAttr($this->searchId)?>"><?=$this->transEsc("Edit this Advanced Search")?></a> | <a href="<?=$this->url($advSearch) . $hiddenFilterParams?>"><?=$this->transEsc("Start a new Advanced Search")?></a> | <a href="<?=$this->url($searchHome) . $hiddenFilterParams?>"><?=$this->transEsc("Start a new Basic Search")?></a> </p> - <? if (!empty($tabs)): ?></div><? endif; ?> + <?php if (!empty($tabs)): ?></div><?php endif; ?> </div> -<? else: ?> +<?php else: ?> <form id="searchForm" class="searchForm navbar-form navbar-left flip" method="get" action="<?=$this->url($basicSearch)?>" name="searchForm" autocomplete="off"> - <?= $this->context($this)->renderInContext('search/searchTabs', ['searchTabs' => $searchTabs['tabs']]); ?> - <? $placeholder = $this->searchbox()->getPlaceholderText(isset($searchTabs['selected']['id']) ? $searchTabs['selected']['id'] : null); ?> - <input id="searchForm_lookfor" class="searchForm_lookfor form-control search-query<? if($this->searchbox()->autocompleteEnabled($this->searchClassId)):?> autocomplete searcher:<?=$this->escapeHtmlAttr($this->searchClassId) ?><? endif ?>" type="text" name="lookfor" value="<?=$this->escapeHtmlAttr($this->lookfor)?>"<? if ($placeholder): ?> placeholder="<?=$this->transEsc($placeholder) ?>"<? endif ?> /> - <? if ($handlerCount > 1): ?> + <?= $this->context($this)->renderInContext('search/searchTabs', ['searchTabs' => $tabConfig['tabs']]); ?> + <?php $placeholder = $this->searchbox()->getPlaceholderText($tabConfig['selected']['id'] ?? null); ?> + <input id="searchForm_lookfor" class="searchForm_lookfor form-control search-query<?php if($this->searchbox()->autocompleteEnabled($this->searchClassId)):?> autocomplete searcher:<?=$this->escapeHtmlAttr($this->searchClassId) ?><?php endif ?>" type="text" name="lookfor" value="<?=$this->escapeHtmlAttr($this->lookfor)?>"<?php if ($placeholder): ?> placeholder="<?=$this->transEsc($placeholder) ?>"<?php endif ?> /> + <?php if ($handlerCount > 1): ?> <select id="searchForm_type" class="searchForm_type form-control" name="type" data-native-menu="false"> - <? foreach ($handlers as $handler): ?> + <?php foreach ($handlers as $handler): ?> <option value="<?=$this->escapeHtmlAttr($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$handler['indent'] ? '-- ' : ''?><?=$this->transEsc($handler['label'])?></option> - <? endforeach; ?> + <?php endforeach; ?> </select> - <? elseif ($handlerCount == 1): ?> + <?php elseif ($handlerCount == 1): ?> <input type="hidden" name="type" value="<?=$this->escapeHtmlAttr($handlers[0]['value'])?>" /> - <? endif; ?> + <?php endif; ?> <button type="submit" class="btn btn-primary"><i class="fa fa-search" aria-hidden="true"></i> <?=$this->transEsc("Find")?></button> - <? if ($advSearch): ?> + <?php if ($advSearch): ?> <a href="<?=$this->url($advSearch) . ((isset($this->searchId) && $this->searchId) ? '?edit=' . $this->escapeHtmlAttr($this->searchId) : $hiddenFilterParams) ?>" class="btn btn-link" rel="nofollow"><?=$this->transEsc("Advanced")?></a> - <? endif; ?> - <? if ($geoUrl = $this->geocoords()->getSearchUrl($options)) : ?> + <?php endif; ?> + <?php if ($geoUrl = $this->geocoords()->getSearchUrl($options)) : ?> <a href="<?=$geoUrl ?>" class="btn btn-link"><?=$this->transEsc('Geographic Search')?></a> - <? endif; ?> + <?php endif; ?> - <? $shards = $options->getShards(); if ($options->showShardCheckboxes() && !empty($shards)): ?> - <? + <?php $shards = $options->getShards(); if ($options->showShardCheckboxes() && !empty($shards)): ?> + <?php $selectedShards = isset($this->selectedShards) ? $this->selectedShards : $options->getDefaultSelectedShards(); ?> <br /> - <? foreach ($shards as $shard => $val): ?> - <? $isSelected = empty($selectedShards) || in_array($shard, $selectedShards); ?> + <?php foreach ($shards as $shard => $val): ?> + <?php $isSelected = empty($selectedShards) || in_array($shard, $selectedShards); ?> <input type="checkbox" <?=$isSelected ? 'checked="checked" ' : ''?>name="shard[]" value='<?=$this->escapeHtmlAttr($shard)?>' /> <?=$this->transEsc($shard)?> - <? endforeach; ?> - <? endif; ?> - <? + <?php endforeach; ?> + <?php endif; ?> + <?php $filterDetails = $this->searchbox()->getFilterDetails( isset($this->filterList) && is_array($this->filterList) ? $this->filterList : [], isset($this->checkboxFilters) && is_array($this->checkboxFilters) ? $this->checkboxFilters : [] ); ?> - <? if ((isset($hasDefaultsApplied) && $hasDefaultsApplied) || !empty($filterDetails)): ?> - <? $defaultFilterState = $options->getRetainFilterSetting() ? ' checked="checked"' : ''; ?> + <?php if ((isset($hasDefaultsApplied) && $hasDefaultsApplied) || !empty($filterDetails)): ?> + <?php $defaultFilterState = $options->getRetainFilterSetting() ? ' checked="checked"' : ''; ?> <div class="checkbox"> <label> <input type="checkbox"<?=$defaultFilterState?> class="searchFormKeepFilters"/> @@ -88,23 +88,23 @@ </label> </div> <div class="hidden"> - <? foreach ($filterDetails as $current): ?> + <?php foreach ($filterDetails as $current): ?> <input class="applied-filter" id="<?=$this->escapeHtmlAttr($current['id'])?>" type="checkbox"<?=$defaultFilterState?> name="filter[]" value="<?=$this->escapeHtmlAttr($current['value'])?>" /> <label for="<?=$this->escapeHtmlAttr($current['id'])?>"><?=$this->escapeHtml($current['value'])?></label> - <? endforeach; ?> - <? if (isset($hasDefaultsApplied) && $hasDefaultsApplied): ?> + <?php endforeach; ?> + <?php if (isset($hasDefaultsApplied) && $hasDefaultsApplied): ?> <!-- this is a hidden element that flags whether or not default filters have been applied; it is intentionally unlabeled, as users are not meant to manipulate it directly. --> <input class="applied-filter" id="dfApplied" type="checkbox" name="dfApplied" value="1"<?=$defaultFilterState?> /> - <? endif; ?> + <?php endif; ?> </div> - <? endif; ?> - <? foreach ($hiddenFilters as $key => $filter): ?> - <? foreach ($filter as $value): ?> + <?php endif; ?> + <?php foreach ($hiddenFilters as $key => $filter): ?> + <?php foreach ($filter as $value): ?> <input type="hidden" name="hiddenFilters[]" value="<?=$this->escapeHtmlAttr($key) . ':' . $this->escapeHtmlAttr($value)?>" /> - <? endforeach; ?> - <? endforeach; ?> - <? + <?php endforeach; ?> + <?php endforeach; ?> + <?php /* Show hidden field for active search class when in combined handler mode. */ if ($this->searchbox()->combinedHandlersActive()) { echo '<input type="hidden" name="activeSearchClassId" value="' . $this->escapeHtmlAttr($this->searchClassId) . '" />'; @@ -118,4 +118,4 @@ } ?> </form> -<? endif; ?> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search2/advanced.phtml b/themes/bootstrap3/templates/search2/advanced.phtml new file mode 100644 index 0000000000000000000000000000000000000000..90c56be5520a3d28d64a311f83c0ecd314e33d68 --- /dev/null +++ b/themes/bootstrap3/templates/search2/advanced.phtml @@ -0,0 +1 @@ +<?=$this->render('search/advanced.phtml'); ?> diff --git a/themes/bootstrap3/templates/search2/home.phtml b/themes/bootstrap3/templates/search2/home.phtml new file mode 100644 index 0000000000000000000000000000000000000000..bc5b700660ea8e7baee8cc01de0600b0b4611521 --- /dev/null +++ b/themes/bootstrap3/templates/search2/home.phtml @@ -0,0 +1 @@ +<?=$this->render('search/home.phtml');?> diff --git a/themes/bootstrap3/templates/search2/results.phtml b/themes/bootstrap3/templates/search2/results.phtml new file mode 100644 index 0000000000000000000000000000000000000000..1d94458550eb5622deb7fe9df430fa693c66393f --- /dev/null +++ b/themes/bootstrap3/templates/search2/results.phtml @@ -0,0 +1 @@ +<?=$this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/summon/advanced.phtml b/themes/bootstrap3/templates/summon/advanced.phtml index 22a7c0a135dacc6a705dba7d58c7e2b1c6a86c0e..5ff45ffdc2f65f14d8460277b255e39a27066253 100644 --- a/themes/bootstrap3/templates/summon/advanced.phtml +++ b/themes/bootstrap3/templates/summon/advanced.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load the Summon-specific advanced search controls and inject them into the // standard advanced search layout: $this->extraAdvancedControls = $this->render('search/advanced/summon.phtml'); diff --git a/themes/bootstrap3/templates/summon/search.phtml b/themes/bootstrap3/templates/summon/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/summon/search.phtml +++ b/themes/bootstrap3/templates/summon/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/tag/home.phtml b/themes/bootstrap3/templates/tag/home.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/tag/home.phtml +++ b/themes/bootstrap3/templates/tag/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/upgrade/error.phtml b/themes/bootstrap3/templates/upgrade/error.phtml index ef4b9e2b55f263462fb05e02a8a8b1d0e4e215f8..4df71246f467af18445d020038a9cf9af42c33e6 100644 --- a/themes/bootstrap3/templates/upgrade/error.phtml +++ b/themes/bootstrap3/templates/upgrade/error.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/upgrade/fixanonymoustags.phtml b/themes/bootstrap3/templates/upgrade/fixanonymoustags.phtml index 9ac6a5a13b8c382dc286f85d4f4c0ff4dc6abe89..26b14cc2cefb53142170526212b08ec57a04f28c 100644 --- a/themes/bootstrap3/templates/upgrade/fixanonymoustags.phtml +++ b/themes/bootstrap3/templates/upgrade/fixanonymoustags.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/upgrade/fixduplicatetags.phtml b/themes/bootstrap3/templates/upgrade/fixduplicatetags.phtml index e49f008ff915129bf488ba0ed9f24a91d9df4c56..e3add9f0c57d3445fb9f7efe29f2ea334a19db3b 100644 --- a/themes/bootstrap3/templates/upgrade/fixduplicatetags.phtml +++ b/themes/bootstrap3/templates/upgrade/fixduplicatetags.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/upgrade/fixmetadata.phtml b/themes/bootstrap3/templates/upgrade/fixmetadata.phtml index 389dda8bcb5594e0521650ade27c99d4b1780a34..4b82d414e4bfc15739d8c2d99c8f1066efa29714 100644 --- a/themes/bootstrap3/templates/upgrade/fixmetadata.phtml +++ b/themes/bootstrap3/templates/upgrade/fixmetadata.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/upgrade/getdbcredentials.phtml b/themes/bootstrap3/templates/upgrade/getdbcredentials.phtml index f13e695155a99df210908f65665c08b3812246f8..3c7fd5c54f699cff560e83dae1f7d3afe3bdd741 100644 --- a/themes/bootstrap3/templates/upgrade/getdbcredentials.phtml +++ b/themes/bootstrap3/templates/upgrade/getdbcredentials.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/upgrade/getdbencodingpreference.phtml b/themes/bootstrap3/templates/upgrade/getdbencodingpreference.phtml index d9c735d9c647ef20603edee685f9c0f4d5ec8501..570a8b821f61931e3d6b578070c66f3fd1549d75 100644 --- a/themes/bootstrap3/templates/upgrade/getdbencodingpreference.phtml +++ b/themes/bootstrap3/templates/upgrade/getdbencodingpreference.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/upgrade/getsourcedir.phtml b/themes/bootstrap3/templates/upgrade/getsourcedir.phtml index c7f6b16d7e6f2d89f0e304db025a8754b7094f85..dd79ca3957242a5a8c7f710e6b6ab1ced7d4053e 100644 --- a/themes/bootstrap3/templates/upgrade/getsourcedir.phtml +++ b/themes/bootstrap3/templates/upgrade/getsourcedir.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; ?> <h2><?=$this->transEsc('Upgrade VuFind')?></h2> <?=$this->flashmessages()?> @@ -20,10 +20,10 @@ <input type="text" name="sourceversion" /> </p> <p>Options:<br /> - <? foreach (['config' => 'Configuration', 'database' => 'Database Structure', 'metadata' => 'Stored Metadata'] as $key => $value): ?> + <?php foreach (['config' => 'Configuration', 'database' => 'Database Structure', 'metadata' => 'Stored Metadata'] as $key => $value): ?> <input type="checkbox" name="skip[]" id="skip-<?=$this->escapeHtmlAttr($key)?>" value="<?=$this->escapeHtmlAttr($key)?>" /> <label for="skip-<?=$this->escapeHtmlAttr($key)?>">Skip <?=$this->escapeHtml($value); ?> Upgrade</label><br /> - <? endforeach; ?> + <?php endforeach; ?> </p> <input class="btn btn-primary" type="submit" /> </form> diff --git a/themes/bootstrap3/templates/upgrade/home.phtml b/themes/bootstrap3/templates/upgrade/home.phtml index 6ceb7eac08d124d570d7ec2c2c6e4d41b6c480f3..e63cbf66ac09b2eb51c93b6351a8b17891a79027 100644 --- a/themes/bootstrap3/templates/upgrade/home.phtml +++ b/themes/bootstrap3/templates/upgrade/home.phtml @@ -1,4 +1,4 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); @@ -10,15 +10,15 @@ <p>Upgrade complete. You may still have some work to do:</p> <ol> - <? if ($oldVersion < 2): ?> + <?php if ($oldVersion < 2): ?> <li>If you have customized your SolrMarc import settings, your marc_local.properties file has been migrated, but you will need to move custom translation maps, index scripts, etc. by hand. Custom import files belong under <?=$this->escapeHtml($this->importDir)?> -- this will make future upgrades easier.</li> <li>You should look over the configuration files in <?=$this->escapeHtml($this->configDir)?> and make sure settings look correct. The automatic update process sometimes re-enables disabled settings and removes comments.</li> <li>If you have customized any of the YAML searchspecs files without using the *_local.yaml override mechanism, you will need to reapply those changes.</li> <li>If you have customized code or templates in your previous version, you will need to adapt those changes to the new architecture.</li> - <? else: ?> + <?php else: ?> <li>You should look over the configuration files in <?=$this->escapeHtml($this->configDir)?> and make sure settings look correct. The automatic update process sometimes re-enables disabled settings and removes comments. Backups of your old configurations have been created for comparison purposes.</li> <li>If you have customized code or templates in your previous version, you should test them to be sure they still work correctly; see the <a href="https://vufind.org/wiki/changelog">changelog</a> for notes on possible breaks in backward compatibility.</li> - <? endif; ?> + <?php endif; ?> <li>You should reindex all of your content.</li> <li>You may want to check for problems on the <a href="<?=$this->url('install-home')?>"><?=$this->transEsc('auto_configure_title')?></a> page.</li> </ol> diff --git a/themes/bootstrap3/templates/upgrade/showsql.phtml b/themes/bootstrap3/templates/upgrade/showsql.phtml index 094ce028020c7860a8f3aec671f10ec1c775d7f0..96e9a9af81a0af76f7520d6f96c84b555b027ad5 100644 --- a/themes/bootstrap3/templates/upgrade/showsql.phtml +++ b/themes/bootstrap3/templates/upgrade/showsql.phtml @@ -1,9 +1,9 @@ -<? +<?php // Set page title. $this->headTitle($this->translate('Upgrade VuFind')); // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li><a href="'.$this->url('upgrade-home').'">'.$this->transEsc('Upgrade').'</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('upgrade-home') . '">' . $this->transEsc('Upgrade') . '</a></li> <li class="active">' . $this->transEsc('Upgrade VuFind') . '</li>'; // Set up styles: $this->headstyle()->appendStyle( diff --git a/themes/bootstrap3/templates/web/results.phtml b/themes/bootstrap3/templates/web/results.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/web/results.phtml +++ b/themes/bootstrap3/templates/web/results.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/templates/worldcat/advanced.phtml b/themes/bootstrap3/templates/worldcat/advanced.phtml index b9aa96af774c943d2ad91ae3f1900ad9d1f5653e..8ce0b8d4220436aaf282f6d76acc2f7f3ff96e84 100644 --- a/themes/bootstrap3/templates/worldcat/advanced.phtml +++ b/themes/bootstrap3/templates/worldcat/advanced.phtml @@ -1,4 +1,4 @@ -<? +<?php // There are no WorldCat-specific advanced search controls, so just load the // standard advanced search layout: echo $this->render('search/advanced/layout.phtml'); diff --git a/themes/bootstrap3/templates/worldcat/search.phtml b/themes/bootstrap3/templates/worldcat/search.phtml index a0c6dd4bbf5077e0b43b335e98733b44bba7b4e6..ab0354bab32bdde5b38f2542fd218fb39c204078 100644 --- a/themes/bootstrap3/templates/worldcat/search.phtml +++ b/themes/bootstrap3/templates/worldcat/search.phtml @@ -1,4 +1,4 @@ -<? +<?php // Load standard settings from the default search results screen: echo $this->render('search/results.phtml'); ?> diff --git a/themes/bootstrap3/theme.config.php b/themes/bootstrap3/theme.config.php index 9712fcbd8b607a3995f9163d3dbe6c3176db48ed..733dd1aa5cb6978549f80bb490c62c914637f421 100644 --- a/themes/bootstrap3/theme.config.php +++ b/themes/bootstrap3/theme.config.php @@ -1,7 +1,7 @@ <?php -return array( +return [ 'extends' => 'root', - 'css' => array( + 'css' => [ //'vendor/bootstrap.min.css', //'vendor/bootstrap-accessibility.css', //'vendor/font-awesome.min.css', @@ -9,9 +9,8 @@ return array( 'compiled.css', 'print.css:print', 'flex-fallback.css::lt IE 10', // flex polyfill - ), - 'js' => array( - 'vendor/base64.js:lt IE 10', // btoa polyfill + ], + 'js' => [ 'vendor/jquery.min.js', 'vendor/bootstrap.min.js', 'vendor/bootstrap-accessibility.min.js', @@ -20,21 +19,26 @@ return array( 'lib/autocomplete.js', 'common.js', 'lightbox.js', - ), - 'less' => array( + ], + 'less' => [ 'active' => false, 'compiled.less' - ), + ], 'favicon' => 'vufind-favicon.ico', - 'helpers' => array( - 'factories' => array( - 'flashmessages' => 'VuFind\View\Helper\Bootstrap3\Factory::getFlashmessages', - 'layoutclass' => 'VuFind\View\Helper\Bootstrap3\Factory::getLayoutClass', - 'recaptcha' => 'VuFind\View\Helper\Bootstrap3\Factory::getRecaptcha', - ), - 'invokables' => array( + 'helpers' => [ + 'factories' => [ + 'VuFind\View\Helper\Bootstrap3\Flashmessages' => 'VuFind\View\Helper\Root\FlashmessagesFactory', + 'VuFind\View\Helper\Bootstrap3\Highlight' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Bootstrap3\LayoutClass' => 'VuFind\View\Helper\Bootstrap3\LayoutClassFactory', + 'VuFind\View\Helper\Bootstrap3\Recaptcha' => 'VuFind\View\Helper\Root\RecaptchaFactory', + 'VuFind\View\Helper\Bootstrap3\Search' => 'Zend\ServiceManager\Factory\InvokableFactory', + ], + 'aliases' => [ + 'flashmessages' => 'VuFind\View\Helper\Bootstrap3\Flashmessages', 'highlight' => 'VuFind\View\Helper\Bootstrap3\Highlight', + 'layoutClass' => 'VuFind\View\Helper\Bootstrap3\LayoutClass', + 'recaptcha' => 'VuFind\View\Helper\Bootstrap3\Recaptcha', 'search' => 'VuFind\View\Helper\Bootstrap3\Search' - ) - ) -); + ] + ] +]; diff --git a/themes/local_mixin_example/mixin.config.php b/themes/local_mixin_example/mixin.config.php index b7b10fe6ebc0cd025ff2a31e9956f49c304e8933..c25386314e03dc4f05080c5b720e58562a0df8a5 100644 --- a/themes/local_mixin_example/mixin.config.php +++ b/themes/local_mixin_example/mixin.config.php @@ -1,4 +1,4 @@ <?php return [ 'js' => ['mixin-popup.js'], -]; \ No newline at end of file +]; diff --git a/themes/local_theme_example/templates/search/home.phtml b/themes/local_theme_example/templates/search/home.phtml index 1ed63d196ada0f90aa9f35f0669ba26e3b356e96..6b93aa67645db88f2f59bf9c68bf2895740712df 100644 --- a/themes/local_theme_example/templates/search/home.phtml +++ b/themes/local_theme_example/templates/search/home.phtml @@ -6,7 +6,7 @@ </b></p> <div class="searchHomeContent"> <div class="search-hero" role="search"> - <? require(__DIR__ . '/../../../bootstrap3/templates/search/home.phtml'); ?> + <?php require(__DIR__ . '/../../../bootstrap3/templates/search/home.phtml'); ?> <div class="image-credit"> Yosemite Valley. Photo by <a class="name" href="https://unsplash.com/@heytowner">John Towner</a>. </div> diff --git a/themes/local_theme_example/theme.config.php b/themes/local_theme_example/theme.config.php index 018a473aeb6395abb5ed5a8c63dbb2661fd46f33..2f5a8373cd19585855303f5d660dc033e9828a92 100644 --- a/themes/local_theme_example/theme.config.php +++ b/themes/local_theme_example/theme.config.php @@ -1,4 +1,4 @@ <?php -return array( +return [ 'extends' => 'bootstrap3' -); +]; diff --git a/themes/root/templates/Citation/apa-article.phtml b/themes/root/templates/Citation/apa-article.phtml index 35f08a280ed2862feb9099ca3ca3e7c37a520fbb..f3814457a73d35c658f974312280253bfe9659ab 100644 --- a/themes/root/templates/Citation/apa-article.phtml +++ b/themes/root/templates/Citation/apa-article.phtml @@ -1,8 +1,8 @@ -<? if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?> <? endif; ?> -<? if (!empty($this->date)): ?>(<?=$this->escapeHtml($this->date)?>). <? endif; ?> -<?=$this->escapeHtml($this->title)?><? if ($this->periodAfterTitle): ?>.<? endif ?> -<span style="font-style:italic;"><?=$this->escapeHtml($this->journal)?><? if (!empty($this->volume) || !empty($this->issue) || !empty($this->pageRange)): ?>, <? endif; ?> -<? if (!empty($this->volume)): ?><?=$this->escapeHtml($this->volume)?><? endif; ?></span><? if (!empty($this->issue)): ?>(<?=$this->escapeHtml($this->issue)?>)<? endif; ?> -<? if (!empty($this->volume) || !empty($this->issue)): ?>, <? endif; ?> -<? if (!empty($this->pageRange)) echo strstr($this->pageRange, '-') ? 'pp. ' : 'p. '; ?> -<?=$this->escapeHtml($this->pageRange)?>. <? if (isset($this->doi)): ?>doi:<?=$this->escapeHtml($this->doi)?><? endif; ?> +<?php if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?> <?php endif; ?> +<?php if (!empty($this->date)): ?>(<?=$this->escapeHtml($this->date)?>). <?php endif; ?> +<?=$this->escapeHtml($this->title)?><?php if ($this->periodAfterTitle): ?>.<?php endif ?> +<span style="font-style:italic;"><?=$this->escapeHtml($this->journal)?><?php if (!empty($this->volume) || !empty($this->issue) || !empty($this->pageRange)): ?>, <?php endif; ?> +<?php if (!empty($this->volume)): ?><?=$this->escapeHtml($this->volume)?><?php endif; ?></span><?php if (!empty($this->issue)): ?>(<?=$this->escapeHtml($this->issue)?>)<?php endif; ?> +<?php if (!empty($this->volume) || !empty($this->issue)): ?>, <?php endif; ?> +<?php if (!empty($this->pageRange)) echo strstr($this->pageRange, '-') ? 'pp. ' : 'p. '; ?> +<?=$this->escapeHtml($this->pageRange)?>. <?php if (isset($this->doi)): ?>doi:<?=$this->escapeHtml($this->doi)?><?php endif; ?> diff --git a/themes/root/templates/Citation/apa.phtml b/themes/root/templates/Citation/apa.phtml index 5c5170205782b670df3667eeecdb1ee5e8be91cf..729138cc28af75bb4b2b9cdefa343e40141fa6c8 100644 --- a/themes/root/templates/Citation/apa.phtml +++ b/themes/root/templates/Citation/apa.phtml @@ -1,5 +1,5 @@ -<? if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?> <? endif; ?> -<? if (!empty($this->year)): ?>(<?=$this->escapeHtml($this->year)?>). <? endif; ?> -<span style="font-style:italic;"><?=$this->escapeHtml($this->title)?></span><? if ($this->periodAfterTitle): ?>.<? endif ?> -<? if (!empty($this->edition)): ?>(<?=$this->escapeHtml($this->edition)?>). <? endif; ?> -<? if (!empty($this->publisher)): ?><?=$this->escapeHtml($this->publisher)?>.<? endif; ?> +<?php if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?> <?php endif; ?> +<?php if (!empty($this->year)): ?>(<?=$this->escapeHtml($this->year)?>). <?php endif; ?> +<span style="font-style:italic;"><?=$this->escapeHtml($this->title)?></span><?php if ($this->periodAfterTitle): ?>.<?php endif ?> +<?php if (!empty($this->edition)): ?>(<?=$this->escapeHtml($this->edition)?>). <?php endif; ?> +<?php if (!empty($this->publisher)): ?><?=$this->escapeHtml($this->publisher)?>.<?php endif; ?> diff --git a/themes/root/templates/Citation/mla-article.phtml b/themes/root/templates/Citation/mla-article.phtml index d51442557270cc5a75eb458d32816de01714b914..67f153bf42987e43db97afaa6604ff7407572039 100644 --- a/themes/root/templates/Citation/mla-article.phtml +++ b/themes/root/templates/Citation/mla-article.phtml @@ -1,5 +1,5 @@ -<? if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?>. <? endif; ?> -"<?=$this->title?><? if ($this->periodAfterTitle): ?>.<? endif ?>" +<?php if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?>. <?php endif; ?> +"<?=$this->title?><?php if ($this->periodAfterTitle): ?>.<?php endif ?>" <span style="font-style:italic;"><?=$this->escapeHtml($this->journal)?></span> -<? if (!empty($this->numberAndDate)): ?><?=$this->escapeHtml($this->numberAndDate)?><? if (!empty($this->pageRange)): ?>: <? endif; ?><? endif; ?> -<? if (!empty($this->pageRange)): ?><?=$this->escapeHtml($this->pageRange)?><? endif; ?>. +<?php if (!empty($this->numberAndDate)): ?><?=$this->escapeHtml($this->numberAndDate)?><?php if (!empty($this->pageRange)): ?>: <?php endif; ?><?php endif; ?> +<?php if (!empty($this->pageRange)): ?><?=$this->escapeHtml($this->pageRange)?><?php endif; ?>. diff --git a/themes/root/templates/Citation/mla.phtml b/themes/root/templates/Citation/mla.phtml index d10656a7b9f7a4f8314a84292629569cb4ae026f..790e08c35a16315c781ff9a5546b7618da943385 100644 --- a/themes/root/templates/Citation/mla.phtml +++ b/themes/root/templates/Citation/mla.phtml @@ -1,5 +1,5 @@ -<? if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?>. <? endif; ?> -<span style="font-style:italic;"><?=$this->escapeHtml($this->title)?></span><? if ($this->periodAfterTitle): ?>.<? endif ?> -<? if (!empty($this->edition)): ?><?=$this->escapeHtml($this->edition)?> <? endif; ?> -<? if (!empty($this->publisher)): ?><?=$this->escapeHtml($this->publisher)?><? endif; ?> -<? if (!empty($this->year)): ?><? if (!empty($this->publisher)): ?>, <? endif; ?><?=$this->escapeHtml($this->year)?><? endif; ?><? if (!empty($this->year) || !empty($this->publisher)): ?>.<? endif; ?> +<?php if (!empty($this->authors)): ?><?=$this->escapeHtml($this->authors)?>. <?php endif; ?> +<span style="font-style:italic;"><?=$this->escapeHtml($this->title)?></span><?php if ($this->periodAfterTitle): ?>.<?php endif ?> +<?php if (!empty($this->edition)): ?><?=$this->escapeHtml($this->edition)?> <?php endif; ?> +<?php if (!empty($this->publisher)): ?><?=$this->escapeHtml($this->publisher)?><?php endif; ?> +<?php if (!empty($this->year)): ?><?php if (!empty($this->publisher)): ?>, <?php endif; ?><?=$this->escapeHtml($this->year)?><?php endif; ?><?php if (!empty($this->year) || !empty($this->publisher)): ?>.<?php endif; ?> diff --git a/themes/root/templates/Email/new-user-welcome.phtml b/themes/root/templates/Email/new-user-welcome.phtml new file mode 100644 index 0000000000000000000000000000000000000000..fc5ff95d7da760a2c3c4fc6df255f62047eefdc5 --- /dev/null +++ b/themes/root/templates/Email/new-user-welcome.phtml @@ -0,0 +1,10 @@ +<?=$this->translate( + 'new_user_welcome_text', + [ + '%%library%%' => $this->library, + '%%firstname%%' => $this->firstname, + '%%lastname%%' => $this->lastname, + '%%username%%' => $this->username, + '%%url%%' => $this->url + ]); +?> \ No newline at end of file diff --git a/themes/root/templates/Email/record-sms.phtml b/themes/root/templates/Email/record-sms.phtml index a456ea481233ae2764efd637e54aceb573b72e1c..268c4a5cd5e0ad8539c336c54bbc863759b26f79 100644 --- a/themes/root/templates/Email/record-sms.phtml +++ b/themes/root/templates/Email/record-sms.phtml @@ -1,4 +1,4 @@ -<? +<?php // This is a text-only email template; do not include HTML! // If this record supports real-time status, we'll assume it's a local @@ -18,14 +18,14 @@ foreach ($holdings['holdings'] as $details) { $location = $details['location']; foreach ($details['items'] as $item) { - $callno = isset($item['callnumber']) ? $item['callnumber'] : false; + $callno = $item['callnumber'] ?? false; if ($item['availability']) { if ($callno) { // Best case scenario -- available item with call number! // Exit loop in this situation. $useBackupLocation = false; break 2; - } else if (!$backupLocation) { + } elseif (!$backupLocation) { // Save first "available" location without call # as backup // (better than an unavailable location if no call #'s found) $backupLocation = $location; diff --git a/themes/root/templates/Email/record.phtml b/themes/root/templates/Email/record.phtml index 10a26c71b4e57867e51f30d3e8d53a062fc4efca..9759908a0a43c9824290faaf17f7dc69b87df305 100644 --- a/themes/root/templates/Email/record.phtml +++ b/themes/root/templates/Email/record.phtml @@ -1,4 +1,4 @@ -<? // This is a text-only email template; do not include HTML! ?> +<?php // This is a text-only email template; do not include HTML! ?> <?=$this->translate('This email was sent from')?>: <?=$this->from?> ------------------------------------------------------------ @@ -7,6 +7,6 @@ <?=$this->translate('email_link')?>: <?=$this->serverUrl($this->recordLink()->getUrl($this->driver))?> ------------------------------------------------------------ -<? if (!empty($this->message)): ?><?=$this->translate("Message From Sender")?>: +<?php if (!empty($this->message)): ?><?=$this->translate("Message From Sender")?>: <?=$this->message?> -<? endif; ?> +<?php endif; ?> diff --git a/themes/root/templates/Email/recover-password.phtml b/themes/root/templates/Email/recover-password.phtml index dd84e2ecc3c1e7f5f5871027673aeaf56742b2ec..e391ac2a7b5b8bcae2b8e7880641d6153d2d632a 100644 --- a/themes/root/templates/Email/recover-password.phtml +++ b/themes/root/templates/Email/recover-password.phtml @@ -1,3 +1,3 @@ -<?=$this->translate('recovery_email_notification', array('%%library%%' => $this->library)) ?> +<?=$this->translate('recovery_email_notification', ['%%library%%' => $this->library]) ?> -<?=$this->translate('recovery_email_url_pretext', array('%%url%%' => $this->url)) ?> +<?=$this->translate('recovery_email_url_pretext', ['%%url%%' => $this->url]) ?> diff --git a/themes/root/templates/Email/share-link.phtml b/themes/root/templates/Email/share-link.phtml index 955127043464558072af9b8fe621462e6ad33de2..bcfca31ed209b944c6962da8e891dbe0314f48ef 100644 --- a/themes/root/templates/Email/share-link.phtml +++ b/themes/root/templates/Email/share-link.phtml @@ -1,12 +1,12 @@ -<? // This is a text-only email template; do not include HTML! ?> +<?php // This is a text-only email template; do not include HTML! ?> <?=$this->translate('This email was sent from')?>: <?=$this->from?> ------------------------------------------------------------ -<? if (!empty($this->message)): ?><?=$this->translate("Message From Sender")?>: +<?php if (!empty($this->message)): ?><?=$this->translate("Message From Sender")?>: <?=$this->message?> -<? endif; ?> +<?php endif; ?> <?=$this->translate('email_link')?>: <<?=$this->msgUrl?>> ------------------------------------------------------------ diff --git a/themes/root/templates/HelpTranslations/cs/geosearch.phtml b/themes/root/templates/HelpTranslations/cs/geosearch.phtml index 7f8f7a5ee6924fd95dfababea15d14b19bb99d29..9136979ea0a075fb169c794d7de887218d50f468 100644 --- a/themes/root/templates/HelpTranslations/cs/geosearch.phtml +++ b/themes/root/templates/HelpTranslations/cs/geosearch.phtml @@ -9,16 +9,17 @@ prohledávané územÃ, a táhnÄ›te myÅ¡Ã to opaÄného rohu, poté pusÅ¥te tlaÄÃtko myÅ¡i.</li> </ol> - <h3>Práce s výsledky vyhledávánÃInteracting with the search results</h3> + <h3>Práce s výsledky vyhledávánÃ</h3> <p>Výsledky se na mapÄ› zobrazà jako shluky (koleÄka s ÄÃslem uprostÅ™ed). Tato ÄÃsla vyjadÅ™ujà poÄet záznamů v každém shluku. Seznam výsledků je zobrazen pod mapou.</p> <p>Mapu můžete pÅ™ibližovat a oddalovat pomocà tlaÄÃtek "+" a "-" nebo pomocà myÅ¡i - dvojklikem (pÅ™iblÞenÃ) a shift + dvojklik (oddálenÃ).</p> - <p>Shluky s ménÄ› než 5 záznamy umožňujà zobrazenà vyskakovacà okno s informacemi - o dotyÄných záznamech. Pokud najede na shluk s ménÄ› než 5 záznamy kurzor - myÅ¡i se zmÄ›ni na ruÄiÄku a pokud kliknete, zobrazà se okno se seznamem - záznamů týkajÃcÃch se daného mÃsta. Každá položka v seznamu je zároveň - odkazem na úplný záznam.</p> + dvojklikem (pÅ™iblÞenÃ) a shift + dvojklik (oddálenÃ). Můžete také kliknout + na nÄ›který ze zobrazených shluků por pÅ™iblÞenà na jeho úroveň.</p> + <p>PÅ™i pÅ™ibližovánà se shluky rozložà na jednotlivé body. Zakulacené ikony + znázorňujà body, ikony s rovnou hornà hranou znázorňujà oblasti. KliknutÃm + na ikonu se zobrazà souvisejÃcà názvy dokumentů. VÅ¡echny body souvisejÃcà se + stejným záznamem budou zvýraznÄ›ny modÅ™e. Každý název je odkazem na úplný + záznam.</p> </dd> </dl> diff --git a/themes/root/templates/HelpTranslations/el/advsearch.phtml b/themes/root/templates/HelpTranslations/el/advsearch.phtml index 4e9cab7f7c8e58e998737a8d1fae80109aaaf3d6..ebcb43425e2d48ba13633c96067f745a8afc1da3 100644 --- a/themes/root/templates/HelpTranslations/el/advsearch.phtml +++ b/themes/root/templates/HelpTranslations/el/advsearch.phtml @@ -1,4 +1,4 @@ -<h1>ΣυμβουλÎÏ‚ ΣÏνθετης Αναζήτησης</h1> +<h1>ΣυμβουλÎÏ‚ ΣÏνθετης Αναζήτησης</h1> <ul class="HelpMenu"> <li><a href="#Search Fields">Πεδία Αναζήτησης</a></li> diff --git a/themes/root/templates/HelpTranslations/el/tag.phtml b/themes/root/templates/HelpTranslations/el/tag.phtml index 6c3f71d1937f598b3e4ab7ca4b00c00bfe72ad77..01b1e0ad1e0953ea4ee219af9c324ed9d6127718 100644 --- a/themes/root/templates/HelpTranslations/el/tag.phtml +++ b/themes/root/templates/HelpTranslations/el/tag.phtml @@ -1,4 +1,4 @@ -<h1>Tagging</h1> +<h1>Tagging</h1> <p> Μια ετικÎτα είναι μια (σχετική) λÎξη ή ÏŒÏος που συνδÎεται με ή ανατίθεται σε Îνα κομμάτι των πληÏοφοÏιών (όπως εικόνα, άÏθÏο, ή βίντεο κλιπ), με αποτÎλεσμα να πεÏιγÏάφει το είδος και τη δυνατότητα λÎξεων-κλειδιών με βάση τη διαβάθμιση των πληÏοφοÏιών που εφαÏμόζεται. diff --git a/themes/root/templates/HelpTranslations/en/geosearch.phtml b/themes/root/templates/HelpTranslations/en/geosearch.phtml index 9373c98df8017eec056717b115d0345566040607..0b62ef5cf7ca46880c47104493932244c4b151a7 100644 --- a/themes/root/templates/HelpTranslations/en/geosearch.phtml +++ b/themes/root/templates/HelpTranslations/en/geosearch.phtml @@ -11,11 +11,11 @@ <p>Search results are displayed on the map as clusters (circular icons with numbers). The numbers represent the number of records in each cluster. A list of results is displayed below the map view. </p> <p>You can interact with the map by zooming by either using the zoom buttons (+/-) or by using the mouse to - double-click (zoom in) or shift + double-click (zoom out).</p> - <p>Clusters with less than 5 records will display pop-up information about the records at that location. - When you hover over a cluster with less than 5 records, the mouse pointer will change to a hand with a finger - pointing at the cluster. Click on the cluster, and a pop-up window will appear showing a list of titles for the - records at that location. Each title is hyperlinked to the associated record. - Click on the title to view the full record.</p> + double-click (zoom in) or shift + double-click (zoom out). You can also click on a cluster to zoom to + the features in that cluster. </p> + <p>Clusters will de-aggregate as you zoom in or click on a cluster. Icons with rounded tops are point features. + Icons with square tops are the center point of a rectangle feature. Click on the icon to see the record title + associated with the icon. If there are multiple icons associated with the same record, they will all be highlighted + in blue. Each title is hyperlinked to the associated record. Click on the title to view the full record.</p> </dd> </dl> diff --git a/themes/root/templates/HelpTranslations/en/search.phtml b/themes/root/templates/HelpTranslations/en/search.phtml index 70bf4d10e506f58f8658f4b12b774e13cea7508e..b84e6135a2d51e56211b93727f40d29425189634 100644 --- a/themes/root/templates/HelpTranslations/en/search.phtml +++ b/themes/root/templates/HelpTranslations/en/search.phtml @@ -110,7 +110,7 @@ </dd> <dt><a name="-"></a>-</dt> <dd> - <p>The <Strong>-</strong> or prohibit operator excludes documents that contain the term after the "-" symbol.</p> + <p>The <strong>-</strong> or prohibit operator excludes documents that contain the term after the "-" symbol.</p> <p>To search for documents that contain "economics" but not "Keynes" use the query: </p> <pre class="code">economics -Keynes</pre> </dd> diff --git a/themes/root/templates/RecordDriver/AbstractBase/export-bibtex.phtml b/themes/root/templates/RecordDriver/AbstractBase/export-bibtex.phtml index 746271099571f4aac6066d85339c7f9d60b71ac7..2af6e7f240b34b44d1a9e1e24baec5311c153484 100644 --- a/themes/root/templates/RecordDriver/AbstractBase/export-bibtex.phtml +++ b/themes/root/templates/RecordDriver/AbstractBase/export-bibtex.phtml @@ -1,4 +1,4 @@ -<? +<?php // TODO: fold this logic into record driver methods at some point: $marc = $this->driver->tryMethod('getMarcRecord'); if (is_object($marc)) { @@ -11,11 +11,11 @@ $journalTitle = $this->driver->tryMethod('getContainerTitle'); $formats = $this->driver->tryMethod('getFormats'); if ($marcProceedingsField) { $format = 'proceedings'; -} else if ($marcPhdField) { +} elseif ($marcPhdField) { $format = 'phdthesis'; -} else if (!empty($journalTitle) || (is_array($formats) && in_array('Article', $formats))) { +} elseif (!empty($journalTitle) || (is_array($formats) && in_array('Article', $formats))) { $format = 'article'; -} else if (is_array($formats) && in_array('Journal', $formats)) { +} elseif (is_array($formats) && in_array('Journal', $formats)) { $format = 'misc'; } else { $format = 'book'; @@ -25,7 +25,7 @@ if ($marcProceedingsField) { echo '@' . $format . "{\n"; // Citation key: -echo $this->driver->getSourceIdentifier() . '-' . $this->driver->getUniqueId() . ",\n"; +echo $this->driver->getSourceIdentifier() . '-' . $this->driver->getUniqueId() . ",\n"; $title = rtrim($this->driver->getTitle(), " /"); echo "title = {{$title}},\n"; @@ -83,7 +83,7 @@ if (is_array($pubPlaces) && is_array($pubDates) && is_array($pubNames)) { } for ($i = 0; $i < $total; $i++) { if (isset($pubPlaces[$i])) { - echo "address = {" . rtrim(str_replace(array('[', ']'), '', $pubPlaces[$i]), ': ') . "},\n"; + echo "address = {" . rtrim(str_replace(['[', ']'], '', $pubPlaces[$i]), ': ') . "},\n"; } if (isset($pubNames[$i])) { echo "publisher = {" . rtrim($pubNames[$i], ", ") . "},\n"; diff --git a/themes/root/templates/RecordDriver/AbstractBase/export-endnote.phtml b/themes/root/templates/RecordDriver/AbstractBase/export-endnote.phtml index d0838ee50cf2e1f97fc89130b5f22a51dbe8c500..614f668d251657f5d5a3077bedfea3919d9bba65 100644 --- a/themes/root/templates/RecordDriver/AbstractBase/export-endnote.phtml +++ b/themes/root/templates/RecordDriver/AbstractBase/export-endnote.phtml @@ -1,4 +1,4 @@ -<? +<?php // A driver-specific template may pass in format overrides; check for these before going to the driver itself: $formats = isset($this->overrideFormats) ? $this->overrideFormats : $this->driver->tryMethod('getFormats'); if (is_array($formats) && !empty($formats)) { @@ -43,7 +43,7 @@ if (is_array($pubPlaces) && is_array($pubDates) && is_array($pubNames)) { echo "%D $date\n"; } if (isset($pubPlaces[$i])) { - echo "%C " . rtrim(str_replace(array('[', ']'), '', $pubPlaces[$i]), ': '). "\n"; + echo "%C " . rtrim(str_replace(['[', ']'], '', $pubPlaces[$i]), ': ') . "\n"; } } } diff --git a/themes/root/templates/RecordDriver/AbstractBase/export-endnoteweb.phtml b/themes/root/templates/RecordDriver/AbstractBase/export-endnoteweb.phtml index fbc8e6936fe1cd52d1fe84adbb704bc0f5349e72..5aa1284f9f9a2512d6b4699d43a54220ef77340f 100644 --- a/themes/root/templates/RecordDriver/AbstractBase/export-endnoteweb.phtml +++ b/themes/root/templates/RecordDriver/AbstractBase/export-endnoteweb.phtml @@ -1,10 +1,10 @@ -<? +<?php // A driver-specific template may pass in format overrides; check for these before going to the driver itself: $formats = isset($this->overrideFormats) ? $this->overrideFormats : $this->driver->tryMethod('getFormats'); if (is_array($formats)) { foreach ($formats as $format) { switch (strtolower($format)) { - case "book"; + case "book": $format = "BOOK"; break; case "journal": @@ -84,7 +84,7 @@ if (is_array($pubPlaces) && is_array($pubDates) && is_array($pubNames)) { } for ($i = 0; $i < $total; $i++) { if (isset($pubPlaces[$i])) { - echo "PP - " . rtrim(str_replace(array('[', ']'), '', $pubPlaces[$i]), ': '). "\n"; + echo "PP - " . rtrim(str_replace(['[', ']'], '', $pubPlaces[$i]), ': ') . "\n"; } if (isset($pubNames[$i])) { echo "PB - " . rtrim($pubNames[$i], ", ") . "\n"; diff --git a/themes/root/templates/RecordDriver/AbstractBase/export-refworks.phtml b/themes/root/templates/RecordDriver/AbstractBase/export-refworks.phtml index 3404a6ce3b8ece4a48edd3c5a3de6e6c3acf16fb..83bdd8559c997db3fa9baedb04b1fa814f56f432 100644 --- a/themes/root/templates/RecordDriver/AbstractBase/export-refworks.phtml +++ b/themes/root/templates/RecordDriver/AbstractBase/export-refworks.phtml @@ -1,4 +1,4 @@ -<? +<?php // A driver-specific template may pass in format overrides; check for these before going to the driver itself: $formats = isset($this->overrideFormats) ? $this->overrideFormats : $this->driver->tryMethod('getFormats'); if (is_array($formats) && !empty($formats)) { @@ -70,7 +70,7 @@ if (is_array($pubPlaces) && is_array($pubDates) && is_array($pubNames)) { } for ($i = 0; $i < $total; $i++) { if (isset($pubPlaces[$i])) { - echo "PP " . rtrim(str_replace(array('[', ']'), '', $pubPlaces[$i]), ': '). "\n"; + echo "PP " . rtrim(str_replace(['[', ']'], '', $pubPlaces[$i]), ': ') . "\n"; } if (isset($pubNames[$i])) { echo "PB " . rtrim($pubNames[$i], ", ") . "\n"; diff --git a/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml b/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml index 0aaceb66d4afd7ba1de4491dadf45c6c98bec583..bb3f3ef6c6dcf1b7fce670817fc3ef41aad70a0d 100644 --- a/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml +++ b/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml @@ -1,4 +1,4 @@ -<? +<?php // TODO: fold this logic into record driver methods at some point: $marc = $this->driver->tryMethod('getMarcRecord'); if (is_object($marc)) { @@ -11,11 +11,11 @@ $journalTitle = $this->driver->tryMethod('getContainerTitle'); $formats = $this->driver->tryMethod('getFormats'); if ($marcProceedingsField) { $format = 'CONF'; -} else if ($marcPhdField) { +} elseif ($marcPhdField) { $format = 'THES'; -} else if (!empty($journalTitle) || (is_array($formats) && in_array('Article', $formats))) { +} elseif (!empty($journalTitle) || (is_array($formats) && in_array('Article', $formats))) { $format = 'JOUR'; -} else if (is_array($formats) && in_array('Journal', $formats)) { +} elseif (is_array($formats) && in_array('Journal', $formats)) { $format = 'JFULL'; } else { $format = 'BOOK'; @@ -86,7 +86,7 @@ if (is_array($pubPlaces) && is_array($pubDates) && is_array($pubNames)) { } for ($i = 0; $i < $total; $i++) { if (isset($pubPlaces[$i])) { - echo "CY - " . rtrim(str_replace(array('[', ']'), '', $pubPlaces[$i]), ': '). "\r\n"; + echo "CY - " . rtrim(str_replace(['[', ']'], '', $pubPlaces[$i]), ': ') . "\r\n"; } if (isset($pubNames[$i])) { echo "PB - " . rtrim($pubNames[$i], ", ") . "\r\n"; @@ -108,14 +108,14 @@ if (is_array($languages)) { } } -$genres = $this->driver->tryMethod('getGenres'); +$genres = $this->driver->tryMethod('getGenres'); if (is_array($genres)) { foreach ($genres as $genre) { echo 'M3 - ' . "$genre\r\n"; } } -$topics = $this->driver->tryMethod('getTopics'); +$topics = $this->driver->tryMethod('getTopics'); if (is_array($topics)) { foreach ($topics as $topic) { echo 'KW - ' . "$topic\r\n"; diff --git a/themes/root/templates/RecordDriver/Primo/export-endnote.phtml b/themes/root/templates/RecordDriver/Primo/export-endnote.phtml index 9d1fe45b77b080f2250ae722127867624520a208..6a69596db1f44b46a6ad940e80d982afe02c5436 100644 --- a/themes/root/templates/RecordDriver/Primo/export-endnote.phtml +++ b/themes/root/templates/RecordDriver/Primo/export-endnote.phtml @@ -1,4 +1,4 @@ -<? +<?php // Convert Primo format to EndNote format: $formats = $this->driver->tryMethod('getFormats'); if (is_array($formats) && !empty($formats[0])) { diff --git a/themes/root/templates/RecordDriver/Primo/export-refworks.phtml b/themes/root/templates/RecordDriver/Primo/export-refworks.phtml index 290aef6604348cb84fdde86dfc88c473003e94eb..c4904e8259bc3955f806e165e55d28fdb9727db6 100644 --- a/themes/root/templates/RecordDriver/Primo/export-refworks.phtml +++ b/themes/root/templates/RecordDriver/Primo/export-refworks.phtml @@ -1,4 +1,4 @@ -<? +<?php // Convert Primo format to RefWorks format: $formats = $this->driver->tryMethod('getFormats'); if (is_array($formats) && !empty($formats[0])) { diff --git a/themes/root/templates/RecordDriver/Summon/export-endnote.phtml b/themes/root/templates/RecordDriver/Summon/export-endnote.phtml index fba5898a7320dc50b0327fed14cd683efdc5a729..e733fdfec86e816ea74da5d22f50a7028449c688 100644 --- a/themes/root/templates/RecordDriver/Summon/export-endnote.phtml +++ b/themes/root/templates/RecordDriver/Summon/export-endnote.phtml @@ -1,4 +1,4 @@ -<? +<?php // Convert Summon formats to EndNote formats: $formats = $this->driver->getFormats(); foreach ($formats as $i => $format) { diff --git a/themes/root/templates/RecordDriver/Summon/export-refworks.phtml b/themes/root/templates/RecordDriver/Summon/export-refworks.phtml index 09781ee3ec658c036f37863d8dee957de99f62b5..008f016744e2da544e8710fdda821f1ecad97ee6 100644 --- a/themes/root/templates/RecordDriver/Summon/export-refworks.phtml +++ b/themes/root/templates/RecordDriver/Summon/export-refworks.phtml @@ -1,4 +1,4 @@ -<? +<?php // Convert Summon formats to RefWorks formats: $formats = $this->driver->getFormats(); foreach ($formats as $i => $format) { diff --git a/themes/root/templates/api/swagger.phtml b/themes/root/templates/api/swagger.phtml index d136d4667de0f0c9bd2ad8b2ceeedd32d7cf8a76..26369bd0d7092461ad6bb6775b20f4c5babb3fac 100644 --- a/themes/root/templates/api/swagger.phtml +++ b/themes/root/templates/api/swagger.phtml @@ -1,5 +1,5 @@ -<? // This template is a JSON Swagger specification ?> -<? $basePath = rtrim($this->url('home'), '/') . '/api/v1'; ?> +<?php // This template is a JSON Swagger specification ?> +<?php $basePath = rtrim($this->url('home'), '/') . '/api/v1'; ?> { "swagger": "2.0", "info": { diff --git a/themes/root/templates/error/404.phtml b/themes/root/templates/error/404.phtml index 27c756e64f670ac67f4a012f481629a8d12bf86b..1a9c7539251087233e6a09da8b7e73aa7e858253 100644 --- a/themes/root/templates/error/404.phtml +++ b/themes/root/templates/error/404.phtml @@ -1,3 +1,3 @@ <h1><?=$this->transEsc('An error has occurred')?></h1> <h2><?=$this->transEsc($this->message)?></h2> -<? if (isset($this->reason) && $this->reason): ?><b><?=$this->transEsc('Message')?>:</b> <?=$this->transEsc($this->reason)?><? endif; ?> +<?php if (isset($this->reason) && $this->reason): ?><b><?=$this->transEsc('Message')?>:</b> <?=$this->transEsc($this->reason)?><?php endif; ?> diff --git a/themes/root/templates/error/index.phtml b/themes/root/templates/error/index.phtml index 4e4bb612df97f384bab294e6b6b84bbeb880e4b5..670e45ea0ef62cc60d244f38b5f5e395e00c83f7 100644 --- a/themes/root/templates/error/index.phtml +++ b/themes/root/templates/error/index.phtml @@ -1,14 +1,14 @@ <h1><?=$this->transEsc('An error has occurred')?></h1> <h2><?=$this->transEsc($this->message)?></h2> -<? if ($this->showInstallLink): ?> - <h3><a href="<?=$this->url('install-home')?>"><?=$this->transEsc('auto_configure_title', array(), 'Auto Configure')?></a></h3> - <?=$this->transEsc('auto_configure_description', array(), 'If this is a new installation, you may be able to fix the error using VuFind\'s Auto Configure tool.')?> +<?php if ($this->showInstallLink): ?> + <h3><a href="<?=$this->url('install-home')?>"><?=$this->transEsc('auto_configure_title', [], 'Auto Configure')?></a></h3> + <?=$this->transEsc('auto_configure_description', [], 'If this is a new installation, you may be able to fix the error using VuFind\'s Auto Configure tool.')?> <h3><a href="<?=$this->url('upgrade-home')?>"><?=$this->transEsc('Upgrade VuFind')?></a></h3> - <?=$this->transEsc('upgrade_description', array(), 'If you are upgrading a previous VuFind version, you can load your old settings with this tool.')?> -<? endif; ?> + <?=$this->transEsc('upgrade_description', [], 'If you are upgrading a previous VuFind version, you can load your old settings with this tool.')?> +<?php endif; ?> -<? if (isset($this->display_exceptions) && $this->display_exceptions): ?> +<?php if (isset($this->display_exceptions) && $this->display_exceptions): ?> <h3><?=$this->transEsc('Exception')?>:</h3> <p> <b><?=$this->transEsc('Message')?>:</b> <?=$this->escapeHtml($this->exception->getMessage())?> @@ -18,18 +18,18 @@ <pre><?=$this->exception->getTraceAsString()?> </pre> - <? if ($e = $this->exception->getPrevious()): ?> + <?php if ($e = $this->exception->getPrevious()): ?> <h3>Previous exceptions:</h3> - <? while($e): ?> - <h4><?php echo get_class($e); ?></h4> + <?php while($e): ?> + <h4><?=get_class($e)?></h4> <p><?=$e->getMessage()?></p> <pre><?=$e->getTraceAsString()?></pre> - <? $e = $e->getPrevious(); ?> - <? endwhile; ?> - <? endif; ?> + <?php $e = $e->getPrevious(); ?> + <?php endwhile; ?> + <?php endif; ?> - <? if (isset($this->request)): ?> + <?php if (isset($this->request)): ?> <h3><?=$this->transEsc('error_page_parameter_list_heading')?>:</h3> <pre><?=$this->escapeHtml(var_export($this->request->getParams(), true))?></pre> - <? endif; ?> -<? endif ?> + <?php endif; ?> +<?php endif ?> diff --git a/themes/root/templates/help/home.phtml b/themes/root/templates/help/home.phtml index 6752e0d66216cb61dca40ed218f7aa968a9aae4e..d76f592155bee4100ab6c1f927f03c77167c2cdc 100644 --- a/themes/root/templates/help/home.phtml +++ b/themes/root/templates/help/home.phtml @@ -1,9 +1,9 @@ -<? $this->headTitle($this->translate('Help')); ?> -<? if ($help = $this->helpText()->render($topic)): ?> - <? foreach ($this->helpText()->getWarnings() as $warning): ?> +<?php $this->headTitle($this->translate('Help')); ?> +<?php if ($help = $this->helpText()->render($topic)): ?> + <?php foreach ($this->helpText()->getWarnings() as $warning): ?> <p class="warning"><?=$this->transEsc($warning)?></p> - <? endforeach; ?> + <?php endforeach; ?> <?=$help?> -<? else: ?> +<?php else: ?> <p class="warning"><?=$this->transEsc('help_page_missing')?></p> -<? endif; ?> +<?php endif; ?> diff --git a/themes/root/templates/layout/help.phtml b/themes/root/templates/layout/help.phtml index 8a5cc8ac2314454a63b39d74f57f9772538b4a43..455791e4a957c0bc110657ca6f7d3aad55511762 100644 --- a/themes/root/templates/layout/help.phtml +++ b/themes/root/templates/layout/help.phtml @@ -1,7 +1,7 @@ <?=$this->doctype('XHTML1_TRANSITIONAL')?> <html xmlns="http://www.w3.org/1999/xhtml" lang="<?=$this->layout()->userLang ?>" xml:lang="en"> <head> - <?$this->headThemeResources()?> + <?php $this->headThemeResources(); ?> <?=$this->headMeta()?> <?=$this->headTitle() ?> <?=$this->headLink()->setStylesheet('help.css') ?> diff --git a/themes/root/templates/search/opensearch-describe.phtml b/themes/root/templates/search/opensearch-describe.phtml index 3dec0f13ba5c8fa7f0fab853b7fead91acfb34c8..3ac7818f6215ce7a31ac1b5cdf1cc951fd8ac246 100644 --- a/themes/root/templates/search/opensearch-describe.phtml +++ b/themes/root/templates/search/opensearch-describe.phtml @@ -1,5 +1,5 @@ -<? $searchBase = $this->serverUrl($this->url('search-results')); ?> -<? $suggestions = $this->serverUrl($this->url('search-suggest')); ?> +<?php $searchBase = $this->serverUrl($this->url('search-results')); ?> +<?php $suggestions = $this->serverUrl($this->url('search-suggest')); ?> <?='<'?>?xml version="1.0"?<?='>'?> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName><?=$this->escapeHtml($this->site->title)?></ShortName> diff --git a/themes/root/templates/searchapi/swagger.phtml b/themes/root/templates/searchapi/swagger.phtml index b50c6c49cf36b81c1c3a8b059b62a3dff86ab8e9..ae64273bc456dc6d78c95bdc8754786be0758bde 100644 --- a/themes/root/templates/searchapi/swagger.phtml +++ b/themes/root/templates/searchapi/swagger.phtml @@ -1,18 +1,18 @@ -<? // This template is a JSON fragment of a complete Swagger specification ?> -<? ob_start(); ?> +<?php // This template is a JSON fragment of a complete Swagger specification ?> +<?php ob_start(); ?> { "name": "field[]", "in": "query", - "description": "Fields to return.<? if ($this->defaultFields): ?> If not specified, a set of default fields is returned.\n\nThe default fields are:\n- <?=implode('\n- ', array_map('addslashes', $this->defaultFields)) ?><? endif; ?>", + "description": "Fields to return.<?php if ($this->defaultFields): ?> If not specified, a set of default fields is returned.\n\nThe default fields are:\n- <?=implode('\n- ', array_map('addslashes', $this->defaultFields)) ?><?php endif; ?>", "type": "array", "collectionFormat": "multi", "items": { "type": "string" } } -<? $field = ob_get_contents(); ?> -<? ob_end_clean(); ?> -<? ob_start(); ?> +<?php $field = ob_get_contents(); ?> +<?php ob_end_clean(); ?> +<?php ob_start(); ?> { "name": "prettyPrint", "in": "query", @@ -34,8 +34,8 @@ "description": "A callback that can be used for JSONP.", "type": "string" } -<? $commonFields = ob_get_contents(); ?> -<? ob_end_clean(); ?> +<?php $commonFields = ob_get_contents(); ?> +<?php ob_end_clean(); ?> { "paths": { "/record": { @@ -117,7 +117,7 @@ { "name": "facet[]", "in": "query", - "description": "Fields to facet on. Repeat for every field. <? if ($this->facetConfig): ?>At least the following fields are available for faceting (there may be additional facetable index fields not published here):\n- <?=implode('\n- ', array_map('addslashes', array_keys($this->facetConfig))) ?><? endif; ?>", + "description": "Fields to facet on. Repeat for every field. <?php if ($this->facetConfig): ?>At least the following fields are available for faceting (there may be additional facetable index fields not published here):\n- <?=implode('\n- ', array_map('addslashes', array_keys($this->facetConfig))) ?><?php endif; ?>", "type": "array", "collectionFormat": "multi", "items": { diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php index 79082aaddbfb37f9becd88997f8e44c4c37d6883..d175736cfc7abff1ea122edcefe462b5efa00f60 100644 --- a/themes/root/theme.config.php +++ b/themes/root/theme.config.php @@ -1,64 +1,124 @@ <?php -return array( +return [ 'extends' => false, - 'helpers' => array( - 'factories' => array( - 'accountcapabilities' => 'VuFind\View\Helper\Root\Factory::getAccountCapabilities', - 'addthis' => 'VuFind\View\Helper\Root\Factory::getAddThis', - 'alphabrowse' => 'VuFind\View\Helper\Root\Factory::getAlphaBrowse', - 'auth' => 'VuFind\View\Helper\Root\Factory::getAuth', - 'authornotes' => 'VuFind\View\Helper\Root\Factory::getAuthorNotes', - 'cart' => 'VuFind\View\Helper\Root\Factory::getCart', - 'citation' => 'VuFind\View\Helper\Root\Factory::getCitation', - 'datetime' => 'VuFind\View\Helper\Root\Factory::getDateTime', - 'displaylanguageoption' => 'VuFind\View\Helper\Root\Factory::getDisplayLanguageOption', - 'export' => 'VuFind\View\Helper\Root\Factory::getExport', - 'feedback' => 'VuFind\View\Helper\Root\Factory::getFeedback', - 'flashmessages' => 'VuFind\View\Helper\Root\Factory::getFlashmessages', - 'geocoords' => 'VuFind\View\Helper\Root\Factory::getGeoCoords', - 'googleanalytics' => 'VuFind\View\Helper\Root\Factory::getGoogleAnalytics', - 'helptext' => 'VuFind\View\Helper\Root\Factory::getHelpText', - 'historylabel' => 'VuFind\View\Helper\Root\Factory::getHistoryLabel', - 'ils' => 'VuFind\View\Helper\Root\Factory::getIls', - 'jstranslations' => 'VuFind\View\Helper\Root\Factory::getJsTranslations', - 'keepalive' => 'VuFind\View\Helper\Root\Factory::getKeepAlive', - 'permission' => 'VuFind\View\Helper\Root\Factory::getPermission', - 'proxyurl' => 'VuFind\View\Helper\Root\Factory::getProxyUrl', - 'openurl' => 'VuFind\View\Helper\Root\Factory::getOpenUrl', - 'piwik' => 'VuFind\View\Helper\Root\Factory::getPiwik', - 'recaptcha' => 'VuFind\View\Helper\Root\Factory::getRecaptcha', - 'record' => 'VuFind\View\Helper\Root\Factory::getRecord', - 'recorddataformatter' => 'VuFind\View\Helper\Root\RecordDataFormatterFactory', - 'recordlink' => 'VuFind\View\Helper\Root\Factory::getRecordLink', - 'related' => 'VuFind\View\Helper\Root\Factory::getRelated', - 'safemoneyformat' => 'VuFind\View\Helper\Root\Factory::getSafeMoneyFormat', - 'searchbox' => 'VuFind\View\Helper\Root\Factory::getSearchBox', - 'searchmemory' => 'VuFind\View\Helper\Root\Factory::getSearchMemory', - 'searchoptions' => 'VuFind\View\Helper\Root\Factory::getSearchOptions', - 'searchparams' => 'VuFind\View\Helper\Root\Factory::getSearchParams', - 'searchtabs' => 'VuFind\View\Helper\Root\Factory::getSearchTabs', - 'syndeticsplus' => 'VuFind\View\Helper\Root\Factory::getSyndeticsPlus', - 'systememail' => 'VuFind\View\Helper\Root\Factory::getSystemEmail', - 'userlist' => 'VuFind\View\Helper\Root\Factory::getUserList', - 'usertags' => 'VuFind\View\Helper\Root\Factory::getUserTags', - ), - 'invokables' => array( - 'addellipsis' => 'VuFind\View\Helper\Root\AddEllipsis', + 'helpers' => [ + 'factories' => [ + 'VuFind\View\Helper\Root\AccountCapabilities' => 'VuFind\View\Helper\Root\AccountCapabilitiesFactory', + 'VuFind\View\Helper\Root\AddEllipsis' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\AddThis' => 'VuFind\View\Helper\Root\AddThisFactory', + 'VuFind\View\Helper\Root\AlphaBrowse' => 'VuFind\View\Helper\Root\AlphaBrowseFactory', + 'VuFind\View\Helper\Root\Auth' => 'VuFind\View\Helper\Root\AuthFactory', + 'VuFind\View\Helper\Root\AuthorNotes' => 'VuFind\View\Helper\Root\ContentLoaderFactory', + 'VuFind\View\Helper\Root\Browse' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Cart' => 'VuFind\View\Helper\Root\CartFactory', + 'VuFind\View\Helper\Root\Citation' => 'VuFind\View\Helper\Root\CitationFactory', + 'VuFind\View\Helper\Root\Config' => 'VuFind\View\Helper\Root\ConfigFactory', + 'VuFind\View\Helper\Root\ContentBlock' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Context' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\CurrentPath' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\DateTime' => 'VuFind\View\Helper\Root\DateTimeFactory', + 'VuFind\View\Helper\Root\DisplayLanguageOption' => 'VuFind\View\Helper\Root\DisplayLanguageOptionFactory', + 'VuFind\View\Helper\Root\Export' => 'VuFind\View\Helper\Root\ExportFactory', + 'VuFind\View\Helper\Root\Feedback' => 'VuFind\View\Helper\Root\FeedbackFactory', + 'VuFind\View\Helper\Root\Flashmessages' => 'VuFind\View\Helper\Root\FlashmessagesFactory', + 'VuFind\View\Helper\Root\GeoCoords' => 'VuFind\View\Helper\Root\GeoCoordsFactory', + 'VuFind\View\Helper\Root\GoogleAnalytics' => 'VuFind\View\Helper\Root\GoogleAnalyticsFactory', + 'VuFind\View\Helper\Root\HelpText' => 'VuFind\View\Helper\Root\HelpTextFactory', + 'VuFind\View\Helper\Root\Highlight' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\HistoryLabel' => 'VuFind\View\Helper\Root\HistoryLabelFactory', + 'VuFind\View\Helper\Root\Ils' => 'VuFind\View\Helper\Root\IlsFactory', + 'VuFind\View\Helper\Root\JsTranslations' => 'VuFind\View\Helper\Root\JsTranslationsFactory', + 'VuFind\View\Helper\Root\KeepAlive' => 'VuFind\View\Helper\Root\KeepAliveFactory', + 'VuFind\View\Helper\Root\LocalizedNumber' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\OpenUrl' => 'VuFind\View\Helper\Root\OpenUrlFactory', + 'VuFind\View\Helper\Root\Permission' => 'VuFind\View\Helper\Root\PermissionFactory', + 'VuFind\View\Helper\Root\Piwik' => 'VuFind\View\Helper\Root\PiwikFactory', + 'VuFind\View\Helper\Root\Printms' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\ProxyUrl' => 'VuFind\View\Helper\Root\ProxyUrlFactory', + 'VuFind\View\Helper\Root\Recaptcha' => 'VuFind\View\Helper\Root\RecaptchaFactory', + 'VuFind\View\Helper\Root\Recommend' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Record' => 'VuFind\View\Helper\Root\RecordFactory', + 'VuFind\View\Helper\Root\RecordDataFormatter' => 'VuFind\View\Helper\Root\RecordDataFormatterFactory', + 'VuFind\View\Helper\Root\RecordLink' => 'VuFind\View\Helper\Root\RecordLinkFactory', + 'VuFind\View\Helper\Root\Relais' => 'VuFind\View\Helper\Root\RelaisFactory', + 'VuFind\View\Helper\Root\Related' => 'VuFind\View\Helper\Root\RelatedFactory', + 'VuFind\View\Helper\Root\RenderArray' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\ResultFeed' => 'VuFind\View\Helper\Root\ResultFeedFactory', + 'VuFind\View\Helper\Root\SafeMoneyFormat' => 'VuFind\View\Helper\Root\SafeMoneyFormatFactory', + 'VuFind\View\Helper\Root\SearchBox' => 'VuFind\View\Helper\Root\SearchBoxFactory', + 'VuFind\View\Helper\Root\SearchMemory' => 'VuFind\View\Helper\Root\SearchMemoryFactory', + 'VuFind\View\Helper\Root\SearchOptions' => 'VuFind\View\Helper\Root\SearchOptionsFactory', + 'VuFind\View\Helper\Root\SearchParams' => 'VuFind\View\Helper\Root\SearchParamsFactory', + 'VuFind\View\Helper\Root\SearchTabs' => 'VuFind\View\Helper\Root\SearchTabsFactory', + 'VuFind\View\Helper\Root\SortFacetList' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Summaries' => 'VuFind\View\Helper\Root\ContentLoaderFactory', + 'VuFind\View\Helper\Root\Summon' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\SyndeticsPlus' => 'VuFind\View\Helper\Root\SyndeticsPlusFactory', + 'VuFind\View\Helper\Root\SystemEmail' => 'VuFind\View\Helper\Root\SystemEmailFactory', + 'VuFind\View\Helper\Root\TransEsc' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Translate' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Truncate' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\UserList' => 'VuFind\View\Helper\Root\UserListFactory', + 'VuFind\View\Helper\Root\UserTags' => 'VuFind\View\Helper\Root\UserTagsFactory', + ], + 'aliases' => [ + 'accountCapabilities' => 'VuFind\View\Helper\Root\AccountCapabilities', + 'addEllipsis' => 'VuFind\View\Helper\Root\AddEllipsis', + 'addThis' => 'VuFind\View\Helper\Root\AddThis', + 'alphabrowse' => 'VuFind\View\Helper\Root\AlphaBrowse', + 'auth' => 'VuFind\View\Helper\Root\Auth', + 'authorNotes' => 'VuFind\View\Helper\Root\AuthorNotes', 'browse' => 'VuFind\View\Helper\Root\Browse', + 'cart' => 'VuFind\View\Helper\Root\Cart', + 'citation' => 'VuFind\View\Helper\Root\Citation', + 'config' => 'VuFind\View\Helper\Root\Config', + 'contentBlock' => 'VuFind\View\Helper\Root\ContentBlock', 'context' => 'VuFind\View\Helper\Root\Context', - 'currentpath' => 'VuFind\View\Helper\Root\CurrentPath', + 'currentPath' => 'VuFind\View\Helper\Root\CurrentPath', + 'dateTime' => 'VuFind\View\Helper\Root\DateTime', + 'displayLanguageOption' => 'VuFind\View\Helper\Root\DisplayLanguageOption', + 'export' => 'VuFind\View\Helper\Root\Export', + 'feedback' => 'VuFind\View\Helper\Root\Feedback', + 'flashmessages' => 'VuFind\View\Helper\Root\Flashmessages', + 'geocoords' => 'VuFind\View\Helper\Root\GeoCoords', + 'googleanalytics' => 'VuFind\View\Helper\Root\GoogleAnalytics', + 'helpText' => 'VuFind\View\Helper\Root\HelpText', 'highlight' => 'VuFind\View\Helper\Root\Highlight', - 'jqueryvalidation' => 'VuFind\View\Helper\Root\JqueryValidation', + 'historylabel' => 'VuFind\View\Helper\Root\HistoryLabel', + 'ils' => 'VuFind\View\Helper\Root\Ils', + 'jsTranslations' => 'VuFind\View\Helper\Root\JsTranslations', + 'keepAlive' => 'VuFind\View\Helper\Root\KeepAlive', 'localizedNumber' => 'VuFind\View\Helper\Root\LocalizedNumber', + 'openUrl' => 'VuFind\View\Helper\Root\OpenUrl', + 'permission' => 'VuFind\View\Helper\Root\Permission', + 'piwik' => 'VuFind\View\Helper\Root\Piwik', 'printms' => 'VuFind\View\Helper\Root\Printms', + 'proxyUrl' => 'VuFind\View\Helper\Root\ProxyUrl', + 'recaptcha' => 'VuFind\View\Helper\Root\Recaptcha', 'recommend' => 'VuFind\View\Helper\Root\Recommend', - 'renderarray' => 'VuFind\View\Helper\Root\RenderArray', + 'record' => 'VuFind\View\Helper\Root\Record', + 'recordDataFormatter' => 'VuFind\View\Helper\Root\RecordDataFormatter', + 'recordLink' => 'VuFind\View\Helper\Root\RecordLink', + 'relais' => 'VuFind\View\Helper\Root\Relais', + 'related' => 'VuFind\View\Helper\Root\Related', + 'renderArray' => 'VuFind\View\Helper\Root\RenderArray', 'resultfeed' => 'VuFind\View\Helper\Root\ResultFeed', - 'sortfacetlist' => 'VuFind\View\Helper\Root\SortFacetList', + 'safeMoneyFormat' => 'VuFind\View\Helper\Root\SafeMoneyFormat', + 'searchMemory' => 'VuFind\View\Helper\Root\SearchMemory', + 'searchOptions' => 'VuFind\View\Helper\Root\SearchOptions', + 'searchParams' => 'VuFind\View\Helper\Root\SearchParams', + 'searchTabs' => 'VuFind\View\Helper\Root\SearchTabs', + 'searchbox' => 'VuFind\View\Helper\Root\SearchBox', + 'sortFacetList' => 'VuFind\View\Helper\Root\SortFacetList', + 'summaries' => 'VuFind\View\Helper\Root\Summaries', 'summon' => 'VuFind\View\Helper\Root\Summon', - 'transesc' => 'VuFind\View\Helper\Root\TransEsc', + 'syndeticsPlus' => 'VuFind\View\Helper\Root\SyndeticsPlus', + 'systemEmail' => 'VuFind\View\Helper\Root\SystemEmail', + 'transEsc' => 'VuFind\View\Helper\Root\TransEsc', 'translate' => 'VuFind\View\Helper\Root\Translate', 'truncate' => 'VuFind\View\Helper\Root\Truncate', - ) - ), -); + 'userlist' => 'VuFind\View\Helper\Root\UserList', + 'usertags' => 'VuFind\View\Helper\Root\UserTags', + ] + ], +]; diff --git a/themes/sandal/less/common.less b/themes/sandal/less/common.less index 0989391bb57428570ccf07d80bb33bcb0cf1586d..8d920092ebf8e90b49916d0af83c8b102c8605c3 100644 --- a/themes/sandal/less/common.less +++ b/themes/sandal/less/common.less @@ -3,12 +3,13 @@ @border-radius-large: 0; // modal /* ------ Common element changes ------ */ -h1, h2, h3, h4 { - margin: 0; - margin-bottom: 1rem; - - .help-block & { margin: 0; } +.help-block { + h1, + h2, + h3, + h4 { margin: 0; } } + .highlight, mark { background-color: @highlighter-color; } table { font-size: @reduced-font-size; } @@ -37,6 +38,9 @@ table { font-size: @reduced-font-size; } } .btn-link { color: @brand-primary; } +.media, +.media-body { overflow: initial; } + .form-control { box-shadow: none; } .form-control:hover, .form-control:focus { border-color: @brand-primary; } @@ -83,8 +87,8 @@ select.form-control { .alert-success, .alert-success:hover { color: @dark-green; - background-color: @light-green; - a { color: @white; } + background-color: @washed-green; + a { color: @green; } } .alert-danger, .alert-danger:hover { diff --git a/themes/sandal/less/sandal.less b/themes/sandal/less/sandal.less index d04ad964146ccd346abd9011a29d7a1321ccb663..a3ee3044f57f7d745b85ff01a14677b6ebf27b16 100644 --- a/themes/sandal/less/sandal.less +++ b/themes/sandal/less/sandal.less @@ -162,6 +162,8 @@ header { font-size: @reduced-font-size; background-color: @light-gray; + h1,h2,h3,h4 { margin-top: 0; } + .facet-group { margin-left: 1rem; margin-right: 1rem; @@ -206,10 +208,16 @@ header { .facet .badge { max-height: 15px; margin-top: 5px; - line-height: .8; - background-color: @sidebar-item-badge; } } + +.facet .badge { + flex-shrink: 0; + max-height: 19px; + line-height: .8; + background-color: @sidebar-item-badge; +} + .sidebar.left, .sidebar.right { padding-left: 0; @@ -218,7 +226,11 @@ header { .sidebar .checkbox-filter, .sidebar > h4 { margin-left: 1rem; } -.jstree-facet .main .fa-check { margin-top: 3px; } +.jstree-facet { + .main .fa-check { margin-top: 3px; } + .jstree-icon::before { margin-top: 2px; } +} + @media (min-width: 768px) and (max-width: 991px) { .sidebar .facet { flex-flow: wrap; diff --git a/themes/sandal/less/search.less b/themes/sandal/less/search.less index 120af8c066cbf25611f29a5bd6f0c2d9e2b86eff..42dc30cc666894335bc7e0e0ecbd7d8bf9efce35 100644 --- a/themes/sandal/less/search.less +++ b/themes/sandal/less/search.less @@ -17,9 +17,8 @@ .result { margin-left: -1rem; margin-right: -1rem; - padding-top: 1rem; - padding-left: 1rem; - padding-bottom: 1rem; + padding: 1rem; + padding-right: 0; border-bottom: 1px solid @moon-gray; font-size: @reduced-font-size; @@ -112,7 +111,6 @@ .grid-result .grid-checkbox label { width: 100%; } @media (min-width: 768px) { - .result { padding-right: 1rem; } .result-body { width: 75%; } .result-links { width: 25%; @@ -259,8 +257,12 @@ padding-top: 1rem; padding-bottom: 1rem; } -.combined-search-container { - .full-width(); - - .result { margin-right: 0; } +.template-dir-combined.template-name-results { + .bulkActionButtons { + max-width: 1170px; + margin: auto; + } + .main .container { + width: 100%; + } } diff --git a/themes/sandal/scss/colors.scss b/themes/sandal/scss/colors.scss index d942e58b6bda013b2455c0a901592e93fd175383..59d6aa824cd02cdf4950894520ceebacfad013d9 100644 --- a/themes/sandal/scss/colors.scss +++ b/themes/sandal/scss/colors.scss @@ -45,5 +45,6 @@ $input-border-radius: .25rem !default; $autocomplete-hover-bg: $header-light !default; $dropdown-link-hover-bg: $header-light !default; $dropdown-link-hover-color: $black !default; +$list-group-active-bg: $brand-secondary !default; $table-bg-hover: $header-light !default; $pagination-color: $header-bg !default; diff --git a/themes/sandal/scss/common.scss b/themes/sandal/scss/common.scss index 3b81087eca65addd58c73787f5070b8b4503a906..30c1391f0e593f709b198a00ae30a8ae69b6268a 100644 --- a/themes/sandal/scss/common.scss +++ b/themes/sandal/scss/common.scss @@ -37,6 +37,9 @@ table { font-size: $reduced-font-size; } } .btn-link { color: $brand-primary; } +.media, +.media-body { overflow: initial; } + .form-control { box-shadow: none; } .form-control:hover, .form-control:focus { border-color: $brand-primary; } diff --git a/themes/sandal/scss/sandal.scss b/themes/sandal/scss/sandal.scss index 6a3086916aca544af7a19e6b43fe027e05c1871b..5d34610ba2c5608a3d52d84d2a10f02b8d4c906d 100644 --- a/themes/sandal/scss/sandal.scss +++ b/themes/sandal/scss/sandal.scss @@ -165,6 +165,8 @@ header { .facet-group { margin-left: 1rem; margin-right: 1rem; + background-color: $sidebar-item-bg; + color: $sidebar-item-color; } /* Collapsed arrows */ .facet-group .title { @@ -182,10 +184,6 @@ header { .active-filters .title::after, #advSearchForm & .title:after { content: none; } // No arrow on active filter title - .facet { - background-color: $sidebar-item-bg; - color: $sidebar-item-color; - } a.facet:hover, .facet.checkbox:hover { background-color: $sidebar-item-hover-bg; } @@ -193,6 +191,7 @@ header { .facet.active:hover, .active-filters .facet, .active-filters .facet:hover, + .active .jstree-anchor, .jstree-facet .jstree-container-ul > .active { border-color: $sidebar-active-bg; background-color: $sidebar-active-bg; @@ -219,7 +218,11 @@ header { .sidebar .checkbox-filter, .sidebar > h4 { margin-left: 1rem; } -.jstree-facet .main .fa-check { margin-top: 3px; } +.jstree-facet { + .main .fa-check { margin-top: 3px; } + .jstree-icon::before { margin-top: 2px; } +} + @media (min-width: 768px) and (max-width: 991px) { .sidebar .facet { flex-flow: wrap; diff --git a/themes/sandal/scss/search.scss b/themes/sandal/scss/search.scss index a3ad03d24f024a0ec964becd40c978fc20908c03..0ad5d19635c0815901596bbb4642228801156d92 100644 --- a/themes/sandal/scss/search.scss +++ b/themes/sandal/scss/search.scss @@ -151,8 +151,8 @@ @include full-width(); @include clearfix(); position: relative; - padding: 6rem 3rem; - padding-left: calc(50vw - 50%); // Align searchbox with container + padding: 1rem; + padding-bottom: 2rem; background-color: $header-bg; background-image: url('../../sandal/images/forest.jpg'); background-repeat: no-repeat; @@ -164,12 +164,30 @@ color: $white; text-shadow: 0 1px 0 $black; } + @media (min-width: 768px) { + height: 300px; + padding: 7rem 3rem; + padding-left: calc(50vw - 50% + .5rem); // Align searchbox with container + } } -.searchHomeContent::before { +.searchHomeContent::after { position: absolute; - top: 0; + bottom: 0; left: 0; - background-color: rgba(255,255,255,.5); + display: block; + width: 100%; + padding: .25rem .5rem; + padding-left: 1rem; + font-size: 10px; + color: #fff; + background-color: rgba(0,0,0,.3); + content: "Photo by Paul Gilmore on Unsplash"; + @media (min-width: 768px) { + left: auto; + right: calc(50% - 50vw + .5rem); + width: auto; + padding-left: .5rem; + } } .template-dir-search.template-name-home .breadcrumbs { border: 0; } .home-facet h2 { margin-top: 1rem; } @@ -177,7 +195,7 @@ .search { border: 0; } #searchForm { padding-top: .5rem; - padding-bottom: calc($search-tab-height + 1.5rem); + padding-bottom: calc(#{$search-tab-height} + 1.5rem); } .nav.searchbox, #searchForm { @@ -187,7 +205,13 @@ .nav-tabs { position: absolute; bottom: 0; + margin-top: .5rem; border: 0; + white-space: nowrap; + } + .nav-tabs > li { + float: none; + display: inline-block; } .nav-tabs a, .nav-tabs a:hover { @@ -195,8 +219,8 @@ border-bottom: 3px solid transparent; } .nav-tabs a { - height: calc($search-tab-height + .5rem); - line-height: calc($search-tab-height + .5rem); + height: calc(#{$search-tab-height} + .5rem); + line-height: calc(#{$search-tab-height} + .5rem); padding: 0 1rem; border-radius: 0; background-color: transparent; diff --git a/themes/sandal/scss/variables.scss b/themes/sandal/scss/variables.scss index 228134c356b6aa42fec64b2021fc885d4be76f3c..8e18e8bbbd73663bc9fa2cb937470343265b8f3c 100644 --- a/themes/sandal/scss/variables.scss +++ b/themes/sandal/scss/variables.scss @@ -13,6 +13,10 @@ $result-sm-icon-size: 1.5rem !default; @mixin full-width(){ // Break out of the container margin-right: calc(50% - 50vw + .5rem); // .5rem seems to be the margin of error in Chrome margin-left: calc(50% - 50vw + .5rem); + @media (min-width: 992px) { + margin-left: calc(50% - 50vw); + margin-right: calc(50% - 50vw); + } } @mixin background-image($file){ background-image: url("#{$img-path}#{$file}"); diff --git a/themes/sandal/theme.config.php b/themes/sandal/theme.config.php index 2bb286a457e59310d49c43b0861688cc8eada802..9dd3b6e4e4cee75d9929969cac5487b49bb78041 100644 --- a/themes/sandal/theme.config.php +++ b/themes/sandal/theme.config.php @@ -1,4 +1,4 @@ <?php -return array( +return [ 'extends' => 'bootstrap3', -); +]; diff --git a/util/commit.php b/util/commit.php index c5ff187b38b09a82cd0b7f7f5c19d26c4b97181f..35fb75d6bf83041532a96354dc49c236018d2761 100644 --- a/util/commit.php +++ b/util/commit.php @@ -2,7 +2,7 @@ /** * Command-line tool to commit the Solr index. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/util/createHierarchyTrees.php b/util/createHierarchyTrees.php index a9fdb0596ccc6478cb5a23b49339dd9eaca691c3..0ec9eb7b907b90445d19bc89e68ce3d580d548ea 100644 --- a/util/createHierarchyTrees.php +++ b/util/createHierarchyTrees.php @@ -8,7 +8,7 @@ * * -!!!!-This script is specifically for trees built for JSTree from Solr.-!!!!- * - * PHP version 5 + * PHP version 7 * * Copyright (C) National Library of Ireland 2012. * diff --git a/util/cssBuilder.php b/util/cssBuilder.php index b2e0e26a2d2e1d887191dc903cd38a7187461b53..cc82ccd0acd6ed840197d4db7362bb12833c7337 100644 --- a/util/cssBuilder.php +++ b/util/cssBuilder.php @@ -2,7 +2,7 @@ /** * Compile CSS files from LESS. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2014. * diff --git a/util/dedupe.php b/util/dedupe.php index b7de0743fc1af1db41f3a6aea445618bb1427717..d5cc104cca3d7e4fe3281188eb1bbd68ba6de88d 100644 --- a/util/dedupe.php +++ b/util/dedupe.php @@ -4,7 +4,7 @@ * the alphabetical browse database generator, since Windows sort does not * support deduplication. Assumed presorted * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/util/deletes.php b/util/deletes.php index 9b68a09479ad43caf66796714c2613cf805a8389..b29656e32ec9b2546dad995bed0fda53d8de3b72 100644 --- a/util/deletes.php +++ b/util/deletes.php @@ -2,7 +2,7 @@ /** * Command-line tool to batch-delete records from the Solr index. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * diff --git a/util/expire_searches.php b/util/expire_searches.php index c1275ff59cb4189c43190c9a774544ab02e3c293..2ad488392c48c935362589ef2164314ffb45ca0a 100644 --- a/util/expire_searches.php +++ b/util/expire_searches.php @@ -2,7 +2,7 @@ /** * Command-line tool to clear unwanted entries from search history database table. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/util/expire_sessions.php b/util/expire_sessions.php index 92a46f27a81fa43296846c3edf8d557c60c70d23..75968ec461feda2bcdb832a8e7a77ac341381cbd 100644 --- a/util/expire_sessions.php +++ b/util/expire_sessions.php @@ -2,7 +2,7 @@ /** * Command-line tool to clear unwanted entries from session database table. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2010. * diff --git a/util/index_reserves.php b/util/index_reserves.php index 546ad8c5f0704b74ec5ee3085b11f4f1f657e5d4..6d1d10510678809f4b1604c4fa7f8837c27fb31d 100644 --- a/util/index_reserves.php +++ b/util/index_reserves.php @@ -2,7 +2,7 @@ /** * Command-line tool to index reserves records to the Solr index. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/util/optimize.php b/util/optimize.php index 3436f6d263f307492494d3f37bdc4ee0aaa8f7f1..1fad42d30a0678b0b159c28f315c02da9fa7051a 100644 --- a/util/optimize.php +++ b/util/optimize.php @@ -2,7 +2,7 @@ /** * Command-line tool to optimize the Solr index. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/util/sitemap.php b/util/sitemap.php index 756192e965ebee93b82b0fa13abf9131418d3ad1..a2976724b29fad2c7dbbcea8f0d81bc66b277e05 100644 --- a/util/sitemap.php +++ b/util/sitemap.php @@ -2,7 +2,7 @@ /** * Command-line tool to generate sitemaps based on Solr index contents. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2009. * diff --git a/util/suppressed.php b/util/suppressed.php index 65239ad84a75e1eb24c48b0ec3f31010f72cbac4..52c6b417a0b0999caa30f1b2aff5120c1abccfae 100644 --- a/util/suppressed.php +++ b/util/suppressed.php @@ -2,7 +2,7 @@ /** * Command-line tool to delete suppressed records from the index. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. *